File Coverage

blib/lib/OpenAPI/Generator/From/Pod.pm
Criterion Covered Total %
statement 78 82 95.1
branch 28 34 82.3
condition 3 6 50.0
subroutine 17 17 100.0
pod 2 2 100.0
total 128 141 90.7


line stmt bran cond sub pod time code
1             package OpenAPI::Generator::From::Pod;
2              
3 2     2   1049 use strict;
  2         5  
  2         59  
4 2     2   10 use warnings;
  2         4  
  2         50  
5              
6 2     2   9 use Carp;
  2         4  
  2         105  
7 2     2   11 use File::Find;
  2         2  
  2         142  
8 2     2   928 use OpenAPI::Generator::Util qw(merge_definitions);
  2         6  
  2         108  
9 2     2   1104 use Pod::Simple::SimpleTree;
  2         63617  
  2         72  
10              
11 2     2   15 use constant OPENAPI_HEAD_NAME => 'OPENAPI';
  2         5  
  2         307  
12              
13             BEGIN {
14              
15 2 50   2   9 if (eval { require YAML::XS }) {
  2 50       518  
16 0         0 YAML::XS->import('Load');
17             }
18 2         371 elsif (eval { require YAML }) {
19 0         0 YAML->import('Load');
20             }
21             else {
22 2         1939 CPAN::Meta::YAML->import('Load');
23             }
24              
25             }
26              
27             sub new {
28              
29 3     3 1 15 bless {}, shift
30             }
31              
32             sub generate {
33              
34 3     3 1 9 my($self, $conf) = @_;
35              
36 3         10 my $src = $conf->{src};
37 3         14 $self->_check_src($src);
38 3         14 my @files = $self->_src_as_files($src);
39              
40 3         10 my @defs;
41 3         19 push @defs, $self->_parse_file($_) for @files;
42              
43 3 100       11 return unless @defs;
44              
45 2         10 merge_definitions(@defs);
46             }
47              
48             sub _parse_file {
49 5     5   14 my($self, $file) = @_;
50 5         41 my $parser = Pod::Simple::SimpleTree->new;
51              
52 5         137 my $struct = $parser->parse_file($file)->root;
53 5         14245 my $openapi_node = $self->_extract_openapi_node($struct);
54              
55 5 100       14 unless ($openapi_node) {
56 2         19 return;
57             }
58              
59 3         19 my %common_definition = (
60             paths => {},
61             components => {
62             schemas => {},
63             parameters => {},
64             securitySchemes => {},
65             },
66             );
67              
68 3         7 while (my($index, $node) = each @{$openapi_node}) {
  39         115  
69 36 100       82 next unless ref $node eq uc'array';
70 30 100       68 next unless $node->[0] eq 'item-text';
71              
72 15         27 my $item_name = $node->[2];
73              
74 15         28 my $definition_node = $openapi_node->[$index + 1];
75 15 50 33     70 if (!$definition_node || $definition_node->[0] ne 'Verbatim') {
76 0         0 croak("can not find definition for $node->[2]")
77             }
78 15         44 my $definition = Load $definition_node->[2];
79              
80 15 100       8193 if ($item_name =~ /^\s*SCHEMA/) {
    100          
    100          
81 3         8 my $schema_name = $self->_extract_component_name($item_name);
82 3         12 $common_definition{components}{schemas}{$schema_name} = $definition;
83             }
84             elsif ($item_name =~ /^\s*PARAM/) {
85 3         9 my $param_name = $self->_extract_component_name($item_name);
86 3         23 $common_definition{components}{parameters}{$param_name} = $definition;
87             }
88             elsif ($item_name =~ /^\s*SECURITY/) {
89 3         49 my $security_schema_name = $self->_extract_component_name($item_name);
90 3         12 $common_definition{components}{securitySchemes}{$security_schema_name} = $definition;
91             }
92             else {
93 6         17 my($method, $route) = $self->_extract_method_and_route($item_name);
94 6         28 $common_definition{paths}{$route}{$method} = $definition;
95             }
96             }
97              
98 3         59 return \%common_definition;
99             }
100              
101             sub _extract_component_name {
102              
103 9     9   41 my($type, $name) = split /\s/, $_[1];
104 9         23 return $name;
105             }
106              
107             sub _extract_method_and_route {
108              
109 6     6   27 my ($method, $route) = split /\s/, $_[1];
110 6         23 return lc($method), $route
111             }
112              
113             sub _extract_openapi_node {
114              
115 5     5   14 my($self, $struct) = @_;
116 5         10 while (my($index, $node) = each @{$struct}) {
  21         80  
117 19 100       49 next unless ref $node eq uc'array';
118 9 100       21 if ($node->[2] eq OPENAPI_HEAD_NAME) {
119 3         8 my $openapi_node = $struct->[$index + 1];
120 3 50       9 if (!$openapi_node) {
121 0         0 croak 'can not find openapi node: no nodes found below openapi annotation'
122             }
123              
124 3         9 return $openapi_node;
125             }
126             }
127             }
128              
129             sub _check_src {
130              
131 3 50 66 3   120 croak "$_[1] is not file or directory" unless(-f $_[1] or -d $_[1]);
132 3 50       55 croak "$_[1] is not readable" unless(-r $_[1]);
133             }
134              
135             sub _src_as_files {
136              
137 3 100   3   46 return $_[1] if -f $_[1];
138              
139 1         5 my @files;
140 1 100   4   120 find sub { push @files, $File::Find::name if /\.(pm|pl|t|pod)$/ }, $_[1];
  4         175  
141             @files
142 1         9 }
143              
144             1
145              
146             __END__