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   214 use v5.26;
  16         65  
5 16     16   62 use strict;
  16         20  
  16         343  
6 16     16   88 use warnings;
  16         108  
  16         896  
7 16     16   72 use warnings qw( FATAL utf8 );
  16         22  
  16         684  
8 16     16   62 use utf8;
  16         28  
  16         92  
9 16     16   491 use open qw( :std :utf8 );
  16         19  
  16         97  
10 16     16   1913 use Unicode::Normalize qw( NFC );
  16         29  
  16         968  
11 16     16   79 use Unicode::Collate;
  16         29  
  16         486  
12 16     16   70 use Encode qw( decode );
  16         19  
  16         2184  
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   84 use autodie;
  16         28  
  16         141  
24 16     16   68099 use Carp qw( carp croak confess cluck );
  16         27  
  16         1333  
25 16     16   95 use English qw( -no_match_vars );
  16         35  
  16         137  
26 16     16   5525 use Data::Dumper qw( Dumper );
  16         28  
  16         3412  
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   8458 use Module::Runtime qw( use_module );
  16         26157  
  16         120  
44 16     16   7451 use Clone qw( clone );
  16         7016  
  16         20830  
45              
46             sub new {
47 2605     2605 1 21482 my ( $class, $the_rest ) = @_;
48 2605         48321 my $self = clone( $the_rest );
49 2605         3700 bless $self, $class;
50 2605         4390 return $self;
51             }
52              
53             sub make_from {
54 3525     3525 1 4983 my ( $self, $data ) = @_;
55              
56 3525 50       5051 return unless defined $data;
57              
58 3525 100       5550 if ( 'ARRAY' eq ref $data ) {
59 916         1013 return [ map { $self->make_from( $_ ) } @{ $data } ];
  1411         2448  
  916         1228  
60             }
61              
62 2609 50       3986 croak( 'Invalid data for making Pg::SQL::PrettyPrinter::Node: ' . Dumper( $data ) ) unless 'HASH' eq ref $data;
63              
64 2609         2665 my @all_keys = keys %{ $data };
  2609         4366  
65 2609 100       4015 return if 0 == scalar @all_keys;
66 2605 50       3799 croak( 'Invalid data for making Pg::SQL::PrettyPrinter::Node (#2): ' . join( ', ', @all_keys ) ) unless 1 == scalar @all_keys;
67 2605         3290 my $class_suffix = $all_keys[ 0 ];
68 2605 50       7626 croak( "Invalid data for making Pg::SQL::PrettyPrinter::Node (#3): $class_suffix" ) unless $class_suffix =~ /^[A-Z][a-zA-Z0-9_-]+$/;
69              
70 2605         3460 my $class = 'Pg::SQL::PrettyPrinter::Node::' . $class_suffix;
71 2605         2740 my $object;
72 2605         2871 eval { $object = use_module( $class )->new( $data->{ $class_suffix } ); };
  2605         4367  
73 2605 50       3869 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         11129 return $object;
79             }
80              
81             sub objectify {
82 1787     1787 1 2054 my $self = shift;
83 1787         2851 my @keys = @_;
84              
85             # Only arrays and hashes (well, references to them) can be objectified.
86 1787         2545 my %types_ok = map { $_ => 1 } qw{ ARRAY HASH };
  3574         6545  
87              
88 1787         2970 for my $key ( @keys ) {
89 3733         5907 my ( $container, $real_key ) = $self->get_container_key( $key );
90 3733 100       5725 next unless defined $container;
91 3380 100       5818 next unless exists $container->{ $real_key };
92              
93 1973         2498 my $val = $container->{ $real_key };
94 1973         2566 my $type = ref $val;
95 1973 50       3101 next unless $types_ok{ $type };
96              
97 1973         3512 $container->{ $real_key } = $self->make_from( $val );
98             }
99              
100 1787         3730 return;
101             }
102              
103             sub get_container_key {
104 3733     3733 1 4029 my $self = shift;
105 3733         4073 my $path = shift;
106              
107 3733         4353 my $type = ref $path;
108 3733 100       7691 return $self, $path if '' eq $type;
109 546 50       904 croak( "Can't get container/key for non-array: $type" ) unless 'ARRAY' eq $type;
110 546 50       605 croak( "Can't get container/key for empty array" ) if 0 == scalar @{ $path };
  546         947  
111 546 50       656 return $self, $path->[ 0 ] if 1 == scalar @{ $path };
  546         824  
112              
113 546         710 my $container = $self;
114 546         721 for ( my $i = 0 ; $i < $#{ $path } ; $i++ ) {
  746         1200  
115 553         700 my $key = $path->[ $i ];
116 553 100       1173 return unless exists $container->{ $key };
117 200         355 $container = $container->{ $key };
118             }
119              
120 193         336 return $container, $path->[ -1 ];
121             }
122              
123             sub pretty_print {
124 557     557 1 601 my $self = shift;
125 557         1103 return $self->as_text( @_ );
126             }
127              
128             sub quote_literal {
129 227     227 1 280 my $self = shift;
130 227         280 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         373 my $rep = {};
134 227         416 $rep->{ "\r" } = "\\r";
135 227         330 $rep->{ "\t" } = "\\t";
136 227         298 $rep->{ "\n" } = "\\n";
137 227         243 my $look_for = join( '|', keys %{ $rep } );
  227         572  
138              
139 227 50       3813 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         527 $val =~ s/'/''/g;
152 227         950 return "'${val}'";
153             }
154              
155             sub quote_ident {
156 1302     1302 1 1396 my $self = shift;
157 1302         1454 my $val = shift;
158 1302 100       4895 return $val if $val =~ m{\A[a-z0-9_]+\z};
159 17         31 $val =~ s/"/""/g;
160 17         56 return '"' . $val . '"';
161             }
162              
163             sub increase_indent {
164 569     569 1 701 my $self = shift;
165 569         631 my $input = shift;
166 569         1055 return $self->increase_indent_n( 1, $input );
167             }
168              
169             sub increase_indent_n {
170 571     571 1 2750 my $self = shift;
171 571         613 my $levels = shift;
172 571         648 my $input = shift;
173 571 50       1496 croak( "Bad number of levels ($levels) to increase indent!" ) unless $levels =~ m{\A[1-9]\d*\z};
174 571         770 my $prefix = ' ' x $levels;
175 571         1112 my @lines = split /\n/, $input;
176 571         808 return join( "\n", map { $prefix . $_ } @lines );
  946         2855  
177             }
178              
179             1;