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 16     16   263 use v5.26;
  16         92  
5 16     16   100 use strict;
  16         38  
  16         503  
6 16     16   142 use warnings;
  16         58  
  16         1301  
7 16     16   107 use warnings qw( FATAL utf8 );
  16         25  
  16         1119  
8 16     16   161 use utf8;
  16         41  
  16         134  
9 16     16   693 use open qw( :std :utf8 );
  16         35  
  16         116  
10 16     16   2898 use Unicode::Normalize qw( NFC );
  16         34  
  16         1290  
11 16     16   106 use Unicode::Collate;
  16         56  
  16         746  
12 16     16   134 use Encode qw( decode );
  16         44  
  16         2993  
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 16     16   148 use autodie;
  16         36  
  16         187  
24 16     16   110461 use Carp qw( carp croak confess cluck );
  16         32  
  16         2538  
25 16     16   181 use English qw( -no_match_vars );
  16         31  
  16         171  
26 16     16   8575 use Data::Dumper qw( Dumper );
  16         35  
  16         6651  
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 16     16   12940 use Module::Runtime qw( use_module );
  16         48028  
  16         168  
44 16     16   10630 use Clone qw( clone );
  16         10935  
  16         42472  
45              
46             sub new {
47 2605     2605 1 31215 my ( $class, $the_rest ) = @_;
48 2605         79792 my $self = clone( $the_rest );
49 2605         5443 bless $self, $class;
50 2605         5801 return $self;
51             }
52              
53             sub make_from {
54 3525     3525 1 6089 my ( $self, $data ) = @_;
55              
56 3525 50       6694 return unless defined $data;
57              
58 3525 100       10878 if ( 'ARRAY' eq ref $data ) {
59 916         1311 return [ map { $self->make_from( $_ ) } @{ $data } ];
  1411         3294  
  916         1756  
60             }
61              
62 2609 50       5605 croak( 'Invalid data for making Pg::SQL::PrettyPrinter::Node: ' . Dumper( $data ) ) unless 'HASH' eq ref $data;
63              
64 2609         3572 my @all_keys = keys %{ $data };
  2609         10933  
65 2609 100       5639 return if 0 == scalar @all_keys;
66 2605 50       5006 croak( 'Invalid data for making Pg::SQL::PrettyPrinter::Node (#2): ' . join( ', ', @all_keys ) ) unless 1 == scalar @all_keys;
67 2605         4366 my $class_suffix = $all_keys[ 0 ];
68 2605 50       15310 croak( "Invalid data for making Pg::SQL::PrettyPrinter::Node (#3): $class_suffix" ) unless $class_suffix =~ /^[A-Z][a-zA-Z0-9_-]+$/;
69              
70 2605         5904 my $class = 'Pg::SQL::PrettyPrinter::Node::' . $class_suffix;
71 2605         3365 my $object;
72 2605         3750 eval { $object = use_module( $class )->new( $data->{ $class_suffix } ); };
  2605         6609  
73 2605 50       6953 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 2605         16732 return $object;
79             }
80              
81             sub objectify {
82 1787     1787 1 2868 my $self = shift;
83 1787         4133 my @keys = @_;
84              
85             # Only arrays and hashes (well, references to them) can be objectified.
86 1787         3508 my %types_ok = map { $_ => 1 } qw{ ARRAY HASH };
  3574         10053  
87              
88 1787         4060 for my $key ( @keys ) {
89 3733         11710 my ( $container, $real_key ) = $self->get_container_key( $key );
90 3733 100       13641 next unless defined $container;
91 3380 100       8498 next unless exists $container->{ $real_key };
92              
93 1973         3284 my $val = $container->{ $real_key };
94 1973         3385 my $type = ref $val;
95 1973 50       4424 next unless $types_ok{ $type };
96              
97 1973         5409 $container->{ $real_key } = $self->make_from( $val );
98             }
99              
100 1787         5463 return;
101             }
102              
103             sub get_container_key {
104 3733     3733 1 5735 my $self = shift;
105 3733         6338 my $path = shift;
106              
107 3733         5951 my $type = ref $path;
108 3733 100       11200 return $self, $path if '' eq $type;
109 546 50       1204 croak( "Can't get container/key for non-array: $type" ) unless 'ARRAY' eq $type;
110 546 50       697 croak( "Can't get container/key for empty array" ) if 0 == scalar @{ $path };
  546         1242  
111 546 50       779 return $self, $path->[ 0 ] if 1 == scalar @{ $path };
  546         1195  
112              
113 546         861 my $container = $self;
114 546         894 for ( my $i = 0 ; $i < $#{ $path } ; $i++ ) {
  746         1628  
115 553         932 my $key = $path->[ $i ];
116 553 100       1670 return unless exists $container->{ $key };
117 200         400 $container = $container->{ $key };
118             }
119              
120 193         475 return $container, $path->[ -1 ];
121             }
122              
123             sub pretty_print {
124 557     557 1 823 my $self = shift;
125 557         1520 return $self->as_text( @_ );
126             }
127              
128             sub quote_literal {
129 227     227 1 358 my $self = shift;
130 227         410 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 227         406 my $rep = {};
134 227         641 $rep->{ "\r" } = "\\r";
135 227         436 $rep->{ "\t" } = "\\t";
136 227         465 $rep->{ "\n" } = "\\n";
137 227         327 my $look_for = join( '|', keys %{ $rep } );
  227         781  
138              
139 227 50       6791 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 227         653 $val =~ s/'/''/g;
152 227         1389 return "'${val}'";
153             }
154              
155             sub quote_ident {
156 1302     1302 1 1889 my $self = shift;
157 1302         2040 my $val = shift;
158 1302 100       7623 return $val if $val =~ m{\A[a-z0-9_]+\z};
159 17         35 $val =~ s/"/""/g;
160 17         62 return '"' . $val . '"';
161             }
162              
163             sub increase_indent {
164 569     569 1 889 my $self = shift;
165 569         880 my $input = shift;
166 569         3059 return $self->increase_indent_n( 1, $input );
167             }
168              
169             sub increase_indent_n {
170 571     571 1 775 my $self = shift;
171 571         821 my $levels = shift;
172 571         837 my $input = shift;
173 571 50       2139 croak( "Bad number of levels ($levels) to increase indent!" ) unless $levels =~ m{\A[1-9]\d*\z};
174 571         1046 my $prefix = ' ' x $levels;
175 571         1563 my @lines = split /\n/, $input;
176 571         1091 return join( "\n", map { $prefix . $_ } @lines );
  946         3623  
177             }
178              
179             1;