///|
pub(all) enum CliCommandKind {
Query
PointerGet
PointerSet
PointerRemove
Patch
Help
} derive(Eq, Debug)
///|
pub(all) struct CliCommand {
kind : CliCommandKind
path_text : String
pointer_text : String
value_text : String
patch_file_path : String
file_path : String
output : OutputMode
indent : Int
} derive(Eq, Debug)
///|
pub fn CliCommandKind::to_string(self : CliCommandKind) -> String {
match self {
Query => "Query"
PointerGet => "PointerGet"
PointerSet => "PointerSet"
PointerRemove => "PointerRemove"
Patch => "Patch"
Help => "Help"
}
}
///|
pub fn OutputMode::to_string(self : OutputMode) -> String {
match self {
Values => "Values"
Pointers => "Pointers"
Matches => "Matches"
}
}
///|
pub fn CliCommand::parse(args : Array[String]) -> Result[CliCommand, String] {
if args.length() <= 1 {
return Ok(make_help_command())
}
let mut output = Values
let mut indent = 0
let positional : Array[String] = []
let mut i = 1
while i < args.length() {
let arg = args[i]
match arg {
"--help" | "-h" => return Ok(make_help_command())
"--values" => {
output = Values
i += 1
}
"--pointers" => {
output = Pointers
i += 1
}
"--matches" => {
output = Matches
i += 1
}
"--pretty" => {
indent = 2
i += 1
}
_ =>
if arg.has_prefix("-") {
return Err("unknown option: \{arg}")
} else {
positional.push(arg)
i += 1
}
}
}
if positional.length() == 0 {
return Err("expected a command or JSONPath query")
}
match positional[0] {
"query" => parse_query_command(positional, output, indent, offset=1)
"get" => parse_pointer_get_command(positional, output, indent)
"set" => parse_pointer_set_command(positional, output, indent)
"remove" => parse_pointer_remove_command(positional, output, indent)
"patch" => parse_patch_command(positional, output, indent)
"help" => Ok(make_help_command())
other =>
if other.has_prefix("$") {
parse_query_command(positional, output, indent, offset=0)
} else {
Err("unknown command: \{other}")
}
}
}
///|
fn parse_query_command(
positional : Array[String],
output : OutputMode,
indent : Int,
offset~ : Int,
) -> Result[CliCommand, String] {
if positional.length() - offset != 2 {
return Err("query expects ")
}
Ok({
kind: Query,
path_text: positional[offset],
pointer_text: "",
value_text: "",
patch_file_path: "",
file_path: positional[offset + 1],
output,
indent,
})
}
///|
fn parse_pointer_get_command(
positional : Array[String],
output : OutputMode,
indent : Int,
) -> Result[CliCommand, String] {
if positional.length() != 3 {
return Err("get expects ")
}
Ok({
kind: PointerGet,
path_text: "",
pointer_text: positional[1],
value_text: "",
patch_file_path: "",
file_path: positional[2],
output,
indent,
})
}
///|
fn parse_pointer_set_command(
positional : Array[String],
output : OutputMode,
indent : Int,
) -> Result[CliCommand, String] {
if positional.length() != 4 {
return Err("set expects ")
}
Ok({
kind: PointerSet,
path_text: "",
pointer_text: positional[1],
value_text: positional[2],
patch_file_path: "",
file_path: positional[3],
output,
indent,
})
}
///|
fn parse_pointer_remove_command(
positional : Array[String],
output : OutputMode,
indent : Int,
) -> Result[CliCommand, String] {
if positional.length() != 3 {
return Err("remove expects ")
}
Ok({
kind: PointerRemove,
path_text: "",
pointer_text: positional[1],
value_text: "",
patch_file_path: "",
file_path: positional[2],
output,
indent,
})
}
///|
fn parse_patch_command(
positional : Array[String],
output : OutputMode,
indent : Int,
) -> Result[CliCommand, String] {
if positional.length() != 3 {
return Err("patch expects ")
}
Ok({
kind: Patch,
path_text: "",
pointer_text: "",
value_text: "",
patch_file_path: positional[1],
file_path: positional[2],
output,
indent,
})
}
///|
fn make_help_command() -> CliCommand {
{
kind: Help,
path_text: "",
pointer_text: "",
value_text: "",
patch_file_path: "",
file_path: "",
output: Values,
indent: 0,
}
}
///|
pub fn execute_cli_text(
command : CliCommand,
json_text : String,
patch_text : String,
) -> Result[String, String] {
match command.kind {
Query =>
query_json_text_with_options(command.path_text, json_text, {
output: command.output,
indent: command.indent,
})
PointerGet =>
pointer_get_json_text(
command.pointer_text,
json_text,
indent=command.indent,
)
PointerSet =>
pointer_set_json_text(
command.pointer_text,
command.value_text,
json_text,
indent=command.indent,
)
PointerRemove =>
pointer_remove_json_text(
command.pointer_text,
json_text,
indent=command.indent,
)
Patch => patch_json_text(patch_text, json_text, indent=command.indent)
Help => Ok(cli_command_usage())
}
}
///|
pub fn run_cli_command(args : Array[String]) -> Int {
match CliCommand::parse(args) {
Ok(command) =>
if command.kind == Help {
println(cli_command_usage())
0
} else {
match execute_cli_files(command) {
Ok(output) => {
println(output)
0
}
Err(message) => {
println("MoonJSONPath error: \{message}")
1
}
}
}
Err(message) => {
println("MoonJSONPath error: \{message}")
println(cli_command_usage())
2
}
}
}
///|
fn execute_cli_files(command : CliCommand) -> Result[String, String] {
let json_text = @fs.read_file_to_string(command.file_path) catch {
_ => return Err("failed to read JSON file '\{command.file_path}'")
}
let patch_text = if command.kind == Patch {
@fs.read_file_to_string(command.patch_file_path) catch {
_ => return Err("failed to read patch file '\{command.patch_file_path}'")
}
} else {
""
}
execute_cli_text(command, json_text, patch_text)
}
///|
pub fn cli_command_usage() -> String {
(
#|Usage:
#| moonjsonpath query [--values|--pointers|--matches] [--pretty]
#| moonjsonpath get [--pretty]
#| moonjsonpath set [--pretty]
#| moonjsonpath remove [--pretty]
#| moonjsonpath patch [--pretty]
#|
#|Legacy:
#| moonjsonpath [--values|--pointers|--matches] [--pretty]
#|
#|Examples:
#| moonjsonpath query '$.users[*].name' data.json
#| moonjsonpath query --matches --pretty '$..name' data.json
#| moonjsonpath get '/users/0/name' data.json
#| moonjsonpath set '/users/0/active' true data.json
#| moonjsonpath patch patch.json data.json
#|
)
}