// Command line front end.
//
// moon run cmd/main -- ... validate values against a built-in
// moon run cmd/main -- --list list the built-in type names
// moon run cmd/main -- --xml print the element tree of a document
//
// Exit status is 1 when any value is invalid, so the tool can be used in a
// shell pipeline.
///|
fn usage() -> Unit {
println("moonbit-xsd-datatypes")
println("")
println(" validate one or more values against a built-in type")
println(" --list list built-in type names")
println(" --types same as --list")
println("")
println("examples:")
println(" ... -- dateTime 2026-02-30T00:00:00")
println(" ... -- decimal 1.5 2 3.25")
}
///|
fn builtin_type_names() -> Array[String] {
[
"string", "boolean", "decimal", "float", "double", "duration", "dateTime",
"time", "date", "gYearMonth", "gYear", "gMonthDay", "gDay", "gMonth",
"hexBinary", "base64Binary", "anyURI", "QName", "NOTATION",
"normalizedString", "token", "language", "NMTOKEN", "NMTOKENS", "Name",
"NCName", "ID", "IDREF", "IDREFS", "ENTITY", "ENTITIES", "integer",
"nonPositiveInteger", "negativeInteger", "long", "int", "short", "byte",
"nonNegativeInteger", "unsignedLong", "unsignedInt", "unsignedShort",
"unsignedByte", "positiveInteger",
]
}
///|
fn main {
let args = @env.args()
// Different back ends put different things at argv[0]: node's executable
// path under , the wasm path under wasm, nothing under native.
// Start at the first option-looking argument, or just after an executable.
let mut start = 0
let mut scan = 0
while scan < args.length() {
if args[scan].has_prefix("--") {
start = scan
break
}
scan = scan + 1
}
if start == 0 && args.length() > 0 && args[0].contains(".exe") {
start = 1
}
let argv : Array[String] = []
let mut i = start
while i < args.length() {
argv.push(args[i])
i = i + 1
}
if argv.is_empty() {
usage()
return
}
if argv[0] == "--list" || argv[0] == "--types" {
for name in builtin_type_names() {
println(name)
}
return
}
if argv[0] == "--schema" {
if argv.length() < 4 {
println("usage: --schema ...")
return
}
let path = argv[1]
let source = try {
@fs.read_file_to_string(path)
} catch {
_ => {
println("cannot read \{path}")
return
}
}
let schema = match @dt.load_schema(source) {
Ok(s) => s
Err(e) => {
println("schema error: \{e}")
return
}
}
let type_name = argv[2]
let mut bad = 0
let mut k = 3
while k < argv.length() {
let value = argv[k]
match @dt.validate_named(schema, type_name, value) {
Ok(v) => println("valid \{type_name} \{value} => \{@dt.lexical_text(v)}")
Err(e) => {
println("INVALID \{type_name} \{value} => \{e}")
bad = bad + 1
}
}
k = k + 1
}
if bad > 0 {
println("\{bad} value(s) invalid")
} else {
println("all values valid")
}
return
}
if argv[0] == "--xml" {
println("--xml needs a file reader; run the datatype checks instead")
return
}
if argv.length() < 2 {
usage()
return
}
let type_name = argv[0]
let mut failures = 0
let mut k = 1
while k < argv.length() {
let value = argv[k]
match @dt.validate_builtin(type_name, value) {
Ok(v) => println("valid \{type_name} \{value} => \{@dt.lexical_text(v)}")
Err(e) => {
println("INVALID \{type_name} \{value} => \{e}")
failures = failures + 1
}
}
k = k + 1
}
if failures > 0 {
println("\{failures} value(s) invalid")
} else {
println("all values valid")
}
}