File Coverage

blib/lib/JSON/Color.pm
Criterion Covered Total %
statement 73 102 71.5
branch 33 62 53.2
condition 16 35 45.7
subroutine 13 13 100.0
pod 1 1 100.0
total 136 213 63.8


line stmt bran cond sub pod time code
1             package JSON::Color;
2              
3 1     1   68097 use 5.010001;
  1         14  
4 1     1   6 use strict;
  1         4  
  1         21  
5 1     1   8 use warnings;
  1         1  
  1         150  
6              
7             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
8             our $DATE = '2023-07-02'; # DATE
9             our $DIST = 'JSON-Color'; # DIST
10             our $VERSION = '0.134'; # VERSION
11              
12             our $sul_available = eval { require Scalar::Util::LooksLikeNumber; 1 } ? 1:0;
13              
14             require Exporter;
15             our @ISA = qw(Exporter);
16             our @EXPORT_OK = qw(encode_json);
17              
18 1     1   623 use Color::ANSI::Util qw(ansi_reset);
  1         10921  
  1         201  
19 1     1   1123 use ColorThemeRole::ANSI (); # for scan_prereqs
  1         8365  
  1         1857  
20              
21             my %esc = (
22             "\n" => '\n',
23             "\r" => '\r',
24             "\t" => '\t',
25             "\f" => '\f',
26             "\b" => '\b',
27             "\"" => '\"',
28             "\\" => '\\\\',
29             "\'" => '\\\'',
30             );
31             sub _string {
32 2     2   6 my ($value, $opts) = @_;
33              
34 2         4 my $ct = $opts->{_color_theme_obj};
35 2         7 my $c_reset = ansi_reset(1);
36 2         229 my ($c_q, $c_s, $c_e);
37 2 50       6 if ($opts->{obj_key}) {
38 0         0 $c_s = $ct->get_item_color_as_ansi('object_key');
39 0         0 $c_q = $ct->get_item_color_as_ansi('object_key_quote');
40 0         0 $c_e = $ct->get_item_color_as_ansi('object_key_escape');
41             } else {
42 2         13 $c_s = $ct->get_item_color_as_ansi('string');
43 2         99 $c_q = $ct->get_item_color_as_ansi('string_quote');
44 2         417 $c_e = $ct->get_item_color_as_ansi('string_escape');
45             }
46              
47 2   100     56 for ($c_q, $c_s, $c_e) { $_ //= "" }
  6         17  
48              
49 2         10 $value =~ s/([\x22\x5c\n\r\t\f\b])|([\x00-\x08\x0b\x0e-\x1f])/
50             join("",
51             $c_e,
52 0 0       0 $1 ? $esc{$1} : '\\u00' . unpack('H2', $2),
    0          
53             ($c_e ? $c_reset : ""), $c_s,
54             )
55             /eg;
56              
57 2 100       18 return join(
    100          
    100          
58             "",
59             $c_q, '"', ($c_q ? $c_reset : ""),
60             $c_s, $value, ($c_s ? $c_reset : ""),
61             $c_q, '"', ($c_q ? $c_reset : ""),
62             );
63             }
64              
65             sub _number {
66 1     1   4 my ($value, $opts) = @_;
67              
68 1         3 my $ct = $opts->{_color_theme_obj};
69 1         3 my $c_reset = ansi_reset(1);
70 1   50     13 my $c_s = $ct->get_item_color_as_ansi('number') // "";
71 1 50       38 return join(
72             "",
73             $c_s,
74             $value,
75             ($c_s ? $c_reset : ""),
76             );
77             }
78              
79             sub _null {
80 1     1   2 my ($value, $opts) = @_;
81              
82 1         3 my $ct = $opts->{_color_theme_obj};
83 1         3 my $c_reset = ansi_reset(1);
84 1   50     13 my $c_s = $ct->get_item_color_as_ansi('null') // "";
85 1 50       33 return join(
86             "",
87             $c_s,
88             "null",
89             ($c_s ? $c_reset : ""),
90             );
91             }
92              
93             sub _bool {
94 1     1   3 my ($value, $opts) = @_;
95              
96 1         3 my $ct = $opts->{_color_theme_obj};
97 1         2 my $c_reset = ansi_reset(1);
98 1 50 50     39 my $c_s = $ct->get_item_color_as_ansi($value ? 'true' : 'false') // "";
99 1 50       33 return join(
100             "",
101             $c_s,
102             "$value",
103             ($c_s ? $c_reset : ""),
104             );
105             }
106              
107             sub _array {
108 2     2   3 my ($value, $opts) = @_;
109              
110             #my $ct = $opts->{_color_theme_obj};
111 2 100       14 return "[]" unless @$value;
112 1 50       4 my $indent = $opts->{pretty} ? " " x $opts->{_indent} : "";
113 1 50       3 my $indent2 = $opts->{pretty} ? " " x ($opts->{_indent}+1) : "";
114 1 50       3 my $nl = $opts->{pretty} ? "\n" : "";
115 1         3 local $opts->{_indent} = $opts->{_indent}+1;
116             return join(
117             "",
118             "[$nl",
119 1         4 (map {(
120 6 100       17 $indent2,
121             _encode($value->[$_], $opts),
122             $_ == @$value-1 ? $nl : ",$nl",)
123             } 0..@$value-1),
124             $indent, "]",
125             );
126             }
127              
128             sub _hash {
129 1     1   2 my ($value, $opts) = @_;
130              
131             #my $ct = $opts->{_color_theme_obj};
132 1 50       6 return "{}" unless keys %$value;
133 0 0       0 my $indent = $opts->{pretty} ? " " x $opts->{_indent} : "";
134 0 0       0 my $indent2 = $opts->{pretty} ? " " x ($opts->{_indent}+1) : "";
135 0 0       0 my $nl = $opts->{pretty} ? "\n" : "";
136 0 0       0 my $colon = $opts->{pretty} ? ": " : ":";
137 0         0 my @res;
138              
139 0         0 push @res, "{$nl";
140 0         0 my @k;
141 0 0       0 if ($opts->{sort_by}) {
142 0         0 @k = sort { $opts->{sort_by}->() } keys %$value;
  0         0  
143             } else {
144 0         0 @k = sort keys(%$value);
145             }
146 0         0 local $opts->{_indent} = $opts->{_indent}+1;
147 0         0 for (0..@k-1) {
148 0         0 my $k = $k[$_];
149             push @res, (
150             $indent2,
151             _string($k, {%$opts, obj_key=>1}),
152             $colon,
153 0 0       0 _encode($value->{$k}, $opts),
154             $_ == @k-1 ? $nl : ",$nl",
155             );
156             }
157 0         0 push @res, $indent, "}";
158 0         0 join "", @res;
159             }
160              
161             sub _encode {
162 8     8   15 my ($data, $opts) = @_;
163              
164 8         14 my $ref = ref($data);
165              
166 8 100 66     47 if (!defined($data)) {
    100 0        
    100 0        
    100          
    50          
    0          
167 1         5 return _null($data, $opts);
168             } elsif ($ref eq 'ARRAY') {
169 2         9 return _array($data, $opts);
170             } elsif ($ref eq 'HASH') {
171 1         5 return _hash($data, $opts);
172             } elsif ($ref eq 'JSON::XS::Boolean' || $ref eq 'JSON::PP::Boolean') {
173 1         6 return _bool($data, $opts);
174             } elsif (!$ref) {
175 3 100 66     29 if ($sul_available &&
176             Scalar::Util::LooksLikeNumber::looks_like_number($data) =~
177             /^(4|12|4352|8704)$/o) {
178 1         4 return _number($data, $opts);
179             } else {
180 2         6 return _string($data, $opts);
181             }
182             } elsif ($sul_available &&
183             Scalar::Util::blessed($data) && $data->can('TO_JSON')) {
184 0         0 return _encode($data->TO_JSON, $opts);
185             } else {
186 0         0 die "Can't encode $data";
187             }
188             }
189              
190             sub encode_json {
191 2     2 1 4418 my ($value, $opts) = @_;
192 2   50     15 $opts //= {};
193 2   50     11 $opts->{_indent} //= 0;
194             $opts->{color_theme} //=
195             (defined $ENV{NO_COLOR} ? "NoColor" : undef) //
196             $ENV{JSON_COLOR_COLOR_THEME} //
197             $ENV{COLOR_THEME} //
198 2 100 66     23 "default_ansi";
      33        
      50        
      33        
199              
200 2         521 require Module::Load::Util;
201             my $ct = Module::Load::Util::instantiate_class_with_optional_args(
202             {ns_prefixes=>["ColorTheme::JSON::Color", "ColorTheme", ""]},
203             $opts->{color_theme},
204 2         1644 );
205 2         1167 require Role::Tiny;
206 2         16 Role::Tiny->apply_roles_to_object($ct, 'ColorThemeRole::ANSI');
207 2         709 $opts->{_color_theme_obj} = $ct;
208              
209 2         7 my $res = _encode($value , $opts);
210              
211 2 50       35 if ($opts->{linum}) {
212 0         0 my $lines = 0;
213 0         0 $lines++ while $res =~ /^/mog;
214 0         0 my $fmt = "%".length($lines)."d";
215 0         0 my $i = 0;
216 0         0 $res =~ s/^/
217 0         0 $ct->get_item_color('linum') . sprintf($fmt, ++$i) . ansi_reset(1)
218             /meg;
219             }
220 2         36 $res;
221             }
222              
223             1;
224             # ABSTRACT: Encode to colored JSON
225              
226             __END__