File Coverage

blib/lib/Data/Structure/Deserialize/Auto.pm
Criterion Covered Total %
statement 76 77 98.7
branch 8 10 80.0
condition 2 3 66.6
subroutine 19 19 100.0
pod 1 1 100.0
total 106 110 96.3


line stmt bran cond sub pod time code
1             package Data::Structure::Deserialize::Auto 1.01;
2 9     9   1927970 use v5.22;
  9         38  
3 9     9   56 use warnings;
  9         18  
  9         679  
4              
5             # ABSTRACT: Deserializes data structures from perl, JSON, YAML, or TOML data, from strings or files
6              
7             =encoding UTF-8
8            
9             =head1 NAME
10            
11             Data::Structure::Deserialize::Auto - deserializes data structures from perl, JSON, YAML, or TOML data, from strings or files
12            
13             =head1 SYNOPSIS
14              
15             use Data::Structure::Deserialize::Auto qw(deserialize);
16              
17             my $str = '{"db": {"host": "localhost"}}';
18             my $ds = deserialize($str); #autodetects JSON
19             say $ds->{db}->{host}; # localhost
20              
21             # OR
22              
23             $str = <<'END';
24             options:
25             autosave: 1
26             END
27             $ds = deserialize($str); #autodetects YAML
28             say $ds->{options}->{autosave}; # 1
29              
30             # OR
31              
32             use Data::Dumper;
33             $Data::Dumper::Terse = 1;
34             my $filename = ...;
35             open(my $FH, '>', $filename);
36             my $data = {
37             a => 1,
38             b => 2,
39             c => 3
40             };
41             print $FH Dumper($data);
42             close($FH);
43             $ds = deserialize($filename); #autodetects perl in referenced file
44             say $ds->{b}; # 2
45              
46             =head1 DESCRIPTION
47              
48             L is a module for converting a string in an
49             arbitrary format (one of perl/JSON/YAML/TOML) into a perl data structure, without
50             needing to worry about what format it was in.
51              
52             If the string argument given to it is a valid local filename, it is treated as
53             such, and that file's contents are processed instead.
54              
55             =head1 FUNCTIONS
56              
57             =head2 deserialize( $str[, $hint] )
58              
59             Given a string as its first argument, returns a perl data structure by decoding
60             the perl (L), JSON, YAML, or TOML string. Or, if the string is a valid
61             filename, by decoding the contents of that file.
62              
63             If a hint is given as the second argument, where its value is one of C,
64             C, C or C, then this type of deserialization is tried first.
65             This may be necessary in certain rare edge cases where the input value's format
66             is ambiguous.
67              
68             This function can be exported
69              
70             =cut
71              
72 9     9   56 use base qw(Exporter);
  9         18  
  9         1274  
73              
74 9     9   72 use File::Basename;
  9         22  
  9         1264  
75 9     9   4895 use IO::All;
  9         136574  
  9         81  
76 9     9   7504 use JSON qw(decode_json);
  9         111372  
  9         62  
77 9     9   7040 use Readonly;
  9         41779  
  9         742  
78 9     9   5332 use Syntax::Keyword::Try;
  9         36830  
  9         68  
79 9     9   5778 use TOML qw(from_toml);
  9         338185  
  9         974  
80 9     9   4578 use YAML::XS;
  9         39438  
  9         1204  
81              
82 9     9   5123 use experimental qw(signatures);
  9         50194  
  9         62  
83              
84             Readonly::Hash my %FILE_TYPES => (
85             yml => 'yaml',
86             yaml => 'yaml',
87             toml => 'toml',
88             json => 'json',
89             );
90             Readonly::Array my @DECODER_PRIORITY => qw(perl yaml json toml);
91              
92             our @EXPORT_OK = qw(
93             deserialize
94             );
95              
96 9     9   40 sub _decoders() {
  9         48  
97             return (
98 7     7   11 yaml => sub($v) {
  7         10  
  7         12  
99 7         510 Load($v);
100             },
101 3     3   8 json => sub($v) {
  3         6  
  3         4  
102 3         105 decode_json($v);
103             },
104 4     4   10 toml => sub($v) {
  4         11  
  4         10  
105 4         22 from_toml($v);
106             },
107 9     9   13 perl => sub($v) {
  9         16  
  9         13  
108 9     9   4950 no warnings 'syntax';
  9         21  
  9         8314  
109 9         1020 eval($v); ## no critic (ProhibitStringyEval)
110             },
111 9         206 );
112             }
113              
114 9     9   22 sub _is_filename($str) {
  9         23  
  9         17  
115 9 100       75 return 0 if ($str =~ /\n/);
116 4         189 return (-f $str);
117             }
118              
119 9     9 1 1935643 sub deserialize($v, $hint = undef) {
  9         28  
  9         80  
  9         33  
120 9 50       48 return $v if (ref($v) eq 'HASH');
121              
122 9         89 my @decoders = @DECODER_PRIORITY;
123 9         407 my %decoders = _decoders();
124 9 100       43 if (_is_filename($v)) {
125 4         37 my ($fn, $dirs, $suffix) = fileparse($v, keys(%FILE_TYPES));
126 4 100 66     551 unshift(@decoders, $FILE_TYPES{$suffix}) if (defined($suffix) && defined($FILE_TYPES{$suffix}));
127 4         83 $v = io->file($v)->slurp;
128             }
129 9 50       89944 unshift(@decoders, $hint) if (defined($hint));
130 9         90 my $n;
131 9         17 do {
132 14         33 $n = shift(@decoders);
133 14         50 my $decoder = $decoders{$n};
134             try {
135             my $structure = $decoder->($v);
136             if (ref($structure) eq 'HASH' || ref($structure) eq 'ARRAY') {
137             # warn("decoded using '$n'");
138             return $decoder->($v);
139             }
140 14         36 } catch {
141             # ignore any errors and try the next decoder, or die out at the bottom
142             };
143             } while (@decoders);
144 0           die("Data::Structure::Deserialize::Auto was unable to process the input");
145             }
146              
147             =pod
148              
149             =head1 AUTHOR
150              
151             Mark Tyrrell C<< >>
152              
153             =head1 LICENSE
154              
155             Copyright (c) 2024 Mark Tyrrell
156              
157             Permission is hereby granted, free of charge, to any person obtaining a copy
158             of this software and associated documentation files (the "Software"), to deal
159             in the Software without restriction, including without limitation the rights
160             to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
161             copies of the Software, and to permit persons to whom the Software is
162             furnished to do so, subject to the following conditions:
163              
164             The above copyright notice and this permission notice shall be included in all
165             copies or substantial portions of the Software.
166              
167             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
168             IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
169             FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
170             AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
171             LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
172             OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
173             SOFTWARE.
174              
175             =cut
176              
177             1;
178              
179             __END__