///|
priv enum ValueDiagnostics {
TextFindings
JsonFindings
} derive(Eq)
///|
priv struct ValueOptions {
target : @smithy.ShapeId
file : String
diagnostics : ValueDiagnostics
config : @validation.Config
}
///|
fn value_problem(
opts : ValueOptions,
code : String,
message : String,
status : Int32,
offset~ : Int32? = None,
source~ : String? = None,
) -> Int32 {
let source = source.unwrap_or(opts.file)
if opts.diagnostics == JsonFindings {
let finding : Map[String, Json] = {
"code": Json::string(code),
"severity": Json::string("error"),
"message": Json::string(message),
"shape": Json::string(opts.target.to_string()),
"source": Json::string(source),
"path": Json::array([]),
"pointer": Json::string(""),
}
if offset is Some(offset) {
finding["offset"] = Json::number(offset.to_double())
}
let result = Json::object({
"valid": Json::boolean(false),
"complete": Json::boolean(false),
"findings": Json::array([Json::object(finding)]),
})
println(result.stringify())
} else {
eprintln("\{source}: error[\{code}]: \{message}")
}
status
}
///|
fn run_value_validation(
opts : ValueOptions,
assembled : @smithy.LoadResult,
) -> Int32 {
let errors = assembled
.validation_events()
.filter(event => event.severity == "ERROR" || event.severity == "DANGER")
if !errors.is_empty() {
if opts.diagnostics == JsonFindings {
let findings = errors.map(event => {
let finding : Map[String, Json] = {
"code": Json::string(event.id),
"severity": Json::string(event.severity),
"message": Json::string(event.message),
"shape": event.shape
.map(id => Json::string(id.to_string()))
.unwrap_or(Json::null()),
"source": event.source
.map(source => Json::string(source.file))
.unwrap_or(Json::null()),
"path": Json::array([]),
"pointer": Json::string(""),
}
Json::object(finding)
})
println(
Json::object({
"valid": Json::boolean(false),
"complete": Json::boolean(false),
"findings": Json::array(findings),
}).stringify(),
)
} else {
for event in errors {
eprintln(validation_event_text(event))
}
}
return 1
}
guard assembled.model is Some(model) else {
return value_problem(
opts,
"InvalidModel",
assembled.diagnostics
.map(d => d.source.file + ": " + d.message)
.join("\n"),
1,
source=assembled.diagnostics.get(0).map(d => d.source.file),
)
}
let prepared = @validation.PreparedValidator::prepare(
model,
opts.target,
config=opts.config,
) catch {
@validation.InvalidTarget(id, message) =>
return value_problem(
opts,
"InvalidTarget",
id.to_string() + ": " + message,
2,
source=Some("--shape"),
)
@validation.InvalidConfiguration(message) =>
return value_problem(opts, "InvalidConfiguration", message, 2)
@validation.InvalidModel(id, message) =>
return value_problem(
opts,
"InvalidModel",
id.to_string() + ": " + message,
1,
source=model.shapes.get(id).map(s => s.source.file),
)
@validation.UnsupportedConstraint(id, message) =>
return value_problem(
opts,
"UnsupportedConstraint",
id.to_string() + ": " + message,
2,
source=model.shapes.get(id).map(s => s.source.file),
)
}
let text = try {
if opts.file == "-" {
read_stdin()
} else {
@fs.read_file_to_string(opts.file)
}
} catch {
_ =>
return value_problem(
opts, "InputRead", "I could not read the JSON value input.", 1,
)
}
let value = @validation.parse_json_value(text) catch {
@validation.InvalidJson(offset, message) =>
return value_problem(opts, "InvalidJson", message, 1, offset=Some(offset))
@validation.DuplicateJsonKey(offset) =>
return value_problem(
opts,
"DuplicateJsonKey",
"I expected unique JSON object member names, but found a duplicate.",
1,
offset=Some(offset),
)
@validation.JsonInputLimit(offset, message) =>
return value_problem(
opts,
"JsonInputLimit",
message,
1,
offset=Some(offset),
)
}
let result = prepared.validate_json(value)
if opts.diagnostics == JsonFindings {
println(@json.to_json(result).stringify())
} else {
for finding in result.findings {
eprintln(
"\{opts.file}\{finding.path.pointer()}: error[\{finding.code.to_string()}] \{finding.shape}: \{finding.message}",
)
}
}
if result.is_valid() {
0
} else {
1
}
}