File Coverage

blib/lib/RT/Client/REST/Forms.pm
Criterion Covered Total %
statement 68 126 53.9
branch 23 64 35.9
condition 12 36 33.3
subroutine 7 9 77.7
pod 5 5 100.0
total 115 240 47.9


line stmt bran cond sub pod time code
1             #!perl
2             # PODNAME: RT::Client::REST::Forms
3             # ABSTRACT: This package provides functions from RT::Interface::REST, because we don't want to depend on rt being installed. Derived from rt 3.4.5.
4              
5 22     22   86194 use strict;
  22         49  
  22         596  
6 22     22   103 use warnings;
  22         37  
  22         933  
7              
8             $RT::Client::REST::Forms::VERSION = '0.70';
9             use Exporter;
10 22     22   114  
  22         44  
  22         841  
11             use vars qw(@EXPORT @ISA);
12 22     22   105  
  22         39  
  22         32043  
13             @ISA = qw(Exporter);
14             @EXPORT = qw(expand_list form_parse form_compose vpush vsplit);
15              
16             my $CF_name = q%[#\s\w:()?/-]+%;
17             my $field = qr/[a-z][\w-]*|C(?:ustom)?F(?:ield)?-$CF_name|CF\.\{$CF_name}/i;
18             # always 9 https://rt-wiki.bestpractical.com/wiki/REST#Ticket_Attachments
19             my $spaces = ' ' x 9;
20              
21              
22             my ($list) = @_;
23             my (@elts, %elts);
24 0     0 1 0  
25 0         0 for my $elt (split /,/, $list) {
26             if ($elt =~ m/^(\d+)-(\d+)$/) { push @elts, ($1..$2) }
27 0         0 else { push @elts, $elt }
28 0 0       0 }
  0         0  
29 0         0  
30             @elts{@elts}=();
31             my @return = sort {$a<=>$b} keys %elts;
32 0         0 return @return
33 0         0 }
  0         0  
34              
35 0         0  
36             my @lines = split /(?<=\n)/, shift;
37             my $state = 0;
38             my @forms = ();
39 8     8 1 5711 my ($c, $o, $k, $e) = ('', [], {}, '');
40 8         23  
41 8         20 LINE:
42 8         109 while (@lines) {
43             my $line = shift @lines;
44              
45 8         43 next LINE if $line eq "\n";
46 96         145  
47             if ($line eq "--\n") {
48 96 50       184 # We reached the end of one form. We'll ignore it if it was
49             # empty, and store it otherwise, errors and all.
50 96 50       168 if ($e || $c || @$o) {
51             push @forms, [ $c, $o, $k, $e ];
52             $c = ''; $o = []; $k = {}; $e = '';
53 0 0 0     0 }
      0        
54 0         0 $state = 0;
55 0         0 next LINE
  0         0  
  0         0  
  0         0  
56             }
57 0         0  
58             if ($state != -1) {
59 0         0  
60             if ($state == 0 && $line =~ m/^#/) {
61 96 50       166 # Read an optional block of comments (only) at the start
62             # of the form.
63 96 50 66     249 $state = 1;
64             $c = $line;
65             while (@lines && $lines[0] =~ m/^#/) {
66 0         0 $c .= shift @lines;
67 0         0 }
68 0   0     0 next LINE
69 0         0 }
70              
71             if ($state <= 1 && $line =~ m/^($field: )(.*)?$/s) {
72 0         0 # Read a field: value specification.
73             my $f = $1;
74 96 50 33     1595 my $value = $2;
75             $f =~ s/: ?$//;
76 96         236  
77 96         141 # Read continuation lines, if any.
78 96         358 while (@lines && ($lines[0] eq "\n" || $lines[0] =~ m/^ +/)) {
79             my $l = shift @lines;
80             $l =~ s/^$spaces//;
81 96   100     486 $value .= $l
      100        
82 216         369 }
83 216         734  
84 216         963 # `Content` is always supposed to be followed by three new lines
85             # ... but this doesnt behave as documented
86             # https://rt-wiki.bestpractical.com/wiki/REST#Ticket_Attachments
87             if ($f eq 'Content') {
88             $value =~ s/\n\n\n?$//g
89             }
90 96 100       172 # Chomp everything else
91 8         201 else {
92             chomp $value
93             }
94              
95 88         143 push(@$o, $f) unless exists $k->{$f};
96             vpush($k, $f, $value);
97              
98 96 50       277 $state = 1;
99 96         247  
100             next LINE
101 96         130 }
102              
103             if ($line !~ m/^#/) {
104 96         235 # We've found a syntax error, so we'll reconstruct the
105             # form parsed thus far, and add an error marker. (>>)
106 0 0       0 $state = -1;
107             $e = form_compose([[ '', $o, $k, '' ]]);
108             $e.= $line =~ m/^>>/ ? "$line\n" : ">> $line\n";
109 0         0 next LINE
110 0         0 }
111 0 0       0  
112             # line will be ignored
113 0         0 }
114             else {
115             # We saw a syntax error earlier, so we'll accumulate the
116             # contents of this form until the end.
117             $e .= "$line\n";
118             }
119             }
120 0         0 push(@forms, [ $c, $o, $k, $e ]) if ($e || $c || @$o);
121              
122             for my $l (keys %$k) {
123 8 50 33     108 $k->{$l} = vsplit($k->{$l}) if (ref $k->{$l} eq 'ARRAY');
      33        
124             }
125 8         82  
126 96 50       199 return \@forms;
127             }
128              
129 8         43  
130             my ($forms) = @_;
131             my @text;
132              
133             for my $form (@$forms) {
134 6     6 1 17 my ($c, $o, $k, $e) = @$form;
135 6         13 my $text = '';
136              
137 6         17 if ($c) {
138 6         17 $c =~ s/\n*$/\n/;
139 6         13 $text = "$c\n";
140             }
141 6 50       17 if ($e) {
142 0         0 $text .= $e;
143 0         0 }
144             elsif ($o) {
145 6 50       20 my @lines;
    50          
146 0         0  
147             for my $key (@$o) {
148             my ($line, $sp);
149 6         7 my @values = (ref $k->{$key} eq 'ARRAY') ?
150             @{ $k->{$key} } :
151 6         15 $k->{$key};
152 10         16  
153             $sp = " "x(length("$key: "));
154 0         0 $sp = " "x4 if length($sp) > 16;
155 10 50       33  
156             for my $v (@values) {
157 10         35 if ($v =~ /\n/) {
158 10 50       23 $v =~ s/^/$sp/gm;
159             $v =~ s/^$sp//;
160 10         20  
161 10 50 33     49 if ($line) {
    50          
162 0         0 push @lines, "$line\n\n";
163 0         0 $line = '';
164             }
165 0 0 0     0 elsif (@lines && $lines[-1] !~ m/\n\n$/) {
    0          
166 0         0 $lines[-1] .= "\n";
167 0         0 }
168             push @lines, "$key: $v\n\n";
169             }
170 0         0 elsif ($line &&
171             length($line)+length($v)-rindex($line, "\n") >= 70)
172 0         0 {
173             $line .= ",\n$sp$v";
174             }
175             else {
176             $line = $line ? "$line, $v" : "$key: $v";
177 0         0 }
178             }
179              
180 10 50       32 $line = "$key:" unless @values;
181             if ($line) {
182             if ($line =~ m/\n/) {
183             if (@lines && $lines[-1] !~ m/\n\n$/) {
184 10 50       22 $lines[-1] .= "\n";
185 10 50       21 }
186 10 50       25 $line .= "\n";
187 0 0 0     0 }
188 0         0 push @lines, "$line\n";
189             }
190 0         0 }
191              
192 10         27 $text .= join '', @lines;
193             }
194             else {
195             chomp $text;
196 6         20 }
197             push @text, $text;
198             }
199 0         0  
200             return join "\n--\n\n", @text;
201 6         12 }
202              
203              
204 6         24 my ($hash, $key, $val) = @_;
205             my @val = ref $val eq 'ARRAY' ? @$val : $val;
206              
207             if (exists $hash->{$key}) {
208             unless (ref $hash->{$key} eq 'ARRAY') {
209 96     96 1 311 my @v = $hash->{$key} ne '' ? $hash->{$key} : ();
210 96 50       217 $hash->{$key} = \@v;
211             }
212 96 50       188 push @{ $hash->{$key} }, @val;
213 0 0       0 }
214 0 0       0 else {
215 0         0 $hash->{$key} = $val;
216             }
217 0         0 }
  0         0  
218              
219              
220 96         250 my ($val) = @_;
221             my (@words);
222              
223             for my $line (map {split /\n/} (ref $val eq 'ARRAY') ? @$val : $val)
224             {
225             # XXX: This should become a real parser, à la Text::ParseWords.
226 0     0 1   $line =~ s/^\s+//;
227 0           $line =~ s/\s+$//;
228             push @words, split /\s*,\s*/, $line;
229 0 0         }
  0            
230              
231             return \@words;
232 0           }
233 0            
234 0            
235             =pod
236              
237 0           =encoding UTF-8
238              
239             =head1 NAME
240              
241             RT::Client::REST::Forms - This package provides functions from RT::Interface::REST, because we don't want to depend on rt being installed. Derived from rt 3.4.5.
242              
243             =head1 VERSION
244              
245             version 0.70
246              
247             =head2 METHODS
248              
249             =over 4
250              
251             =item expand_list
252              
253             Expands a list, splitting on commas and stuff.
254              
255             =item form_parse
256              
257             Returns a reference to an array of parsed forms.
258              
259             =item form_compose
260              
261             Returns text representing a set of forms.
262              
263             =for stopwords vpush vsplit
264              
265             =item vpush
266              
267             Add a value to a (possibly multi-valued) hash key.
268              
269             =item vsplit
270              
271             "Normalize" a hash key that's known to be multi-valued.
272              
273             =back
274              
275             1;
276              
277             =head1 AUTHOR
278              
279             Dean Hamstead <dean@fragfest.com.au>
280              
281             =head1 COPYRIGHT AND LICENSE
282              
283             This software is copyright (c) 2022, 2020 by Dmitri Tikhonov.
284              
285             This is free software; you can redistribute it and/or modify it under
286             the same terms as the Perl 5 programming language system itself.
287              
288             =cut