///| YAML Schema definitions
/// Ported from js-yaml v3.13.1:
/// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
/// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
/// Copyright 2018-2025 the Deno authors. MIT license.
///|
/// Schema type for different YAML standards
pub enum SchemaType {
FailSafe // Generic mappings, sequences, strings
Json // FailSafe + nulls, booleans, integers, floats
Core // Same as Json
Default // Core + binary, omap, pairs, set
Extended // Default + regexp, undefined
} derive(Eq, Show)
///|
/// Type map for organizing types by kind
pub struct TypeMap {
pub fallback : Map[String, YamlType[YamlValue]]
pub mapping : Map[String, YamlType[YamlValue]]
pub scalar : Map[String, YamlType[YamlValue]]
pub sequence : Map[String, YamlType[YamlValue]]
}
///|
/// YAML Schema containing implicit and explicit types
pub struct Schema {
pub implicit_types : Array[YamlType[YamlValue]] // Types resolved implicitly
pub explicit_types : Array[YamlType[YamlValue]] // Types requiring explicit tags
pub type_map : TypeMap
}
///|
/// Create a type map from implicit and explicit types
fn create_type_map(
implicit_types : Array[YamlType[YamlValue]],
explicit_types : Array[YamlType[YamlValue]],
) -> TypeMap {
let fallback = Map::new()
let mapping = Map::new()
let scalar = Map::new()
let sequence = Map::new()
// Add all types to their respective maps
for yaml_type in implicit_types {
match yaml_type.kind {
Scalar => scalar[yaml_type.tag] = yaml_type
Mapping => mapping[yaml_type.tag] = yaml_type
Sequence => sequence[yaml_type.tag] = yaml_type
}
fallback[yaml_type.tag] = yaml_type
}
for yaml_type in explicit_types {
match yaml_type.kind {
Scalar => scalar[yaml_type.tag] = yaml_type
Mapping => mapping[yaml_type.tag] = yaml_type
Sequence => sequence[yaml_type.tag] = yaml_type
}
fallback[yaml_type.tag] = yaml_type
}
{ fallback, mapping, scalar, sequence }
}
///|
/// Create a schema from type arrays and optional parent schema
fn create_schema(
implicit_types? : Array[YamlType[YamlValue]] = [],
explicit_types? : Array[YamlType[YamlValue]] = [],
include? : Schema? = None,
) -> Schema {
let all_implicit = []
let all_explicit = []
// Include parent schema types if provided
match include {
Some(parent) => {
all_implicit.append(parent.implicit_types)
all_explicit.append(parent.explicit_types)
}
None => ()
}
// Add new types
all_implicit.append(implicit_types)
all_explicit.append(explicit_types)
let type_map = create_type_map(all_implicit, all_explicit)
{ implicit_types: all_implicit, explicit_types: all_explicit, type_map }
}
// Convert builtin type functions to work with YamlValue
///|
/// Convert string type handler to work with YamlValue
fn str_yaml_type() -> YamlType[YamlValue] {
{
tag: "tag:yaml.org,2002:str",
kind: Scalar,
predicate: fn(value) {
match value {
String(_) => true
_ => false
}
},
represent: Some(fn(data, style) {
match data {
String(s) => s
_ => ""
}
}),
default_style: None,
resolve: fn(value) {
true // Strings always resolve to themselves
},
construct: fn(value) {
match value {
String(s) => String(s)
_ => String("")
}
},
}
}
///|
/// Convert sequence type handler to work with YamlValue
fn seq_yaml_type() -> YamlType[YamlValue] {
{
tag: "tag:yaml.org,2002:seq",
kind: Sequence,
predicate: fn(value) {
match value {
Array(_) => true
_ => false
}
},
represent: None,
default_style: None,
resolve: fn(value) {
match value {
Array(_) => true
_ => false
}
},
construct: fn(value) {
match value {
Array(arr) => Array(arr)
_ => Array([])
}
},
}
}
///|
/// Convert mapping type handler to work with YamlValue
fn map_yaml_type() -> YamlType[YamlValue] {
{
tag: "tag:yaml.org,2002:map",
kind: Mapping,
predicate: fn(value) {
match value {
Object(_) => true
_ => false
}
},
represent: None,
default_style: None,
resolve: fn(value) {
match value {
Object(_) => true
_ => false
}
},
construct: fn(value) {
match value {
Object(obj) => Object(obj)
_ => Object(Map::new())
}
},
}
}
///|
/// Null type handler for YamlValue
fn null_yaml_type() -> YamlType[YamlValue] {
let yaml_null_values = ["null", "Null", "NULL", "~", ""]
{
tag: "tag:yaml.org,2002:null",
kind: Scalar,
predicate: fn(value) {
match value {
Null => true
_ => false
}
},
represent: Some(fn(data, style) {
match style {
Some(Uppercase) => "NULL"
Some(Camelcase) => "Null"
_ => "null"
}
}),
default_style: Some(Lowercase),
resolve: fn(value) {
match value {
String(s) => yaml_null_values.contains(s)
Null => true
_ => false
}
},
construct: fn(value) { Null },
}
}
///|
/// Boolean type handler for YamlValue
fn bool_yaml_type() -> YamlType[YamlValue] {
let yaml_true_values = ["true", "True", "TRUE"]
let yaml_false_values = ["false", "False", "FALSE"]
{
tag: "tag:yaml.org,2002:bool",
kind: Scalar,
predicate: fn(value) {
match value {
Bool(_) => true
_ => false
}
},
represent: Some(fn(data, style) {
match data {
Bool(b) =>
match style {
Some(Uppercase) => if b { "TRUE" } else { "FALSE" }
Some(Camelcase) => if b { "True" } else { "False" }
_ => if b { "true" } else { "false" }
}
_ => "false"
}
}),
default_style: Some(Lowercase),
resolve: fn(value) {
match value {
String(s) =>
yaml_true_values.contains(s) || yaml_false_values.contains(s)
Bool(_) => true
_ => false
}
},
construct: fn(value) {
match value {
String(s) => Bool(yaml_true_values.contains(s))
Bool(b) => Bool(b)
_ => Bool(false)
}
},
}
}
///|
/// Integer type handler for YamlValue
fn int_yaml_type() -> YamlType[YamlValue] {
{
tag: "tag:yaml.org,2002:int",
kind: Scalar,
predicate: fn(value) {
match value {
Int(_) => true
_ => false
}
},
represent: Some(fn(data, style) {
match data {
Int(i) => i.to_string()
_ => "0"
}
}),
default_style: Some(Decimal),
resolve: fn(value) {
match value {
String(s) => resolve_yaml_integer(s)
Int(_) => true
_ => false
}
},
construct: fn(value) {
match value {
String(s) => Int(construct_yaml_integer(s))
Int(i) => Int(i)
_ => Int(0)
}
},
}
}
///|
/// Float type handler for YamlValue
fn float_yaml_type() -> YamlType[YamlValue] {
{
tag: "tag:yaml.org,2002:float",
kind: Scalar,
predicate: fn(value) {
match value {
Float(_) => true
Int(_) => true
_ => false
}
},
represent: Some(fn(data, style) {
match data {
Float(f) =>
if f != f { // NaN check
".nan"
} else if f == infinity() {
".inf"
} else if f == -infinity() {
"-.inf"
} else {
f.to_string()
}
Int(i) => i.to_double().to_string()
_ => "0.0"
}
}),
default_style: None,
resolve: fn(value) {
match value {
String(s) => resolve_yaml_float(s)
Float(_) => true
Int(_) => true
_ => false
}
},
construct: fn(value) {
match value {
String(s) => Float(construct_yaml_float(s))
Float(f) => Float(f)
Int(i) => Float(i.to_double())
_ => Float(0.0)
}
},
}
}
// Schema definitions
///|
/// Standard YAML's failsafe schema
/// Supports: generic mappings, generic sequences, generic strings
pub fn failsafe_schema() -> Schema {
create_schema(explicit_types=[
str_yaml_type(),
seq_yaml_type(),
map_yaml_type(),
])
}
///|
/// Standard YAML's JSON schema
/// Supports: failsafe + nulls, booleans, integers, floats
pub fn json_schema() -> Schema {
create_schema(
implicit_types=[
null_yaml_type(),
bool_yaml_type(),
int_yaml_type(),
float_yaml_type(),
],
include=Some(failsafe_schema()),
)
}
///|
/// Standard YAML's core schema
/// Functionally the same as JSON schema
pub fn core_schema() -> Schema {
json_schema()
}
///|
/// Default YAML schema (not in YAML specification)
/// Supports: core + binary, omap, pairs, set
/// For now, same as core until we implement additional types
pub fn default_schema() -> Schema {
core_schema()
}
///|
/// Extended YAML schema (not in YAML specification)
/// Supports: default + regexp, undefined
/// For now, same as default until we implement additional types
pub fn extended_schema() -> Schema {
default_schema()
}
///|
/// Get schema by name
pub fn get_schema(name : String) -> Schema {
match name {
"failsafe" => failsafe_schema()
"json" => json_schema()
"core" => core_schema()
"default" => default_schema()
"extended" => extended_schema()
_ => default_schema() // Default fallback
}
}
///|
/// Resolve a scalar value according to schema
pub fn resolve_scalar(data : String, schema : Schema) -> YamlValue {
// Try implicit types first
for yaml_type in schema.implicit_types {
if (yaml_type.resolve)(String(data)) {
return (yaml_type.construct)(String(data))
}
}
// If no implicit type matches, return as string
String(data)
}