1
1
mirror of https://github.com/wader/fq.git synced 2024-12-29 00:22:38 +03:00
fq/pkg/interp/options.jq
Mattias Wadman f4480c6fe5 decode,interp: Support for format specific options
interp: Refactor format help and also include options
interp: Add -o name=@path to load file content as value (not documented yet, might change)
interp,decode: Expose decode out value as _out (might change)
interp: Refactor foramts.jq into format_{decode,func,include}.jq
interp: Refactor torepr into _format_func for generic format function overloading
interp: Refactor -o options parsing to be more generic and collect unknowns options to be used as format options
decode of decode alises
func for format overloaded functions
include for format specific jq functions (also _help, torepr etc)
flac_frame: Add bits_per_sample option
mp3: Add max_unique_header_config and max_sync_seek options
mp4: Add decode_samples and allow_truncate options
avc_au: Has length_size option
hevc_au: Has length_size option
aac_frame: Has object_typee option
doc: Rewrite format doc generation, less hack more jq
2022-05-01 17:08:30 +02:00

498 lines
12 KiB
Plaintext

include "internal";
include "binary";
def _opt_build_default_fixed:
( stdout_tty as $stdout
| {
addrbase: 16,
arg: [],
argjson: [],
array_truncate: 50,
bits_format: "snippet",
# 0-0xff=brightwhite,0=brightblack,32-126:9-13=white
byte_colors: [
{ ranges: [[0,255]],
value: "brightwhite"
},
{ ranges: [[0]],
value: "brightblack"
},
{ ranges: [[32,126],[9,13]],
value: "white"
}
],
color: ($stdout.is_terminal and (env.NO_COLOR | . == null or . == "")),
colors: {
null: "brightblack",
false: "yellow",
true: "yellow",
number: "cyan",
string: "green",
objectkey: "brightblue",
array: "white",
object: "white",
index: "white",
value: "white",
error: "brightred",
dumpheader: "yellow+underline",
dumpaddr: "yellow",
prompt_repl_level: "brightblack",
prompt_value: "white"
},
compact: false,
completion_timeout: (env.COMPLETION_TIMEOUT | if . != null then tonumber else 1 end),
decode_file: [],
decode_format: "probe",
decode_progress: (env.NO_DECODE_PROGRESS == null),
depth: 0,
expr: ".",
expr_eval_path: "arg",
expr_file: null,
filenames: null,
force: false,
include_path: null,
join_string: "\n",
null_input: false,
raw_file: [],
raw_output: ($stdout.is_terminal | not),
raw_string: false,
repl: false,
sizebase: 10,
show_formats: false,
show_help: false,
slurp: false,
string_input: false,
unicode: ($stdout.is_terminal and env.CLIUNICODE != null),
verbose: false,
}
);
def _opt_options:
{
addrbase: "number",
arg: "array_string_pair",
argjson: "array_string_pair",
array_truncate: "number",
bits_format: "string",
byte_colors: "csv_ranges_array",
color: "boolean",
colors: "csv_kv_obj",
compact: "boolean",
completion_timeout: "number",
decode_file: "array_string_pair",
decode_format: "string",
decode_progress: "boolean",
depth: "number",
display_bytes: "number",
expr: "string",
expr_eval_path: "string",
expr_file: "string",
filenames: "array_string",
force: "boolean",
include_path: "string",
join_string: "string",
line_bytes: "number",
null_input: "boolean",
raw_file: "array_string_pair",
raw_output: "boolean",
raw_string: "boolean",
repl: "boolean",
sizebase: "number",
show_formats: "boolean",
show_help: "boolean",
slurp: "boolean",
string_input: "boolean",
unicode: "boolean",
verbose: "boolean",
width: "number",
};
def _opt_eval($rest):
( with_entries(
( select(.value | type == "string" and startswith("@"))
| .value |=
( . as $v
| try
( .[1:]
| open
| tobytes
| tostring
)
catch $v
)
)
)
+ { argjson: (
( .argjson
| if . then
map(
( . as $a
| .[1] |=
try fromjson
catch
( "--argjson \($a[0]): \(.)"
| halt_error(_exit_code_args_error)
)
)
)
end
)
),
color: (
if .monochrome_output == true then false
elif .color_output == true then true
else null
end
),
expr: (
# if -f was used, all rest non-args are filenames
# otherwise first is expr rest is filesnames
( .expr_file
| . as $expr_file
| if . then
try (open | tobytes | tostring)
catch ("\($expr_file): \(.)" | halt_error(_exit_code_args_error))
else $rest[0] // null
end
)
),
expr_eval_path: .expr_file,
filenames: (
( if .filenames then .filenames
elif .expr_file then $rest
else $rest[1:]
end
# null means stdin
| if . == [] then [null] end
)
),
join_string: (
if .join_output then ""
elif .null_output then "\u0000"
else null
end
),
null_input: (
( ( if .expr_file then $rest
else $rest[1:]
end
) as $files
| if $files == [] and .repl then true
else null
end
)
),
raw_file: (
( .raw_file
| if . then
( map(.[1] |=
( . as $f
| try (open | tobytes | tostring)
catch ("\($f): \(.)" | halt_error(_exit_code_args_error))
)
)
)
end
)
),
raw_string: (
if .raw_string
or .join_output
or .null_output
then true
else null
end
)
}
| with_entries(select(.value != null))
);
# these _to* function do a bit for fuzzy string to type conversions
def _opt_to_boolean:
try
if . == "true" then true
elif . == "false" then false
else tonumber != 0
end
catch
null;
def _opt_from_boolean: tostring;
def _opt_to_number:
try tonumber catch null;
def _opt_from_number: tostring;
def _opt_to_string:
if . != null then
( "\"\(.)\""
| try
( fromjson
| if type != "string" then error end
)
catch null
)
end;
def _opt_from_string: if . then tojson[1:-1] else "" end;
def _opt_is_string_pair:
type == "array" and length == 2 and all(type == "string");
def _opt_to_array(f):
try
( fromjson
| if type == "array" and (all(f) | not) then null end
)
catch null;
def _opt_to_array_string_pair: _opt_to_array(_opt_is_string_pair);
def _opt_to_array_string: _opt_to_array(type == "string");
def _opt_from_array: tojson;
# TODO: cleanup
def _trim: capture("^\\s*(?<str>.*?)\\s*$"; "").str;
# "0-255=brightwhite,0=brightblack,32-126:9-13=white" -> [{"ranges": [[0-255]], value: "brightwhite"}, ...]
def _csv_ranges_to_array:
( split(",")
| map(
( _trim
| split("=")
| { ranges:
( .[0]
| split(":")
| map(split("-") | map(tonumber))
),
value: .[1]
}
))
);
def _opt_to_csv_ranges_array:
try _csv_ranges_to_array
catch null;
def _opt_from_csv_ranges_array:
( map(
( (.ranges | map(join("-")) | join(":"))
+ "="
+ .value
)
)
| join(",")
);
# "key=value,a=b,..." -> {"key": "value", "a": "b", ...}
def _csv_kv_to_obj:
( split(",")
| map(_trim | split("=") | {key: .[0], value: .[1]})
| from_entries
);
def _opt_to_csv_kv_obj:
try _csv_kv_to_obj
catch null;
def _opt_from_csv_kv_obj:
( to_entries
| map("\(.key)=\(.value)")
| join(",")
);
def _opt_to_fuzzy:
( . as $s
| try fromjson
catch $s
);
def _opt_to($type):
if $type == "array_string" then _opt_to_array_string
elif $type == "array_string_pair" then _opt_to_array_string_pair
elif $type == "boolean" then _opt_to_boolean
elif $type == "csv_kv_obj" then _opt_to_csv_kv_obj
elif $type == "csv_ranges_array" then _opt_to_csv_ranges_array
elif $type == "number" then _opt_to_number
elif $type == "string" then _opt_to_string
elif $type == "fuzzy" then _opt_to_fuzzy
else error("unknown type \($type)")
end;
def _opt_from($type):
if $type == "array_string" then _opt_from_array
elif $type == "array_string_pair" then _opt_from_array
elif $type == "boolean" then _opt_from_boolean
elif $type == "csv_kv_obj" then _opt_from_csv_kv_obj
elif $type == "csv_ranges_array" then _opt_from_csv_ranges_array
elif $type == "number" then _opt_from_number
elif $type == "string" then _opt_from_string
else error("unknown type \($type)")
end;
def _opt_cli_arg_to_options:
( _opt_options as $opts
| with_entries(
( .key as $k
| .value |= _opt_to($opts[$k] // "fuzzy")
| select(.value != null)
)
)
);
def _opt_cli_arg_from_options:
( _opt_options as $opts
| with_entries(
( .key as $k
| .value |= _opt_from($opts[$k] // "string")
| select(.value != null)
)
)
);
def _opt_cli_opts:
{
"arg": {
long: "--arg",
description: "Set variable $NAME to string VALUE",
pairs: "NAME VALUE"
},
"argjson": {
long: "--argjson",
description: "Set variable $NAME to JSON",
pairs: "NAME JSON"
},
"compact": {
short: "-c",
long: "--compact-output",
description: "Compact output",
bool: true
},
"color_output": {
short: "-C",
long: "--color-output",
description: "Force color output",
bool: true
},
"decode_format": {
short: "-d",
long: "--decode",
description: "Decode format (probe)",
string: "NAME"
},
"decode_file": {
long: "--decode-file",
description: "Set variable $NAME to decode of file",
pairs: "NAME PATH"
},
"expr_file": {
short: "-f",
long: "--from-file",
description: "Read EXPR from file",
string: "PATH"
},
"show_help": {
short: "-h",
long: "--help",
description: "Show help for TOPIC (ex: --help, --help formats)",
string: "[TOPIC]",
optional: true
},
"join_output": {
short: "-j",
long: "--join-output",
description: "No newline between outputs",
bool: true
},
"include_path": {
short: "-L",
long: "--include-path",
description: "Include search path",
array: "PATH"
},
"null_output": {
short: "-0",
long: "--null-output",
# for jq compatibility
aliases: ["--nul-output"],
description: "Null byte between outputs",
bool: true
},
"null_input": {
short: "-n",
long: "--null-input",
description: "Null input (use input and inputs functions to read)",
bool: true
},
"monochrome_output": {
short: "-M",
long: "--monochrome-output",
description: "Force monochrome output",
bool: true
},
"option": {
short: "-o",
long: "--option",
description: "Set option (ex: -o color=true, see --help options)",
object: "KEY=VALUE",
},
"string_input": {
short: "-R",
long: "--raw-input",
description: "Read raw input strings (don't decode)",
bool: true
},
"raw_file": {
long: "--raw-file",
# for jq compatibility
aliases: ["--raw-file"],
description: "Set variable $NAME to string content of file",
pairs: "NAME PATH"
},
"raw_string": {
short: "-r",
# for jq compat, is called raw string internally, "raw output" is if
# we can output raw bytes or not
long: "--raw-output",
description: "Raw string output (without quotes)",
bool: true
},
"repl": {
short: "-i",
long: "--repl",
description: "Interactive REPL",
bool: true
},
"slurp": {
short: "-s",
long: "--slurp",
description: "Read (slurp) all inputs into an array",
bool: true
},
"show_version": {
short: "-v",
long: "--version",
description: "Show version",
bool: true
},
};
def options($opts):
( stdout_tty as $stdout
| ( [{width: $stdout.width}]
+ _options_stack
+ [$opts]
)
| add
| ( if .width != 0 then [_intdiv(_intdiv(.width; 8); 2) * 2, 4] | max
else 16
end
) as $display_bytes
# default if not set
| .display_bytes |= (. // $display_bytes)
| .line_bytes |= (. // $display_bytes)
);
def options: options({});