File Coverage

blib/lib/Text/vCard/Precisely/V3/Node.pm
Criterion Covered Total %
statement 49 52 94.2
branch 11 16 68.7
condition 8 11 72.7
subroutine 11 12 91.6
pod 0 3 0.0
total 79 94 84.0


line stmt bran cond sub pod time code
1             package Text::vCard::Precisely::V3::Node;
2              
3 28     28   7552 use Carp;
  28         77  
  28         1865  
4 28     28   233 use Encode qw( decode_utf8 encode_utf8 is_utf8);
  28         76  
  28         1324  
5              
6 28     28   366 use 5.12.5;
  28         116  
7 28     28   6729 use Text::LineFold;
  28         239828  
  28         1594  
8              
9 28     28   216 use overload( '""' => \&as_string );
  28         68  
  28         241  
10              
11 28     28   1754 use Moose;
  28         69  
  28         347  
12 28     28   184630 use Moose::Util::TypeConstraints;
  28         64  
  28         246  
13              
14             enum 'Name' => [
15             qw( FN N SORT_STRING ORG TITLE ROLE
16             ADR LABEL TEL EMAIL PHOTO LOGO URL SOURCE SOUND
17             TZ GEO KEY NOTE
18             X-SOCIALPROFILE
19             )
20             ];
21             has name => ( is => 'rw', required => 1, isa => 'Name' );
22              
23             subtype 'Content' => as 'Str'; # => where {
24              
25             # !is_utf8($_) && decode_utf8($_) =~ m|^[\w\p{ascii}\s]+$|s # It seems these lines
26             #} # Does it need to be more strictly? # do NOT work
27             #=> message {"The value you provided, $_, was not supported"}; # like what I've thought
28             has content => ( is => 'rw', required => 1, isa => 'Content' );
29              
30             subtype 'Preffered' => as 'Int' => where { $_ > 0 and $_ <= 100 }
31             => message {"The number you provided, $_, was not supported in 'Preffered'"};
32             has pref => ( is => 'rw', isa => 'Preffered' );
33              
34             subtype 'Type' => as 'Str' => where {
35             m/^(?:work|home|PGP)$/is or #common
36             m|^(?:[a-zA-z0-9\-]+/X-[a-zA-z0-9\-]+)$|s; # does everything pass?
37             } => message {"The text you provided, $_, was not supported in 'Type'"};
38              
39             subtype 'Types' => as 'ArrayRef[Type]';
40             coerce 'Types' => from 'Str' => via { [$_] };
41             has types => ( is => 'rw', isa => 'Types', default => sub { [] }, coerce => 1 );
42              
43             subtype 'Language' => as 'Str' =>
44             where {m|^[a-z]{2}(?:-[a-z]{2})?$|s} # does it need something strictly?
45             => message {"The Language you provided, $_, was not supported"};
46             has language => ( is => 'rw', isa => 'Language' );
47              
48             sub charset { # DEPRECATED in vCard 3.0
49 0     0 0 0 my $self = shift;
50 0         0 croak "'CHARSET' param is DEPRECATED! vCard3.0 will accept just ONLY UTF-8";
51             }
52              
53             __PACKAGE__->meta->make_immutable;
54 28     28   70232 no Moose;
  28         235  
  28         206  
55              
56             sub as_string {
57 129     129 0 300 my ($self) = @_;
58 129         244 my @lines;
59 129         3875 my $node = $self->name();
60 129         255 $node =~ tr/_/-/;
61              
62 129   33     435 push @lines, uc($node) || croak "Empty name";
63 129 100 66     3685 push @lines, 'TYPE=' . join( ',', map { uc $_ } @{ $self->types() } )
  6         27  
  6         175  
64             if ref $self->types() eq 'ARRAY' and $self->types()->[0];
65 129 50       3563 push @lines, 'PREF=' . $self->pref() if $self->pref();
66 129 50       3662 push @lines, 'LANGUAGE=' . $self->language() if $self->language();
67              
68 129         3649 my $content = $self->content();
69             my $string
70             = join( ';', @lines ) . ':'
71             . (
72             ref($content) eq 'Array'
73 129 0       838 ? map { $node =~ /^(?:LABEL|GEO)$/s ? $content : $self->_escape($_) } @$content
  0 100       0  
    50          
74             : $node =~ /^(?:LABEL|GEO)$/s ? $content
75             : $self->_escape($content)
76             );
77 129         346 return $self->fold($string);
78             }
79              
80             sub fold {
81 448     448 0 742 my $self = shift;
82 448         663 my $string = shift;
83 448         973 my %arg = @_;
84 448         1928 my $lf = Text::LineFold->new( CharMax => 74, Newline => "\x0D\x0A", TabSize => 1 )
85             ; # line break with 75bytes
86 448         278664 my $decoded = decode_utf8($string);
87              
88 448         8244 $string =~ s/(?<!\r)\n/\t/g;
89             $string
90 448 100 100     3366 = ( $decoded =~ /\P{ascii}+/ || $arg{'-force'} )
91             ? $lf->fold( "", " ", $string )
92             : $lf->fold( "", " ", $string );
93              
94 448         186243 $string =~ tr/\t/\n/;
95              
96 448         8433 return $string;
97             }
98              
99             sub _escape {
100 563     563   928 my $self = shift;
101 563         790 my $txt = shift;
102 563 100       1333 ( my $r = $txt ) =~ s/([,;\\])/\\$1/sg if $txt;
103 563   100     1929 return $r || '';
104             }
105              
106             1;