File Coverage

blib/lib/Data/ENAML.pm
Criterion Covered Total %
statement 63 107 58.8
branch 25 52 48.0
condition 3 6 50.0
subroutine 8 9 88.8
pod 0 6 0.0
total 99 180 55.0


line stmt bran cond sub pod time code
1             package Data::ENAML;
2              
3 1     1   523 use strict;
  1         1  
  1         25  
4 1     1   4 use Carp;
  1         1  
  1         79  
5 1     1   4 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
  1         9  
  1         1751  
6             require Exporter;
7              
8             @ISA = qw(Exporter);
9              
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              
14             # This allows declaration use Data::ENAML ':all';
15             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
16             # will save memory.
17             %EXPORT_TAGS = ( 'all' => [ qw(
18             serialize deserialize
19             ) ] );
20              
21             @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
22              
23             @EXPORT = qw(
24            
25             );
26             $VERSION = '0.03';
27              
28              
29             # Preloaded methods go here.
30              
31             # Autoload methods go after =cut, and are processed by the autosplit program.
32              
33             sub serialize {
34 2     2 0 51 my @pair;
35 2 50 33     16 if (@_ == 1 && UNIVERSAL::isa($_[0], 'HASH')) {
    0          
36 2         3 my %hash = %{shift()};
  2         8  
37 2         6 my @keys = keys %hash;
38 2 50       5 if (scalar(@keys) != 1) {
39 0         0 croak "serialized must be called with a key:value pair";
40             }
41 2         8 @pair = %hash;
42             } elsif (@_ == 2) {
43 0         0 @pair = @_;
44             } else {
45 0         0 croak "serialized must be called with a key:value pair";
46             }
47 2 50       10 unless ($pair[0] =~ /[a-z_-]{1,32}/i) {
48 0         0 croak "$pair[0] is an illegal key name";
49             }
50 2         10 "$pair[0]: " . &do_serialize($pair[1], {}) . "\r\n";
51             }
52              
53             sub do_serialize {
54 8     8 0 11 my ($datum, $history) = @_;
55 8 100       15 unless (ref($datum)) {
56 6 50       25 return $datum if ($datum =~ /^\d+$/);
57 6 50       13 unless ($datum =~ /[\000-\037"%\x7F\xFF]/) {
58 6         33 return qq!"$datum"!; # TO DO: add UTF8 support
59             }
60 0         0 my $opt1 = $datum;
61 0         0 $opt1 =~ s/([\000-\037"%\x7F\xFF])/sprintf("%%%02X", ord($1))/ge;
  0         0  
62 0         0 $opt1 = qq!"$opt1"!;
63 0         0 my $opt2 = '%' . join("", map {sprintf("%02X", $_);}
  0         0  
64             unpack("C*", $datum));
65 0 0       0 return (length($opt1) <= length($opt2)) ? $opt1 : $opt2;
66             }
67 2 50       12 croak "Circular referencing detected" if ++$history->{$datum} > 1;
68 2 50       7 if (UNIVERSAL::isa($datum, 'HASH')) {
    0          
69 2         4 my $str = "{ ";
70 2         2 my $count = 0;
71 2         8 while (my ($key, $val) = each %$datum) {
72 8 50       34 unless ($key =~ /[a-z_-]{1,32}/i) {
73 0         0 croak "$datum is an illegal key name";
74             }
75 8 100       19 $str .= " " if (++$count > 1);
76 8 100 66     30 if ($val eq "\000" || !defined($val)) {
77 2         3 $str .= $key;
78 2         7 next;
79             }
80 6         23 $str .= "$key: " . &do_serialize($val);
81             }
82 2         3 $str .= " }";
83 2         11 return $str;
84             } elsif (UNIVERSAL::isa($datum, 'ARRAY')) {
85 0         0 my $str = "[ ";
86 0         0 my $count = 0;
87 0         0 foreach (@$datum) {
88 0 0       0 $str .= " " if (++$count > 1);
89 0         0 $str .= &do_serialize($_);
90             }
91 0         0 $str .= " ]";
92 0         0 return $str;
93             }
94 0         0 croak "Object type " . ref($datum) . " not supported";
95             }
96              
97             sub deserialize {
98 1     1 0 5 my $text = shift;
99 1         7 $text =~ s/[\r\n]+$//;
100 1         4 my ($hash, $rem) = &deserialize_hash("$text }");
101 1 50       4 croak "Ended at $rem" if ($rem);
102 1         19 $hash;
103             }
104              
105             sub deserialize_hash {
106 2     2 0 5 local ($_) = shift;
107 2         3 my $struct = {};
108 2         6 while ($_) {
109 7         18 s/^\s+//;
110 7 100       19 if (s/^\}//) {
111 2         6 return ($struct, $_);
112             }
113 5 50       22 unless (s/^([A-Za-z-_]{1,32})(:?)\s*//) {
114 0         0 croak "Expected: key, at $_";
115             }
116 5         9 my $key = $1;
117 5 100       13 unless ($2) {
118 1         2 $struct->{$key} = undef;
119 1         2 next;
120             }
121 4         13 ($struct->{$key}, $_) = &eat_one($_);
122             }
123 0         0 croak "expected }";
124             }
125              
126             sub deserialize_array {
127 0     0 0 0 local ($_) = shift;
128 0         0 my $array = [];
129 0         0 while ($_) {
130 0         0 s/^\s+//;
131 0 0       0 if (s/^\]//) {
132 0         0 return ($array, $_);
133             }
134 0         0 my ($elem, $rem) = &eat_one($_);
135 0         0 $_ = $rem;
136 0         0 push(@$array, $elem);
137             }
138 0         0 croak "expected ]";
139             }
140              
141             sub eat_one {
142 4     4 0 6 local ($_) = shift;
143 4 100       18 if (s/^\{//) {
144 1         9 my ($hash, $text) = &deserialize_hash($_);
145 1         5 return ($hash, $text);
146             }
147 3 50       8 if (s/^\[//) {
148 0         0 my ($ary, $text) = &deserialize_array($_);
149 0         0 return ($ary, $text);
150             }
151 3 50       9 if (s/^(\d+)(?![^}\]\t ])//) {
152 0         0 return($1, $_);
153             }
154 3 50       15 if (s/^\"(.*?)\"//) {
155 3         4 my $text = $1;
156 3         5 $text =~ s/%([0-9A-F]{2})/chr(hex($1))/gei;
  0         0  
157 3         16 return ($text, $_);
158             }
159 0 0         if (s/^%([0-9A-F]+)//i) {
160 0           my $hex = $1;
161 0 0         croak "Odd number of hex digits" if (length($hex) % 2);
162 0           my @tokens = ($hex =~ /(..)/g);
163 0           my $str = pack("C*", map {hex($_);} @tokens);
  0            
164 0           return ($str, $_);
165             }
166 0           croak "Could not get token at $_";
167             }
168              
169             1;
170             __END__