have no rows marker work as expected with columns
[scpubgit/JSON-Tree-Viewer.git] / br.pl
diff --git a/br.pl b/br.pl
index 21eec68..08fe3b6 100644 (file)
--- a/br.pl
+++ b/br.pl
@@ -5,6 +5,7 @@ use Module::Runtime qw(use_module);
 use Scalar::Util qw(blessed);
 use IO::All;
 use JSON;
+use URI::Escape;
 
 has root => (is => 'lazy');
 
@@ -39,7 +40,11 @@ sub dispatch_request {
     },
   },
   sub (/**/) {
-    $self->structure(split '/', $_[1]);
+    $self->structure(map {
+      s{\\/}{/}g;
+      s{\\\\}{\\}g;
+      $_;
+    } split qr{(?<!\\)/}, $_[1]);
   },
 }
 
@@ -70,11 +75,38 @@ sub structure {
   if ($parts[0] =~ /\+/) {
     ($parts[0], my @extra) = split /\+/, $parts[0];
     my $struct = $self->mangle_structure($self->descend($self->root, @parts));
-    return $struct unless $struct->[0]{show_columns};
     my $first = shift @parts;
     my @rest = map [ $_, $self->mangle_structure(
                      $self->descend($self->root, $_, @parts)
                    )->[0] ], @extra;
+    unless ($struct->[0]{show_columns}) {
+      my @cols = @{ $struct->[0]{columns} };
+      if (@cols == 2) {
+        my ($key_name, $value_name) = @cols;
+        my %name;
+        $name{ $_ }++
+          for map $_->{$key_name},
+              map @$_, $struct->[0]{data}, map $_->[1]{data}, @rest;
+        my %value_by_host = (map {
+          my $host = $_->[0];
+          my $data = $_->[1]{data};
+          ($host, +{
+            map { ($_->{$key_name}, $_->{$value_name}) } @$data,
+          });
+        } [$first, $struct->[0]], @rest);
+        my @hosts = ($first, @extra);
+        return [{
+          columns => ['key', @hosts],
+          show_columns => 1,
+          data => [ map {
+            my $key = $_;
+            +{ key => $key, (map {
+              ($_, $value_by_host{$_}{$key});
+            } @hosts)};
+          } sort keys %name ],
+        }];
+      }
+    }
     my %by_name;
     my %host_cols;
     my %complex_cols;
@@ -128,6 +160,17 @@ sub mangle_structure {
       columns => [ 'key', 'value' ],
       data => [ map +{ key => $_, value => $data->{$_} }, sort keys %$data ],
     }];
+  } elsif (ref($data) eq 'ARRAY') {
+    if (not grep { not ref($_) eq 'HASH' } @$data) {
+      my %key;
+      $key{$_} = 1
+        for map { keys %$_ } @$data;
+      return [{
+        columns => [sort keys %key],
+        show_columns => 1,
+        data => $data,
+      }];
+    }
   } elsif (blessed($data) and $data->isa('IO::All::Dir')) {
     return [{
       columns => [ 'name', 'explore' ],
@@ -143,11 +186,16 @@ sub mangle_structure {
 sub link_to {
   my ($self, @to) = @_;
   use HTML::Tags;
-  s/\//\./g for my @link = @to;
+  my @link = map {
+    my $link = $_;
+    $link =~ s{\\}{\\\\}g;
+    $link =~ s{/}{\\/}g;
+    $link;
+  } @to;
   my $link = join('/', @link, '');
   my $to = $to[-1];
   my $html = join '', HTML::Tags::to_html_string(
-    <a href="${link}">, "Explore $to", </a>
+    <a href="./${link}">, "Explore $to", </a>
   );
   return \$html;
 }
@@ -156,11 +204,12 @@ sub descend {
   my ($self, $target, @path) = @_;
   return unless $target;
   if (blessed($target) and $target->isa('IO::All::File')) {
-    $target = $self->json->decode(scalar $target->all);
+    my $all = $target->all;
+    $target = $self->json->decode($all);
   }
   return $target unless @path;
-  (my $undot = my $step = shift @path) =~ s/\./\//g;
-  $self->descend($target->{$step}||$target->{$undot}, @path);
+  my $step = shift @path;
+  $self->descend($target->{$step}, @path);
 }
 
 sub render_table {
@@ -170,6 +219,7 @@ sub render_table {
     $data->{show_columns} ? { map +($_ => $_), @{$data->{columns}} } : (),
     @{$data->{data}}
   );
+  my $column_count = scalar @{$data->{columns}};
   [ 200, [ 'Content-type' => 'text/html' ], [
     HTML::Tags::to_html_string(
       <html>, <body>, "\n",
@@ -182,6 +232,13 @@ sub render_table {
               } @{$data->{columns}}),
             </tr>, "\n"
           } @rows),
+          @{$data->{data}}
+          ? ()
+          : (<tr class="no-rows">,
+              <td colspan="$column_count">,
+                'No entries in this data structure',
+              </td>,
+            </tr>),
         '', </table>, "\n",
       ),
       </body>, </html>, "\n",
@@ -192,7 +249,17 @@ sub render_table {
 sub render_el {
   my ($self, $whole, $key, $part) = @_;
   if (ref($part) eq 'ARRAY') {
-    return join(', ', @$part);
+    if (grep { ref($_) eq 'HASH' } @$part) {
+      if ($whole->{key}) {
+        return $self->link_to($whole->{key})
+      } elsif ($whole->{name}) {
+        return $self->link_to($whole->{name}, $key);
+      }
+    }
+    return join ', ', @$part
+      if @$part < 5;
+    use HTML::Tags;
+    return <ul>, (map { (<li>, $_, </li>) } @$part), </ul>;
   }
   if (ref($part) eq 'HASH') {
     if ($whole->{key}) {