File Coverage

blib/lib/Pg/SQL/PrettyPrinter/Node.pm
Criterion Covered Total %
statement 125 135 92.5
branch 25 36 69.4
condition n/a
subroutine 24 24 100.0
pod 9 9 100.0
total 183 204 89.7


line stmt bran cond sub pod time code
1             package Pg::SQL::PrettyPrinter::Node;
2              
3             # UTF8 boilerplace, per http://stackoverflow.com/questions/6162484/why-does-modern-perl-avoid-utf-8-by-default/
4 15     15   229 use v5.26;
  15         63  
5 15     15   102 use strict;
  15         37  
  15         365  
6 15     15   83 use warnings;
  15         33  
  15         420  
7 15     15   75 use warnings qw( FATAL utf8 );
  15         41  
  15         693  
8 15     15   136 use utf8;
  15         32  
  15         163  
9 15     15   489 use open qw( :std :utf8 );
  15         469  
  15         145  
10 15     15   2779 use Unicode::Normalize qw( NFC );
  15         38  
  15         854  
11 15     15   113 use Unicode::Collate;
  15         43  
  15         578  
12 15     15   203 use Encode qw( decode );
  15         56  
  15         1097  
13              
14             if ( grep /\P{ASCII}/ => @ARGV ) {
15             @ARGV = map { decode( 'UTF-8', $_ ) } @ARGV;
16             }
17              
18             # If there is __DATA__,then uncomment next line:
19             # binmode( DATA, ':encoding(UTF-8)' );
20             # UTF8 boilerplace, per http://stackoverflow.com/questions/6162484/why-does-modern-perl-avoid-utf-8-by-default/
21              
22             # Useful common code
23 15     15   4095 use autodie;
  15         47  
  15         188  
24 15     15   85366 use Carp qw( carp croak confess cluck );
  15         36  
  15         1275  
25 15     15   121 use English qw( -no_match_vars );
  15         49  
  15         164  
26 15     15   6518 use Data::Dumper qw( Dumper );
  15         34  
  15         3566  
27              
28             # give a full stack dump on any untrapped exceptions
29             local $SIG{ __DIE__ } = sub {
30             confess "Uncaught exception: @_" unless $^S;
31             };
32              
33             # now promote run-time warnings into stackdumped exceptions
34             # *unless* we're in an try block, in which
35             # case just generate a clucking stackdump instead
36             local $SIG{ __WARN__ } = sub {
37             if ( $^S ) { cluck "Trapped warning: @_" }
38             else { confess "Deadly warning: @_" }
39             };
40              
41             # Useful common code
42              
43 15     15   8301 use Module::Runtime qw( use_module );
  15         27596  
  15         98  
44 15     15   7015 use Clone qw( clone );
  15         36033  
  15         23271  
45              
46             sub new {
47 2526     2526 1 29906 my ( $class, $the_rest ) = @_;
48 2526         55516 my $self = clone( $the_rest );
49 2526         6463 bless $self, $class;
50 2526         5657 return $self;
51             }
52              
53             sub make_from {
54 3417     3417 1 6081 my ( $self, $data ) = @_;
55              
56 3417 50       6456 return unless defined $data;
57              
58 3417 100       7218 if ( 'ARRAY' eq ref $data ) {
59 887         1295 return [ map { $self->make_from( $_ ) } @{ $data } ];
  1359         2780  
  887         1549  
60             }
61              
62 2530 50       5072 croak( 'Invalid data for making Pg::SQL::PrettyPrinter::Node: ' . Dumper( $data ) ) unless 'HASH' eq ref $data;
63              
64 2530         3606 my @all_keys = keys %{ $data };
  2530         7070  
65 2530 100       6162 return if 0 == scalar @all_keys;
66 2526 50       5083 croak( 'Invalid data for making Pg::SQL::PrettyPrinter::Node (#2): ' . join( ', ', @all_keys ) ) unless 1 == scalar @all_keys;
67 2526         4227 my $class_suffix = $all_keys[ 0 ];
68 2526 50       11155 croak( "Invalid data for making Pg::SQL::PrettyPrinter::Node (#3): $class_suffix" ) unless $class_suffix =~ /^[A-Z][a-zA-Z0-9_-]+$/;
69              
70 2526         5578 my $class = 'Pg::SQL::PrettyPrinter::Node::' . $class_suffix;
71 2526         3705 my $object;
72 2526         3765 eval { $object = use_module( $class )->new( $data->{ $class_suffix } ); };
  2526         6038  
73 2526 50       5527 if ( $EVAL_ERROR ) {
74 0         0 my $msg = $EVAL_ERROR;
75 0         0 my $keys = join( '; ', sort keys %{ $data } );
  0         0  
76 0         0 croak( "Can't make object out of [${keys}]:\n" . Dumper( $data ) . "\n" . $msg );
77             }
78 2526         12441 return $object;
79             }
80              
81             sub objectify {
82 1743     1743 1 2876 my $self = shift;
83 1743         3744 my @keys = @_;
84              
85             # Only arrays and hashes (well, references to them) can be objectified.
86 1743         3246 my %types_ok = map { $_ => 1 } qw{ ARRAY HASH };
  3486         8222  
87              
88 1743         3846 for my $key ( @keys ) {
89 3616         7458 my ( $container, $real_key ) = $self->get_container_key( $key );
90 3616 100       7977 next unless defined $container;
91 3264 100       7327 next unless exists $container->{ $real_key };
92              
93 1918         3097 my $val = $container->{ $real_key };
94 1918         3247 my $type = ref $val;
95 1918 50       3806 next unless $types_ok{ $type };
96              
97 1918         4493 $container->{ $real_key } = $self->make_from( $val );
98             }
99              
100 1743         4669 return;
101             }
102              
103             sub get_container_key {
104 3616     3616 1 5409 my $self = shift;
105 3616         5220 my $path = shift;
106              
107 3616         5714 my $type = ref $path;
108 3616 100       10208 return $self, $path if '' eq $type;
109 542 50       1232 croak( "Can't get container/key for non-array: $type" ) unless 'ARRAY' eq $type;
110 542 50       784 croak( "Can't get container/key for empty array" ) if 0 == scalar @{ $path };
  542         1288  
111 542 50       1425 return $self, $path->[ 0 ] if 1 == scalar @{ $path };
  542         1065  
112              
113 542         898 my $container = $self;
114 542         891 for ( my $i = 0 ; $i < $#{ $path } ; $i++ ) {
  739         1660  
115 549         973 my $key = $path->[ $i ];
116 549 100       1577 return unless exists $container->{ $key };
117 197         415 $container = $container->{ $key };
118             }
119              
120 190         479 return $container, $path->[ -1 ];
121             }
122              
123             sub pretty_print {
124 538     538 1 843 my $self = shift;
125 538         1280 return $self->as_text( @_ );
126             }
127              
128             sub quote_literal {
129 225     225 1 363 my $self = shift;
130 225         423 my $val = shift;
131              
132             # Set of characters that, if found, should be converted to \escaped, and they change how string is printed (E'' vs. '')
133 225         435 my $rep = {};
134 225         505 $rep->{ "\r" } = "\\r";
135 225         402 $rep->{ "\t" } = "\\t";
136 225         380 $rep->{ "\n" } = "\\n";
137 225         324 my $look_for = join( '|', keys %{ $rep } );
  225         727  
138              
139 225 50       3933 if ( $val =~ /${look_for}/ ) {
140              
141             # If we are representing string using E'' notation, ' character has to be escaped too
142 0         0 $rep->{ "'" } = "\\'";
143 0         0 $look_for = join( '|', keys %{ $rep } );
  0         0  
144              
145             # Replace all characters that need it
146 0         0 $val =~ s/(${look_for})/$rep->{$1}/ge;
  0         0  
147 0         0 return "E'${val}'";
148             }
149              
150             # For '' strings, we just need to change each ' into ''.
151 225         664 $val =~ s/'/''/g;
152 225         1273 return "'${val}'";
153             }
154              
155             sub quote_ident {
156 1190     1190 1 1828 my $self = shift;
157 1190         1878 my $val = shift;
158 1190 100       6510 return $val if $val =~ m{\A[a-z0-9_]+\z};
159 17         46 $val =~ s/"/""/g;
160 17         80 return '"' . $val . '"';
161             }
162              
163             sub increase_indent {
164 562     562 1 931 my $self = shift;
165 562         839 my $input = shift;
166 562         1219 return $self->increase_indent_n( 1, $input );
167             }
168              
169             sub increase_indent_n {
170 564     564 1 1173 my $self = shift;
171 564         834 my $levels = shift;
172 564         803 my $input = shift;
173 564 50       2042 croak( "Bad number of levels ($levels) to increase indent!" ) unless $levels =~ m{\A[1-9]\d*\z};
174 564         1115 my $prefix = ' ' x $levels;
175 564         1402 my @lines = split /\n/, $input;
176 564         1038 return join( "\n", map { $prefix . $_ } @lines );
  927         3301  
177             }
178              
179             1;