// cmd/main/main.mbt(草稿,等 LZMA 内核落地后移入仓库)
//
// 命令行:列目录 / 解包。
// moon run cmd/main -- list
// moon run cmd/main -- extract
//
// 输出契约(tools/check_compat.py 依赖它,各后端一致、不用退出码):
// list 成功:每行 "\t\t",目录的 size 与 crc 为 "-"
// 失败:一行 "UNSUPPORTED\t<原因>" 或 "ERROR\t<原因>"
///|
/// 32 位 CRC 的 8 位十六进制渲染。
fn crc_hex(crc : Int64) -> String {
let digits : Array[String] = [
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f",
]
let mut out = ""
let mut i = 7
while i >= 0 {
out = out + digits[((crc >> (4 * i)) & 0xFL).to_int()]
i -= 1
}
out
}
///|
/// 把路径按 '/' 与 '\\' 切分(UTF-8 续字节不会是 0x2F/0x5C,按字节切安全)。
fn path_parts(path : String) -> Array[String] {
let bytes = @utf8.encode(path)
let out : Array[String] = []
let buf = @buffer.Buffer()
for i in 0.. 0 {
out.push(@utf8.decode_lossy(buf.to_bytes()))
buf.reset()
}
} else {
buf.write_byte(bytes.unsafe_get(i))
}
}
if buf.length() > 0 {
out.push(@utf8.decode_lossy(buf.to_bytes()))
}
out
}
///|
/// 按需创建 `outdir` 下 `rel` 的所有父目录。
fn ensure_parent(outdir : String, rel : String) -> Unit raise {
let parts = path_parts(rel)
if parts.length() <= 1 {
return
}
let mut acc = outdir
for i in 0..<(parts.length() - 1) {
acc = acc + "/" + parts[i]
if !@fs.path_exists(acc) {
@fs.create_dir(acc)
}
}
}
///|
/// 列目录。
fn cmd_list(path : String) -> Unit {
try {
let z = @moon7z.SevenZ::open(@fs.read_file_to_bytes(path))
for e in z.entries() {
let size = if e.is_dir { "-" } else { e.size.to_string() }
let crc = match e.crc {
Some(c) => crc_hex(c)
None => "-"
}
println(e.name + "\t" + size + "\t" + crc)
}
} catch {
@moon7z.SevenZError::Unsupported(what~) => println("UNSUPPORTED\t\{what}")
@fs.IOError::IOError(msg) => println("ERROR\tI/O 错误: \{msg}")
e => println("ERROR\t\{e}")
}
}
///|
/// 解包到目录。
fn cmd_extract(path : String, outdir : String) -> Unit {
try {
let z = @moon7z.SevenZ::open(@fs.read_file_to_bytes(path))
if !@fs.path_exists(outdir) {
@fs.create_dir(outdir)
}
let files = z.extract_all()
for f in files {
ensure_parent(outdir, f.name)
@fs.write_bytes_to_file(outdir + "/" + f.name, f.data)
}
println("OK\t\{files.length()} 个文件 → \{outdir}")
} catch {
@moon7z.SevenZError::Unsupported(what~) => println("UNSUPPORTED\t\{what}")
@fs.IOError::IOError(msg) => println("ERROR\tI/O 错误: \{msg}")
e => println("ERROR\t\{e}")
}
}
///|
fn main {
let args = @env.args()
// 各后端 argv 形状不同:wasm-gc/native 是 [程序, ...],js 是 [node, 脚本.js, ...]
let mut base = 1
if base < args.length() && args[base].has_suffix(".js") {
base += 1
}
if base + 1 >= args.length() {
println("用法: moon run cmd/main -- list ")
println(" moon run cmd/main -- extract ")
return
}
let action = args[base]
let archive = args[base + 1]
if action == "list" {
cmd_list(archive)
} else if action == "extract" && base + 2 < args.length() {
cmd_extract(archive, args[base + 2])
} else {
println("ERROR\t未知动作: \{action}")
}
}