File Coverage

blib/lib/Data/Sah/From/JSONSchema.pm
Criterion Covered Total %
statement 20 113 17.7
branch 10 66 15.1
condition 0 8 0.0
subroutine 6 14 42.8
pod 1 1 100.0
total 37 202 18.3


line stmt bran cond sub pod time code
1             package Data::Sah::From::JSONSchema;
2              
3             our $DATE = '2015-09-06'; # DATE
4             our $VERSION = '0.02'; # VERSION
5              
6 1     1   23379 use 5.010001;
  1         4  
7 1     1   5 use strict;
  1         2  
  1         19  
8 1     1   4 use warnings;
  1         2  
  1         1283  
9              
10             require Exporter;
11             our @ISA = qw(Exporter);
12             our @EXPORT_OK = qw(
13             convert_json_schema_to_sah
14             );
15              
16             sub _clauses_common {
17 0     0   0 my ($jsonsch, $sahsch) = @_;
18 0 0       0 if (exists $jsonsch->{title}) {
19 0         0 $sahsch->[1]{summary} = $jsonsch->{title};
20             }
21 0 0       0 if (exists $jsonsch->{description}) {
22 0         0 $sahsch->[1]{description} = $jsonsch->{description};
23             }
24 0 0       0 if (exists $jsonsch->{default}) {
25 0         0 $sahsch->[1]{default} = $jsonsch->{default};
26             }
27             # XXX enum/oneOf (which can be specified without 'type')
28             }
29              
30             sub _convert_null {
31 0     0   0 my $jsonsch = shift;
32 0         0 my $sahsch = ["undef"];
33 0         0 _clauses_common($jsonsch, $sahsch);
34 0         0 $sahsch;
35             }
36              
37             sub _clauses_num {
38 0     0   0 my ($jsonsch, $sahsch) = @_;
39 0 0       0 if (exists $jsonsch->{minimum}) {
40 0         0 $sahsch->[1]{min} = $jsonsch->{minimum};
41             }
42 0 0       0 if (exists $jsonsch->{maximum}) {
43 0         0 $sahsch->[1]{max} = $jsonsch->{maximum};
44             }
45 0 0       0 if (exists $jsonsch->{exclusiveMinimum}) {
46 0         0 $sahsch->[1]{xmin} = $jsonsch->{exclusiveMinimum};
47             }
48 0 0       0 if (exists $jsonsch->{exclusiveMaximum}) {
49 0         0 $sahsch->[1]{xmax} = $jsonsch->{exclusiveMaximum};
50             }
51             # XXX in sah, div_by is int only, not num
52 0 0       0 if (exists $jsonsch->{multipleOf}) {
53 0         0 $sahsch->[1]{div_by} = $jsonsch->{multipleOf};
54             }
55             }
56              
57             sub _convert_number {
58 0     0   0 my $jsonsch = shift;
59 0         0 my $sahsch = ["num", {req=>1}];
60 0         0 _clauses_common($jsonsch, $sahsch);
61 0         0 _clauses_num($jsonsch, $sahsch);
62 0         0 $sahsch;
63             }
64              
65             sub _convert_integer {
66 0     0   0 my $jsonsch = shift;
67 0         0 my $sahsch = ["int", {req=>1}];
68 0         0 _clauses_common($jsonsch, $sahsch);
69 0         0 _clauses_num($jsonsch, $sahsch);
70 0         0 $sahsch;
71             }
72              
73             sub _convert_boolean {
74 0     0   0 my $jsonsch = shift;
75 0         0 my $sahsch = ["bool", {req=>1}];
76 0         0 _clauses_common($jsonsch, $sahsch);
77 0         0 $sahsch;
78             }
79              
80             sub _convert_string {
81 1     1   3 my $jsonsch = shift;
82 1         4 my $sahsch = ["str", {req=>1}];
83 1 50       5 if (exists $jsonsch->{pattern}) {
84 0         0 $sahsch->[1]{match} = $jsonsch->{pattern};
85             }
86 1 50       4 if (exists $jsonsch->{minLength}) {
87 0         0 $sahsch->[1]{min_len} = $jsonsch->{minLength};
88             }
89 1 50       3 if (exists $jsonsch->{maxLength}) {
90 0         0 $sahsch->[1]{max_len} = $jsonsch->{maxLength};
91             }
92             # XXX format, and builtin formats: date-time (RFC3339 section 5.6), email, hostname, ipv4, ipv6, uri
93 1         9 $sahsch;
94             }
95              
96             sub _convert_array {
97 0     0   0 my $jsonsch = shift;
98 0         0 my $sahsch = ["array", {req=>1}];
99 0 0       0 if (exists($jsonsch->{minItems})) {
100 0         0 $sahsch->[1]{min_len} = $jsonsch->{minItems};
101             }
102 0 0       0 if (exists($jsonsch->{maxItems})) {
103 0         0 $sahsch->[1]{max_len} = $jsonsch->{maxItems};
104             }
105 0 0       0 if (exists($jsonsch->{items})) {
106 0 0       0 if (ref($jsonsch->{items}) eq 'ARRAY') {
107 0         0 $sahsch->[1]{elems} = [];
108 0         0 my $i = 0;
109 0         0 for my $el (@{ $jsonsch->{items} }) {
  0         0  
110 0         0 $sahsch->[1]{elems}[$i] = _convert($el);
111 0         0 $i++;
112             }
113 0 0 0     0 if (exists($jsonsch->{additionalItems}) && !$jsonsch->{additionalItems}) {
114 0         0 $sahsch->[1]{max_len} = $i;
115             }
116             } else {
117 0         0 $sahsch->[1]{of} = _convert($jsonsch->{items});
118             }
119             }
120             # XXX uniqueItems
121 0         0 $sahsch;
122             }
123              
124             sub _convert_object {
125 0     0   0 my $jsonsch = shift;
126 0         0 my $sahsch = ["hash", {req=>1, 'keys.restrict'=>0}];
127 0 0       0 if (exists($jsonsch->{minProperties})) {
128 0         0 $sahsch->[1]{min_len} = $jsonsch->{minProperties};
129             }
130 0 0       0 if (exists($jsonsch->{maxProperties})) {
131 0         0 $sahsch->[1]{max_len} = $jsonsch->{maxProperties};
132             }
133 0 0       0 if (exists $jsonsch->{properties}) {
134 0         0 $sahsch->[1]{keys} = {};
135 0         0 for my $k (keys %{ $jsonsch->{properties} }) {
  0         0  
136 0         0 my $v = $jsonsch->{properties}{$k};
137 0         0 $sahsch->[1]{keys}{$k} = _convert($v);
138             }
139             }
140 0 0 0     0 if (exists($jsonsch->{additionalProperties}) && !$jsonsch->{additionalProperties}) {
141 0         0 $sahsch->[1]{'keys.restrict'} = 1;
142             }
143 0 0       0 if (exists $jsonsch->{required}) {
144 0         0 $sahsch->[1]{req_keys} = $jsonsch->{required};
145             }
146 0 0       0 if (exists $jsonsch->{dependencies}) {
147 0         0 for my $k (keys %{ $jsonsch->{dependencies} }) {
  0         0  
148 0         0 my $v = $jsonsch->{dependencies}{$k};
149 0 0       0 if (ref($v) eq 'HASH') {
150             # XXX schema dependencies
151 0         0 die "Schema dependencies is not yet supported";
152             } else {
153 0   0     0 $sahsch->[1]{'req_dep_all&'} //= [];
154 0         0 for my $d (@$v) {
155 0         0 push @{ $sahsch->[1]{'req_dep_all&'} }, [$d, [$k]];
  0         0  
156             }
157             }
158             }
159             }
160 0 0       0 if (exists $jsonsch->{patternProperties}) {
161 0         0 $sahsch->[1]{allowed_keys_re} = $jsonsch->{patternProperties};
162             }
163 0         0 $sahsch;
164             }
165              
166             sub _convert {
167 4     4   7 my $jsonsch = shift;
168              
169 4 100       30 ref($jsonsch) eq 'HASH' or die "JSON schema must be a hash";
170 2 100       14 my $type = $jsonsch->{type} or die "JSON schema must have a type";
171             # XXX type can be an array, e.g. [number, string] which means any one of those
172             # XXX $ref instead of type
173             # XXX format can be specified without type, implies string
174             # XXX enum/oneOf (which can be specified without 'type')
175 1 50       8 if ($type eq 'object') {
    50          
    50          
    0          
    0          
    0          
    0          
176 0         0 _convert_object($jsonsch);
177             } elsif ($type eq 'array') {
178 0         0 _convert_array($jsonsch);
179             } elsif ($type eq 'string') {
180 1         4 _convert_string($jsonsch);
181             } elsif ($type eq 'boolean') {
182 0         0 _convert_boolean($jsonsch);
183             } elsif ($type eq 'integer') {
184 0         0 _convert_integer($jsonsch);
185             } elsif ($type eq 'number') {
186 0         0 _convert_number($jsonsch);
187             } elsif ($type eq 'null') {
188 0         0 _convert_null($jsonsch);
189             } else {
190 0         0 die "Unknown type '$type'";
191             }
192             }
193              
194             sub convert_json_schema_to_sah {
195 4     4 1 905 _convert(@_);
196             }
197              
198             1;
199             # ABSTRACT: Convert JSON schema to Sah schema
200              
201             __END__