File Coverage

blib/lib/Text/vCard/Precisely/V3/Node.pm
Criterion Covered Total %
statement 55 58 94.8
branch 11 16 68.7
condition 8 11 72.7
subroutine 13 14 92.8
pod 0 3 0.0
total 87 102 85.2


line stmt bran cond sub pod time code
1             package Text::vCard::Precisely::V3::Node;
2              
3 28     28   8322 use Carp;
  28         86  
  28         2003  
4 28     28   221 use Encode qw( decode_utf8 encode_utf8 );
  28         71  
  28         1409  
5              
6 28     28   407 use 5.12.5;
  28         108  
7 28     28   6638 use Text::LineFold;
  28         249558  
  28         1697  
8              
9 28     28   307 use overload( '""' => \&as_string );
  28         72  
  28         307  
10              
11 28     28   2047 use Moose;
  28         72  
  28         297  
12 28     28   200839 use Moose::Util::TypeConstraints;
  28         73  
  28         306  
13              
14             enum 'Name' => [
15             qw( FN N SORT_STRING
16             ADR LABEL TEL EMAIL PHOTO LOGO URL
17             TZ GEO NICKNAME KEY NOTE
18             ORG TITLE ROLE CATEGORIES
19             SOURCE SOUND
20             X-SOCIALPROFILE
21             )
22             ];
23             has name => ( is => 'rw', required => 1, isa => 'Name' );
24              
25             subtype 'Content' => as 'Str' => where {
26 28     28   63530 use utf8;
  28         630  
  28         279  
27             decode_utf8($_) =~ m|^[\w\p{ascii}\s]*$|s
28             } # Does it need to be more strictly?
29             => message {"The value you provided, $_, was not supported"};
30             has content => ( is => 'rw', required => 1, isa => 'Content' );
31              
32             subtype 'Preffered' => as 'Int' => where { $_ > 0 and $_ <= 100 }
33             => message {"The number you provided, $_, was not supported in 'Preffered'"};
34             has pref => ( is => 'rw', isa => 'Preffered' );
35              
36             subtype 'Type' => as 'Str' => where {
37             m/^(?:work|home|PGP)$/is or #common
38             m|^(?:[a-zA-z0-9\-]+/X-[a-zA-z0-9\-]+)$|s; # does everything pass?
39             } => message {"The text you provided, $_, was not supported in 'Type'"};
40              
41             subtype 'Types' => as 'ArrayRef[Type]';
42             coerce 'Types' => from 'Str' => via { [$_] };
43             has types => ( is => 'rw', isa => 'Types', default => sub { [] }, coerce => 1 );
44              
45             subtype 'Language' => as 'Str' =>
46             where {m|^[a-z]{2}(?:-[a-z]{2})?$|s} # does it need something strictly?
47             => message {"The Language you provided, $_, was not supported"};
48             has language => ( is => 'rw', isa => 'Language' );
49              
50             sub charset { # DEPRECATED in vCard 3.0
51 0     0 0 0 my $self = shift;
52 0         0 croak "'CHARSET' param is DEPRECATED! vCard3.0 will accept just ONLY UTF-8";
53             }
54              
55             __PACKAGE__->meta->make_immutable;
56 28     28   22900 no Moose;
  28         94  
  28         203  
57              
58             sub as_string {
59 137     137 0 337 my ($self) = @_;
60 137         236 my @lines;
61 137         4231 my $node = $self->name();
62 137         275 $node =~ tr/_/-/;
63              
64 137   33     474 push @lines, uc($node) || croak "Empty name";
65 137 100 66     4155 push @lines, 'TYPE=' . join( ',', map { uc $_ } @{ $self->types() } )
  6         24  
  6         144  
66             if ref $self->types() eq 'ARRAY' and $self->types()->[0];
67 137 50       3939 push @lines, 'PREF=' . $self->pref() if $self->pref();
68 137 50       3923 push @lines, 'LANGUAGE=' . $self->language() if $self->language();
69              
70 137         4120 my $content = $self->content();
71             my $string
72             = join( ';', @lines ) . ':'
73             . (
74             ref($content) eq 'Array'
75 137 0       819 ? map { $node =~ /^(?:LABEL|GEO)$/s ? $content : $self->_escape($_) } @$content
  0 100       0  
    50          
76             : $node =~ /^(?:LABEL|GEO)$/s ? $content
77             : $self->_escape($content)
78             );
79 137         384 return $self->fold($string);
80             }
81              
82             sub fold {
83 384     384 0 725 my $self = shift;
84 384         634 my $string = shift;
85 384         876 my %arg = @_;
86 384         1760 my $lf = Text::LineFold->new( CharMax => 74, Newline => "\x0D\x0A", TabSize => 1 )
87             ; # line break with 75bytes
88 384         254903 my $decoded = decode_utf8($string);
89              
90 28     28   16954 use utf8;
  28         68  
  28         154  
91 384         7315 $string =~ s/(?<!\r)\n/\t/g;
92             $string
93 384 100 100     3040 = ( $decoded =~ /\P{ascii}+/ || $arg{'-force'} )
94             ? $lf->fold( "", " ", $string )
95             : $lf->fold( "", " ", $string );
96              
97 384         176433 $string =~ tr/\t/\n/;
98              
99 384         7566 return $string;
100             }
101              
102             sub _escape {
103 579     579   998 my $self = shift;
104 579         871 my $txt = shift;
105 579 100       1413 ( my $r = $txt ) =~ s/([,;\\])/\\$1/sg if $txt;
106 579   100     2338 return $r || '';
107             }
108              
109             1;