///|
fn main {
let args = @env.args()
let result : Result[Unit, Error] = Ok(run(args)) catch { err => Err(err) }
match result {
Ok(_) => ()
Err(err) => {
println(err.to_string())
panic()
}
}
}
///|
fn run(args : Array[String]) -> Unit raise {
let idx = if args.length() > 0 { 1 } else { 0 }
if args.length() - idx < 2 {
fail("usage: yamlsuite ")
}
let mode = args[idx]
let payload = args[idx + 1]
let bytes = @yaml.decode_base64("base64", payload)
let input = @encoding/utf8.decode(bytes)
if mode is "tree" {
let docs = @yaml.parse_yaml_stream_raw(input)
let json = Json::array(docs.map(yaml_to_tree_json))
println(json.stringify())
} else if mode is "json" {
let docs = @yaml.parse_yaml_stream_raw(input).map(@yaml.resolve_aliases)
if docs.length() == 1 {
let json = yaml_to_json_value(docs[0])
println(json.stringify())
} else {
let json = Json::array(docs.map(yaml_to_json_value))
println(json.stringify())
}
} else {
fail("unknown mode: " + mode)
}
}
///|
fn yaml_to_tree_json(value : @yaml.Yaml) -> Json {
match value {
Null => tree_obj("Null", [])
Bool(b) => tree_obj("Bool", [("value", Json::boolean(b))])
Int(i) => tree_obj("Int", [("value", Json::string(i.to_string()))])
Float(f) => tree_obj("Float", [("value", float_to_json_value(f))])
String(s) => tree_obj("String", [("value", Json::string(s))])
Binary(bytes) =>
tree_obj("String", [("value", Json::string(@yaml.encode_base64(bytes)))])
Seq(items) =>
tree_obj("Seq", [("items", Json::array(items.map(yaml_to_tree_json)))])
Map(items) =>
tree_obj("Map", [("items", Json::array(items.map(pair_to_json)))])
Alias(name) => tree_obj("Alias", [("name", Json::string(name))])
Anchor(name~, value~) =>
tree_obj("Anchor", [
("name", Json::string(name)),
("value", yaml_to_tree_json(value)),
])
Tagged(tag~, value~) =>
tree_obj("Tagged", [
("tag", Json::string(tag)),
("value", yaml_to_tree_json(value)),
])
}
}
///|
fn pair_to_json(pair : (@yaml.Yaml, @yaml.Yaml)) -> Json {
let (key, value) = pair
Json::array([yaml_to_tree_json(key), yaml_to_tree_json(value)])
}
///|
fn tree_obj(kind : String, fields : Array[(String, Json)]) -> Json {
let obj : Map[String, Json] = Map([])
obj["type"] = Json::string(kind)
for field in fields {
let (key, value) = field
obj[key] = value
}
Json::object(obj)
}
///|
fn float_to_json_value(value : Double) -> Json {
if value.is_nan() {
Json::string("nan")
} else if value.is_pos_inf() {
Json::string("inf")
} else if value.is_neg_inf() {
Json::string("-inf")
} else {
Json::number(value)
}
}
///|
fn yaml_to_json_value(value : @yaml.Yaml) -> Json raise @yaml.ParseError {
match value {
Null => Json::null()
Bool(b) => Json::boolean(b)
Int(i) => Json::number(Double::from_int(i))
Float(f) => float_to_json_value(f)
String(s) => Json::string(s)
Binary(bytes) => Json::string(@yaml.encode_base64(bytes))
Seq(items) => Json::array(items.map(yaml_to_json_value))
Map(items) => map_to_json(items)
Anchor(name~, value~) => {
ignore(name)
yaml_to_json_value(value)
}
Tagged(tag~, value~) =>
if is_set_tag(tag) {
match value {
Map(items) => set_map_to_json(items)
_ => yaml_to_json_value(value)
}
} else if is_str_tag(tag) || is_non_specific_tag(tag) {
Json::string(scalar_to_string(strip_props(value)))
} else {
ignore(tag)
yaml_to_json_value(value)
}
Alias(name) =>
raise @yaml.ParseError::InvalidTagValue(tag="alias", value=name)
}
}
///|
fn map_to_json(
items : Array[(@yaml.Yaml, @yaml.Yaml)],
) -> Json raise @yaml.ParseError {
let obj : Map[String, Json] = Map([])
for pair in items {
let (key, value) = pair
match key_to_string(key) {
Some(s) => obj[s] = yaml_to_json_value(value)
None =>
raise @yaml.ParseError::InvalidTagValue(
tag="json",
value="non-string key",
)
}
}
Json::object(obj)
}
///|
fn set_map_to_json(
items : Array[(@yaml.Yaml, @yaml.Yaml)],
) -> Json raise @yaml.ParseError {
let obj : Map[String, Json] = Map([])
for pair in items {
let (key, _value) = pair
match key_to_string(key) {
Some(s) => obj[s] = Json::null()
None =>
raise @yaml.ParseError::InvalidTagValue(
tag="json",
value="non-string key",
)
}
}
Json::object(obj)
}
///|
fn is_set_tag(tag : String) -> Bool {
tag is "tag:yaml.org,2002:set" ||
tag is "!!set" ||
tag is "!"
}
///|
fn is_str_tag(tag : String) -> Bool {
tag is "tag:yaml.org,2002:str" ||
tag is "!!str" ||
tag is "!"
}
///|
fn is_non_specific_tag(tag : String) -> Bool {
tag is "!"
}
///|
fn scalar_to_string(value : @yaml.Yaml) -> String {
match value {
String(s) => s
Null => ""
Bool(b) => b.to_string()
Int(i) => i.to_string()
Float(f) => f.to_string()
Binary(bytes) => @yaml.encode_base64(bytes)
Anchor(name~, value~) => {
ignore(name)
scalar_to_string(value)
}
Tagged(tag~, value~) => {
ignore(tag)
scalar_to_string(value)
}
Seq(_) => ""
Map(_) => ""
Alias(_) => ""
}
}
///|
fn key_to_string(value : @yaml.Yaml) -> String? {
match value {
String(s) => Some(s)
Anchor(name~, value~) => {
ignore(name)
key_to_string(value)
}
Tagged(tag~, value~) =>
if is_str_tag(tag) || is_non_specific_tag(tag) {
Some(scalar_to_string(strip_props(value)))
} else {
key_to_string(value)
}
_ => None
}
}
///|
fn strip_props(value : @yaml.Yaml) -> @yaml.Yaml {
match value {
Anchor(name~, value~) => {
ignore(name)
strip_props(value)
}
Tagged(tag~, value~) => {
ignore(tag)
strip_props(value)
}
_ => value
}
}