App-CSVUtils
view release on metacpan or search on metacpan
lib/App/CSVUtils.pm view on Meta::CPAN
},
);
our %argspecopt_input_filename = (
input_filename => {
summary => 'Input CSV file',
description => <<'_',
Use `-` to read from stdin.
Encoding of input file is assumed to be UTF-8.
_
schema => 'filename*',
default => '-',
'x.completion' => $xcomp_csvfiles,
tags => ['category:input'],
},
);
our %argspecopt_input_filenames = (
input_filenames => {
'x.name.is_plural' => 1,
'x.name.singular' => 'input_filename',
summary => 'Input CSV files',
description => <<'_',
Use `-` to read from stdin.
Encoding of input file is assumed to be UTF-8.
_
schema => ['array*', of=>'filename*'],
default => ['-'],
'x.completion' => $xcomp_csvfiles,
tags => ['category:input'],
},
);
our %argspecopt_overwrite = (
overwrite => {
summary => 'Whether to override existing output file',
schema => 'bool*',
cmdline_aliases=>{O=>{}},
tags => ['category:output'],
},
);
our %argspecsopt_inplace = (
inplace => {
summary => 'Output to the same file as input',
schema => 'true*',
description => <<'_',
Normally, you output to a different file than input. If you try to output to the
same file (`-o INPUT.csv -O`) you will clobber the input file; thus the utility
prevents you from doing it. However, with this `--inplace` option, you can
output to the same file. Like perl's `-i` option, this will first output to a
temporary file in the same directory as the input file then rename to the final
file at the end. You cannot specify output file (`-o`) when using this option,
but you can specify backup extension with `-b` option.
Some caveats:
- if input file is a symbolic link, it will be replaced with a regular file;
- renaming (implemented using `rename()`) can fail if input filename is too long;
- value specified in `-b` is currently not checked for acceptable characters;
- things can also fail if permissions are restrictive;
_
tags => ['category:output'],
},
inplace_backup_ext => {
summary => 'Extension to add for backup of input file',
schema => 'str*',
default => '',
description => <<'_',
In inplace mode (`--inplace`), if this option is set to a non-empty string, will
rename the input file using this extension as a backup. The old existing backup
will be overwritten, if any.
_
cmdline_aliases => {b=>{}},
tags => ['category:output'],
},
);
our %argspecopt_output_filename = (
output_filename => {
summary => 'Output filename',
description => <<'_',
Use `-` to output to stdout (the default if you don't specify this option).
Encoding of output file is assumed to be UTF-8.
_
schema => 'filename*',
cmdline_aliases=>{o=>{}},
tags => ['category:output'],
},
);
our %argspecopt_output_filenames = (
output_filenames => {
summary => 'Output filenames',
description => <<'_',
Use `-` to output to stdout (the default if you don't specify this option).
Encoding of output file is assumed to be UTF-8.
_
schema => ['array*', of=>'filename*'],
cmdline_aliases=>{o=>{}},
tags => ['category:output'],
},
);
our %argspecopt_field = (
field => {
summary => 'Field name',
schema => 'str*',
cmdline_aliases => { f=>{} },
completion => \&_complete_field,
},
);
our %argspecopt_field_1 = (
field => {
summary => 'Field name',
schema => 'str*',
pos => 1,
cmdline_aliases => { f=>{} },
completion => \&_complete_field,
},
);
our %argspec_field_1 = (
lib/App/CSVUtils.pm view on Meta::CPAN
} # while getline
# XXX actually close filehandle except stdin
if ($after_close_input_file) {
log_trace "[csvutil] Calling after_close_input_file handler ...";
$after_close_input_file->($r);
if (delete $r->{wants_skip_files}) {
log_trace "[csvutil] Handler wants to skip reading all file, skipping";
last READ_CSV;
}
}
} # for input_filename
if ($after_close_input_files) {
log_trace "[csvutil] Calling after_close_input_files handler ...";
$after_close_input_files->($r);
}
} # READ_CSV
# cleanup stash from csv-reading-related keys
delete $r->{input_filenames};
delete $r->{input_filenum};
delete $r->{input_filename};
delete $r->{input_fh};
delete $r->{input_rownum};
delete $r->{input_data_rownum};
delete $r->{input_row};
delete $r->{input_row_as_hashref};
delete $r->{input_fields};
delete $r->{input_fields_idx};
delete $r->{orig_input_fields_idx};
delete $r->{code_getline};
delete $r->{wants_input_row_as_hashref};
if ($after_read_input) {
log_trace "[csvutil] Calling after_read_input handler ...";
$after_read_input->($r);
}
# cleanup stash from csv-outputting-related keys
delete $r->{output_num_of_files};
delete $r->{output_filenum};
if ($r->{output_fh}) {
if ($r->{output_filename} ne '-') {
log_info "[csvutil] Closing output file '$r->{output_filename}' ...";
close $r->{output_fh} or die [500, "Can't close output file '$r->{output_filename}': $!"];
}
delete $r->{output_fh};
}
if ($r->{util_args}{inplace}) {
my $output_filenum = $r->{output_filenum} // 0;
my $i = -1;
for my $output_filename (@{ $r->{output_filenames} }) {
$i++;
last if $i > $output_filenum;
(my $input_filename = $output_filename) =~ s/\.\w{5}\z//
or die [500, "BUG: Can't get original input file '$output_filename'"];
if (length(my $ext = $r->{util_args}{inplace_backup_ext})) {
my $backup_filename = $input_filename . $ext;
log_info "[csvutil] Backing up input file '$output_filename' -> '$backup_filename' ...";
rename $input_filename, $backup_filename or die [500, "Can't rename '$input_filename' -> '$backup_filename': $!"];
}
log_info "[csvutil] Renaming from temporary output file '$output_filename' -> '$input_filename' ...";
rename $output_filename, $input_filename or die [500, "Can't rename back '$output_filename' -> '$input_filename': $!"];
}
}
delete $r->{output_filenames};
delete $r->{output_filename};
delete $r->{output_rownum};
delete $r->{output_data_rownum};
delete $r->{code_print};
delete $r->{code_print_row};
delete $r->{code_print_header_row};
delete $r->{has_printed_header};
delete $r->{wants_switch_to_next_output_file};
if ($on_end) {
log_trace "[csvutil] Calling on_end hook handler ...";
$on_end->($r);
}
}; # MAIN_EVAL
my $err = $@;
if ($err) {
$err = [500, $err] unless ref $err;
return $err;
}
RETURN_RESULT:
if (!$r->{result}) {
$r->{result} = [200];
} elsif (!ref($r->{result})) {
$r->{result} = [500, "BUG: Result (r->{result}) is set to a non-reference ($r->{result}), probably by one of the handlers"];
} elsif (ref($r->{result}) ne 'ARRAY') {
$r->{result} = [500, "BUG: Result (r->{result}) is not set to an enveloped result (arrayref) ($r->{result}), probably by one of the handlers"];
}
$r->{result};
};
} # CREATE_CODE
my $meta;
CREATE_META: {
$meta = {
v => 1.1,
summary => $summary,
description => $description,
args => {},
args_rels => {},
links => $links,
examples => $examples,
tags => $tags,
};
CREATE_ARGS_PROP: {
if ($add_args) {
$meta->{args}{$_} = $add_args->{$_} for keys %$add_args;
}
if ($reads_csv) {
$meta->{args}{$_} = {%{$argspecs_csv_input{$_}}} for keys %argspecs_csv_input;
if ($reads_multiple_csv) {
$meta->{args}{input_filenames} = {%{$argspecopt_input_filenames{input_filenames}}};
_add_arg_pos($meta->{args}, 'input_filenames', 'slurpy');
push @$tags, 'reads-multiple-csv';
} else {
$meta->{args}{input_filename} = {%{$argspecopt_input_filename{input_filename}}};
_add_arg_pos($meta->{args}, 'input_filename');
}
push @$tags, 'reads-csv';
} # if reads_csv
if ($writes_csv) {
$meta->{args}{$_} = {%{$argspecs_csv_output{$_}}} for keys %argspecs_csv_output;
if ($reads_csv) {
$meta->{args}{$_} = {%{$argspecsopt_inplace{$_}}} for keys %argspecsopt_inplace;
$meta->{args_rels}{'dep_all&'} //= [];
push @{ $meta->{args_rels}{'dep_all&'} }, ['inplace_backup_ext', ['inplace']];
$meta->{args_rels}{'choose_one&'} //= [];
push @{ $meta->{args_rels}{'choose_one&'} }, ['inplace', 'output_filename'];
push @{ $meta->{args_rels}{'choose_one&'} }, ['inplace', 'output_filenames'];
}
if ($writes_multiple_csv) {
$meta->{args}{output_filenames} = {%{$argspecopt_output_filenames{output_filenames}}};
_add_arg_pos($meta->{args}, 'output_filenames', 'slurpy');
if ($reads_csv) {
$meta->{args_rels}{'choose_one&'} //= [];
push @{ $meta->{args_rels}{'choose_one&'} }, [qw/output_filenames inplace/];
}
push @$tags, 'writes-multiple-csv';
} else {
$meta->{args}{output_filename} = {%{$argspecopt_output_filename{output_filename}}};
_add_arg_pos($meta->{args}, 'output_filename');
if ($reads_csv) {
$meta->{args_rels}{'choose_one&'} //= [];
push @{ $meta->{args_rels}{'choose_one&'} }, [qw/output_filename inplace/];
}
}
$meta->{args}{overwrite} = {%{$argspecopt_overwrite{overwrite}}};
$meta->{args_rels}{'dep_any&'} //= [];
push @{ $meta->{args_rels}{'dep_any&'} }, ['overwrite', ['output_filename', 'output_filenames']];
push @$tags, 'writes-csv';
} # if writes csv
} # CREATE_ARGS_PROP
CREATE_ARGS_RELS_PROP: {
$meta->{args_rels} = {};
if ($add_args_rels) {
$meta->{args_rels}{$_} = $add_args_rels->{$_} for keys %$add_args_rels;
}
} # CREATE_ARGS_RELS_PROP
if ($add_meta_props) {
$meta->{$_} = $add_meta_props->{$_} for keys %$add_meta_props;
}
} # CREATE_META
{
my $package = caller();
no strict 'refs'; ## no critic: TestingAndDebugging::ProhibitNoStrict
*{"$package\::$name"} = $code;
#use DD; dd $meta;
${"$package\::SPEC"}{$name} = $meta;
}
1;
}
1;
# ABSTRACT: CLI utilities related to CSV
__END__
( run in 2.193 seconds using v1.01-cache-2.11-cpan-df04353d9ac )