File Coverage

blib/lib/Text/vCard/Precisely/V3/Node.pm
Criterion Covered Total %
statement 51 54 94.4
branch 11 16 68.7
condition 6 9 66.6
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   6852 use Carp;
  28         78  
  28         1789  
4 28     28   176 use Encode qw( decode_utf8 encode_utf8 is_utf8);
  28         54  
  28         1307  
5              
6 28     28   360 use 5.12.5;
  28         145  
7 28     28   5853 use Text::LineFold;
  28         220925  
  28         1556  
8              
9 28     28   210 use overload( '""' => \&as_string );
  28         64  
  28         222  
10              
11 28     28   1805 use Moose;
  28         77  
  28         317  
12 28     28   180993 use Moose::Util::TypeConstraints;
  28         70  
  28         240  
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   69190 no Moose;
  28         284  
  28         185  
55              
56             sub as_string {
57 129     129 0 296 my ($self) = @_;
58 129         211 my @lines;
59 129         3832 my $node = $self->name();
60 129         266 $node =~ tr/_/-/;
61              
62 129   33     423 push @lines, uc($node) || croak "Empty name";
63 129 100 66     3548 push @lines, 'TYPE=' . join( ',', map {uc} @{ $self->types() } )
  6         27  
  6         172  
64             if ref $self->types() eq 'ARRAY' and $self->types()->[0];
65 129 50       3553 push @lines, 'PREF=' . $self->pref() if $self->pref();
66 129 50       3525 push @lines, 'LANGUAGE=' . $self->language() if $self->language();
67              
68 129         3648 my $content = $self->content();
69             my $string
70             = join( ';', @lines ) . ':'
71             . (
72             ref($content) eq 'Array'
73 129 0       781 ? 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         381 return $self->fold($string);
78             }
79              
80             sub fold {
81 448     448 0 740 my $self = shift;
82 448         643 my $string = shift;
83 448         924 my %arg = @_;
84 448         1800 my $lf = Text::LineFold->new( CharMax => 74, Newline => "\x0D\x0A", TabSize => 1 )
85             ; # line break with 75bytes
86 448         269712 my $decoded = decode_utf8($string);
87              
88 448         7971 $string =~ s/(?<!\r)\n/\t/g;
89             $string
90 448 100 100     3250 = ( $decoded =~ /\P{ascii}+/ || $arg{'-force'} )
91             ? $lf->fold( "", " ", $string )
92             : $lf->fold( "", " ", $string );
93 448         178852 $string =~ tr/\t/\n/;
94              
95 448         8165 return $string;
96             }
97              
98             sub _escape {
99 563     563   899 my $self = shift;
100 563         758 my $txt = shift;
101 563 100       1457 return '' unless defined $txt;
102 457         834 $txt =~ s/([,;\\])/\\$1/g;
103 457         724 $txt =~ s/\n/\\n/g;
104 457         1174 return $txt;
105             }
106              
107             1;