///|
/// toolsfx-moonbit demo 鈥?argv front-end over the shared JSON dispatch.
/// Every computation goes through `@workbench.dispatch`, exactly like the
/// browser workbench (design D3: one dispatch, three front-ends).
///
/// Usage:
/// moon run cmd/demo -- [input] [--key=value ...]
/// moon run cmd/demo -- sm3 abc
/// moon run cmd/demo -- caesar_encode Hello --shift=3
/// moon run cmd/demo -- vigenere_encode ATTACKATDAWN --key=LEMON
/// moon run cmd/demo -- sm4_encrypt secret --mode=cbc \
/// --key_hex=0123456789abcdeffedcba9876543210 \
/// --iv_hex=000102030405060708090a0b0c0d0e0f
/// moon run cmd/demo -- detect "Gur dhvpx..."
fn main {
let args = @env.args()
if args.length() <= 1 {
print_usage()
return
}
// The first non-flag token is the op; module defaults by known ops.
let op = args[1]
let module_name = module_of(op)
// Remaining tokens: one optional positional input, then --key=value flags.
let mut input : String? = None
let flags : Array[(String, String)] = []
for i in 2.. 2 && arg[:2].to_owned() == "--" {
let body = arg[2:].to_owned()
match find_first(body, "=") {
Some(eq) => flags.push((body[:eq].to_owned(), body[eq + 1:].to_owned()))
None => flags.push((body, "true"))
}
} else if input is None {
input = Some(arg)
}
}
// Build the flat dispatch JSON.
let parts : Array[String] = [
"\"module\":\"\{module_name}\"",
"\"op\":\"\{op}\"",
]
match input {
Some(t) => parts.push("\"input\":\{json_escape(t)}")
None => ()
}
for pair in flags {
let (k, v) = pair
parts.push("\"\{k}\":\{json_escape(v)}")
}
let request = "{" + parts.join(",") + "}"
let response = @workbench.dispatch(request)
// Pretty-print: success shows the result; errors show code + messages.
print_response(response)
}
///|
/// Index of the first occurrence of `c` in `s`, if any.
fn find_first(s : String, c : String) -> Int? {
s.find(c)
}
///|
/// Map an op to its module; `module.op` syntax overrides.
fn module_of(op : String) -> String {
match find_first(op, ".") {
Some(dot) => return op[:dot].to_owned()
None => ()
}
match op {
"sm3"
| "sm4_encrypt"
| "sm4_decrypt"
| "sm2_keygen"
| "sm2_sign"
| "sm2_verify"
| "sm2_encrypt"
| "sm2_decrypt" => "sm"
"caesar_encode"
| "caesar_decode"
| "vigenere_encode"
| "vigenere_decode"
| "atbash"
| "morse_encode"
| "morse_decode"
| "railfence_encode"
| "railfence_decode"
| "detect" => "classical"
"base16_encode"
| "base16_decode"
| "base32_encode"
| "base32_decode"
| "base58_encode"
| "base58_decode"
| "base58check_encode"
| "base58check_decode"
| "base62_encode"
| "base62_decode"
| "base64_encode"
| "base64_decode"
| "base85_encode"
| "base85_decode"
| "base91_encode"
| "base91_decode"
| "punycode_encode"
| "punycode_decode"
| "url_encode"
| "url_decode"
| "radix_convert" => "encoding"
"md5"
| "sha1"
| "sha256"
| "sha512"
| "hmac_sha256"
| "aes_cbc_encrypt"
| "aes_cbc_decrypt"
| "aes_gcm_encrypt"
| "aes_gcm_decrypt"
| "chacha20" => "crypto"
_ => "sm"
}
}
///|
/// Minimal JSON string escaping for user-supplied values.
fn json_escape(s : String) -> String {
let buf = StringBuilder()
buf.write_char('"')
for c in s {
match c {
'"' => buf.write_string("\\\"")
'\\' => buf.write_string("\\\\")
'\n' => buf.write_string("\\n")
'\r' => buf.write_string("\\r")
'\t' => buf.write_string("\\t")
_ => buf.write_char(c)
}
}
buf.write_char('"')
buf.to_string()
}
///|
/// Extract `"key":"value"` from a response string (small fixed-shape JSON).
fn field(json : String, key : String) -> String? {
let marker = "\"\{key}\":\""
match json.find(marker) {
Some(at) => {
let start = at + marker.length()
let rest = json[start:]
match rest.find("\"") {
Some(end) => Some("\{rest[:end]}")
None => None
}
}
None => None
}
}
///|
/// Print a dispatch response in a human-friendly way; errors exit non-zero
/// semantics via a clear ERROR line.
fn print_response(response : String) -> Unit {
if response.contains("\"ok\":true") {
match field(response, "result") {
Some(r) => println(r)
None => println(response)
}
} else {
let code = field(response, "code") |> or_else("E_DISPATCH_INTERNAL")
let en = field(response, "msg_en") |> or_else("unknown error")
let zh = field(response, "msg_zh")
println("ERROR \{code}: \{en}")
match zh {
Some(z) => println("\{z}")
None => ()
}
}
}
///|
fn or_else(opt : String?, default : String) -> String {
match opt {
Some(s) => s
None => default
}
}
///|
fn print_usage() -> Unit {
println("toolsfx-moonbit demo 鈥?crypto toolbox via one JSON dispatch")
println("=============================================================")
println("Usage: moon run cmd/demo -- [input] [--key=value ...]")
println("")
println("Capabilities (module/op): \{@workbench.capabilities()}")
println("")
println("Examples:")
println(" moon run cmd/demo -- sm3 abc")
println(" moon run cmd/demo -- caesar_encode Hello --shift=3")
println(" moon run cmd/demo -- vigenere_encode ATTACKATDAWN --key=LEMON")
println(" moon run cmd/demo -- morse_encode SOS")
println(
" moon run cmd/demo -- detect \"Gur dhvpx oebja sbk whzcf bire gur ynml qbt\"",
)
println(" moon run cmd/demo -- base64_encode Hello")
println(" moon run cmd/demo -- sha256 abc")
println(" moon run cmd/demo -- sm4_encrypt secret --mode=cbc \\")
println(" --key_hex=0123456789abcdeffedcba9876543210 \\")
println(" --iv_hex=000102030405060708090a0b0c0d0e0f")
}