File Coverage

blib/lib/DataStore/CAS/FS/DirCodec/Universal.pm
Criterion Covered Total %
statement 58 58 100.0
branch 19 24 79.1
condition 14 26 53.8
subroutine 10 10 100.0
pod 2 2 100.0
total 103 120 85.8


line stmt bran cond sub pod time code
1             package DataStore::CAS::FS::DirCodec::Universal;
2 5     5   76434 use 5.0080001;
  5         19  
  5         201  
3 5     5   69 use strict;
  5         29  
  5         188  
4 5     5   27 use warnings;
  5         9  
  5         133  
5 5     5   25 use Try::Tiny;
  5         10  
  5         372  
6 5     5   29 use Carp;
  5         8  
  5         319  
7 5     5   28 use JSON 2.53 ();
  5         134  
  5         324  
8             require DataStore::CAS::FS::Dir;
9             require DataStore::CAS::FS::DirEnt;
10             require DataStore::CAS::FS::InvalidUTF8;
11             *decode_utf8= *DataStore::CAS::FS::InvalidUTF8::decode_utf8;
12              
13 5     5   31 use parent 'DataStore::CAS::FS::DirCodec';
  5         9  
  5         41  
14              
15             our $VERSION= '0.011000';
16              
17             __PACKAGE__->register_format( universal => __PACKAGE__ );
18              
19             # ABSTRACT: Codec for saving all arbitrary fields of a DirEnt
20              
21              
22             our $_json_coder;
23             sub _build_json_coder {
24 3     3   131 DataStore::CAS::FS::InvalidUTF8->add_json_filter(
25             JSON->new->utf8->canonical->convert_blessed, 1
26             );
27             }
28              
29             sub encode {
30 30     30 1 5586 my ($class, $entry_list, $metadata)= @_;
31 30 50 33     129 ref($metadata) eq 'HASH' or croak "Metadata must be a hashref"
32             if $metadata;
33              
34 53 100       185 my @entries= sort { $a->{name} cmp $b->{name} }
  62         189  
35             map {
36 30         63 my $entry= ref $_ eq 'HASH'? $_ : $_->as_hash;
37 62 100       342 defined $entry->{name} or croak "Can't serialize nameless directory entry: ".JSON::encode_json($entry);
38 61 100       440 defined $entry->{type} or croak "Can't serialize typeless directory entry: ".JSON::encode_json($entry);
39             !defined($_) || (ref $_? ref($_)->can("TO_JSON") : &utf8::is_utf8($_) || !($_ =~ /[\x80-\xFF]/))
40             or croak "Can't serialize $entry->{name}, all attributes must be unicode string, or have TO_JSON: '$_'"
41 60 100 100     2210 for values %$entry;
      66        
      66        
42 57         196 $entry;
43             } @$entry_list;
44              
45 25   66     108 $_json_coder ||= _build_json_coder();
46              
47 25   50     227 my $json= $_json_coder->encode($metadata || {});
48 25         87 my $ret= "CAS_Dir 09 universal\n"
49             ."{\"metadata\":$json,\n"
50             ." \"entries\":[\n";
51 25         55 for (@entries) {
52 57         457 $ret .= $_json_coder->encode($_).",\n"
53             }
54              
55             # remove trailing comma
56 25 100       100 substr($ret, -2)= "\n" if @entries;
57 25         146 return $ret."]}";
58             }
59              
60              
61             sub decode {
62 50     50 1 1077 my ($class, $params)= @_;
63 50 100       194 defined $params->{format} or $params->{format}= $class->_read_format($params);
64 50         108 my $bytes= $params->{data};
65 50         88 my $handle= $params->{handle};
66              
67             # This implementation just processes the file as a whole.
68             # Read it in if we don't have it yet.
69 50         270 my $header_len= $class->_calc_header_length($params->{format});
70 50 100       158 if (defined $bytes) {
71 1         4 substr($bytes, 0, $header_len)= '';
72             }
73             else {
74 49 50       122 defined $handle or $handle= $params->{file}->open;
75 49 50       148 seek($handle, $header_len, 0) or croak "seek: $!";
76 49         210 local $/= undef;
77 49         414 $bytes= <$handle>;
78             }
79              
80 50   33     150 $_json_coder ||= _build_json_coder();
81              
82 50         958 my $data= $_json_coder->decode($bytes);
83 50 50 33     363 defined $data->{metadata} && ref($data->{metadata}) eq 'HASH'
84             or croak "Directory data is missing 'metadata'";
85 50 50 33     283 defined $data->{entries} && ref($data->{entries}) eq 'ARRAY'
86             or croak "Directory data is missing 'entries'";
87 50         65 my @entries;
88 50         75 for my $ent (@{$data->{entries}}) {
  50         127  
89 128         579 push @entries, DataStore::CAS::FS::DirEnt->new($ent);
90             };
91 50         424 return DataStore::CAS::FS::Dir->new(
92             file => $params->{file},
93             format => $params->{format},
94             entries => \@entries,
95             metadata => $data->{metadata}
96             );
97             }
98              
99             1;
100              
101             __END__