File Coverage

blib/lib/Sort/Fields.pm
Criterion Covered Total %
statement 91 93 97.8
branch 45 48 93.7
condition 7 9 77.7
subroutine 8 8 100.0
pod 0 4 0.0
total 151 162 93.2


line stmt bran cond sub pod time code
1             package Sort::Fields;
2              
3 1     1   121974 use strict;
  1         2  
  1         46  
4 1     1   5 use vars qw($VERSION @EXPORT);
  1         2  
  1         56  
5              
6 1     1   4 use Exporter qw(import);
  1         2  
  1         67  
7             require 5.003_03;
8             ;
9             # Items to export into callers namespace by default. Note: do not export
10             # names by default without a very good reason. Use EXPORT_OK instead.
11             # Do not simply export all your public functions/methods/constants.
12             @EXPORT = qw(
13             make_fieldsort
14             fieldsort
15             make_stable_fieldsort
16             stable_fieldsort
17             );
18             $VERSION = '1.003';
19              
20 1     1   6 use Carp;
  1         3  
  1         1009  
21              
22             sub make_fieldsort {
23 34     34 0 21382 my $selfname;
24 34 100       154 if ((caller)[0] eq 'Sort::Fields') {
25 29         334 ($selfname) = (caller 1)[3] =~ /([^:]*)$/;
26             } else {
27 5         10 $selfname = 'make_fieldsort'
28             };
29 34 100       104 unless (@_) {
30 1         85 croak "$selfname requires argument(s)";
31             }
32              
33 33         45 my ($sep, $cols);
34 33 100       56 if (ref $_[0]) {
35 4         10 $sep = '\\s+'
36             } else {
37 29         39 $sep = shift;
38             }
39 33 100       86 unless (ref($cols = shift) eq 'ARRAY') {
40 4         588 croak "$selfname field specifiers must be in anon array";
41             }
42 29         35 my (@sortcode, @col);
43 29         49 my $level = 1;
44 29         54 my $maxcol = -1;
45 29         39 my $stable = 0;
46 29 100 100     133 if (@$cols and $$cols[0] eq '-') {
47 9         16 shift @$cols;
48 9         13 $stable = 1;
49             }
50 29 100       56 unless (@$cols) {
51 4         496 croak "$selfname must have at least one field specifier";
52             }
53 25         51 for (@$cols) {
54 30 100       116 unless (/^-?\d+n?$/) {
55 4         579 croak "improperly formatted $selfname column specifier '$_'";
56             }
57 26 100       68 my ($a, $b) = /^-/ ? qw(b a) : qw(a b);
58 26 100       74 my $op = /n$/ ? '<=>' : 'cmp';
59 26         59 my ($col) = /^-?(\d+)/;
60 26 100       58 if ($col == 0) { # column 0 gives the entire string
61 2         2 push @sortcode, "\$${a}->[0] $op \$${b}->[0]";
62 2         5 next;
63             }
64 24         587 push @col, (/(\d+)/)[0] - 1;
65 24 50       56 $maxcol = $col[-1] if $maxcol < $col[-1];
66 24 100       38 if ($stable) {
67             # indices are offset by 1 in this case
68 6         10 my $levp1 = $level + 1;
69 6         14 push @sortcode, "\$${a}->[$levp1] $op \$${b}->[$levp1]";
70             } else {
71 18         43 push @sortcode, "\$${a}->[$level] $op \$${b}->[$level]";
72             }
73 24         44 $level++;
74             }
75             # have to check this all by itself, since if there's a regex
76             # error it won't show up until the sub is called (urk!)
77 21         1285 eval '"" =~ /$sep/';
78 21 100       140 if ($@) {
79 4         622 croak "probable regexp error in $selfname arg: /$sep/\n$@";
80             }
81 17         20 my $splitfunc;
82 17         1160 $splitfunc = eval 'sub { (split /$sep/o, $_, $maxcol + 2)[@col] } ';
83 17 50       36 if ($@) {
84 0         0 die "eval failed in $selfname (internal error?)\n$@";
85             }
86 17         38 my $sortcode = join " or ", @sortcode;
87 17         20 my $sub;
88 17 100       26 if ($stable) {
89 4         5 my $i; # the $i for the stable sort closure
90 4         510 $sub = eval qq{
91             sub {
92             if (\$^W and not wantarray) {
93             carp "fieldsort called in scalar or void context";
94             }
95             \$i = 0; # reset counter in case reusing this closure
96             map \$_->[0],
97             sort { $sortcode or \$a->[1] <=> \$b->[1] }
98             map [\$_, \$i++, \$splitfunc->(\$_)],
99             \@_;
100             }
101             }
102             } else {
103 13         1494 $sub = eval qq{
104             sub {
105             if (\$^W and not wantarray) {
106             carp "fieldsort called in scalar or void context";
107             }
108             map \$_->[0],
109             sort { $sortcode }
110             map [\$_, \$splitfunc->(\$_)],
111             \@_;
112             }
113             }
114             }
115 17 50       54 if ($@) {
116 0         0 die "eval failed in $selfname (internal error?)\n$@";
117             }
118 17         274 $sub;
119             }
120              
121             sub make_stable_fieldsort {
122 5 100   5 0 30373 unless (@_) {
123 1         158 croak "make_stable_fieldsort requires argument(s)";
124             }
125 4 100 66     32 if (ref $_[0] eq 'ARRAY') {
    100          
126 2         6 unshift @{$_[0]}, '-';
  2         10  
127             } elsif (@_ > 1 and ref $_[1] eq 'ARRAY') {
128 1         3 unshift @{$_[1]}, '-';
  1         5  
129             }
130 4         15 make_fieldsort @_;
131             }
132              
133             sub fieldsort {
134 19 100   19 0 251477 unless (@_) {
135 1         133 croak "fieldsort requires argument(s)";
136             }
137 18         30 my ($sep, $cols);
138 18 100       37 if (ref $_[0]) {
139 16         20 $sep = '\\s+'
140             } else {
141 2         3 $sep = shift;
142             }
143 18         41 $cols = shift;
144 18         39 make_fieldsort($sep, $cols)->(@_);
145             }
146              
147             sub stable_fieldsort {
148 8 100   8 0 22337 unless (@_) {
149 1         86 croak "stable_fieldsort requires argument(s)";
150             }
151 7         13 my ($sep, $cols);
152 7 100 66     28 if (ref $_[0] eq 'ARRAY') {
    100          
153 5         7 $sep = '\\s+';
154 5         8 unshift @{$_[0]}, '-';
  5         14  
155             } elsif (@_ > 1 and ref $_[1] eq 'ARRAY') {
156 1         2 $sep = shift;
157 1         2 unshift @{$_[1]}, '-';
  1         3  
158             }
159 7         10 $cols = shift;
160 7         15 make_fieldsort($sep, $cols)->(@_);
161             }
162              
163              
164             1;
165             __END__