File Coverage

blib/lib/Specio/Library/Structured.pm
Criterion Covered Total %
statement 39 39 100.0
branch n/a
condition n/a
subroutine 15 15 100.0
pod 0 2 0.0
total 54 56 96.4


line stmt bran cond sub pod time code
1             package Specio::Library::Structured;
2              
3 4     4   2393 use strict;
  4         10  
  4         191  
4 4     4   42 use warnings;
  4         8  
  4         357  
5              
6             our $VERSION = '0.53';
7              
8 4     4   21 use parent 'Specio::Exporter';
  4         8  
  4         38  
9              
10 4     4   315 use Carp qw( confess );
  4         8  
  4         298  
11 4     4   24 use Scalar::Util qw( blessed );
  4         17  
  4         231  
12 4     4   1973 use Specio::Constraint::Structurable;
  4         13  
  4         200  
13 4     4   33 use Specio::Declare;
  4         8  
  4         32  
14 4     4   20 use Specio::Library::Builtins;
  4         10  
  4         31  
15 4     4   2663 use Specio::Library::Structured::Dict;
  4         42  
  4         195  
16 4     4   2459 use Specio::Library::Structured::Map;
  4         20  
  4         197  
17 4     4   2585 use Specio::Library::Structured::Tuple;
  4         17  
  4         211  
18 4     4   30 use Specio::TypeChecks qw( does_role );
  4         9  
  4         1122  
19              
20             ## no critic (Variables::ProtectPrivateVars)
21             declare(
22             'Dict',
23             type_class => 'Specio::Constraint::Structurable',
24             parent => Specio::Library::Structured::Dict->parent,
25             inline => \&Specio::Library::Structured::Dict::_inline,
26             parameterization_args_builder =>
27             \&Specio::Library::Structured::Dict::_parameterization_args_builder,
28             name_builder => \&Specio::Library::Structured::Dict::_name_builder,
29             structured_inline_generator =>
30             \&Specio::Library::Structured::Dict::_structured_inline_generator,
31             );
32              
33             declare(
34             'Map',
35             type_class => 'Specio::Constraint::Structurable',
36             parent => Specio::Library::Structured::Map->parent,
37             inline => \&Specio::Library::Structured::Map::_inline,
38             parameterization_args_builder =>
39             \&Specio::Library::Structured::Map::_parameterization_args_builder,
40             name_builder => \&Specio::Library::Structured::Map::_name_builder,
41             structured_inline_generator =>
42             \&Specio::Library::Structured::Map::_structured_inline_generator,
43             );
44              
45             declare(
46             'Tuple',
47             type_class => 'Specio::Constraint::Structurable',
48             parent => Specio::Library::Structured::Tuple->parent,
49             inline => \&Specio::Library::Structured::Tuple::_inline,
50             parameterization_args_builder =>
51             \&Specio::Library::Structured::Tuple::_parameterization_args_builder,
52             name_builder => \&Specio::Library::Structured::Tuple::_name_builder,
53             structured_inline_generator =>
54             \&Specio::Library::Structured::Tuple::_structured_inline_generator,
55             );
56             ## use critic
57              
58             sub optional {
59 5     5 0 35 return { optional => shift };
60             }
61              
62             sub slurpy {
63 1     1 0 7 return { slurpy => shift };
64             }
65              
66             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
67 7     7   36 sub _also_export {qw( optional slurpy )}
68             ## use critic
69              
70             1;
71              
72             # ABSTRACT: Structured types for Specio (Dict, Map, Tuple)
73              
74             __END__
75              
76             =pod
77              
78             =encoding UTF-8
79              
80             =head1 NAME
81              
82             Specio::Library::Structured - Structured types for Specio (Dict, Map, Tuple)
83              
84             =head1 VERSION
85              
86             version 0.53
87              
88             =head1 SYNOPSIS
89              
90             use Specio::Library::Builtins;
91             use Specio::Library::String;
92             use Specio::Library::Structured;
93              
94             my $map = t(
95             'Map',
96             of => {
97             key => t('NonEmptyStr'),
98             value => t('Int'),
99             },
100             );
101             my $tuple = t(
102             'Tuple',
103             of => [ t('Str'), t('Num') ],
104             );
105             my $dict = t(
106             'Dict',
107             of => {
108             kv => {
109             name => t('Str'),
110             age => t('Int'),
111             },
112             },
113             );
114              
115             =head1 DESCRIPTION
116              
117             This library provides a set of structured types for Specio, C<Dict>, C<Map>,
118             and C<Tuple>. This library also exports two helper subs used for some types,
119             C<optional> and C<slurpy>.
120              
121             All structured types are parameterized by calling C<< t( 'Type Name', of => ...
122             ) >>. The arguments passed after C<of> vary for each type.
123              
124             =head2 Dict
125              
126             A C<Dict> is a hashref with a well-defined set of keys and types for those key.
127              
128             The argument passed to C<of> should be a single hashref. That hashref must
129             contain a C<kv> key defining the expected keys and the types for their values.
130             This C<kv> value is itself a hashref. If a key/value pair is optional, use
131             C<optional> around the I<type> for that key:
132              
133             my $person = t(
134             'Dict',
135             of => {
136             kv => {
137             first => t('NonEmptyStr'),
138             middle => optional( t('NonEmptyStr') ),
139             last => t('NonEmptyStr'),
140             },
141             },
142             );
143              
144             If a key is optional, then it can be omitted entirely, but if it passed then
145             it's type will be checked, so it cannot just be set to C<undef>.
146              
147             You can also pass a C<slurpy> key. If this is passed, then the C<Dict> will
148             allow other, unknown keys, as long as they match the specified type:
149              
150             my $person = t(
151             'Dict',
152             of => {
153             kv => {
154             first => t('NonEmptyStr'),
155             middle => optional( t('NonEmptyStr') ),
156             last => t('NonEmptyStr'),
157             },
158             slurpy => t('Int'),
159             },
160             );
161              
162             =head2 Map
163              
164             A C<Map> is a hashref with specified types for its keys and values, but no
165             well-defined key names.
166              
167             The argument passed to C<of> should be a single hashref with two keys, C<key>
168             and C<value>. The type for the C<key> will typically be some sort of key, but
169             if you're using a tied hash or an object with hash overloading it could
170             conceivably be any sort of value.
171              
172             =head2 Tuple
173              
174             A C<Tuple> is an arrayref with a fixed set of members in a specific order.
175              
176             The argument passed to C<of> should be a single arrayref consisting of types.
177             You can mark a slot in the C<Tuple> as optional by wrapping the type in a call
178             to C<optional>:
179              
180             my $record = t(
181             'Tuple',
182             of => [
183             t('PositiveInt'),
184             t('Str'),
185             optional( t('Num') ),
186             optional( t('Num') ),
187             ],
188             );
189              
190             You can have as many C<optional> elements as you want, but they must always
191             come in sequence at the end of the tuple definition. You cannot interleave
192             required and optional elements.
193              
194             You can also make the Tuple accept an arbitrary number of values by wrapping
195             the last type in a call to C<slurpy>:
196              
197             my $record = t(
198             'Tuple',
199             of => [
200             t('PositiveInt'),
201             t('Str'),
202             slurpy( t('Num') ),
203             ],
204             );
205              
206             In this case, the C<Tuple> will require the first two elements and then allow
207             any number (including zero) of C<Num> elements.
208              
209             You cannot mix C<optional> and C<slurpy> in a C<Tuple> definition.
210              
211             =for Pod::Coverage optional slurpy
212              
213             =head1 LIMITATIONS
214              
215             Currently all structured types require that the types they are structured with
216             can be inlined. This may change in the future, but inlining all your types is a
217             really good idea, so you should do that anyway.
218              
219             =head1 SUPPORT
220              
221             Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
222              
223             =head1 SOURCE
224              
225             The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
226              
227             =head1 AUTHOR
228              
229             Dave Rolsky <autarch@urth.org>
230              
231             =head1 COPYRIGHT AND LICENSE
232              
233             This software is Copyright (c) 2012 - 2025 by Dave Rolsky.
234              
235             This is free software, licensed under:
236              
237             The Artistic License 2.0 (GPL Compatible)
238              
239             The full text of the license can be found in the
240             F<LICENSE> file included with this distribution.
241              
242             =cut