///|
fn string_array_json(items : ArrayView[String]) -> Json {
Json::array(items.map(item => Json::string(item)))
}
///|
fn message_json(message : SarifMessage) -> Json {
let obj : Map[String, Json] = Map([])
obj["text"] = Json::string(message.text)
if message.markdown is Some(markdown) {
obj["markdown"] = Json::string(markdown)
}
Json::object(obj)
}
///|
fn artifact_location_json(location : ArtifactLocation) -> Json {
let obj : Map[String, Json] = Map([])
obj["uri"] = Json::string(location.uri)
if location.uri_base_id is Some(value) {
obj["uriBaseId"] = Json::string(value)
}
if location.index is Some(value) {
obj["index"] = Json::number(value.to_double())
}
Json::object(obj)
}
///|
fn region_json(region : Region) -> Json {
let obj : Map[String, Json] = Map([])
obj["startLine"] = Json::number(region.start_line.to_double())
obj["startColumn"] = Json::number(region.start_column.to_double())
if region.end_line is Some(value) {
obj["endLine"] = Json::number(value.to_double())
}
if region.end_column is Some(value) {
obj["endColumn"] = Json::number(value.to_double())
}
if region.char_offset is Some(value) {
obj["charOffset"] = Json::number(value.to_double())
}
if region.char_length is Some(value) {
obj["charLength"] = Json::number(value.to_double())
}
Json::object(obj)
}
///|
fn physical_location_json(location : PhysicalLocation) -> Json {
let obj : Map[String, Json] = Map([])
obj["artifactLocation"] = artifact_location_json(location.artifact_location)
if location.region is Some(region) {
obj["region"] = region_json(region)
}
Json::object(obj)
}
///|
fn location_json(location : SarifLocation) -> Json {
let obj : Map[String, Json] = Map([])
obj["physicalLocation"] = physical_location_json(location.physical_location)
if location.message is Some(message) {
obj["message"] = message_json(message)
}
if location.logical_name is Some(name) {
let logical : Map[String, Json] = Map([])
logical["name"] = Json::string(name)
obj["logicalLocations"] = Json::array([Json::object(logical)])
}
Json::object(obj)
}
///|
fn artifact_json(artifact : Artifact) -> Json {
let obj : Map[String, Json] = Map([])
obj["location"] = artifact_location_json(artifact.location)
if artifact.length is Some(value) {
obj["length"] = Json::number(value.to_double())
}
if artifact.mime_type is Some(value) {
obj["mimeType"] = Json::string(value)
}
if artifact.roles.length() > 0 {
obj["roles"] = string_array_json(artifact.roles)
}
Json::object(obj)
}
///|
fn rule_json(rule : SarifRule) -> Json {
let obj : Map[String, Json] = Map([])
obj["id"] = Json::string(rule.id)
if rule.name is Some(name) {
obj["name"] = Json::string(name)
}
if rule.short_description is Some(message) {
obj["shortDescription"] = message_json(message)
}
if rule.full_description is Some(message) {
obj["fullDescription"] = message_json(message)
}
let config : Map[String, Json] = Map([])
config["level"] = Json::string(rule.default_level)
obj["defaultConfiguration"] = Json::object(config)
if rule.help_uri is Some(uri) {
obj["helpUri"] = Json::string(uri)
}
if rule.tags.length() > 0 {
let props : Map[String, Json] = Map([])
props["tags"] = string_array_json(rule.tags)
obj["properties"] = Json::object(props)
}
Json::object(obj)
}
///|
fn tool_json(tool : ToolDriver) -> Json {
let driver : Map[String, Json] = Map([])
driver["name"] = Json::string(tool.name)
if tool.version is Some(value) {
driver["version"] = Json::string(value)
}
if tool.information_uri is Some(value) {
driver["informationUri"] = Json::string(value)
}
if tool.rules.length() > 0 {
driver["rules"] = Json::array(tool.rules.map(rule => rule_json(rule)))
}
let obj : Map[String, Json] = Map([])
obj["driver"] = Json::object(driver)
Json::object(obj)
}
///|
fn fingerprints_json(items : ArrayView[Fingerprint]) -> Json {
let obj : Map[String, Json] = Map([])
for item in items {
obj[item.key] = Json::string(item.value)
}
Json::object(obj)
}
///|
fn properties_json(items : ArrayView[SarifProperty]) -> Json {
let obj : Map[String, Json] = Map([])
for item in items {
obj[item.key] = Json::string(item.value)
}
Json::object(obj)
}
///|
fn result_json(result : SarifResult) -> Json {
let obj : Map[String, Json] = Map([])
obj["ruleId"] = Json::string(result.rule_id)
obj["level"] = Json::string(normalize_level(result.level))
obj["kind"] = Json::string(result.kind)
obj["message"] = message_json(result.message)
if result.locations.length() > 0 {
obj["locations"] = Json::array(
result.locations.map(location => location_json(location)),
)
}
if result.fingerprints.length() > 0 {
obj["partialFingerprints"] = fingerprints_json(result.fingerprints)
}
if result.properties.length() > 0 {
obj["properties"] = properties_json(result.properties)
}
if result.baseline_state is Some(value) {
obj["baselineState"] = Json::string(value)
}
Json::object(obj)
}
///|
fn invocation_json(invocation : SarifInvocation) -> Json {
let obj : Map[String, Json] = Map([])
if invocation.command_line is Some(value) {
obj["commandLine"] = Json::string(value)
}
if invocation.working_directory is Some(value) {
obj["workingDirectory"] = artifact_location_json(
ArtifactLocation::ArtifactLocation(value),
)
}
obj["executionSuccessful"] = Json::boolean(invocation.execution_successful)
Json::object(obj)
}
///|
fn run_json(run : SarifRun) -> Json {
let obj : Map[String, Json] = Map([])
obj["tool"] = tool_json(run.tool)
obj["results"] = Json::array(run.results.map(result => result_json(result)))
if run.artifacts.length() > 0 {
obj["artifacts"] = Json::array(
run.artifacts.map(artifact => artifact_json(artifact)),
)
}
if run.invocations.length() > 0 {
obj["invocations"] = Json::array(
run.invocations.map(item => invocation_json(item)),
)
}
if run.automation_id is Some(value) {
let details : Map[String, Json] = Map([])
details["id"] = Json::string(value)
obj["automationDetails"] = Json::object(details)
}
Json::object(obj)
}
///|
pub fn SarifLog::to_json(self : SarifLog) -> Json {
let obj : Map[String, Json] = Map([])
obj["version"] = Json::string(self.version)
if self.schema is Some(schema) {
obj["$schema"] = Json::string(schema)
}
obj["runs"] = Json::array(self.runs.map(run => run_json(run)))
Json::object(obj)
}
///|
pub fn SarifLog::to_json_string(self : SarifLog, indent? : Int = 0) -> String {
self.to_json().stringify(indent~)
}
///|
fn json_get(obj : Map[String, Json], key : String) -> Json? {
obj.get(key)
}
///|
fn required(obj : Map[String, Json], key : String) -> Result[Json, SarifError] {
match json_get(obj, key) {
Some(value) => Ok(value)
None => Err(MissingField(field=key))
}
}
///|
fn decode_string(
obj : Map[String, Json],
key : String,
) -> Result[String, SarifError] {
match required(obj, key) {
Ok(String(value)) => Ok(value)
Ok(_) => Err(JsonDecode(path=key, expected="string"))
Err(err) => Err(err)
}
}
///|
fn decode_optional_string(
obj : Map[String, Json],
key : String,
) -> Result[String?, SarifError] {
match json_get(obj, key) {
None | Some(Null) => Ok(None)
Some(String(value)) => Ok(Some(value))
Some(_) => Err(JsonDecode(path=key, expected="string or null"))
}
}
///|
fn decode_int_optional(
obj : Map[String, Json],
key : String,
) -> Result[Int?, SarifError] {
match json_get(obj, key) {
None | Some(Null) => Ok(None)
Some(Number(value, ..)) => Ok(Some(value.to_int()))
Some(_) => Err(JsonDecode(path=key, expected="integer or null"))
}
}
///|
fn decode_message(
json : Json,
path : String,
) -> Result[SarifMessage, SarifError] {
match json {
Object(obj) => {
let text = match decode_string(obj, "text") {
Ok(value) => value
Err(err) => return Err(err)
}
let markdown = match decode_optional_string(obj, "markdown") {
Ok(value) => value
Err(_) => None
}
Ok({ text, markdown })
}
_ => Err(JsonDecode(path~, expected="message object"))
}
}
///|
fn decode_artifact_location(
json : Json,
path : String,
) -> Result[ArtifactLocation, SarifError] {
match json {
Object(obj) => {
let uri = match decode_string(obj, "uri") {
Ok(value) => value
Err(err) => return Err(err)
}
let uri_base_id = match decode_optional_string(obj, "uriBaseId") {
Ok(value) => value
Err(err) => return Err(err)
}
let index = match decode_int_optional(obj, "index") {
Ok(value) => value
Err(err) => return Err(err)
}
Ok({ uri, uri_base_id, index })
}
_ => Err(JsonDecode(path~, expected="artifactLocation object"))
}
}
///|
fn decode_region(json : Json, path : String) -> Result[Region, SarifError] {
match json {
Object(obj) => {
let start_line = match required(obj, "startLine") {
Ok(Number(value, ..)) => value.to_int()
Ok(_) => return Err(JsonDecode(path="startLine", expected="integer"))
Err(err) => return Err(err)
}
let start_column = match json_get(obj, "startColumn") {
Some(Number(value, ..)) => value.to_int()
None => 1
Some(_) =>
return Err(JsonDecode(path="startColumn", expected="integer"))
}
let end_line = match decode_int_optional(obj, "endLine") {
Ok(value) => value
Err(err) => return Err(err)
}
let end_column = match decode_int_optional(obj, "endColumn") {
Ok(value) => value
Err(err) => return Err(err)
}
Ok({
start_line,
start_column,
end_line,
end_column,
char_offset: None,
char_length: None,
})
}
_ => Err(JsonDecode(path~, expected="region object"))
}
}
///|
fn decode_location(
json : Json,
path : String,
) -> Result[SarifLocation, SarifError] {
match json {
Object(obj) => {
let physical = match required(obj, "physicalLocation") {
Ok(Object(phys)) => {
let artifact = match required(phys, "artifactLocation") {
Ok(value) =>
match
decode_artifact_location(
value,
"\{path}.physicalLocation.artifactLocation",
) {
Ok(decoded) => decoded
Err(err) => return Err(err)
}
Err(err) => return Err(err)
}
let region = match json_get(phys, "region") {
Some(value) =>
match decode_region(value, "\{path}.physicalLocation.region") {
Ok(decoded) => Some(decoded)
Err(err) => return Err(err)
}
None => None
}
{ artifact_location: artifact, region }
}
Ok(_) =>
return Err(
JsonDecode(path="\{path}.physicalLocation", expected="object"),
)
Err(err) => return Err(err)
}
let message = match json_get(obj, "message") {
Some(value) =>
match decode_message(value, "\{path}.message") {
Ok(decoded) => Some(decoded)
Err(err) => return Err(err)
}
None => None
}
Ok({ physical_location: physical, message, logical_name: None })
}
_ => Err(JsonDecode(path~, expected="location object"))
}
}
///|
fn decode_locations(
obj : Map[String, Json],
path : String,
) -> Result[Array[SarifLocation], SarifError] {
match json_get(obj, "locations") {
None => Ok([])
Some(Array(items)) => {
let out : Array[SarifLocation] = []
for i, item in items {
match decode_location(item, "\{path}.locations[\{i}]") {
Ok(value) => out.push(value)
Err(err) => return Err(err)
}
}
Ok(out)
}
Some(_) => Err(JsonDecode(path="\{path}.locations", expected="array"))
}
}
///|
fn decode_result(json : Json, path : String) -> Result[SarifResult, SarifError] {
match json {
Object(obj) => {
let rule_id = match decode_string(obj, "ruleId") {
Ok(value) => value
Err(err) => return Err(err)
}
let level = match decode_optional_string(obj, "level") {
Ok(Some(value)) => value
Ok(None) => "warning"
Err(err) => return Err(err)
}
let kind = match decode_optional_string(obj, "kind") {
Ok(Some(value)) => value
Ok(None) => "fail"
Err(err) => return Err(err)
}
let message = match required(obj, "message") {
Ok(value) =>
match decode_message(value, "\{path}.message") {
Ok(decoded) => decoded
Err(err) => return Err(err)
}
Err(err) => return Err(err)
}
let locations = match decode_locations(obj, path) {
Ok(value) => value
Err(err) => return Err(err)
}
let baseline_state = match decode_optional_string(obj, "baselineState") {
Ok(value) => value
Err(err) => return Err(err)
}
Ok({
rule_id,
level,
kind,
message,
locations,
fingerprints: [],
properties: [],
baseline_state,
})
}
_ => Err(JsonDecode(path~, expected="result object"))
}
}
///|
fn decode_tool(json : Json, path : String) -> Result[ToolDriver, SarifError] {
match json {
Object(obj) =>
match required(obj, "driver") {
Ok(Object(driver)) => {
let name = match decode_string(driver, "name") {
Ok(value) => value
Err(err) => return Err(err)
}
let version = match decode_optional_string(driver, "version") {
Ok(value) => value
Err(err) => return Err(err)
}
let information_uri = match
decode_optional_string(driver, "informationUri") {
Ok(value) => value
Err(err) => return Err(err)
}
Ok({ name, version, information_uri, rules: [] })
}
Ok(_) => Err(JsonDecode(path="\{path}.driver", expected="object"))
Err(err) => Err(err)
}
_ => Err(JsonDecode(path~, expected="tool object"))
}
}
///|
fn decode_run(json : Json, path : String) -> Result[SarifRun, SarifError] {
match json {
Object(obj) => {
let tool = match required(obj, "tool") {
Ok(value) =>
match decode_tool(value, "\{path}.tool") {
Ok(decoded) => decoded
Err(err) => return Err(err)
}
Err(err) => return Err(err)
}
let results = match json_get(obj, "results") {
None => []
Some(Array(items)) => {
let out : Array[SarifResult] = []
for i, item in items {
match decode_result(item, "\{path}.results[\{i}]") {
Ok(value) => out.push(value)
Err(err) => return Err(err)
}
}
out
}
Some(_) =>
return Err(JsonDecode(path="\{path}.results", expected="array"))
}
Ok({ tool, results, artifacts: [], invocations: [], automation_id: None })
}
_ => Err(JsonDecode(path~, expected="run object"))
}
}
///|
pub fn SarifLog::from_json(json : Json) -> Result[SarifLog, SarifError] {
match json {
Object(obj) => {
let version = match decode_string(obj, "version") {
Ok(value) => value
Err(err) => return Err(err)
}
let schema = match decode_optional_string(obj, "$schema") {
Ok(value) => value
Err(err) => return Err(err)
}
let runs = match required(obj, "runs") {
Ok(Array(items)) => {
let out : Array[SarifRun] = []
for i, item in items {
match decode_run(item, "runs[\{i}]") {
Ok(value) => out.push(value)
Err(err) => return Err(err)
}
}
out
}
Ok(_) => return Err(JsonDecode(path="runs", expected="array"))
Err(err) => return Err(err)
}
Ok({ version, schema, runs })
}
_ => Err(JsonDecode(path="root", expected="object"))
}
}
///|
pub fn SarifLog::from_json_string(
text : StringView,
) -> Result[SarifLog, SarifError] {
let json = @json.parse(text) catch {
err => return Err(JsonParse(message=err.to_string()))
}
SarifLog::from_json(json)
}