File Coverage

blib/lib/DBGp/Client/Parser.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package DBGp::Client::Parser;
2              
3 22     22   9064 use strict;
  22         28  
  22         677  
4 22     22   76 use warnings;
  22         23  
  22         593  
5              
6 22     22   8278 use XML::Parser;
  0            
  0            
7             use XML::Parser::EasyTree;
8              
9             use MIME::Base64 qw(decode_base64);
10              
11             use DBGp::Client::Response::Init;
12             use DBGp::Client::Response::Error;
13             use DBGp::Client::Response::InternalError;
14             use DBGp::Client::Response::Notification;
15             use DBGp::Client::Response::Stream;
16              
17             my $parser = XML::Parser->new(Style => 'XML::Parser::EasyTree');
18              
19             my %response_map;
20             BEGIN {
21             %response_map = (
22             'status' => 'DBGp::Client::Response::Step',
23             'step_into' => 'DBGp::Client::Response::Step',
24             'step_over' => 'DBGp::Client::Response::Step',
25             'run' => 'DBGp::Client::Response::Step',
26             'step_out' => 'DBGp::Client::Response::Step',
27             'detach' => 'DBGp::Client::Response::Step',
28             'stop' => 'DBGp::Client::Response::Step',
29             'stack_depth' => 'DBGp::Client::Response::StackDepth',
30             'stack_get' => 'DBGp::Client::Response::StackGet',
31             'eval' => 'DBGp::Client::Response::Eval',
32             'expr' => 'DBGp::Client::Response::Eval',
33             'exec' => 'DBGp::Client::Response::Eval',
34             'typemap_get' => 'DBGp::Client::Response::Typemap',
35             'context_names' => 'DBGp::Client::Response::ContextNames',
36             'context_get' => 'DBGp::Client::Response::ContextGet',
37             'breakpoint_get' => 'DBGp::Client::Response::BreakpointGetUpdateRemove',
38             'breakpoint_set' => 'DBGp::Client::Response::BreakpointSet',
39             'breakpoint_update' => 'DBGp::Client::Response::BreakpointGetUpdateRemove',
40             'breakpoint_remove' => 'DBGp::Client::Response::BreakpointGetUpdateRemove',
41             'breakpoint_list' => 'DBGp::Client::Response::BreakpointList',
42             'feature_set' => 'DBGp::Client::Response::FeatureSet',
43             'feature_get' => 'DBGp::Client::Response::FeatureGet',
44             'property_get' => 'DBGp::Client::Response::PropertyGet',
45             'property_value' => 'DBGp::Client::Response::PropertyValue',
46             'property_set' => 'DBGp::Client::Response::PropertySet',
47             'source' => 'DBGp::Client::Response::Source',
48             'stdout' => 'DBGp::Client::Response::Redirect',
49             'stderr' => 'DBGp::Client::Response::Redirect',
50             'stdin' => 'DBGp::Client::Response::Redirect',
51             'break' => 'DBGp::Client::Response::Break',
52             'interact' => 'DBGp::Client::Response::Interact',
53             );
54              
55             my $load = join "\n", map "require $_;", values %response_map;
56             eval $load or do {
57             die "$@";
58             };
59             }
60              
61             sub _nodes {
62             my ($nodes, $node) = @_;
63              
64             return grep $_->{type} eq 'e' && $_->{name} eq $node, @{$nodes->{content}};
65             }
66              
67             sub _node {
68             my ($nodes, $node) = @_;
69              
70             return (_nodes($nodes, $node))[0];
71             }
72              
73             sub _text {
74             my ($nodes) = @_;
75             my $text = '';
76              
77             for my $node (@{$nodes->{content}}) {
78             $text .= $node->{content}
79             if $node->{type} eq 't';
80             }
81              
82             return $text;
83             }
84              
85             sub _decode {
86             my ($text, $encoding) = @_;
87             $encoding ||= 'none';
88             return $encoding eq 'base64' ? decode_base64($text) :
89             $encoding eq 'none' ? $text :
90             die "Unsupported encoding '$encoding'";
91             }
92              
93             sub parse {
94             return undef unless defined $_[0];
95              
96             my $tree = $parser->parse($_[0]);
97             require Data::Dumper, die "Unexpected return value from parse(): ", Data::Dumper::Dumper($tree)
98             if !ref $tree || ref $tree ne 'ARRAY';
99             die "Unexpected XML"
100             if @$tree != 1 || $tree->[0]{type} ne 'e';
101              
102             my $root = $tree->[0];
103             if ($root->{name} eq 'init') {
104             return bless $root->{attrib}, 'DBGp::Client::Response::Init';
105             } elsif ($root->{name} eq 'response') {
106             if (ref $root->{content} && (my $error = _node($root, 'error'))) {
107             return bless [$root->{attrib}, $error], 'DBGp::Client::Response::Error';
108             }
109              
110             my $cmd = $root->{attrib}{command};
111             if (my $package = $response_map{$cmd}) {
112             return bless $root, $package;
113             } else {
114             require Data::Dumper;
115              
116             die "Unknown command '$cmd' " . Data::Dumper::Dumper($root);
117             }
118             } elsif ($root->{name} eq 'stream') {
119             return bless $root, 'DBGp::Client::Response::Stream';
120             } elsif ($root->{name} eq 'notify') {
121             return bless $root, 'DBGp::Client::Response::Notification';
122             } else {
123             require Data::Dumper;
124              
125             die "Unknown response '$root' " . Data::Dumper::Dumper($root);
126             }
127             }
128              
129             1;