File Coverage

blib/lib/Org/Parser.pm
Criterion Covered Total %
statement 48 66 72.7
branch 21 32 65.6
condition 15 22 68.1
subroutine 6 6 100.0
pod 2 2 100.0
total 92 128 71.8


line stmt bran cond sub pod time code
1              
2             use 5.014; # compilation failure in older perls, RT#141560
3 23     23   1084639 use Moo;
  23         206  
4 23     23   10077  
  23         253857  
  23         102  
5             use Org::Document;
6 23     23   37765 use Scalar::Util qw(blessed);
  23         63  
  23         1046  
7 23     23   168  
  23         31  
  23         14304  
8             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
9             our $DATE = '2022-06-23'; # DATE
10             our $DIST = 'Org-Parser'; # DIST
11             our $VERSION = '0.558'; # VERSION
12              
13             my ($self, $arg, $opts) = @_;
14             die "Please specify a defined argument to parse()\n" unless defined($arg);
15 96     96 1 406149  
16 96 100       325 $opts //= {};
17              
18 94   100     431 my $str;
19             my $r = ref($arg);
20 94         132 if (!$r) {
21 94         162 $str = $arg;
22 94 100 33     262 } elsif ($r eq 'ARRAY') {
    100 66        
    100          
    100          
23 90         159 $str = join "", @$arg;
24             } elsif ($r eq 'GLOB' || blessed($arg) && $arg->isa('IO::Handle')) {
25 1         4 $str = join "", <$arg>;
26             } elsif ($r eq 'CODE') {
27 1         26 my @chunks;
28             while (defined(my $chunk = $arg->())) {
29 1         2 push @chunks, $chunk;
30 1         5 }
31 4         20 $str = join "", @chunks;
32             } else {
33 1         10 die "Invalid argument, please supply a ".
34             "string|arrayref|coderef|filehandle\n";
35 1         7 }
36             Org::Document->new(
37             from_string=>$str,
38             time_zone=>$opts->{time_zone},
39             ignore_unknown_settings=>$opts->{ignore_unknown_settings},
40             );
41             }
42 93         2094  
43             require File::Slurper;
44             my ($self, $filename, $opts) = @_;
45             $opts //= {};
46 13     13 1 4042981  
47 13         46584 state $loaded;
48 13   100     78  
49             my $content = File::Slurper::read_text($filename);
50 13         22  
51             my $cf = $opts->{cache_file}; # old option, new option is 'cache' (automatic setting of cache file)
52 13         55 my $doc;
53             my $cache; # undef = no caching; 0 = not cached, should cache; 1 = cached
54 13         1698 if (!$cf && ($opts->{cache} // $ENV{PERL_ORG_PARSER_CACHE})) {
55 13         36 require Cwd;
56             require Digest::MD5;
57 13 50 33     99 my @dirs = ("$ENV{HOME}/.cache/perl-org-parser", $ENV{HOME});
      66        
58 0         0 my $dir;
59 0         0 for (@dirs) {
60 0         0 if (-d $_) { $dir = $_; last }
61 0         0 elsif (mkdir $_) { $dir = $_; last }
62 0         0 }
63 0 0       0 die "Can't find a suitable cache directory" unless $dir;
  0 0       0  
  0         0  
64 0         0 my $abs = Cwd::abs_path($filename) or die "Can't find $filename";
  0         0  
65             my $base = $abs; $base =~ s!.+/!!;
66 0 0       0 $cf = "$dir/$base.".Digest::MD5::md5_hex($abs).".storable";
67 0 0       0 }
68 0         0 if ($cf) {
  0         0  
69 0         0 require Storable;
70             $cache = !!((-e $cf) && (-M $cf) <= (-M $filename));
71 13 100       40 if ($cache) {
72 3         724 eval {
73 3   66     3946 $doc = Storable::retrieve($cf);
74 3 100       16 $doc->load_element_modules unless $loaded++;
75 1         3 };
76 1         5 if ($@) {
77 1 50       120 warn "Failed retrieving document from cache: $@, reparsing ...";
78             $cache = 0;
79 1 50       27 }
80 0         0 }
81 0         0 }
82              
83             $doc = $self->parse($content, $opts) unless $cache;
84             if (defined($cache) && !$cache) {
85             require Storable;
86 13 100       77 for ($doc->find('Timestamp')) {
87 12 100 100     306 $_->clear_parse_result;
88 1         5 }
89 1         18 Storable::store($doc, $cf);
90 0         0 }
91              
92 1         6 $doc;
93             }
94              
95 12         520 1;
96             # ABSTRACT: Parse Org documents
97              
98              
99             =pod
100              
101             =encoding UTF-8
102              
103             =head1 NAME
104              
105             Org::Parser - Parse Org documents
106              
107             =head1 VERSION
108              
109             This document describes version 0.558 of Org::Parser (from Perl distribution Org-Parser), released on 2022-06-23.
110              
111             =head1 SYNOPSIS
112              
113             use 5.010;
114             use Org::Parser;
115             my $orgp = Org::Parser->new();
116              
117             # parse a file
118             my $doc = $orgp->parse_file("$ENV{HOME}/todo.org");
119              
120             # parse a string
121             $doc = $orgp->parse(<<EOF);
122             #+TODO: TODO | DONE CANCELLED
123             <<<radio target>>>
124             * heading1a
125             ** TODO heading2a
126             SCHEDULED: <2011-03-31 Thu>
127             [[some][link]]
128             ** DONE heading2b
129             [2011-03-18 ]
130             this will become a link: radio target
131             * TODO heading1b *bold*
132             - some
133             - plain
134             - list
135             - [ ] with /checkbox/
136             * and
137             * sublist
138             * CANCELLED heading1c
139             + definition :: list
140             + another :: def
141             EOF
142              
143             # walk the document tree
144             $doc->walk(sub {
145             my ($el) = @_;
146             return unless $el->isa('Org::Element::Headline');
147             say "heading level ", $el->level, ": ", $el->title->as_string;
148             });
149              
150             will print something like:
151              
152             heading level 1: heading1a
153             heading level 2: heading2a
154             heading level 2: heading2b *bold*
155             heading level 1: heading1b
156             heading level 1: heading1c
157              
158             A command-line utility (in a separate distribution: L<App::OrgUtils>) is
159             available for debugging:
160              
161             % dump-org-structure ~/todo.org
162             Document:
163             Setting: "#+TODO: TODO | DONE CANCELLED\n"
164             RadioTarget: "<<<radio target>>>"
165             Text: "\n"
166             Headline: l=1
167             (title)
168             Text: "heading1a"
169             (children)
170             Headline: l=2 todo=TODO
171             (title)
172             Text: "heading2a"
173             (children)
174             Text: "SCHEDULED: "
175             ...
176              
177             =head1 DESCRIPTION
178              
179             This module parses Org documents. See http://orgmode.org/ for more details on
180             Org documents.
181              
182             See C<todo.org> in the distribution for the list of already- and not yet
183             implemented stuffs.
184              
185             =head1 ATTRIBUTES
186              
187             =head1 METHODS
188              
189             =head2 new()
190              
191             Create a new parser instance.
192              
193             =head2 $orgp->parse($str | $arrayref | $coderef | $filehandle, \%opts) => $doc
194              
195             Parse document (which can be contained in a scalar $str, an arrayref of lines
196             $arrayref, a subroutine which will be called for chunks until it returns undef,
197             or a filehandle).
198              
199             Returns L<Org::Document> object.
200              
201             If 'handler' attribute is specified, will call handler repeatedly during
202             parsing. See the 'handler' attribute for more details.
203              
204             Will die if there are syntax errors in documents.
205              
206             Known options:
207              
208             =over 4
209              
210             =item * time_zone => STR
211              
212             Will be passed to Org::Document's constructor.
213              
214             =back
215              
216             =head2 $orgp->parse_file($filename, \%opts) => $doc
217              
218             Just like parse(), but will load document from file instead.
219              
220             Known options (aside from those known by parse()):
221              
222             =over 4
223              
224             =item * cache => bool (default: from PERL_ORG_PARSER_CACHE, or 0)
225              
226             Since Org::Parser can spend some time to parse largish Org files, this is an
227             option to store the parse result (using L<Storable>). If caching is turned on,
228             then after the first parse, the result will be stored in:
229              
230             ~/.cache/perl-org-parser/<filename>.<md5-digest-of-file-absolute-path>.storable
231              
232             and subsequent calls to this function can directly use this cache, as long as
233             the cache is not stale.
234              
235             =back
236              
237             =head1 FAQ
238              
239             =head2 Why? Just as only perl can parse Perl, only org-mode can parse Org anyway!
240              
241             True. I'm only targetting good enough. As long as I can parse/process all my Org
242             notes and todo files, I have no complaints.
243              
244             =head2 It's too slow!
245              
246             Parser is completely regex-based at the moment (I plan to use L<Marpa> someday).
247             Performance is quite lousy but I'm not annoyed enough at the moment to overhaul
248             it.
249              
250             =head1 ENVIRONMENT
251              
252             =head2 PERL_ORG_PARSER_CACHE => bool
253              
254             Set default for C<cache> option in C<parse_file()>.
255              
256             =head1 HOMEPAGE
257              
258             Please visit the project's homepage at L<https://metacpan.org/release/Org-Parser>.
259              
260             =head1 SOURCE
261              
262             Source repository is at L<https://github.com/perlancar/perl-Org-Parser>.
263              
264             =head1 SEE ALSO
265              
266             L<Org::Document>
267              
268             =head1 AUTHOR
269              
270             perlancar <perlancar@cpan.org>
271              
272             =head1 CONTRIBUTORS
273              
274             =for stopwords Alex White Karl Williamson Steven Haryanto Tekki Trent Fisher Wong Meng Weng
275              
276             =over 4
277              
278             =item *
279              
280             Alex White <VVu@geekfarm.org>
281              
282             =item *
283              
284             Karl Williamson <khw@cpan.org>
285              
286             =item *
287              
288             Steven Haryanto <stevenharyanto@gmail.com>
289              
290             =item *
291              
292             Tekki <tekki@tekki.ch>
293              
294             =item *
295              
296             Trent Fisher <trent@cs.pdx.edu>
297              
298             =item *
299              
300             Wong Meng Weng <mengwong@pobox.com>
301              
302             =back
303              
304             =head1 CONTRIBUTING
305              
306              
307             To contribute, you can send patches by email/via RT, or send pull requests on
308             GitHub.
309              
310             Most of the time, you don't need to build the distribution yourself. You can
311             simply modify the code, then test via:
312              
313             % prove -l
314              
315             If you want to build the distribution (e.g. to try to install it locally on your
316             system), you can install L<Dist::Zilla>,
317             L<Dist::Zilla::PluginBundle::Author::PERLANCAR>, and sometimes one or two other
318             Dist::Zilla plugin and/or Pod::Weaver::Plugin. Any additional steps required
319             beyond that are considered a bug and can be reported to me.
320              
321             =head1 COPYRIGHT AND LICENSE
322              
323             This software is copyright (c) 2022, 2021, 2020, 2019, 2017, 2016, 2015, 2014, 2013, 2012, 2011 by perlancar <perlancar@cpan.org>.
324              
325             This is free software; you can redistribute it and/or modify it under
326             the same terms as the Perl 5 programming language system itself.
327              
328             =head1 BUGS
329              
330             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Org-Parser>
331              
332             When submitting a bug or request, please include a test-file or a
333             patch to an existing test-file that illustrates the bug or desired
334             feature.
335              
336             =cut