File Coverage

lib/Data/Identifier/Wellknown.pm
Criterion Covered Total %
statement 41 153 26.8
branch 2 66 3.0
condition 2 45 4.4
subroutine 12 15 80.0
pod 2 2 100.0
total 59 281 21.0


line stmt bran cond sub pod time code
1             # Copyright (c) 2023-2026 Philipp Schafft
2              
3             # licensed under Artistic License 2.0 (see LICENSE file)
4              
5             # ABSTRACT: format independent identifier object
6              
7              
8             package Data::Identifier::Wellknown;
9              
10 1     1   1393 use v5.14;
  1         2  
11 1     1   4 use strict;
  1         1  
  1         19  
12 1     1   3 use warnings;
  1         1  
  1         42  
13 1     1   505 use utf8;
  1         245  
  1         5  
14              
15 1     1   24 use Carp;
  1         2  
  1         68  
16 1     1   4 use Fcntl qw(SEEK_SET);
  1         1  
  1         33  
17              
18 1     1   3 use Data::Identifier;
  1         1  
  1         4  
19 1     1   410 use Data::Identifier::Generate;
  1         1  
  1         33  
20              
21 1     1   6 use parent 'Data::Identifier::Interface::Known';
  1         1  
  1         7  
22              
23             our $VERSION = v0.29;
24              
25             use constant {
26 1         1512 WK_UUID => '8be115d2-dc2f-4a98-91e1-a6e3075cbc31', # uuid
27             WK_SID => 'f87a38cb-fd13-4e15-866c-e49901adbec5', # small-identifier
28             WK_SNI => '039e0bb7-5dd3-40ee-a98c-596ff6cce405', # sirtx-numerical-identifier
29 1     1   85 };
  1         1  
30              
31             my %imported;
32             my %loaded = (wellknown => undef, registered => undef); # consider two loaded to begin with.
33             my $start_of_data = DATA->tell;
34              
35             foreach my $class (keys %loaded) {
36             foreach my $id (Data::Identifier->known($class)) {
37             _add_classes($id, $class);
38             }
39             }
40              
41             sub _known_provider {
42 0     0   0 my ($pkg, $class, %opts) = @_;
43 0 0       0 croak 'Unsupported options passed' if scalar(keys %opts);
44              
45 0 0       0 unless (exists $loaded{$class}) {
46 0         0 __PACKAGE__->import($class);
47             }
48              
49 0 0 0     0 if ($class eq 'wellknown' || $class eq 'registered' || $class eq ':all') {
      0        
50 0         0 goto &Data::Identifier::_known_provider;
51             } else {
52 0         0 return ($imported{$class}, rawtype => 'Data::Identifier');
53             }
54             }
55              
56             sub import {
57 1     1   13 my ($pkg, @args) = @_;
58 1         5 my $id_type = Data::Identifier->new(wellknown => 'uuid');
59 1         5 my $default_class;
60             my %generator;
61 1         0 my $namespace;
62 1         0 my %found;
63 1         0 my @extra_classes;
64              
65 1 50       3 return if exists $loaded{':all'}; # if we done :all we ... did all!
66              
67 1         2 @args = grep {!exists($loaded{$_})} @args;
  0         0  
68              
69 1 50       19 return unless scalar @args;
70              
71 0         0 DATA->seek($start_of_data, SEEK_SET);
72              
73 0         0 while (my $line = ) {
74 0         0 my ($classes, $id, $displayname, $special);
75 0         0 my @classes;
76 0         0 my $found;
77              
78 0         0 $line =~ s/\s{2,}%.*$//;
79 0         0 $line =~ s/^\s*%.*$//;
80 0         0 $line =~ s/\s+$//;
81 0         0 $line =~ s/^\s+//;
82 0         0 $line =~ s/\r?\n$//;
83              
84 0 0       0 next if $line eq '';
85              
86 0 0       0 if ($line =~ /^\$/) {
87 0         0 my ($command, $arg) = split(/\s+/, $line, 2);
88            
89 0 0       0 if ($command eq '$type') {
    0          
    0          
    0          
    0          
    0          
90 0 0       0 if ($arg =~ /^(.+)=(.+)$/) {
91 0         0 $id_type = Data::Identifier->new($1, $2);
92             } else {
93 0         0 $id_type = Data::Identifier->new(wellknown => $arg);
94             }
95 0         0 $namespace = undef;
96             } elsif ($command eq '$class') {
97 0         0 $default_class = $arg;
98 0         0 @extra_classes = ();
99             } elsif ($command eq '$extra_classes') {
100 0         0 @extra_classes = split(/,/, $arg);
101             } elsif ($command eq '$generator') {
102 0         0 %generator = split(/[,=]/, $arg);
103 0         0 $id_type = undef;
104 0         0 $namespace = undef;
105 0         0 foreach my $key (qw(generator namespace)) {
106 0 0       0 next unless defined $generator{$key};
107 0         0 $generator{$key} = Data::Identifier->new(from => $generator{$key})->register;
108             }
109             } elsif ($command eq '$namespace') {
110 0         0 $namespace = $arg;
111             } elsif ($command eq '$end') {
112 0         0 last;
113             } else {
114 0         0 croak 'BUG';
115             }
116 0         0 next;
117             }
118              
119 0         0 ($classes, $id, $displayname, $special) = split(/\s{2,}/, $line, 4);
120 0 0       0 if ($classes eq '.') {
121 0         0 @classes = ($default_class);
122             } else {
123 0         0 @classes = split(',', $classes);
124             }
125 0 0       0 if (defined($displayname)) {
126 0 0       0 $displayname = undef if $displayname eq '.';
127             }
128              
129 0         0 push(@classes, @extra_classes);
130              
131 0         0 foreach my $class_a (@classes) {
132 0         0 foreach my $class_b (@args) {
133 0 0 0     0 if ($class_a eq $class_b || $class_b eq ':all') {
134 0         0 $found = 1;
135 0         0 last;
136             }
137             }
138             }
139 0 0       0 next unless $found;
140              
141             {
142 0         0 my $identifier;
  0         0  
143              
144 0 0       0 if (defined $id_type) {
145 0 0       0 $identifier = Data::Identifier->new(
146             $id_type => $id,
147             defined($displayname) ? (displayname => $displayname) : (),
148             );
149             } else {
150 0 0       0 $identifier = Data::Identifier::Generate->generic(
151             %generator,
152             request => $id,
153             defined($displayname) ? (displayname => $displayname) : (),
154             );
155             }
156              
157 0 0       0 if (defined $namespace) {
158 0         0 my $uuid;
159              
160 0 0       0 if ($namespace =~ /^(.+),lc$/) {
161 0         0 $uuid = Data::Identifier::Generate->_uuid_v5($1, lc($id));
162             } else {
163 0         0 $uuid = Data::Identifier::Generate->_uuid_v5($namespace, $id);
164             }
165 0   0     0 $identifier->{id_cache} //= {};
166 0   0     0 $identifier->{id_cache}->{WK_UUID()} //= $uuid;
167             }
168              
169 0 0 0     0 if (defined($special) && length($special)) {
170 0         0 my %special = split(/[,=]/, $special);
171              
172 0 0       0 if (defined $special{sid}) {
173 0   0     0 $identifier->{id_cache} //= {};
174 0   0     0 $identifier->{id_cache}->{WK_SID()} //= $special{sid};
175             }
176              
177 0 0       0 if (defined $special{sni}) {
178 0   0     0 $identifier->{id_cache} //= {};
179 0   0     0 $identifier->{id_cache}->{WK_SNI()} //= $special{sni};
180             }
181             }
182              
183 0   0     0 $identifier->{displayname} //= $displayname; # force-update, using internal API. DO NOT TRY AT HOME!
184 0         0 $identifier->register;
185              
186 0         0 foreach my $class (@classes) {
187 0         0 $found{$class} = undef;
188 0   0     0 $imported{$class} //= [];
189 0         0 push(@{$imported{$class}}, $identifier);
  0         0  
190             }
191              
192 0         0 _add_classes($identifier, @classes);
193             }
194             }
195              
196             # deduplicate:
197 0         0 foreach my $class (keys %found) {
198 0         0 my %tmp = map {$_ => $_} @{$imported{$class}};
  0         0  
  0         0  
199 0         0 @{$imported{$class}} = values %tmp;
  0         0  
200             }
201              
202             # Mark classes loaded that we found data for.
203 0         0 foreach my $class (@args) {
204 0 0 0     0 if (exists($found{$class}) || $class eq ':all') {
205 0         0 $loaded{$class} = undef;
206             } else {
207 0         0 croak 'Unsupported class: '.$class;
208             }
209             }
210             }
211              
212              
213             sub classes_of {
214 0     0 1 0 my ($pkg, $identifier) = @_;
215 0         0 $identifier = Data::Identifier->new(from => $identifier);
216              
217 0   0     0 return keys %{$identifier->userdata(__PACKAGE__, 'classes') // {}};
  0         0  
218             }
219              
220              
221             sub announce {
222 0     0 1 0 my ($pkg, $type, $regs, @opts) = @_;
223              
224 0 0       0 croak 'Stray options passed' if scalar @opts;
225              
226 0         0 $type = Data::Identifier->new(from => $type); # Force to Data::Identifier
227 0         0 $type->register; # Ensure it is registered
228              
229 0 0       0 if (ref($regs) eq 'HASH') {
    0          
230 0         0 $type = $type->uuid; # Convert to UUID for cache usage
231 0         0 foreach my $key (keys %{$regs}) {
  0         0  
232 0         0 my $identifier = Data::Identifier->new(from => $regs->{$key});
233 0   0     0 $identifier->{id_cache} //= {};
234 0   0     0 $identifier->{id_cache}->{$type} //= $key;
235 0         0 $identifier->register;
236             }
237             } elsif (ref($regs) eq 'ARRAY') {
238 0         0 foreach my $key (@{$regs}) {
  0         0  
239 0 0       0 if (ref $key) {
240 0         0 Data::Identifier->new(from => $key)->register;
241             } else {
242 0         0 Data::Identifier->new($type => $key)->register;
243             }
244             }
245             } else {
246 0         0 croak 'Unsupported data type';
247             }
248             }
249              
250             # ---- Private helpers ----
251              
252             sub _add_classes {
253 122     122   166 my ($identifier, @classes) = @_;
254 122   66     152 my $set = $identifier->userdata(__PACKAGE__, 'classes') // $identifier->userdata(__PACKAGE__, 'classes' => {});
255 122         218 $set->{$_} = undef foreach @classes, ':all', 'registered';
256             }
257              
258             1;
259              
260             =pod
261              
262             =encoding UTF-8
263              
264             =head1 NAME
265              
266             Data::Identifier::Wellknown - format independent identifier object
267              
268             =head1 VERSION
269              
270             version v0.29
271              
272             =head1 SYNOPSIS
273              
274             use Data::Identifier::Wellknown qw(classes...);
275             e.g.:
276             use Data::Identifier::Wellknown ':all';
277              
278             (experimental since v0.07)
279              
280             This package provides a simple list of well known identifiers.
281             Classes are loaded on demand. However for speedy lookup classes can
282             be imported (given via C arguments).
283              
284             If a class is already loaded, it is not reloaded.
285             If a program knows the classes it will use early it makes sense to
286             include this module in the main program (or root module) before other modules that make
287             use of this module are used with all the used classes listed.
288             This improves speed as it will reduce the read of the full list to a single pass.
289             In contrast if every use will only list a single class that is not yet loaded loading will be most in-efficient.
290              
291             B
292             This is an B package.
293             It's methods and classes are not stable.
294             But the package itself and the class C<:all> is.
295              
296             This package implements L.
297              
298             =head1 METHODS
299              
300             =head2 classes_of
301              
302             my @classes = Data::Identifier::Wellknown->classes_of($identifier);
303              
304             (experimental since v0.07)
305              
306             Returns the classes the identifier is known for.
307             C<$identifier> is parsed as per C of L.
308              
309             B
310             This module does not guarantee any specific order of the returned list.
311              
312             B
313             Classes may not be included in returned list unless they (or C<:all>) have been
314             imported before.
315              
316             B
317             This is an B method. It may be changed, renamed, or removed without notice.
318              
319             =head2 announce
320              
321             Data::Identifier::Wellknown->announce($type => $data);
322              
323             (experimental since v0.26)
324              
325             This method can be used to announce additional well known identifiers.
326             It is mostly used by other modules to register identifiers for a given type related to those modules
327             and alias them to UUIDs.
328             This is mostly useful if those identifiers are mapped to UUIDs via a register (in contrast to a generator).
329              
330             The type is passed as C<$type> which is parsed as per C of L.
331             It must resolve to an identifier with a defined UUID.
332             This module might make restrictions on the type identifiers can be registered for.
333              
334             If C<$data> is a hashref the keys are understood as the identifiers of the type given via C<$type>
335             and the values are the corresponding identifiers (parsed as per C of L) to map to.
336              
337             If C<$data> is an arrayref the values are understood as the identifiers.
338             L objects are created as needed and registered.
339              
340             C<$type> and all identifiers updated by this method will be registered as per L.
341              
342             =head1 AUTHOR
343              
344             Philipp Schafft
345              
346             =head1 COPYRIGHT AND LICENSE
347              
348             This software is Copyright (c) 2023-2026 by Philipp Schafft .
349              
350             This is free software, licensed under:
351              
352             The Artistic License 2.0 (GPL Compatible)
353              
354             =cut
355              
356             __DATA__