///|
priv struct ParsedInspect {
help : Bool
json : Bool
file : String?
port : Int32?
request_method : String
}
///|
fn inspect_usage() -> String {
(
#|Usage: moui inspect [options]
#|
#|Read the latest DevTools document published by `moui dev --inspect`.
#|
#|Options:
#| --file PATH DevTools document path. Defaults to MOUI_DEVTOOLS_FILE
#| or .moui/devtools.json.
#| --port PORT Connect to the running DevTools socket instead of a file.
#| --method NAME Request method: all (default), snapshot, trace,
#| diagnostics, or ping.
#| --json Print the raw document JSON.
#| -h, --help Show this help.
)
}
///|
fn parse_inspect_args(
args : Array[String],
cwd : String,
) -> Result[ParsedInspect, String] {
let help = Ref(false)
let json = Ref(false)
let file : Ref[String?] = Ref(None)
let port : Ref[Int32?] = Ref(None)
let request_method = Ref("all")
let mut index = 0
while index < args.length() {
let argument = args[index]
let (name, inline) = option_parts(argument)
match name {
"-h" | "--help" =>
match flag_without_value(inline, name) {
Ok(_) => help.val = true
Err(message) => return Err(message)
}
"--json" =>
match flag_without_value(inline, name) {
Ok(_) => json.val = true
Err(message) => return Err(message)
}
"--file" =>
match option_value(args, index, inline, name) {
Ok((value, next)) => {
file.val = Some(make_absolute(value, cwd))
index = next
continue
}
Err(message) => return Err(message)
}
"--port" =>
match option_value(args, index, inline, name) {
Ok((value, next)) =>
match parse_int(value) {
Ok(number) if number > 0 => {
port.val = Some(number)
index = next
continue
}
_ => return Err("--port must be a positive integer")
}
Err(message) => return Err(message)
}
"--method" =>
match option_value(args, index, inline, name) {
Ok((value, next)) => {
request_method.val = value
index = next
continue
}
Err(message) => return Err(message)
}
_ if argument.has_prefix("-") =>
return Err("unknown option for moui inspect: " + name)
_ => return Err("moui inspect does not accept positional arguments")
}
index += 1
}
Ok({
help: help.val,
json: json.val,
file: file.val,
port: port.val,
request_method: request_method.val,
})
}
///|
fn inspect_default_path(cwd : String) -> String {
match @env.get_env_var("MOUI_DEVTOOLS_FILE") {
Some(path) if path != "" => make_absolute(path, cwd)
_ => make_absolute(".moui/devtools.json", cwd)
}
}
///|
/// Render a DevTools document as human text without re-parsing the typed
/// inspector payload. Missing fields fall back to the raw JSON.
fn inspect_document_text(text : String) -> String {
let parsed = @json.parse(text) catch { _ => return text }
let fields = match parsed {
Object(fields) => fields
_ => return text
}
let builder = StringBuilder()
match fields.get("reportText") {
Some(String(value)) => builder.write_string(value)
_ => ()
}
match fields.get("traceText") {
Some(String(value)) => {
if builder.to_string() != "" {
builder.write_string("\n\n")
}
builder.write_string(value)
}
_ => ()
}
match fields.get("diagnostics") {
Some(Array(items)) =>
if items.length() > 0 {
if builder.to_string() != "" {
builder.write_string("\n\n")
}
builder.write_string("diagnostics: " + items.length().to_string())
}
_ => ()
}
let rendered = builder.to_string()
if rendered == "" {
text
} else {
rendered
}
}
///|
fn inspect_read_file(path : String) -> String? {
if !@fs.path_exists(path) {
return None
}
let text = @fs.read_file_to_string(path) catch { _ => return None }
Some(text)
}
///|
fn inspect_default_port(cwd : String) -> Int32? {
let path = match @devtools_socket.devtools_port_file_path() {
Some(value) => make_absolute(value, cwd)
None => make_absolute(".moui/devtools.port", cwd)
}
if !@fs.path_exists(path) {
return None
}
let text = @fs.read_file_to_string(path) catch { _ => return None }
match parse_int(text.trim().to_owned()) {
Ok(port) if port > 0 => Some(port)
_ => None
}
}
///|
fn inspect_source_text(parsed : ParsedInspect, cwd : String) -> String? {
match parsed.file {
Some(path) => inspect_read_file(path)
None => {
let port = match parsed.port {
Some(value) => Some(value)
None => inspect_default_port(cwd)
}
match port {
Some(value) =>
match
@devtools_socket.request(
value,
request_method=parsed.request_method,
) {
Some(text) => Some(text)
None => inspect_read_file(inspect_default_path(cwd))
}
None => inspect_read_file(inspect_default_path(cwd))
}
}
}
}
///|
fn run_inspect(args : Array[String], cwd : String) -> CliOutcome {
let parsed = match parse_inspect_args(args, cwd) {
Ok(value) => value
Err(message) => return failed_outcome(message, false, inspect_usage())
}
if parsed.help {
return stdout_outcome(inspect_usage())
}
let text = match inspect_source_text(parsed, cwd) {
Some(value) => value
None =>
return failed_outcome(
"DevTools snapshot not found; run `moui dev --inspect` and wait for the first sample",
false,
inspect_usage(),
)
}
if parsed.json {
stdout_outcome(text)
} else {
stdout_outcome(inspect_document_text(text) + "\n")
}
}