File Coverage

blib/lib/JSON/YY.pm
Criterion Covered Total %
statement 73 77 94.8
branch 13 16 81.2
condition 7 16 43.7
subroutine 21 23 91.3
pod 8 8 100.0
total 122 140 87.1


line stmt bran cond sub pod time code
1             package JSON::YY;
2              
3 14     14   1188382 use strict;
  14         19  
  14         425  
4 14     14   53 use warnings;
  14         20  
  14         651  
5 14     14   56 use Carp;
  14         19  
  14         5078  
6              
7             our $VERSION = '0.04';
8              
9             require XSLoader;
10             XSLoader::load('JSON::YY', $VERSION);
11              
12             our @EXPORT_OK = qw(encode_json decode_json decode_json_ro);
13              
14             my @DOC_KEYWORDS = qw(jdoc jget jgetp jset jdel jhas jclone jencode
15             jstr jnum jbool jnull jarr jobj jtype jlen jkeys jdecode
16             jiter jnext jkey jpatch jmerge jfrom jvals jeq
17             jpp jraw jread jwrite jpaths jfind
18             jis_obj jis_arr jis_str jis_num jis_int jis_real jis_bool jis_null);
19              
20             # functional API — fast XS path, always utf8
21             *encode_json = \&_xs_encode_json;
22             *decode_json = \&_xs_decode_json;
23             *decode_json_ro = \&_xs_decode_json_ro;
24              
25             my %SETTERS = (
26             utf8 => \&_set_utf8,
27             pretty => \&_set_pretty,
28             canonical => \&_set_canonical,
29             allow_nonref => \&_set_allow_nonref,
30             allow_unknown => \&_set_allow_unknown,
31             allow_blessed => \&_set_allow_blessed,
32             convert_blessed => \&_set_convert_blessed,
33             max_depth => \&_set_max_depth,
34             );
35              
36             sub import {
37 21     21   410 my $class = shift;
38 21         34 my @exports;
39             my @flags;
40 21         22 my $want_doc = 0;
41 21         45 for my $arg (@_) {
42 33 100       94 if ($arg eq ':doc') {
    100          
43 9         15 $want_doc = 1;
44             } elsif ($arg =~ /^-(.+)/) {
45 2         6 push @flags, $1;
46             } else {
47 22         53 push @exports, $arg;
48             }
49             }
50 21         37 my $caller = caller;
51              
52             # activate keywords via XS::Parse::Keyword hint keys
53 21 100       60 if ($want_doc) {
54 9         507 $^H{"JSON::YY/$_"} = 1 for @DOC_KEYWORDS;
55             }
56 21 100       95 if (@flags) {
57 1         2 my $coder = $class->new;
58 1         1 for my $f (@flags) {
59 2 50       6 my $setter = $SETTERS{$f}
60             or Carp::croak("unknown flag: -$f");
61 2         4 $coder->$setter(1);
62             }
63 14     14   90 no strict 'refs';
  14         27  
  14         1398  
64 1     1   4 *{"${caller}::encode_json"} = sub { $coder->encode($_[0]) };
  1         5  
  1         177230  
65 1     1   2 *{"${caller}::decode_json"} = sub { $coder->decode($_[0]) };
  1         2  
  1         941  
66             }
67 21 100       74013 if (@exports) {
68 14     14   62 no strict 'refs';
  14         27  
  14         4372  
69 11         16 for my $e (@exports) {
70             Carp::croak("'$e' is not exported by JSON::YY")
71 22 50       42 unless grep { $_ eq $e } @EXPORT_OK;
  66         118  
72 22         37 *{"${caller}::$e"} = \&{$e};
  22         66  
  22         41  
73             # also enable keyword for exported functions
74 22         75287 $^H{"JSON::YY/$e"} = 1;
75             }
76             }
77             }
78              
79             # chaining setters
80 10   50 10 1 59 sub utf8 { $_[0]->_set_utf8($_[1] // 1); $_[0] }
  10         40  
81 3   50 3 1 15 sub pretty { $_[0]->_set_pretty($_[1] // 1); $_[0] }
  3         92  
82 0   0 0 1 0 sub canonical { $_[0]->_set_canonical($_[1] // 1); $_[0] }
  0         0  
83 2   100 2 1 9 sub allow_nonref { $_[0]->_set_allow_nonref($_[1] // 1); $_[0] }
  2         3  
84 0   0 0 1 0 sub allow_unknown { $_[0]->_set_allow_unknown($_[1] // 1); $_[0] }
  0         0  
85 1   50 1 1 5 sub allow_blessed { $_[0]->_set_allow_blessed($_[1] // 1); $_[0] }
  1         2  
86 1   50 1 1 6 sub convert_blessed { $_[0]->_set_convert_blessed($_[1] // 1); $_[0] }
  1         1  
87 2   50 2 1 11 sub max_depth { $_[0]->_set_max_depth($_[1] // 512); $_[0] }
  2         4  
88              
89             # wrap XS new to accept keyword args
90             {
91             my $orig_new = JSON::YY->can('new');
92 14     14   69 no warnings 'redefine';
  14         17  
  14         3061  
93             *new = sub {
94 16     16   704641 my ($class, %args) = @_;
95 16         104 my $self = $orig_new->($class);
96 16         55 for my $k (keys %args) {
97 8 50       20 my $setter = $SETTERS{$k}
98             or Carp::croak("unknown option: $k");
99 8         23 $self->$setter($args{$k});
100             }
101 16         56 $self;
102             };
103             }
104              
105             # Doc overloading: stringify to JSON, boolean always true, eq/ne deep compare
106             package JSON::YY::Doc;
107             use overload
108 2     2   172004 '""' => sub { JSON::YY::_doc_stringify($_[0]) },
109 1     1   83 'bool' => sub { 1 },
110 1     1   6 'eq' => sub { JSON::YY::_doc_eq($_[0], $_[1]) },
111 1     1   6 'ne' => sub { !JSON::YY::_doc_eq($_[0], $_[1]) },
112 14     14   6262 fallback => 1;
  14         16057  
  14         148  
113              
114             package JSON::YY;
115             1;
116              
117             __END__