File Coverage

blib/lib/File/Serialize.pm
Criterion Covered Total %
statement 91 98 92.8
branch 33 48 68.7
condition 30 43 69.7
subroutine 20 21 95.2
pod n/a
total 174 210 82.8


line stmt bran cond sub pod time code
1             package File::Serialize;
2             our $AUTHORITY = 'cpan:YANICK';
3             # ABSTRACT: DWIM file serialization/deserialization
4             $File::Serialize::VERSION = '1.5.0';
5 7     7   1339321 use v5.16.0;
  7         56  
6              
7 7     7   43 use feature 'current_sub';
  7         11  
  7         865  
8              
9 7     7   39 use strict;
  7         37  
  7         179  
10 7     7   31 use warnings;
  7         13  
  7         206  
11              
12 7     7   3126 use Class::Load qw/ load_class /;
  7         120182  
  7         403  
13 7     7   3865 use List::AllUtils qw/ uniq /;
  7         66169  
  7         653  
14 7     7   53 use List::Util 1.41 qw/ pairgrep first none any pairmap /;
  7         86  
  7         356  
15 7     7   2211 use Path::Tiny;
  7         28876  
  7         404  
16              
17             use Module::Pluggable
18 7         47 require => 1,
19             sub_name => '_all_serializers',
20             search_path => __PACKAGE__ . '::Serializer'
21 7     7   3329 ;
  7         52933  
22              
23 7     7   618 use parent 'Exporter::Tiny';
  7         13  
  7         47  
24              
25             our @EXPORT = qw/ serialize_file deserialize_file transerialize_file /;
26              
27             our $implicit_transform;
28              
29             sub _generate_serialize_file {
30 36     36   3504 my( undef, undef, undef, $global )= @_;
31              
32             return sub {
33 30     30   34297 my( $file, $content, $options ) = @_;
34              
35 30 50 66     150 $options = { format => $options } if $options and not ref $options;
36              
37 30 100       133 $options = { %$global, %{ $options||{} } } if $global;
  30 50       186  
38             # default to utf8 => 1
39 30   100     176 $options->{utf8} //= 1;
40 30   50     133 $options->{allow_nonref} //= 1;
41 30   100     128 $options->{pretty} //= 1;
42 30   100     131 $options->{canonical} //= 1;
43              
44 30 100 66     258 $file = path($file) unless $file =~ /^-/ or ref $file eq 'SCALAR';
45              
46 30         870 my $serializer = _serializer($file, $options);
47              
48             $file = path( join '.', $file, $serializer->extension )
49 29 0 33     131 if $options->{add_extension} and $file ne '-'
      33        
50             and ref $file ne 'SCALAR';
51              
52 29 100       100 my $method = $options->{utf8} ? 'spew_utf8' : 'spew';
53              
54 29         775 my $serialized = $serializer->serialize($content,$options);
55              
56 29 50       1889 return print $serialized if $file eq '-';
57              
58 29 100       191 if( ref $file eq 'SCALAR' ) {
59 4         34 $$file = $serialized;
60             }
61             else {
62 25         116 $file->$method($serialized);
63             }
64             }
65 36         221 }
66              
67             sub _generate_deserialize_file {
68 36     36   2020 my( undef, undef, undef, $global ) = @_;
69              
70             return sub {
71 36     36   20379 my( $file, $options ) = @_;
72              
73 36 100 66     291 $file = path($file) unless $file eq '-' or ref $file eq 'SCALAR';
74              
75 36 100       944 $options = { %$global, %{ $options||{} } } if $global;
  36 50       211  
76 36   50     214 $options->{utf8} //= 1;
77 36   100     165 $options->{allow_nonref} //= 1;
78              
79 36         139 my $method = 'slurp' . ( '_utf8' ) x !! $options->{utf8};
80              
81 36         101 my $serializer = _serializer($file, $options);
82              
83             $file = path( join '.', $file, $serializer->extension )
84 36 0 33     202 if $options->{add_extension} and $file ne '-' and ref $file ne 'SCALAR';
      33        
85              
86             return $serializer->deserialize(
87 36 100       487 $file =~ /^-/ ? do { local $/ = }
  0 50       0  
88             : ref $file eq 'SCALAR' ? $$file
89             : $file->$method,
90             $options
91             );
92             }
93 36         228 }
94              
95             sub _generate_transerialize_file {
96              
97 15     15   696 my $serialize_file = _generate_serialize_file(@_);
98 15         43 my $deserialize_file = _generate_deserialize_file(@_);
99              
100              
101             return sub {
102 12     12   10131 my( $in, @chain ) = @_;
103 12 100       37 my $data = ref($in) ? $in : $deserialize_file->($in);
104              
105 12         66 while( my $step = shift @chain) {
106 22 100 100     219 if ( ref $step eq 'CODE' ) {
    100          
    100          
    100          
    50          
107 8         12 local $_ = $data;
108 8 50       19 if( $implicit_transform ) {
109 0         0 $step->($data);
110 0         0 $data = $_;
111             }
112             else {
113 8         18 $data = $step->($data);
114             }}
115             elsif ( ref $step eq 'ARRAY' ) {
116 1 50       5 die "subranch step can only be the last step of the chain"
117             if @chain;
118 1         3 for my $branch( @$step ) {
119 2         20 __SUB__->($data,@$branch);
120             }
121             }
122             elsif ( not ref $step or ref($step) =~ /Path::Tiny/ ) {
123 8 50       20 die "filename '$step' not at the end of the chain"
124             unless @chain <= 1;
125              
126 8         22 $serialize_file->( $step, $data, shift @chain );
127             }
128             elsif ( ref $step eq 'HASH' ) {
129 4         19 while( my ($f,$o) = each %$step ) {
130 6         32 $serialize_file->($f,$data,$o);
131             }
132             }
133             elsif ( ref $step eq 'SCALAR' ) {
134 1         5 $$step = $data;
135             }
136             else {
137 0         0 die "wrong chain argument";
138             }
139             }
140              
141             }
142 15         94 }
143              
144             sub _all_operative_formats {
145 0     0   0 my $self = shift;
146 0         0 return uniq map { $_->extension } $self->_all_operative_formats;
  0         0  
147             }
148              
149             sub _all_operative_serializers {
150             sort {
151 448 0       11852 $b->precedence <=> $a->precedence
152             or
153             $a cmp $b
154             }
155 504         86136 grep { $_->is_operative }
156 56     56   258 grep { $_->precedence }
  504         479944  
157             __PACKAGE__->_all_serializers;
158             }
159              
160             sub _serializer {
161 66     66   152 my( $self, $options ) = @_;
162              
163 7     7   30530 no warnings qw/ uninitialized /;
  7         27  
  7         1430  
164              
165 66   100     381 my $serializers = $options->{serializers} || [ __PACKAGE__->_all_operative_serializers ];
166 66         332 s/^\+/File::Serialize::Serializer::/ for @$serializers;
167              
168 66   100     504 my $format = $options->{format} || ( ( ref $self ? $self->basename : $self ) =~ /\.(\w+)$/ )[0];
169              
170 66   100 208   2271 return( first { $_->does_extension($format) } @$serializers
  208         669  
171             or die "no serializer found for $format"
172             );
173             }
174              
175             1;
176              
177             __END__