better followiung principles
[scpubgit/Q-Branch.git] / lib / SQL / Abstract / Formatter.pm
1 package SQL::Abstract::Formatter;
2
3 require SQL::Abstract::Parts; # it loads us too, don't cross the streams
4
5 use Moo;
6
7 has indent_by => (is => 'ro', default => '  ');
8 has max_width => (is => 'ro', default => 78);
9
10 sub _join {
11   shift;
12 ::Dwarn [ JOIN => @_ ];
13   return ::Dwarn SQL::Abstract::Parts::stringify(\@_);
14 }
15
16 sub format {
17   my ($self, $join, @parts) = @_;
18   $self->_fold_sql('', '', @{$self->_simplify($join, @parts)});
19 }
20
21 sub _simplify {
22   my ($self, $join, @parts) = @_;
23   return '' unless @parts;
24   return $parts[0] if @parts == 1 and !ref($parts[0]);
25   return $self->_simplify(@{$parts[0]}) if @parts == 1;
26   return [ $join, map ref() ? $self->_simplify(@$_) : $_, @parts ];
27 }
28
29 sub _fold_sql {
30 ::Dwarn \@_;
31   my ($self, $indent0, $indent, $join, @parts) = @_;
32   my @res;
33   my $w = $self->max_width;
34   my $join_len = 0;
35   (s/, \Z/,\n/ and $join_len = 1)
36     or s/\A /\n/
37     or $_ = "\n"
38       for my $line_join = $join;
39   my ($nl_pre, $nl_post) = split "\n", $line_join;
40   my $line = $indent0;
41   my $next_indent = $indent.$self->indent_by;
42   PART: foreach my $idx (0..$#parts) {
43     ::Dwarn [ PARTSTART => $idx, \@parts, $line, \@res ];
44     my $p = $parts[$idx];
45     my $pre = $idx ? $join : '';
46     my $j_part = $pre.(my $j = ref($p) ? $self->_join(@$p) : $p);
47     if (length($j_part) + length($line) + $join_len <= $w) {
48       $line .= $j_part;
49     } else {
50       push @res, $line.$nl_pre."\n";
51       if (length($line = $indent.$nl_post.$j) <= $w) {
52         next PART;
53       }
54       my $folded = $self->_fold_sql($indent, $next_indent, @$p);
55       push @res, $folded.$pre."\n";
56       $line = $indent.$nl_post;
57     }
58     ::Dwarn [ PART => $idx => $line => $j_part => \@res ];
59   }
60   return +(::Dwarn [ join '', @res, $line ])->[0];
61 }
62
63 1;