///|
priv struct CliExecutionResult {
stderr_lines : Array[String]
exit_code : Int32?
}
///|
fn usage_summary_line() -> String {
"Usage: jqx [options] [\"\"] [\"\"]"
}
///|
fn render_error_lines(msg : String) -> Array[String] {
let lines = []
let parts = split_error_lines(msg)
let mut printed = false
for part in parts {
if part.length() == 0 {
continue
}
lines.push(format_error_message(part))
printed = true
}
if !printed {
lines.push(format_error_message(msg))
}
lines
}
///|
fn cli_reads_stdin(args : Array[String]) -> Bool {
match parse_cli_args(args) {
Ok(cfg) => !cfg.no_input && cfg.input is None
Err(_) => false
}
}
///|
fn configure_module_search_paths(cfg : CliArgs) -> Unit {
@lib.clear_module_search_paths()
if cfg.module_search_paths.length() == 0 {
@lib.set_module_search_paths(["."])
} else {
@lib.set_module_search_paths(cfg.module_search_paths)
}
}
///|
fn execute_cli(
args : Array[String],
stdin_text : String,
emit : (Bool, Bool, @lib.Value) -> Unit,
) -> CliExecutionResult {
let parsed = parse_cli_args(args)
guard parsed is Ok(cfg) else {
return {
stderr_lines: ["jqx: error: " + parsed.unwrap_err(), usage_summary_line()],
exit_code: Some(2),
}
}
let cli_stdin = if cfg.no_input || cfg.input is Some(_) {
""
} else {
stdin_text
}
configure_module_search_paths(cfg)
let compiled_result = try @lib.compile(cfg.filter) catch {
err => Err(err.to_string())
} noraise {
compiled => Ok(compiled)
}
guard compiled_result is Ok(compiled) else {
return {
stderr_lines: render_error_lines(compiled_result.unwrap_err()),
exit_code: Some(3),
}
}
try {
let inputs = parse_cli_inputs(cfg, cli_stdin)
let mut last_output : @lib.Value? = None
for input in inputs {
let result = @lib.execute_for_cli(compiled, input)
for value in result.0 {
last_output = Some(value)
emit(cfg.raw, cfg.compact, value)
}
match result.1 {
Some(msg) =>
return { stderr_lines: render_error_lines(msg), exit_code: Some(5), }
None => ()
}
}
let exit_code = if cfg.exit_status {
Some(compute_exit_status(last_output))
} else {
None
}
{ stderr_lines: [], exit_code, }
} catch {
err =>
{ stderr_lines: render_error_lines(err.to_string()), exit_code: Some(5), }
}
}
///|
fn split_error_lines(msg : String) -> Array[String] {
let out = []
let mut start = 0
for i in 0.. String {
let marker = " at line "
let marker2 = ", column "
match msg.find(marker) {
Some(i) => {
let start_line = i + marker.length()
let rest = msg.view(start_offset=start_line)
match rest.find(marker2) {
Some(j) => {
let line_part = msg
.view(start_offset=start_line, end_offset=start_line + j)
.to_owned()
let start_col = start_line + j + marker2.length()
let col_part = msg.view(start_offset=start_col).to_owned()
let parsed_line = try @string.parse_int(line_part) catch {
_ => None
} noraise {
line => Some(line)
}
let parsed_col = try @string.parse_int(col_part) catch {
_ => None
} noraise {
col => Some(col)
}
match (parsed_line, parsed_col) {
(Some(line), Some(col)) => {
let base = msg.view(start_offset=0, end_offset=i).to_owned()
return "jqx: error (at :" +
line.to_string() +
":" +
col.to_string() +
"): " +
base
}
_ => ()
}
}
None => ()
}
}
None => ()
}
"jqx: error: " + msg
}
///|
fn compute_exit_status(last_output : @lib.Value?) -> Int32 {
match last_output {
None => 4
Some(JFalse | JNull) => 1
Some(_) => 0
}
}