File Coverage

blib/lib/File/Edit.pm
Criterion Covered Total %
statement 52 82 63.4
branch 8 22 36.3
condition 0 3 0.0
subroutine 11 15 73.3
pod 1 7 14.2
total 72 129 55.8


line stmt bran cond sub pod time code
1             package File::Edit;
2 2     2   154487 use Mojo::Base -base;
  2         396004  
  2         14  
3 2     2   2481 use Path::Tiny qw/path/;
  2         20198  
  2         134  
4 2     2   16 use Carp;
  2         4  
  2         2980  
5             our $VERSION = '0.0.6';
6              
7             has 'file';
8             has 'found'; # Line numbers of found lines. ArrayRef.
9             has '_lines'; # Text in the form of lines. ArrayRef.
10             has '_line_re'; # Regex for _find_one
11             has 'at'; # Index into _lines for actions such as insert
12              
13             sub new {
14 11 50   11 1 7012 @_ > 1
15             ? $_[0]->SUPER::new({ file => path($_[1]),
16             _lines => [path($_[1])->lines],
17             found => [] })
18             : $_[0]->SUPER::new
19             }
20             sub text {
21 4     4 0 31 my ($self, $text) = @_;
22              
23 4         21 $text =~ s/\n/\nx-x/g;
24 4         23 $self->{_lines} = [split('x-x',$text)];
25              
26 4         13 return $self;
27             }
28             sub replace {
29 1     1 0 20 my ($o, $orig, $repstr) = @_;
30              
31             # Replaces one line
32 1         3 $o->_find_one($orig)
33             ->_replace_found($repstr);
34              
35 1         2 return $o;
36             }
37             sub get_block {
38 0     0 0 0 my ($o, %opt) = @_;
39              
40             return $o->_find_block($opt{from},$opt{to})
41 0         0 ->_found_lines;
42             }
43             sub save {
44 0     0 0 0 my ($o, $file) = @_;
45              
46 0 0       0 if ($file) {
47 0         0 path($file)->spew(join('',@{$o->_lines}));
  0         0  
48             } else {
49 0         0 $o->file->spew(join('',@{$o->_lines}));
  0         0  
50             }
51              
52 0         0 return $o;
53             }
54              
55             sub _find_block {
56 0     0   0 my ($o, $begin_re, $end_re) = @_;
57 0         0 my $in_block = 0; # True if line is in block
58 0         0 my $line_begin = -1; # First line num of found block. -1 if not found
59 0         0 my $line_end = -1; # Last line num of found block. -1 if not found
60              
61 0         0 foreach my $n (0 .. $#{$o->_lines}) {
  0         0  
62 0 0       0 if (!$in_block) {
63 0 0       0 if ($o->_lines->[$n] =~ $begin_re) {
64 0         0 $line_begin = $n;
65 0         0 $in_block = 1;
66             }
67             } else {
68 0 0       0 if ($o->_lines->[$n] =~ $end_re) {
69 0         0 $line_end = $n;
70 0         0 $in_block = 0;
71 0         0 last;
72             }
73             }
74             }
75              
76             # Error if block not found
77 0 0 0     0 croak "Block not found." if $line_begin == -1 or $line_end == -1;
78              
79 0         0 $o->found([$line_begin, $line_end]);
80              
81 0         0 return $o;
82             }
83             sub _found_lines {
84 0     0   0 my ($o) = @_;
85              
86 0         0 return [@{$o->_lines}[$o->found->[0] .. $o->found->[1]]];
  0         0  
87             }
88              
89             sub _find_one {
90 10     10   104 my ($o, $line_re) = @_;
91 10         13 my $n = 0;
92              
93             # Init search result
94 10         26 $o->found([]);
95 10 50       67 $line_re = ref $line_re eq 'Regexp' ? $line_re : _qre($line_re);
96 10         29 $o->_line_re($line_re);
97              
98 10         55 foreach my $l (@{$o->_lines}) {
  10         17  
99 24 100       108 push @{$o->found}, $n if $l =~ $line_re;
  9         18  
100 24         53 $n++;
101             }
102              
103             # Error if more than one line found
104 0         0 croak "Multiple lines found: ".join(', ',@{$o->found})
105 10 50       11 if scalar(@{$o->found}) > 1;
  10         17  
106              
107             # Error if more than one line found
108             croak "Line not found."
109 10 100       36 if scalar(@{$o->found}) == 0;
  10         24  
110              
111 9         45 return $o;
112             }
113             sub _replace_found {
114             # Replaces all lines found (line numbers in $o->found)
115 2     2   33 my ($o, $repstr) = @_;
116              
117 2         5 my $line_re = $o->_line_re; # s// does not work with $o-> notation
118              
119 2         8 foreach my $n (@{$o->found}) {
  2         3  
120 2         9 $o->_lines->[$n] =~ s/$line_re/$repstr/;
121             }
122              
123 2         24 return $o;
124             }
125             sub _qre { ## ($string) :> regex
126 10     10   20 my $quoted = quotemeta(shift);
127 10         95 return qr/$quoted/;
128             }
129              
130             sub swap { ## ($s1 :>STRING, $s2 :>STRING) :> SELF
131 2     2 0 6 my ($self,$s1,$s2) = @_;
132              
133             # Find the line indexes
134 2         5 my $idx_1 = $self->_find_one($s1)->found->[0];
135 2         9 my $idx_2 = $self->_find_one($s2)->found->[0];
136              
137             # Swap the lines
138 2         9 my $tmp = $self->_lines->[$idx_1];
139 2         8 $self->_lines->[$idx_1] = $self->_lines->[$idx_2];
140 2         13 $self->_lines->[$idx_2] = $tmp;
141              
142 2         10 return $self;
143             }
144             sub insert { ## ($line :>STRING) :> SELF
145 1     1 0 8 my ($self,$line) = @_;
146              
147 1 50       3 croak "Location to insert not defined" unless defined $self->at;
148 1         7 splice @{$self->_lines}, $self->at, 0, $line;
  1         3  
149              
150 1         10 return $self;
151             }
152              
153             =head1 NAME
154              
155             File::Edit - A naive, probably buggy, file editor.
156              
157             =cut
158             =head1 SYNOPSIS
159              
160             use File::Edit;
161              
162             # Replace string in file
163             File::Edit->new('build.gradle')
164             ->replace('minSdkVersion 16', 'minSdkVersion 21')
165             ->save()
166             ;
167              
168             # Edit text, save to file
169             File::Edit->new()
170             ->text(" minSdkVersion 16\n targetSdkVersion 29")
171             ->replace('minSdkVersion 16', 'minSdkVersion 21')
172             ->save('build.gradle')
173             ;
174              
175             # Swap lines, save to file
176             File::Edit->new()
177             ->text(" Do this first\n Now do that\n Don't do this")
178             ->swap('Do this', 'do that')
179             ->save('todo.txt')
180             ;
181              
182             # Insert at line index, save to file
183             File::Edit->new()
184             ->text(" Line index 0\n Line index 1\n Line index 2")
185             ->at(1)->insert(' Inserted line\n')
186             ->save('todo.txt')
187             ;
188              
189             =cut
190              
191             =head1 METHODS
192              
193             =head2 new
194              
195             my $fe = File::Edit->new("some_file.txt");
196              
197             Reads in a file for editing.
198              
199             =cut
200             =head2 text
201              
202             my $fe = File::Edit->new()->text(some_text);
203              
204             Reads in some text for editing.
205              
206             =cut
207             =head2 replace
208              
209             $fe->replace($old, $new);
210              
211             Replace the $old portion of a single line with $new.
212              
213             =cut
214             =head2 save
215              
216             my $fe = File::Edit->new("some_file.txt");
217             $fe->save(); # Saves to "some_file.txt"
218             $fe->save("other.txt") # Saves to "other.txt"
219              
220             =cut
221             =head2 swap( $text_1, $text_2 )
222              
223             The swap($s1, $s2) method finds the line containing string $s1 and finds
224             the line containg string $s2 and swaps both lines.
225              
226             =cut
227             =head2 at( $idx )
228              
229             The at($idx) sets the index for which actions such as insert($line) will
230             take place.
231              
232             =cut
233             =head2 insert( $line )
234              
235             The insert( $line ) method inserts the $line at the index location
236             specified. The index location is typically by an earlier call to
237             at($idx). For example:
238              
239             $self->at(0)->insert('Start of file')
240              
241             =cut
242              
243             =head1 AUTHOR
244              
245             Hoe Kit CHEW, C<< >>
246              
247             =head1 BUGS
248              
249             Please report any bugs or feature requests to C, or through
250             the web interface at L. I will be notified, and then you'll
251             automatically be notified of progress on your bug as I make changes.
252              
253              
254              
255              
256             =head1 SUPPORT
257              
258             You can find documentation for this module with the perldoc command.
259              
260             perldoc File::Edit
261              
262              
263             You can also look for information at:
264              
265             =over 4
266              
267             =item * RT: CPAN's request tracker (report bugs here)
268              
269             L
270              
271             =item * CPAN Ratings
272              
273             L
274              
275             =item * Search CPAN
276              
277             L
278              
279             =back
280              
281              
282             =head1 LICENSE AND COPYRIGHT
283              
284             This software is Copyright (c) 2021 by Hoe Kit CHEW.
285              
286             This is free software, licensed under:
287              
288             The Artistic License 2.0 (GPL Compatible)
289              
290              
291             =cut
292              
293             1; # End of File::Edit