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   241 use v5.26;
  15         63  
5 15     15   97 use strict;
  15         39  
  15         385  
6 15     15   82 use warnings;
  15         46  
  15         429  
7 15     15   95 use warnings qw( FATAL utf8 );
  15         33  
  15         718  
8 15     15   117 use utf8;
  15         38  
  15         152  
9 15     15   465 use open qw( :std :utf8 );
  15         412  
  15         143  
10 15     15   2716 use Unicode::Normalize qw( NFC );
  15         36  
  15         813  
11 15     15   105 use Unicode::Collate;
  15         29  
  15         563  
12 15     15   175 use Encode qw( decode );
  15         39  
  15         1058  
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   4017 use autodie;
  15         34  
  15         162  
24 15     15   85112 use Carp qw( carp croak confess cluck );
  15         34  
  15         1298  
25 15     15   113 use English qw( -no_match_vars );
  15         32  
  15         193  
26 15     15   6370 use Data::Dumper qw( Dumper );
  15         32  
  15         3496  
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   8192 use Module::Runtime qw( use_module );
  15         27854  
  15         92  
44 15     15   7040 use Clone qw( clone );
  15         36248  
  15         21781  
45              
46             sub new {
47 2526     2526 1 29663 my ( $class, $the_rest ) = @_;
48 2526         55084 my $self = clone( $the_rest );
49 2526         6340 bless $self, $class;
50 2526         5503 return $self;
51             }
52              
53             sub make_from {
54 3417     3417 1 5670 my ( $self, $data ) = @_;
55              
56 3417 50       6529 return unless defined $data;
57              
58 3417 100       7111 if ( 'ARRAY' eq ref $data ) {
59 887         1379 return [ map { $self->make_from( $_ ) } @{ $data } ];
  1359         2784  
  887         1571  
60             }
61              
62 2530 50       5239 croak( 'Invalid data for making Pg::SQL::PrettyPrinter::Node: ' . Dumper( $data ) ) unless 'HASH' eq ref $data;
63              
64 2530         3734 my @all_keys = keys %{ $data };
  2530         7243  
65 2530 100       5762 return if 0 == scalar @all_keys;
66 2526 50       5014 croak( 'Invalid data for making Pg::SQL::PrettyPrinter::Node (#2): ' . join( ', ', @all_keys ) ) unless 1 == scalar @all_keys;
67 2526         4219 my $class_suffix = $all_keys[ 0 ];
68 2526 50       10533 croak( "Invalid data for making Pg::SQL::PrettyPrinter::Node (#3): $class_suffix" ) unless $class_suffix =~ /^[A-Z][a-zA-Z0-9_-]+$/;
69              
70 2526         5691 my $class = 'Pg::SQL::PrettyPrinter::Node::' . $class_suffix;
71 2526         3502 my $object;
72 2526         3816 eval { $object = use_module( $class )->new( $data->{ $class_suffix } ); };
  2526         6220  
73 2526 50       5719 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         12408 return $object;
79             }
80              
81             sub objectify {
82 1743     1743 1 2783 my $self = shift;
83 1743         3746 my @keys = @_;
84              
85             # Only arrays and hashes (well, references to them) can be objectified.
86 1743         3123 my %types_ok = map { $_ => 1 } qw{ ARRAY HASH };
  3486         8294  
87              
88 1743         3881 for my $key ( @keys ) {
89 3663         7853 my ( $container, $real_key ) = $self->get_container_key( $key );
90 3663 100       8058 next unless defined $container;
91 3311 100       7681 next unless exists $container->{ $real_key };
92              
93 1918         3162 my $val = $container->{ $real_key };
94 1918         3308 my $type = ref $val;
95 1918 50       3880 next unless $types_ok{ $type };
96              
97 1918         4388 $container->{ $real_key } = $self->make_from( $val );
98             }
99              
100 1743         4776 return;
101             }
102              
103             sub get_container_key {
104 3663     3663 1 5529 my $self = shift;
105 3663         5363 my $path = shift;
106              
107 3663         5674 my $type = ref $path;
108 3663 100       10394 return $self, $path if '' eq $type;
109 542 50       1255 croak( "Can't get container/key for non-array: $type" ) unless 'ARRAY' eq $type;
110 542 50       832 croak( "Can't get container/key for empty array" ) if 0 == scalar @{ $path };
  542         1303  
111 542 50       869 return $self, $path->[ 0 ] if 1 == scalar @{ $path };
  542         1079  
112              
113 542         849 my $container = $self;
114 542         905 for ( my $i = 0 ; $i < $#{ $path } ; $i++ ) {
  739         1746  
115 549         951 my $key = $path->[ $i ];
116 549 100       1549 return unless exists $container->{ $key };
117 197         421 $container = $container->{ $key };
118             }
119              
120 190         469 return $container, $path->[ -1 ];
121             }
122              
123             sub pretty_print {
124 538     538 1 854 my $self = shift;
125 538         1266 return $self->as_text( @_ );
126             }
127              
128             sub quote_literal {
129 225     225 1 413 my $self = shift;
130 225         338 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         381 my $rep = {};
134 225         496 $rep->{ "\r" } = "\\r";
135 225         442 $rep->{ "\t" } = "\\t";
136 225         383 $rep->{ "\n" } = "\\n";
137 225         339 my $look_for = join( '|', keys %{ $rep } );
  225         724  
138              
139 225 50       4018 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         643 $val =~ s/'/''/g;
152 225         1315 return "'${val}'";
153             }
154              
155             sub quote_ident {
156 1190     1190 1 1806 my $self = shift;
157 1190         1855 my $val = shift;
158 1190 100       6495 return $val if $val =~ m{\A[a-z0-9_]+\z};
159 17         45 $val =~ s/"/""/g;
160 17         79 return '"' . $val . '"';
161             }
162              
163             sub increase_indent {
164 562     562 1 954 my $self = shift;
165 562         785 my $input = shift;
166 562         1181 return $self->increase_indent_n( 1, $input );
167             }
168              
169             sub increase_indent_n {
170 564     564 1 818 my $self = shift;
171 564         787 my $levels = shift;
172 564         827 my $input = shift;
173 564 50       2064 croak( "Bad number of levels ($levels) to increase indent!" ) unless $levels =~ m{\A[1-9]\d*\z};
174 564         1112 my $prefix = ' ' x $levels;
175 564         1401 my @lines = split /\n/, $input;
176 564         1034 return join( "\n", map { $prefix . $_ } @lines );
  927         3357  
177             }
178              
179             1;