#!/usr/bin/env perl
my
$copyright
=
<<'COPYRIGHT';
# Copyright 2021 by Christian Jaeger <ch@christianjaeger.ch>
# Published under the same terms as perl itself
COPYRIGHT
my
(
$email_full
) =
$copyright
=~ / by ([^\n]*)/s;
my
(
$mydir
,
$myname
);
BEGIN {
$0 =~ /(.*?)([^\/]+)\z/s or
die
"?"
;
(
$mydir
,
$myname
) = ($1, $2);
}
sub
usage {
print
STDERR
map
{
"$_\n"
}
@_
if
@_
;
print
"
$myname
file.csv file.mint
Convert CSV to JSON or Mint record literal syntax. It expects the
input file to have a title row, and uses the fields in that as the
field names (
with
Mint compatible mangling).
file.csv or file.mint can also be
'-'
for
stdin or stdout,
respectively
Options:
--json output JSON (
default
)
--mint output Mint record literal syntax
--auto-numbers
treat strings that look like numbers as numbers
--auto-integers
strings that look like numbers and only have zeroes
after
the dot are treated as integers; currently implies
--auto-numbers
--repl
open
a repl instead of carrying out the identity
conversion
--conversion
'code'
code is Perl that must evaluate to a function that
receives the inputs and translate to the outputs.
Example code:
'sub(\$records) { groupByNumber(\$records, \"Quadrat\")}'
or
'sub(\$records) { groupByNumber(\$records, \"Quadrat\")->map(sub (\$group){ \$group->sort(on(hashkey(\"Frequency\"), \\&real_cmp))})}'
(
$email_full
)
";
exit
(
@_
? 1 : 0);
}
my
$verbose
= 0;
our
(
$opt_json
,
$opt_mint
,
$opt_auto_numbers
,
$opt_auto_integers
,
$opt_repl
,
$opt_conversion
);
GetOptions(
"verbose"
=> \
$verbose
,
"help"
=>
sub
{usage},
"json"
=> \
$opt_json
,
"mint"
=> \
$opt_mint
,
"auto-numbers"
=> \
$opt_auto_numbers
,
"auto-integers"
=> \
$opt_auto_integers
,
"repl"
=> \
$opt_repl
,
"conversion=s"
=> \
$opt_conversion
,
) or
exit
1;
usage
unless
@ARGV
== 2;
our
$output_format
=
$opt_json
? (
$opt_mint
? usage
"both --json and --mint given"
:
"JSON"
)
: (
$opt_mint
?
"Mint"
:
"JSON"
);
$opt_auto_numbers
= 1
if
$opt_auto_integers
;
my
$settings
= {
output_format
=>
$output_format
,
auto_numbers
=>
$opt_auto_numbers
,
auto_integers
=>
$opt_auto_integers
,
};
my
(
$file_csv
,
$file_mint
) =
@ARGV
;
sub
convertfile (
$fn
) {
my
$csvinput
=
$file_csv
eq
"-"
? stdin_utf8 :
$file_csv
;
my
$rows
= csv_file_to_rows(
$csvinput
, {
sep_char
=>
","
,
eol
=>
"\n"
});
my
$titles
=
$rows
->first;
my
$body
=
$rows
->rest;
my
$body_hms
=
$body
->
map
(
sub
(
$row
) { ziphash
$titles
,
$row
});
my
$body_hms2
=
$fn
->(
$body_hms
);
my
$mint
=
"[\n"
.
$body_hms2
->
map
(
sub
(
$v
) {
to_json
$v
,
$settings
}
)->strings_join(
",\n"
)
.
"\n]\n"
;
my
$out
;
if
(
$file_mint
eq
"-"
) {
$out
= stdout_utf8;
}
else
{
open
$out
,
">:encoding(UTF-8)"
,
$file_mint
or
die
"can't open file for writing: '$file_mint'"
;
}
print
$out
$mint
or
die
"print: $!"
;
close
$out
or
die
"close($file_mint): $!"
;
}
sub
groupByNumber (
$records
,
$key
) {
$records
->
sort
(on(hashkey(
$key
), \
&real_cmp
))
->group(on(hashkey(
$key
), \
&number_eq
))
}
if
(
$opt_repl
) {
FP::Repl::repl();
}
elsif
(
$opt_conversion
) {
my
$function
=
eval
$opt_conversion
;
die
$@
if
$@;
ref
(
$function
) eq
"CODE"
or
die
"expecting --conversion's argument to evaluate to a function, but got: $function"
;
convertfile(
$function
);
}
else
{
convertfile(\
&identity
);
}