File Coverage

blib/lib/RecentInfo/Entry.pm
Criterion Covered Total %
statement 64 69 92.7
branch 7 12 58.3
condition n/a
subroutine 9 9 100.0
pod 0 3 0.0
total 80 93 86.0


line stmt bran cond sub pod time code
1             package RecentInfo::Entry 0.04;
2 2     2   32 use 5.020;
  2         21  
3 2     2   10 use Moo 2;
  2         26  
  2         15  
4 2     2   755 use XML::LibXML;
  2         5  
  2         14  
5 2     2   404 use experimental 'signatures', 'postderef';
  2         10  
  2         13  
6 2     2   384 use Carp 'croak';
  2         17  
  2         119  
7 2     2   1285 use URI;
  2         11780  
  2         2708  
8              
9             =head1 NAME
10              
11             RecentInfo::Entry - recent files XBEL entry
12              
13             =cut
14              
15             has ['href'] => (
16             is => 'ro',
17             required => 1,
18             );
19              
20             has ['added', 'visited'] => (
21             is => 'rw',
22             );
23             has ['modified'] => (
24             is => 'lazy',
25             default => sub($self) {
26             (stat($self->to_native))[9]
27             }
28             );
29              
30             has ['mime_type'] => (
31             is => 'ro',
32             required => 1,
33             );
34              
35             has ['applications', 'groups'] => (
36             is => 'ro',
37             default => sub { [] },
38             );
39              
40             # XML fragments as strings
41             has 'othermeta' => (
42             is => 'ro',
43             default => sub { [] },
44             );
45              
46 2     2 0 4 sub to_native( $self ) {
  2         3  
  2         3  
47 2         8 my $href = $self->href;
48 2 50       44 return $href =~ m!^file:!
49             ? URI->new( $href )->file
50             : $href
51             }
52              
53             state $xpc = XML::LibXML::XPathContext->new();
54             $xpc->registerNs( bookmark => "http://www.freedesktop.org/standards/desktop-bookmarks");
55             $xpc->registerNs( mime => "http://www.freedesktop.org/standards/shared-mime-info" );
56              
57 5     5 0 8 sub as_XML_fragment($self, $doc) {
  5         8  
  5         7  
  5         8  
58 5         24 my $bookmark = $doc->createElement('bookmark');
59 5         28 $bookmark->setAttribute( 'href' => $self->href );
60             # Validate that $modified, $visited etc. are proper DateTime strings
61             # We enforce here a Z timezone
62              
63 5         38 for my $attr (qw(added modified visited )) {
64 15         207 my $at = $self->$attr;
65              
66             # Sanity check that we add an UTC timestamp to the XBEL structure
67 15 50       141 if( $at !~ /\A\d\d\d\d-[012]\d-[0123]\dT[012]\d:[0-5]\d:[0-6]\d(?:\.\d+)?Z\z/ ) {
68 0         0 croak "Invalid time format in '$attr': $at";
69             };
70              
71 15         103 $bookmark->setAttribute( $attr => $self->$attr );
72             };
73 5         65 my $info = $bookmark->addNewChild( undef, 'info' );
74 5         19 my $metadata = $info->addNewChild( undef, 'metadata' );
75             #my $mime = $metadata->addNewChild( 'mime', 'mime-type' );
76 5         20 my $mime = $metadata->addNewChild( undef,'mime:mime-type' );
77 5         18 $mime->setAttribute( type => $self->mime_type );
78             #$mime->appendText( $self->mime_type );
79 5         49 $metadata->setAttribute('owner' => 'http://freedesktop.org' );
80             # Should we allow this to be empty, or should we leave it out completely then?!
81              
82 5 100       56 if ($self->othermeta->@* ) {
83 1         6 my $parser = XML::LibXML->new();
84 1         19 for my $other ($self->othermeta->@* ) {
85 1         7 $info->addChild( $parser->parse_balanced_chunk( $other, 'UTF-8' )->firstChild);
86             }
87             };
88              
89 5 50       343 if( $self->groups->@* ) {
90 5         24 my $groups = $metadata->addNewChild( undef, "bookmark:groups" );
91 5         16 for my $group ($self->groups->@* ) {
92 7         56 $groups->addChild( $group->as_XML_fragment( $doc ));
93             };
94             }
95              
96 5         161 my $applications = $metadata->addNewChild( undef, "bookmark:applications" );
97 5         13 for my $application ($self->applications->@* ) {
98 7         79 $applications->addChild( $application->as_XML_fragment( $doc ));
99             };
100              
101 5         70 return $bookmark;
102             }
103              
104 12     12 0 20 sub from_XML_fragment( $class, $frag ) {
  12         43  
  12         44  
  12         16  
105 12         66 my $meta = $xpc->findnodes('./info[1]/metadata[@owner="http://freedesktop.org"]', $frag)->[0];
106 12 50       911 if(! $meta) {
107 0         0 warn $frag->toString;
108 0         0 croak "Invalid xml?! No / element found"
109             };
110              
111 12         96 my $othermeta = $xpc->findnodes('./info[1]/metadata[@owner!="http://freedesktop.org"]', $frag);
112 12         524 my @othermeta = map { $_->toString } $othermeta->@*;
  2         70  
113              
114 12         47 my %meta = (
115             mime_type => $xpc->find('./mime:mime-type/@type', $meta)->[0]->nodeValue,
116             );
117              
118 12         681 my @applications = $xpc->find('./bookmark:applications/bookmark:application', $meta)->@*;
119 12 50       537 if( !@applications ) {
120 0         0 warn $meta->toString;
121 0         0 die "No applications found";
122             };
123              
124             $class->new(
125             href => $frag->getAttribute('href'),
126             added => $frag->getAttribute('added'),
127             modified => $frag->getAttribute('modified'),
128             visited => $frag->getAttribute('visited'),
129             # info/metadata/mime-type
130             mime_type => $meta{ mime_type },
131             applications => [map {
132 16         1317 RecentInfo::Application->from_XML_fragment($_)
133             } $xpc->find('./bookmark:applications/bookmark:application', $meta)->@*],
134             groups => [map {
135 12         57 RecentInfo::GroupEntry->from_XML_fragment($_)
  16         2920  
136             } $xpc->find('./bookmark:groups/bookmark:group', $meta)->@*],
137             othermeta => \@othermeta,
138             #...
139             )
140             }
141              
142             1;
143             =head1 REPOSITORY
144              
145             The public repository of this module is
146             L.
147              
148             =head1 SUPPORT
149              
150             The public support forum of this module is L.
151              
152             =head1 BUG TRACKER
153              
154             Please report bugs in this module via Github
155             at L
156              
157             =head1 AUTHOR
158              
159             Max Maischein C
160              
161             =head1 COPYRIGHT (c)
162              
163             Copyright 2024-2024 by Max Maischein C.
164              
165             =head1 LICENSE
166              
167             This module is released under the same terms as Perl itself.
168              
169             =cut
170