File Coverage

blib/lib/File/Serialize.pm
Criterion Covered Total %
statement 90 98 91.8
branch 29 48 60.4
condition 27 43 62.7
subroutine 20 21 95.2
pod n/a
total 166 210 79.0


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.1';
5 7     7   1923956 use v5.16.0;
  7         81  
6              
7 7     7   45 use feature 'current_sub';
  7         14  
  7         1121  
8              
9 7     7   50 use strict;
  7         16  
  7         221  
10 7     7   38 use warnings;
  7         25  
  7         319  
11              
12 7     7   4051 use Class::Load qw/ load_class /;
  7         147233  
  7         506  
13 7     7   4956 use List::AllUtils qw/ uniq /;
  7         82548  
  7         775  
14 7     7   68 use List::Util 1.41 qw/ pairgrep first none any pairmap /;
  7         109  
  7         436  
15 7     7   3030 use Path::Tiny;
  7         38896  
  7         509  
16              
17             use Module::Pluggable
18 7         58 require => 1,
19             sub_name => '_all_serializers',
20             search_path => __PACKAGE__ . '::Serializer'
21 7     7   4688 ;
  7         66139  
22              
23 7     7   807 use parent 'Exporter::Tiny';
  7         19  
  7         52  
24              
25             our @EXPORT = qw/ serialize_file deserialize_file transerialize_file /;
26              
27             our $implicit_transform;
28              
29             sub _generate_serialize_file {
30 33     33   4918 my( undef, undef, undef, $global )= @_;
31              
32             return sub {
33 25     25   34740 my( $file, $content, $options ) = @_;
34              
35 25 50 66     139 $options = { format => $options } if $options and not ref $options;
36              
37 25 100       122 $options = { %$global, %{ $options||{} } } if $global;
  25 50       176  
38             # default to utf8 => 1
39 25   100     165 $options->{utf8} //= 1;
40 25   50     139 $options->{allow_nonref} //= 1;
41 25   100     128 $options->{pretty} //= 1;
42 25   100     96 $options->{canonical} //= 1;
43              
44 25 50 33     183 $file = path($file) unless $file =~ /^-/ or ref $file eq 'SCALAR';
45              
46 25         931 my $serializer = _serializer($file, $options);
47              
48             $file = path( join '.', $file, $serializer->extension )
49 24 0 33     120 if $options->{add_extension} and $file ne '-'
      33        
50             and ref $file ne 'SCALAR';
51              
52 24 100       83 my $method = $options->{utf8} ? 'spew_utf8' : 'spew';
53              
54 24         822 my $serialized = $serializer->serialize($content,$options);
55              
56 24 50       1587 return print $serialized if $file eq '-';
57              
58 24 50       210 if( ref $file eq 'SCALAR' ) {
59 0         0 $$file = $serialized;
60             }
61             else {
62 24         152 $file->$method($serialized);
63             }
64             }
65 33         251 }
66              
67             sub _generate_deserialize_file {
68 33     33   2225 my( undef, undef, undef, $global ) = @_;
69              
70             return sub {
71 25     25   17129 my( $file, $options ) = @_;
72              
73 25 50 33     209 $file = path($file) unless $file eq '-' or ref $file eq 'SCALAR';
74              
75 25 100       1014 $options = { %$global, %{ $options||{} } } if $global;
  25 50       164  
76 25   50     155 $options->{utf8} //= 1;
77 25   100     109 $options->{allow_nonref} //= 1;
78              
79 25         93 my $method = 'slurp' . ( '_utf8' ) x !! $options->{utf8};
80              
81 25         83 my $serializer = _serializer($file, $options);
82              
83             $file = path( join '.', $file, $serializer->extension )
84 25 0 33     125 if $options->{add_extension} and $file ne '-' and ref $file ne 'SCALAR';
      33        
85              
86             return $serializer->deserialize(
87 25 50       129 $file =~ /^-/ ? do { local $/ = }
  0 50       0  
88             : ref $file eq 'SCALAR' ? $$file
89             : $file->$method,
90             $options
91             );
92             }
93 33         236 }
94              
95             sub _generate_transerialize_file {
96              
97 14     14   775 my $serialize_file = _generate_serialize_file(@_);
98 14         57 my $deserialize_file = _generate_deserialize_file(@_);
99              
100              
101             return sub {
102 12     12   12153 my( $in, @chain ) = @_;
103 12 100       39 my $data = ref($in) ? $in : $deserialize_file->($in);
104              
105 12         77 while( my $step = shift @chain) {
106 22 100 100     265 if ( ref $step eq 'CODE' ) {
    100          
    100          
    100          
    50          
107 8         16 local $_ = $data;
108 8 50       18 if( $implicit_transform ) {
109 0         0 $step->($data);
110 0         0 $data = $_;
111             }
112             else {
113 8         23 $data = $step->($data);
114             }}
115             elsif ( ref $step eq 'ARRAY' ) {
116 1 50       4 die "subranch step can only be the last step of the chain"
117             if @chain;
118 1         5 for my $branch( @$step ) {
119 2         21 __SUB__->($data,@$branch);
120             }
121             }
122             elsif ( not ref $step or ref($step) =~ /Path::Tiny/ ) {
123 8 50       27 die "filename '$step' not at the end of the chain"
124             unless @chain <= 1;
125              
126 8         26 $serialize_file->( $step, $data, shift @chain );
127             }
128             elsif ( ref $step eq 'HASH' ) {
129 4         23 while( my ($f,$o) = each %$step ) {
130 6         52 $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 14         124 }
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 168 0       10034 $b->precedence <=> $a->precedence
152             or
153             $a cmp $b
154             }
155 378         84323 grep { $_->is_operative }
156 42     42   246 grep { $_->precedence }
  378         425800  
157             __PACKAGE__->_all_serializers;
158             }
159              
160             sub _serializer {
161 50     50   137 my( $self, $options ) = @_;
162              
163 7     7   37332 no warnings qw/ uninitialized /;
  7         20  
  7         1654  
164              
165 50   100     298 my $serializers = $options->{serializers} || [ __PACKAGE__->_all_operative_serializers ];
166 50         257 s/^\+/File::Serialize::Serializer::/ for @$serializers;
167              
168 50   66     428 my $format = $options->{format} || ( ( ref $self ? $self->basename : $self ) =~ /\.(\w+)$/ )[0];
169              
170 50   100 137   2256 return( first { $_->does_extension($format) } @$serializers
  137         492  
171             or die "no serializer found for $format"
172             );
173             }
174              
175             1;
176              
177             __END__