File Coverage

lib/Mail/Make/Body/File.pm
Criterion Covered Total %
statement 57 65 87.6
branch 8 16 50.0
condition 9 21 42.8
subroutine 13 14 92.8
pod 7 7 100.0
total 94 123 76.4


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## MIME Email Builder - ~/lib/Mail/Make/Body/File.pm
3             ## Version v0.1.0
4             ## Copyright(c) 2026 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2026/03/02
7             ## Modified 2026/03/02
8             ## All rights reserved.
9             ##
10             ## This program is free software; you can redistribute it and/or modify it
11             ## under the same terms as Perl itself.
12             ##----------------------------------------------------------------------------
13             # NOTE: Mail::Make::Body::File package
14             # Body stored on disk; only the file path is kept in memory.
15             package Mail::Make::Body::File;
16             BEGIN
17             {
18 8     8   1659 use strict;
  8         15  
  8         346  
19 8     8   38 use warnings;
  8         16  
  8         765  
20 8     8   54 warnings::register_categories( 'Mail::Make' );
21 8     8   69 use parent qw( Mail::Make::Body );
  8         14  
  8         48  
22 8     8   585 use vars qw( $VERSION $EXCEPTION_CLASS );
  8         14  
  8         509  
23 8         11 our $EXCEPTION_CLASS = 'Mail::Make::Exception';
24 8         186 our $VERSION = 'v0.1.0';
25             }
26              
27 8     8   39 use strict;
  8         12  
  8         191  
28 8     8   32 use warnings;
  8         16  
  8         5865  
29              
30             sub init
31             {
32 45     45 1 1181248 my $self = shift( @_ );
33 45   50     353 my $path = shift( @_ ) ||
34             return( $self->error( "Mail::Make::Body::File->new requires a file path" ) );
35 45         591 $path = $self->new_file( $path );
36 45 100 66     7119734 unless( $path->exists && $path->can_read )
37             {
38 2         184 return( $self->error( "File does not exist or is not readable: $path" ) );
39             }
40             # new_file() is inherited from Module::Generic and returns a new Module::Generic::File object
41 43         367785 $self->{_path} = $self->new_file( $path );
42 43         6590567 $self->{_exception_class} = $EXCEPTION_CLASS;
43 43 50       634 $self->SUPER::init( @_ ) || return( $self->pass_error );
44 43         5078 return( $self );
45             }
46              
47             sub as_string
48             {
49 14     14 1 21036 my $self = shift( @_ );
50 14   50     204 my $fh = $self->open || return( $self->pass_error );
51             # If we passed the 'open' statement, then we have a file, so we are safe.
52 14         76 my $path = $self->{_path};
53 14         123 my $data = '';
54 14         52 my( $buf, $n );
55 14         102 while( $n = $fh->read( $buf, 65536 ) )
56             {
57 14         4416 $data .= $buf;
58             }
59 14 50       3166 return( $self->error( "Error reading file '$path': $!" ) ) if( !defined( $n ) );
60 14         83 $fh->close;
61 14         24949 return( \$data );
62             }
63              
64 13     13 1 66 sub is_on_file { return(1); }
65              
66             sub length
67             {
68 1     1 1 12631 my $self = shift( @_ );
69             my $path = $self->{_path} ||
70 1   50     11 return( $self->error( "No file path has been set yet." ) );
71 1         29 return( $path->length );
72             }
73              
74             # Returns a binary filehandle opened for reading
75             sub open
76             {
77 26     26 1 117 my $self = shift( @_ );
78 26         98 my $path = $self->{_path};
79 26 50       114 return( $self->error( "No file path set." ) ) if( !defined( $path ) );
80 26   50     341 my $fh = $path->open( '<' ) ||
81             return( $self->error( "Cannot open file '$path' for reading: ", $path->error ) );
82 26         126002 $fh->binmode( ':raw' );
83 26         7334 return( $fh );
84             }
85              
86             sub path
87             {
88 2     2 1 26610 my $self = shift( @_ );
89 2 100       10 if( @_ )
90             {
91 1   50     16 my $path = shift( @_ ) ||
92             return( $self->error( "No file was provided." ) );
93 1   50     10 $path = $self->new_file( $path ) || return( $self->pass_error );
94 1 50 33     102678 unless( $path->exists && $path->can_read )
95             {
96 1         76 return( $self->error( "File does not exist or is not readable: $path" ) );
97             }
98 0         0 $self->{_path} = $path;
99 0         0 return( $self );
100             }
101 1         6 return( $self->{_path} );
102             }
103              
104             sub purge
105             {
106 0     0 1   my $self = shift( @_ );
107 0           my $path = $self->{_path};
108 0 0 0       if( defined( $path ) && -e $path )
109             {
110 0 0         $path->remove ||
111             return( $self->error( "Cannot unlink '$path': ", $path->error ) );
112             }
113 0           $self->{_path} = undef;
114 0           return( $self );
115             }
116              
117             1;
118             # NOTE: POD
119             __END__
120              
121             =encoding utf-8
122              
123             =head1 NAME
124              
125             Mail::Make::Body::File - On-Disk Body for Mail::Make
126              
127             =head1 SYNOPSIS
128              
129             use Mail::Make::Body::File;
130             my $body = Mail::Make::Body::File->new( '/path/to/logo.png' ) ||
131             die( Mail::Make::Body::File->error );
132             my $fh = $body->open || die( $body->error );
133              
134             =head1 VERSION
135              
136             v0.1.0
137              
138             =head1 DESCRIPTION
139              
140             Holds a reference to a file on disk. The file is read lazily when C<open()> or C<as_string()> is called.
141              
142             =head1 CONSTRUCTOR
143              
144             =head2 new( $filepath )
145              
146             Accepts an absolute or relative file path. The file must exist and be readable at construction time or the constructor will fail with an explicit error.
147              
148             =head1 METHODS
149              
150             =head2 as_string
151              
152             Slurps the entire file and returns a scalar reference. Use with caution on large files.
153              
154             =head2 is_on_file
155              
156             Returns true (1).
157              
158             =head2 length
159              
160             Returns the file size in bytes from C<stat()>.
161              
162             =head2 open
163              
164             Opens the file in raw binary mode and returns the filehandle.
165              
166             =head2 path( [$newpath] )
167              
168             Gets or sets the file path. When setting, validates that the new path exists and is readable.
169              
170             =head1 AUTHOR
171              
172             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
173              
174             =head1 SEE ALSO
175              
176             L<Mail::Make::Body::InCore>, L<Mail::Make::Body>, L<Mail::Make>
177              
178             =head1 COPYRIGHT & LICENSE
179              
180             Copyright(c) 2026 DEGUEST Pte. Ltd.
181              
182             All rights reserved.
183              
184             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
185              
186             =cut