give modules default loggers so they are usable on their own (in tests)
[scpubgit/System-Introspector-Report.git] / lib / System / Introspector / Report / Source.pm
1 package System::Introspector::Report::Source;
2 use Moo;
3 use JSON::PP;
4 use IO::All;
5 use Module::Runtime qw( use_module );
6 use Log::Contextual::WarnLogger;
7 use Log::Contextual qw( :log ),
8   -default_logger => Log::Contextual::WarnLogger->new({
9     env_prefix => 'SI_REPORT_SOURCE',
10     levels      => [qw( error fatal warn )],
11   });
12
13 my $_json = JSON::PP->new->utf8->relaxed->allow_nonref;
14
15 has root => (is => 'ro', required => 1);
16
17 sub new_from_root {
18   my ($class, $root, @args) = @_;
19   if ($root =~ m{^(.+):(.+)$}) {
20     my ($remote, $remote_root) = ($1, $2);
21     require Object::Remote;
22     return $class->new::on($remote, @args, root => $remote_root);
23   }
24   else {
25     return $class->new(@args, root => $root);
26   }
27 }
28
29 sub generate {
30   my ($self, @types) = @_;
31   my @generators = map { $self->_make_generator($_) } @types;
32   my $required = +{ map { ($_ => 1) } map $_->required_data, @generators };
33   my @dirs = $self->_find_source_dirs;
34   for my $dir_spec (@dirs) {
35     my ($id, $path) = @$dir_spec;
36     my $data = $self->_load_dataset($path, $required)
37       or next;
38     log_trace { "loading node '$id' from '$path'" };
39     $_->collect_from($id, $data)
40       for @generators;
41   }
42   return map { ($_->render_reports) } @generators;
43 }
44
45 sub _load_dataset {
46   my ($self, $path, $required) = @_;
47   my $root = $self->root;
48   return +{
49     map {
50       ($_->[0], $_json->decode(scalar $_->[1]->slurp) || {});
51     } grep {
52       $required->{$_->[0]};
53     } map {
54       my $key = $_;
55       $key =~ s!^\Q$root\E/*(?:[^/]+/+){2}!!;
56       $key =~ s{\.json$}{};
57       [$key, $_];
58     } grep {
59       not(m{^\.}) and m{\.json$};
60     } $path->deep->all_files
61   };
62 }
63
64 sub _find_source_dirs {
65   my ($self) = @_;
66   my $root = $self->root;
67   return map {
68     (my $id = $_) =~ s{^\Q$root\E/*}{};
69     [$id, $_];
70   } io($root)->all_dirs;
71 }
72
73 sub _make_generator {
74   my ($self, $spec) = @_;
75   my ($type, $args) = @$spec;
76   return use_module("System::Introspector::Report::Builder::$type")
77     ->new($args || {});
78 }
79
80 1;