File Coverage

blib/lib/File/Serialize.pm
Criterion Covered Total %
statement 90 95 94.7
branch 32 46 69.5
condition 30 43 69.7
subroutine 20 21 95.2
pod n/a
total 172 205 83.9


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.4.0';
5 7     30   1453701 use v5.16.0;
  7         73  
6              
7 7     7   38 use feature 'current_sub';
  7         12  
  7         946  
8              
9 7     7   43 use strict;
  7         12  
  7         178  
10 7     7   34 use warnings;
  7         13  
  7         228  
11              
12 7     7   3256 use Class::Load qw/ load_class /;
  7         125439  
  7         408  
13 7     7   4129 use List::AllUtils qw/ uniq /;
  7         68995  
  7         623  
14 7     7   51 use List::Util 1.41 qw/ pairgrep first none any pairmap /;
  7         96  
  7         373  
15 7     7   2169 use Path::Tiny;
  7         28300  
  7         386  
16              
17             use Module::Pluggable
18 7         50 require => 1,
19             sub_name => '_all_serializers',
20             search_path => __PACKAGE__ . '::Serializer'
21 7     7   3571 ;
  7         55977  
22              
23 7     7   656 use parent 'Exporter::Tiny';
  7         14  
  7         49  
24              
25             our @EXPORT = qw/ serialize_file deserialize_file transerialize_file /;
26              
27             sub _generate_serialize_file {
28 36     36   3745 my( undef, undef, undef, $global )= @_;
29              
30             return sub {
31 30     30   37787 my( $file, $content, $options ) = @_;
32              
33 30 50 66     169 $options = { format => $options } if $options and not ref $options;
34              
35 30 100       147 $options = { %$global, %{ $options||{} } } if $global;
  30 50       199  
36             # default to utf8 => 1
37 30   100     193 $options->{utf8} //= 1;
38 30   50     160 $options->{allow_nonref} //= 1;
39 30   100     147 $options->{pretty} //= 1;
40 30   100     120 $options->{canonical} //= 1;
41              
42 30 100 66     219 $file = path($file) unless $file =~ /^-/ or ref $file eq 'SCALAR';
43              
44 30         1029 my $serializer = _serializer($file, $options);
45              
46             $file = path( join '.', $file, $serializer->extension )
47 29 0 33     156 if $options->{add_extension} and $file ne '-'
      33        
48             and ref $file ne 'SCALAR';
49              
50 29 100       116 my $method = $options->{utf8} ? 'spew_utf8' : 'spew';
51              
52 29         1028 my $serialized = $serializer->serialize($content,$options);
53              
54 29 50       1859 return print $serialized if $file eq '-';
55              
56 29 100       207 if( ref $file eq 'SCALAR' ) {
57 4         24 $$file = $serialized;
58             }
59             else {
60 25         164 $file->$method($serialized);
61             }
62             }
63 36         228 }
64              
65             sub _generate_deserialize_file {
66 36     36   2127 my( undef, undef, undef, $global ) = @_;
67              
68             return sub {
69 36     36   24421 my( $file, $options ) = @_;
70              
71 36 100 66     305 $file = path($file) unless $file eq '-' or ref $file eq 'SCALAR';
72              
73 36 100       1127 $options = { %$global, %{ $options||{} } } if $global;
  36 50       218  
74 36   50     209 $options->{utf8} //= 1;
75 36   100     147 $options->{allow_nonref} //= 1;
76              
77 36         121 my $method = 'slurp' . ( '_utf8' ) x !! $options->{utf8};
78              
79 36         106 my $serializer = _serializer($file, $options);
80              
81             $file = path( join '.', $file, $serializer->extension )
82 36 0 33     162 if $options->{add_extension} and $file ne '-' and ref $file ne 'SCALAR';
      33        
83              
84             return $serializer->deserialize(
85 36 100       432 $file =~ /^-/ ? do { local $/ = }
  0 50       0  
86             : ref $file eq 'SCALAR' ? $$file
87             : $file->$method,
88             $options
89             );
90             }
91 36         203 }
92              
93             sub _generate_transerialize_file {
94              
95 15     15   709 my $serialize_file = _generate_serialize_file(@_);
96 15         43 my $deserialize_file = _generate_deserialize_file(@_);
97              
98              
99             return sub {
100 12     12   14096 my( $in, @chain ) = @_;
101 12 100       55 my $data = ref($in) ? $in : $deserialize_file->($in);
102              
103 12         72 while( my $step = shift @chain) {
104 22 100 100     262 if ( ref $step eq 'CODE' ) {
    100          
    100          
    100          
    50          
105 8         17 local $_ = $data;
106 8         20 $data = $step->($data);
107             }
108             elsif ( ref $step eq 'ARRAY' ) {
109 1 50       6 die "subranch step can only be the last step of the chain"
110             if @chain;
111 1         3 for my $branch( @$step ) {
112 2         23 __SUB__->($data,@$branch);
113             }
114             }
115             elsif ( not ref $step or ref($step) =~ /Path::Tiny/ ) {
116 8 50       27 die "filename '$step' not at the end of the chain"
117             unless @chain <= 1;
118              
119 8         27 $serialize_file->( $step, $data, shift @chain );
120             }
121             elsif ( ref $step eq 'HASH' ) {
122 4         19 while( my ($f,$o) = each %$step ) {
123 6         46 $serialize_file->($f,$data,$o);
124             }
125             }
126             elsif ( ref $step eq 'SCALAR' ) {
127 1         5 $$step = $data;
128             }
129             else {
130 0         0 die "wrong chain argument";
131             }
132             }
133              
134             }
135 15         95 }
136              
137             sub _all_operative_formats {
138 0     0   0 my $self = shift;
139 0         0 return uniq map { $_->extension } $self->_all_operative_formats;
  0         0  
140             }
141              
142             sub _all_operative_serializers {
143             sort {
144 448 0       12318 $b->precedence <=> $a->precedence
145             or
146             $a cmp $b
147             }
148 504         88949 grep { $_->is_operative }
149 56     56   312 grep { $_->precedence }
  504         516840  
150             __PACKAGE__->_all_serializers;
151             }
152              
153             sub _serializer {
154 66     66   160 my( $self, $options ) = @_;
155              
156 7     7   31504 no warnings qw/ uninitialized /;
  7         20  
  7         1397  
157              
158 66   100     451 my $serializers = $options->{serializers} || [ __PACKAGE__->_all_operative_serializers ];
159 66         349 s/^\+/File::Serialize::Serializer::/ for @$serializers;
160              
161 66   100     606 my $format = $options->{format} || ( ( ref $self ? $self->basename : $self ) =~ /\.(\w+)$/ )[0];
162              
163 66   100 208   2643 return( first { $_->does_extension($format) } @$serializers
  208         684  
164             or die "no serializer found for $format"
165             );
166             }
167              
168             1;
169              
170             __END__