File Coverage

lib/Git/Background.pm
Criterion Covered Total %
statement 107 107 100.0
branch 42 44 95.4
condition 16 17 94.1
subroutine 14 14 100.0
pod 3 3 100.0
total 182 185 98.3


line stmt bran cond sub pod time code
1             # vim: ts=4 sts=4 sw=4 et: syntax=perl
2             #
3             # Copyright (c) 2021-2023 Sven Kirmess
4             #
5             # Permission to use, copy, modify, and distribute this software for any
6             # purpose with or without fee is hereby granted, provided that the above
7             # copyright notice and this permission notice appear in all copies.
8             #
9             # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10             # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11             # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12             # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13             # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14             # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15             # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16              
17 12     12   996872 use 5.010;
  12         154  
18 12     12   62 use strict;
  12         25  
  12         239  
19 12     12   53 use warnings;
  12         22  
  12         641  
20              
21             package Git::Background;
22              
23             our $VERSION = '0.007_01';
24              
25 12     12   104 use Carp ();
  12         32  
  12         232  
26 12     12   9283 use File::Temp ();
  12         253013  
  12         383  
27 12     12   7765 use Future 0.40;
  12         147527  
  12         434  
28 12     12   6383 use Proc::Background 1.30;
  12         80060  
  12         565  
29 12     12   88 use Scalar::Util ();
  12         27  
  12         223  
30              
31 12     12   5777 use Git::Background::Future;
  12         43  
  12         11310  
32              
33             # Git::Background->new;
34             # Git::Background->new($dir);
35             # Git::Background->new( { dir => $dir, fatal => 0 } );
36             # Git::Background->new( $dir, { fatal => 0 } );
37             sub new {
38 23     23 1 36829 my $class = shift;
39              
40             NEW: {
41 23         50 my $self;
  23         43  
42              
43 23 100       99 last NEW if @_ > 2;
44              
45 22         59 my $dir;
46              
47             # first argument is a scalar or object
48 22 100 100     233 if ( @_ && ( ref $_[0] eq ref q{} || defined Scalar::Util::blessed $_[0] ) ) {
      100        
49 7         29 my $arg = shift @_;
50              
51             # stringify objects (e.g. Path::Tiny)
52 7         54 $dir = "$arg";
53             }
54              
55 22 50       86 last NEW if @_ > 1;
56              
57             # first/remaining argument is a hash ref
58 22 100 100     168 if ( @_ && ref $_[0] eq ref {} ) {
59 18         43 my $args = shift @_;
60 18         72 $self = $class->_process_args($args);
61             }
62             else {
63 4         11 $self = $class->_process_args;
64             }
65              
66 20 100       100 last NEW if @_;
67              
68 19 100       64 if ( defined $dir ) {
69 6 100       132 Carp::croak 'Cannot specify dir as positional argument and in argument hash' if exists $self->{_dir};
70 5         26 $self->{_dir} = $dir;
71             }
72              
73 18         44 bless $self, $class;
74 18         101 return $self;
75             }
76              
77             # unknown args
78 2         193 Carp::croak 'usage: new( [DIR], [ARGS] )';
79             }
80              
81             sub run {
82 30     30 1 59946 my ( $self, @cmd ) = @_;
83              
84 30 100       337 Carp::croak 'Cannot use run() in void context. (The git process would immediately get killed.)' if !defined wantarray; ## no critic (Community::Wantarray)
85              
86 29         62 my $config;
87 29 100 100     328 if ( @cmd && ref $cmd[-1] eq ref {} ) {
88 19         61 my $args = pop @cmd;
89 19         116 $config = $self->_process_args($args);
90             }
91             else {
92 10         70 $config = $self->_process_args;
93             }
94              
95 29         339 my $stdout = File::Temp->new;
96 29         20965 my $stderr = File::Temp->new;
97 29     9   11816 binmode $stdout, ':encoding(UTF-8)'; ## no critic (InputOutput::RequireCheckedSyscalls)
  9         68  
  9         28  
  9         62  
98 29         113247 binmode $stderr, ':encoding(UTF-8)'; ## no critic (InputOutput::RequireCheckedSyscalls)
99              
100             # Proc::Background
101             my $proc_args = {
102             stdin => undef,
103             stdout => $stdout,
104             stderr => $stderr,
105 29         364 command => [ @{ $config->{_git} }, @cmd ],
106             autodie => 1,
107             autoterminate => 1,
108 29 100       1503 ( defined $config->{_dir} ? ( cwd => $config->{_dir} ) : () ),
109             };
110              
111 29         105 my $proc;
112             my $e;
113             {
114 29         66 local @_; ## no critic (Variables::RequireInitializationForLocalVars)
  29         74  
115 29         90 local $Carp::Internal{ (__PACKAGE__) } = 1;
116              
117 29         61 $proc = eval { Proc::Background->new($proc_args); };
  29         326  
118              
119 29 100       99038 if ( !defined $proc ) {
120              
121             # The Future->fail exception must be true
122 3   100     44 $e = qq{$@} || 'Failed to run Git with Proc::Background';
123             }
124             }
125 29 100       279 return Git::Background::Future->fail( $e, 'Proc::Background' ) if !defined $proc;
126              
127             return Git::Background::Future->new(
128             {
129             _fatal => $config->{_fatal},
130 26         1437 _proc => $proc,
131             _stdout => $stdout,
132             _stderr => $stderr,
133             },
134             );
135             }
136              
137             sub version {
138 4     4 1 1932 my ( $self, $args ) = @_;
139              
140 4         11 my @cmd = qw(--version);
141              
142 4 100       15 if ( defined $args ) {
143 3         8 push @cmd, $args;
144             }
145              
146 4         9 my $version = eval {
147 4         17 for my $line ( $self->run(@cmd)->stdout ) {
148 3 50       106 if ( $line =~ s{ \A git \s version \s }{}xsm ) {
149 3         44 return $line;
150             }
151             }
152              
153 1         23 return;
154             };
155              
156 4         3118 return $version;
157             }
158              
159             sub _process_args {
160 51     51   151 my ( $self, $args ) = @_;
161              
162 51 100       323 if ( !defined Scalar::Util::blessed($self) ) {
163 25         174 $self = {
164             _fatal => !!1,
165             _git => ['git'],
166             };
167             }
168              
169 51         129 my %args_keys = map { $_ => 1 } keys %{$args};
  40         192  
  51         223  
170 51         155 my %config;
171              
172             # dir
173 51 100       274 if ( exists $args->{dir} ) {
    100          
174              
175             # stringify objects (e.g. Path::Tiny)
176 1         4 $config{_dir} = "$args->{dir}";
177 1         2 delete $args_keys{dir};
178             }
179             elsif ( exists $self->{_dir} ) {
180 5         22 $config{_dir} = $self->{_dir};
181             }
182              
183             # fatal
184 51 100       175 if ( exists $args->{fatal} ) {
185 16         61 $config{_fatal} = !!$args->{fatal};
186 16         40 delete $args_keys{fatal};
187             }
188             else {
189 35         126 $config{_fatal} = $self->{_fatal};
190             }
191              
192             # git
193 51 100       131 if ( exists $args->{git} ) {
194 20         42 my $git = $args->{git};
195 20 100 66     246 $config{_git} = [ ( defined Scalar::Util::reftype($git) && Scalar::Util::reftype($git) eq Scalar::Util::reftype( [] ) ) ? @{ $args->{git} } : $git ];
  18         85  
196 20         62 delete $args_keys{git};
197             }
198             else {
199 31         47 $config{_git} = [ @{ $self->{_git} } ];
  31         161  
200             }
201              
202             #
203 51         231 my @unknown = sort keys %args_keys;
204 51 100       467 Carp::croak 'Unknown argument' . ( @unknown > 1 ? 's' : q{} ) . q{: '} . join( q{', '}, sort @unknown ) . q{'} if @unknown;
    100          
205              
206 49         283 return \%config;
207             }
208              
209             1;
210              
211             __END__