File Coverage

blib/lib/Sort/Fields.pm
Criterion Covered Total %
statement 88 90 97.7
branch 45 48 93.7
condition 7 9 77.7
subroutine 7 7 100.0
pod 0 4 0.0
total 147 158 93.0


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