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   261 use v5.26;
  15         75  
5 15     15   98 use strict;
  15         52  
  15         424  
6 15     15   94 use warnings;
  15         54  
  15         422  
7 15     15   90 use warnings qw( FATAL utf8 );
  15         55  
  15         840  
8 15     15   121 use utf8;
  15         41  
  15         185  
9 15     15   443 use open qw( :std :utf8 );
  15         463  
  15         154  
10 15     15   2924 use Unicode::Normalize qw( NFC );
  15         38  
  15         889  
11 15     15   103 use Unicode::Collate;
  15         38  
  15         653  
12 15     15   188 use Encode qw( decode );
  15         43  
  15         1147  
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   4043 use autodie;
  15         36  
  15         182  
24 15     15   87414 use Carp qw( carp croak confess cluck );
  15         55  
  15         1341  
25 15     15   145 use English qw( -no_match_vars );
  15         31  
  15         169  
26 15     15   6488 use Data::Dumper qw( Dumper );
  15         32  
  15         3602  
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   8745 use Module::Runtime qw( use_module );
  15         28882  
  15         91  
44 15     15   7567 use Clone qw( clone );
  15         38454  
  15         22798  
45              
46             sub new {
47 2526     2526 1 30108 my ( $class, $the_rest ) = @_;
48 2526         58799 my $self = clone( $the_rest );
49 2526         6562 bless $self, $class;
50 2526         5569 return $self;
51             }
52              
53             sub make_from {
54 3417     3417 1 6018 my ( $self, $data ) = @_;
55              
56 3417 50       6818 return unless defined $data;
57              
58 3417 100       7096 if ( 'ARRAY' eq ref $data ) {
59 887         1362 return [ map { $self->make_from( $_ ) } @{ $data } ];
  1359         2979  
  887         1576  
60             }
61              
62 2530 50       5256 croak( 'Invalid data for making Pg::SQL::PrettyPrinter::Node: ' . Dumper( $data ) ) unless 'HASH' eq ref $data;
63              
64 2530         3569 my @all_keys = keys %{ $data };
  2530         7110  
65 2530 100       5896 return if 0 == scalar @all_keys;
66 2526 50       4941 croak( 'Invalid data for making Pg::SQL::PrettyPrinter::Node (#2): ' . join( ', ', @all_keys ) ) unless 1 == scalar @all_keys;
67 2526         4042 my $class_suffix = $all_keys[ 0 ];
68 2526 50       11218 croak( "Invalid data for making Pg::SQL::PrettyPrinter::Node (#3): $class_suffix" ) unless $class_suffix =~ /^[A-Z][a-zA-Z0-9_-]+$/;
69              
70 2526         5546 my $class = 'Pg::SQL::PrettyPrinter::Node::' . $class_suffix;
71 2526         3534 my $object;
72 2526         3864 eval { $object = use_module( $class )->new( $data->{ $class_suffix } ); };
  2526         6109  
73 2526 50       5179 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         12711 return $object;
79             }
80              
81             sub objectify {
82 1743     1743 1 2837 my $self = shift;
83 1743         3859 my @keys = @_;
84              
85             # Only arrays and hashes (well, references to them) can be objectified.
86 1743         3403 my %types_ok = map { $_ => 1 } qw{ ARRAY HASH };
  3486         8337  
87              
88 1743         3911 for my $key ( @keys ) {
89 3663         7669 my ( $container, $real_key ) = $self->get_container_key( $key );
90 3663 100       7870 next unless defined $container;
91 3311 100       7499 next unless exists $container->{ $real_key };
92              
93 1918         3092 my $val = $container->{ $real_key };
94 1918         3284 my $type = ref $val;
95 1918 50       3971 next unless $types_ok{ $type };
96              
97 1918         4471 $container->{ $real_key } = $self->make_from( $val );
98             }
99              
100 1743         4672 return;
101             }
102              
103             sub get_container_key {
104 3663     3663 1 5656 my $self = shift;
105 3663         5286 my $path = shift;
106              
107 3663         5664 my $type = ref $path;
108 3663 100       10292 return $self, $path if '' eq $type;
109 542 50       1262 croak( "Can't get container/key for non-array: $type" ) unless 'ARRAY' eq $type;
110 542 50       815 croak( "Can't get container/key for empty array" ) if 0 == scalar @{ $path };
  542         1295  
111 542 50       828 return $self, $path->[ 0 ] if 1 == scalar @{ $path };
  542         1051  
112              
113 542         842 my $container = $self;
114 542         920 for ( my $i = 0 ; $i < $#{ $path } ; $i++ ) {
  739         1621  
115 549         949 my $key = $path->[ $i ];
116 549 100       1553 return unless exists $container->{ $key };
117 197         409 $container = $container->{ $key };
118             }
119              
120 190         522 return $container, $path->[ -1 ];
121             }
122              
123             sub pretty_print {
124 538     538 1 865 my $self = shift;
125 538         1313 return $self->as_text( @_ );
126             }
127              
128             sub quote_literal {
129 225     225 1 375 my $self = shift;
130 225         359 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         376 my $rep = {};
134 225         648 $rep->{ "\r" } = "\\r";
135 225         414 $rep->{ "\t" } = "\\t";
136 225         366 $rep->{ "\n" } = "\\n";
137 225         325 my $look_for = join( '|', keys %{ $rep } );
  225         722  
138              
139 225 50       3898 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         637 $val =~ s/'/''/g;
152 225         1320 return "'${val}'";
153             }
154              
155             sub quote_ident {
156 1190     1190 1 1873 my $self = shift;
157 1190         1799 my $val = shift;
158 1190 100       6539 return $val if $val =~ m{\A[a-z0-9_]+\z};
159 17         72 $val =~ s/"/""/g;
160 17         77 return '"' . $val . '"';
161             }
162              
163             sub increase_indent {
164 562     562 1 912 my $self = shift;
165 562         868 my $input = shift;
166 562         1152 return $self->increase_indent_n( 1, $input );
167             }
168              
169             sub increase_indent_n {
170 564     564 1 882 my $self = shift;
171 564         812 my $levels = shift;
172 564         831 my $input = shift;
173 564 50       2048 croak( "Bad number of levels ($levels) to increase indent!" ) unless $levels =~ m{\A[1-9]\d*\z};
174 564         1106 my $prefix = ' ' x $levels;
175 564         1373 my @lines = split /\n/, $input;
176 564         1032 return join( "\n", map { $prefix . $_ } @lines );
  927         3473  
177             }
178              
179             1;