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   73210 use strict;
  1         3  
  1         35  
4 1     1   5 use vars qw($VERSION @EXPORT);
  1         2  
  1         93  
5              
6 1     1   7 use Exporter qw(import);
  1         2  
  1         70  
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.001';
19              
20 1     1   6 use Carp;
  1         2  
  1         1033  
21              
22             sub make_fieldsort {
23 34     34 0 16001 my $selfname;
24 34 100       125 if ((caller)[0] eq 'Sort::Fields') {
25 29         404 ($selfname) = (caller 1)[3] =~ /([^:]*)$/;
26             } else {
27 5         10 $selfname = 'make_fieldsort'
28             };
29 34 100       105 unless (@_) {
30 1         83 croak "$selfname requires argument(s)";
31             }
32              
33 33         52 my ($sep, $cols);
34 33 100       65 if (ref $_[0]) {
35 4         7 $sep = '\\s+'
36             } else {
37 29         76 $sep = shift;
38             }
39 33 100       84 unless (ref($cols = shift) eq 'ARRAY') {
40 4         386 croak "$selfname field specifiers must be in anon array";
41             }
42 29         43 my (@sortcode, @col);
43 29         41 my $level = 1;
44 29         39 my $maxcol = -1;
45 29         35 my $stable = 0;
46 29 100 100     174 if (@$cols and $$cols[0] eq '-') {
47 9         16 shift @$cols;
48 9         17 $stable = 1;
49             }
50 29 100       54 unless (@$cols) {
51 4         376 croak "$selfname must have at least one field specifier";
52             }
53 25         49 for (@$cols) {
54 30 100       110 unless (/^-?\d+n?$/) {
55 4         362 croak "improperly formatted $selfname column specifier '$_'";
56             }
57 26 100       82 my ($a, $b) = /^-/ ? qw(b a) : qw(a b);
58 26 100       69 my $op = /n$/ ? '<=>' : 'cmp';
59 26         87 my ($col) = /^-?(\d+)/;
60 26 100       65 if ($col == 0) { # column 0 gives the entire string
61 2         7 push @sortcode, "\$${a}->[0] $op \$${b}->[0]";
62 2         6 next;
63             }
64 24         97 push @col, (/(\d+)/)[0] - 1;
65 24 50       64 $maxcol = $col[-1] if $maxcol < $col[-1];
66 24 100       46 if ($stable) {
67             # indices are offset by 1 in this case
68 6         12 my $levp1 = $level + 1;
69 6         21 push @sortcode, "\$${a}->[$levp1] $op \$${b}->[$levp1]";
70             } else {
71 18         53 push @sortcode, "\$${a}->[$level] $op \$${b}->[$level]";
72             }
73 24         52 $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         1707 eval '"" =~ /$sep/';
78 21 100       135 if ($@) {
79 4         405 croak "probable regexp error in $selfname arg: /$sep/\n$@";
80             }
81 17         25 my $splitfunc;
82 17         1254 $splitfunc = eval 'sub { (split /$sep/o, $_, $maxcol + 2)[@col] } ';
83 17 50       50 if ($@) {
84 0         0 die "eval failed in $selfname (internal error?)\n$@";
85             }
86 17         48 my $sortcode = join " or ", @sortcode;
87 17         26 my $sub;
88 17 100       39 if ($stable) {
89 4         5 my $i; # the $i for the stable sort closure
90 4         563 $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         1684 $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         342 $sub;
119             }
120              
121             sub make_stable_fieldsort {
122 5 100   5 0 15833 unless (@_) {
123 1         80 croak "make_stable_fieldsort requires argument(s)";
124             }
125 4 100 66     24 if (ref $_[0] eq 'ARRAY') {
    100          
126 2         3 unshift @{$_[0]}, '-';
  2         6  
127             } elsif (@_ > 1 and ref $_[1] eq 'ARRAY') {
128 1         2 unshift @{$_[1]}, '-';
  1         4  
129             }
130 4         11 make_fieldsort @_;
131             }
132              
133             sub fieldsort {
134 19 100   19 0 34197 unless (@_) {
135 1         161 croak "fieldsort requires argument(s)";
136             }
137 18         36 my ($sep, $cols);
138 18 100       56 if (ref $_[0]) {
139 16         27 $sep = '\\s+'
140             } else {
141 2         5 $sep = shift;
142             }
143 18         27 $cols = shift;
144 18         41 make_fieldsort($sep, $cols)->(@_);
145             }
146              
147             sub stable_fieldsort {
148 8 100   8 0 21565 unless (@_) {
149 1         85 croak "stable_fieldsort requires argument(s)";
150             }
151 7         15 my ($sep, $cols);
152 7 100 66     31 if (ref $_[0] eq 'ARRAY') {
    100          
153 5         11 $sep = '\\s+';
154 5         7 unshift @{$_[0]}, '-';
  5         17  
155             } elsif (@_ > 1 and ref $_[1] eq 'ARRAY') {
156 1         3 $sep = shift;
157 1         3 unshift @{$_[1]}, '-';
  1         4  
158             }
159 7         13 $cols = shift;
160 7         16 make_fieldsort($sep, $cols)->(@_);
161             }
162              
163              
164             1;
165             __END__