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