File Coverage

blib/lib/Amazon/S3/Thin/ResponseParser.pm
Criterion Covered Total %
statement 36 36 100.0
branch 5 6 83.3
condition n/a
subroutine 10 10 100.0
pod 3 3 100.0
total 54 55 98.1


line stmt bran cond sub pod time code
1             package Amazon::S3::Thin::ResponseParser;
2 3     3   330750 use strict;
  3         15  
  3         93  
3 3     3   17 use warnings;
  3         7  
  3         81  
4 3     3   1986 use XML::LibXML;
  3         214968  
  3         25  
5 3     3   474 use XML::LibXML::XPathContext;
  3         9  
  3         1696  
6              
7             our $VERSION = "0.03";
8              
9             sub new {
10 2     2 1 270 my ($class, %args) = @_;
11              
12 2 50       23 my $xml = exists $args{xml} ? $args{xml} : XML::LibXML->new();
13              
14 2         59 return bless {
15             xml => $xml,
16             }, $class;
17             }
18              
19             sub _xpc {
20 8     8   21 my ($self, $content) = @_;
21 8         39 my $doc = $self->{xml}->parse_string($content);
22 8         2048 my $xpc = XML::LibXML::XPathContext->new($doc);
23 8         77 $xpc->registerNs('s3' => 'http://s3.amazonaws.com/doc/2006-03-01/');
24 8         23 return $xpc;
25             }
26              
27             sub error {
28 2     2 1 11 my ($self, $content) = @_;
29              
30 2         7 my $xpc = $self->_xpc($content);
31              
32             # https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
33             return {
34 2         11 code => $xpc->findvalue('/Error/Code'),
35             message => $xpc->findvalue('/Error/Message'),
36             request_id => $xpc->findvalue('/Error/RequestId'),
37             resource => $xpc->findvalue('/Error/Resource'),
38             };
39             }
40              
41             sub list_objects {
42 6     6 1 25524 my ($self, $content) = @_;
43              
44 6         24 my $xpc = $self->_xpc($content);
45              
46 6 100       28 if ($xpc->findnodes('/Error')) {
47 1         131 return (undef, $self->error($content));
48             }
49              
50             # https://docs.aws.amazon.com/AmazonS3/latest/API/v2-RESTBucketGET.html
51             my $result = {
52             contents => [ map {
53             +{
54 6         627 etag => _remove_quote($xpc->findvalue('./s3:ETag', $_)),
55             key => $xpc->findvalue('./s3:Key', $_),
56             last_modified => $xpc->findvalue('./s3:LastModified', $_),
57             owner => {
58             display_name => $xpc->findvalue('./s3:Owner/s3:DisplayName', $_),
59             id => $xpc->findvalue('./s3:Owner/s3:ID', $_),
60             },
61             size => $xpc->findvalue('./s3:Size', $_),
62             storage_class => $xpc->findvalue('./s3:StorageClass', $_),
63             }
64             } $xpc->findnodes('/s3:ListBucketResult/s3:Contents') ],
65             common_prefixes => [ map {
66 5         542 +{
67 2         649 owner => {
68             display_name => $xpc->findvalue('./s3:Owner/s3:DisplayName', $_),
69             id => $xpc->findvalue('./s3:Owner/s3:ID', $_),
70             },
71             prefix => $xpc->findvalue('./s3:Prefix', $_),
72             }
73             } $xpc->findnodes('/s3:ListBucketResult/s3:CommonPrefixes') ],
74             delimiter => $xpc->findvalue('/s3:ListBucketResult/s3:Delimiter'),
75             encoding_type => $xpc->findvalue('/s3:ListBucketResult/s3:EncodingType'),
76             is_truncated => _boolean($xpc->findvalue('/s3:ListBucketResult/s3:IsTruncated')),
77             max_keys => $xpc->findvalue('/s3:ListBucketResult/s3:MaxKeys'),
78             name => $xpc->findvalue('/s3:ListBucketResult/s3:Name'),
79             prefix => $xpc->findvalue('/s3:ListBucketResult/s3:Prefix'),
80             # v1
81             # https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html
82             marker => $xpc->findvalue('/s3:ListBucketResult/s3:Marker'),
83             next_marker => $xpc->findvalue('/s3:ListBucketResult/s3:NextMarker'),
84             # v2
85             continuation_token => $xpc->findvalue('/s3:ListBucketResult/s3:ContinuationToken'),
86             next_continuation_token => $xpc->findvalue('/s3:ListBucketResult/s3:NextContinuationToken'),
87             key_count => $xpc->findvalue('/s3:ListBucketResult/s3:KeyCount'),
88             start_after => $xpc->findvalue('/s3:ListBucketResult/s3:StartAfter'),
89             };
90 5         2387 return ($result, undef);
91             }
92              
93             sub _boolean {
94 5     5   2656 my $s = shift;
95 5 100       24 return $s eq 'true' ? 1 : 0;
96             }
97              
98             sub _remove_quote {
99 6     6   474 my $s = shift;
100 6         64 $s =~ s/^"|"$//g;
101 6         25 return $s;
102             }
103              
104             1;
105             __END__