File Coverage

blib/lib/AppBase/Sort.pm
Criterion Covered Total %
statement 8 47 17.0
branch 0 22 0.0
condition n/a
subroutine 3 6 50.0
pod 1 1 100.0
total 12 76 15.7


line stmt bran cond sub pod time code
1             package AppBase::Sort;
2              
3 1     1   334015 use 5.010001;
  1         7  
4 1     1   5 use strict;
  1         1  
  1         29  
5 1     1   7 use warnings;
  1         2  
  1         741  
6              
7             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
8             our $DATE = '2024-04-20'; # DATE
9             our $DIST = 'AppBase-Sort'; # DIST
10             our $VERSION = '0.006'; # VERSION
11              
12             our %SPEC;
13              
14             $SPEC{sort_appbase} = {
15             v => 1.1,
16             summary => 'A base for sort-like CLI utilities',
17             description => <<'MARKDOWN',
18              
19             This routine provides a base for Unix sort-like CLI utilities. It accepts
20             coderef as source of lines, which in the actual utilities can be from
21             stdin/files or other sources. It provides common options like `-i`, `-r`, and so
22             on.
23              
24             Examples of CLI utilities that are based on this: (which
25             is included in the `AppBase-Sort` distribution).
26              
27             Why? For sorting lines from files or stdin and "standard" sorting criteria, this
28             utility is no match for the standard Unix `sort` (or its many alternatives). But
29             `AppBase::Sort` is a quick way to create sort-like utilities that sort
30             lines from alternative sources and/or using custom sort criteria.
31              
32             MARKDOWN
33             args => {
34             ignore_case => {
35             summary => 'If set to true, will search case-insensitively',
36             schema => 'bool*',
37             cmdline_aliases => {i=>{}},
38             #tags => ['category:'],
39             },
40             reverse => {
41             summary => 'Reverse sort order',
42             schema => 'bool*',
43             cmdline_aliases => {r=>{}},
44             #tags => ['category:'],
45             },
46             _source => {
47             schema => 'code*',
48             tags => ['hidden'],
49             description => <<'MARKDOWN',
50              
51             Code to produce *chomped* lines of text to sort. Required.
52              
53             Will be called with these arguments:
54              
55             ()
56              
57             Should return the next line or undef if the source is exhausted.
58              
59             MARKDOWN
60             },
61             _gen_keygen => {
62             schema => 'code*',
63             tags => ['hidden'],
64             description => <<'MARKDOWN',
65              
66             Code to generate a key-generating (keygen) routine (routine that accepts a value
67             and converts it to another value (key) that can be compared using `cmp` or
68             `<=>`.
69              
70             Either `_gen_comparer`, `_gen_keygen`, or `_gen_sorter` argument is required.
71              
72             Will be called with these arguments:
73              
74             ($args)
75              
76             Should return the following:
77              
78             ($keygen, $is_numeric)
79              
80             where `$keygen` is the keygen routine and `$is_numeric` is a boolean value which
81             dictates whether key comparison will be done numerically (using `<=>`) or
82             asciibetically (using `cmp`).
83              
84             MARKDOWN
85             },
86             _gen_sorter => {
87             schema => 'code*',
88             tags => ['hidden'],
89             description => <<'MARKDOWN',
90              
91             Code to generate a sorter routine (routine that accepts a list of values and
92             return the sorted values, like Perl's builtin `sort`.
93              
94             Either `_gen_comparer`, `_gen_keygen`, or `_gen_sorter` argument is required.
95              
96             Will be called with these arguments:
97              
98             ($args)
99              
100             Should return the following:
101              
102             $sorter
103              
104             where `$sorter` is the comparer routine which in turn will be called during sort
105             with:
106              
107             (@lines)
108              
109             MARKDOWN
110             },
111             _gen_comparer => {
112             schema => 'code*',
113             tags => ['hidden'],
114             description => <<'MARKDOWN',
115              
116             Code to generate a comparer routine (routine that accepts two values and return
117             -1/0/1, like Perl's builtin `cmp` or `<=>`.
118              
119             Either `_gen_comparer`, `_gen_keygen`, or `_gen_sorter` argument is required.
120              
121             Will be called with these arguments:
122              
123             ($args)
124              
125             Should return the following:
126              
127             $cmp
128              
129             where `$cmp` is the comparer routine which in turn will be called during sort
130             with:
131              
132             ($a, $b)
133              
134             MARKDOWN
135             },
136             },
137             };
138             sub sort_appbase {
139 0     0 1   my %args = @_;
140              
141 0           my $opt_ci = $args{ignore_case};
142 0           my $opt_reverse = $args{reverse};
143              
144 0           my $source = $args{_source};
145 0           my @lines;
146 0           while (defined(my $line = $source->())) { push @lines, $line }
  0            
147              
148 0 0         if ($args{_gen_comparer}) {
    0          
    0          
149 0           my $cmp = $args{_gen_comparer}->(\%args);
150 0 0         if ($opt_ci) {
151 0 0         if ($opt_reverse) {
152 0           @lines = sort { $cmp->(lc($b), lc($a)) } @lines;
  0            
153             } else {
154 0           @lines = sort { $cmp->(lc($a), lc($b)) } @lines;
  0            
155             }
156             } else {
157 0 0         if ($opt_reverse) {
158 0           @lines = sort { $cmp->($b, $a) } @lines;
  0            
159             } else {
160 0           @lines = sort { $cmp->($a, $b) } @lines;
  0            
161             }
162             }
163             } elsif ($args{_gen_sorter}) {
164 0           my $sorter = $args{_gen_sorter}->(\%args);
165 0           @lines = $sorter->(@lines);
166             } elsif ($args{_gen_keygen}) {
167 0           my ($keygen, $is_numeric) = $args{_gen_keygen}->(\%args);
168 0           require Sort::Key;
169 0 0         if ($is_numeric) {
170 0 0         if ($opt_reverse) {
171 0           @lines = &Sort::Key::rnkeysort($keygen, @lines);
172             } else {
173 0           @lines = &Sort::Key::nkeysort ($keygen, @lines);
174             }
175             } else {
176 0 0         if ($opt_reverse) {
177 0 0         if ($opt_ci) {
178 0     0     @lines = &Sort::Key::rkeysort(sub { lc $keygen->($_[0]) }, @lines);
  0            
179             } else {
180 0           @lines = &Sort::Key::rkeysort($keygen, @lines);
181             }
182             } else {
183 0 0         if ($opt_ci) {
184 0     0     @lines = &Sort::Key::keysort (sub { lc $keygen->($_[0]) }, @lines);
  0            
185             } else {
186 0           @lines = &Sort::Key::keysort ($keygen, @lines);
187             }
188             }
189             }
190             } else {
191 0           die "Either _gen_comparer, _gen_sorter, or _gen_keygen must be specified";
192             }
193              
194             return [
195 0           200,
196             "OK",
197             \@lines,
198             ];
199             }
200              
201             1;
202             # ABSTRACT: A base for sort-like CLI utilities
203              
204             __END__