X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=lib%2FApp%2FCodeowners.pm;h=20be9855fa51d7917a683fb6ce8c0321f7c6ff87;hb=26eed33eb4aa577d9347e5ebaf577b3e3a2c0396;hp=eebe058be5e9e61a1d6ecb29e0f8f8a4f406b698;hpb=67e86f990f2556f12465052911eb7b96a03b8dcc;p=chaz%2Fgit-codeowners diff --git a/lib/App/Codeowners.pm b/lib/App/Codeowners.pm index eebe058..20be985 100644 --- a/lib/App/Codeowners.pm +++ b/lib/App/Codeowners.pm @@ -6,9 +6,10 @@ use utf8; use warnings; use strict; +use App::Codeowners::Formatter; use App::Codeowners::Options; -use App::Codeowners::Util qw(find_codeowners_in_directory run_git git_ls_files git_toplevel stringf); -use Color::ANSI::Util qw(ansifg ansi_reset); +use App::Codeowners::Util qw(find_codeowners_in_directory run_git git_ls_files git_toplevel); +use Color::ANSI::Util 0.03 qw(ansifg); use Encode qw(encode); use File::Codeowners; use Path::Tiny; @@ -50,26 +51,25 @@ sub _command_show { or die "No CODEOWNERS file in $toplevel\n"; my $codeowners = File::Codeowners->parse_from_filepath($codeowners_path); - my ($cdup) = run_git(qw{rev-parse --show-cdup}); + my ($proc, $cdup) = run_git(qw{rev-parse --show-cdup}); + $proc->wait and exit 1; - my @results; + my $formatter = App::Codeowners::Formatter->new( + format => $opts->{format} || ' * %-50F %O', + handle => *STDOUT, + columns => [qw(File Owner), $opts->{project} ? 'Project' : ()], + ); - my $filepaths = git_ls_files('.', $opts->args) or die "Cannot list files\n"; - for my $filepath (@$filepaths) { + $proc = git_ls_files('.', $opts->args); + while (my $filepath = $proc->next) { my $match = $codeowners->match(path($filepath)->relative($cdup)); - push @results, [ + $formatter->add_result([ $filepath, $match->{owners}, $opts->{project} ? $match->{project} : (), - ]; + ]); } - - _format( - format => $opts->{format} || ' * %-50F %O', - out => *STDOUT, - headers => [qw(File Owner), $opts->{project} ? 'Project' : ()], - rows => \@results, - ); + $proc->wait and exit 1; } sub _command_owners { @@ -84,12 +84,12 @@ sub _command_owners { my $results = $codeowners->owners($opts->{pattern}); - _format( + my $formatter = App::Codeowners::Formatter->new( format => $opts->{format} || '%O', - out => *STDOUT, - headers => [qw(Owner)], - rows => [map { [$_] } @$results], + handle => *STDOUT, + columns => [qw(Owner)], ); + $formatter->add_result(map { [$_] } @$results); } sub _command_patterns { @@ -104,12 +104,12 @@ sub _command_patterns { my $results = $codeowners->patterns($opts->{owner}); - _format( + my $formatter = App::Codeowners::Formatter->new( format => $opts->{format} || '%T', - out => *STDOUT, - headers => [qw(Pattern)], - rows => [map { [$_] } @$results], + handle => *STDOUT, + columns => [qw(Pattern)], ); + $formatter->add_result(map { [$_] } @$results); } sub _command_create { goto &_command_update } @@ -165,156 +165,4 @@ END print STDERR "Wrote $path\n"; } -sub _format { - my %args = @_; - - my $format = $args{format} || 'table'; - my $fh = $args{out} || *STDOUT; - my $headers = $args{headers} || []; - my $rows = $args{rows} || []; - - if ($format eq 'table') { - eval { require Text::Table } or die "Missing dependency: Text::Table\n"; - - my $table = Text::Table->new(@$headers); - $table->load(map { [map { _stringify($_) } @$_] } @$rows); - print { $fh } encode('UTF-8', "$table"); - } - elsif ($format =~ /^json(:pretty)?$/) { - my $pretty = !!$1; - eval { require JSON::MaybeXS } or die "Missing dependency: JSON::MaybeXS\n"; - - my $json = JSON::MaybeXS->new(canonical => 1, utf8 => 1, pretty => $pretty); - my $data = _combine_headers_rows($headers, $rows); - print { $fh } $json->encode($data); - } - elsif ($format =~ /^([ct])sv$/) { - my $sep = $1 eq 'c' ? ',' : "\t"; - eval { require Text::CSV } or die "Missing dependency: Text::CSV\n"; - - my $csv = Text::CSV->new({binary => 1, eol => $/, sep => $sep}); - $csv->print($fh, $headers); - $csv->print($fh, [map { encode('UTF-8', _stringify($_)) } @$_]) for @$rows; - } - elsif ($format =~ /^ya?ml$/) { - eval { require YAML } or die "Missing dependency: YAML\n"; - - my $data = _combine_headers_rows($headers, $rows); - print { $fh } encode('UTF-8', YAML::Dump($data)); - } - else { - my $data = _combine_headers_rows($headers, $rows); - - # https://sashat.me/2017/01/11/list-of-20-simple-distinct-colors/ - my @contrasting_colors = qw( - e6194b 3cb44b ffe119 4363d8 f58231 - 911eb4 42d4f4 f032e6 bfef45 fabebe - 469990 e6beff 9a6324 fffac8 800000 - aaffc3 808000 ffd8b1 000075 a9a9a9 - ); - - # assign a color to each owner, on demand - my %owner_colors; - my $num = -1; - my $owner_color = sub { - my $owner = shift or return; - $owner_colors{$owner} ||= do { - $num = ($num + 1) % scalar @contrasting_colors; - $contrasting_colors[$num]; - }; - }; - - my %filter = ( - quote => sub { local $_ = $_[0]; s/"/\"/s; "\"$_\"" }, - ); - - my $create_filterer = sub { - my $value = shift || ''; - my $color = shift || ''; - my $gencolor = ref($color) eq 'CODE' ? $color : sub { $color }; - return sub { - my $arg = shift; - my ($filters, $color) = _expand_filter_args($arg); - if (ref($value) eq 'ARRAY') { - $value = join(',', map { _colored($_, $color // $gencolor->($_)) } @$value); - } - else { - $value = _colored($value, $color // $gencolor->($value)); - } - for my $key (@$filters) { - if (my $filter = $filter{$key}) { - $value = $filter->($value); - } - else { - warn "Unknown filter: $key\n" - } - } - $value || ''; - }; - }; - - for my $row (@$data) { - my %info = ( - F => $create_filterer->($row->{File}, undef), - O => $create_filterer->($row->{Owner}, $owner_color), - P => $create_filterer->($row->{Project}, undef), - T => $create_filterer->($row->{Pattern}, undef), - ); - - my $text = stringf($format, %info); - print { $fh } encode('UTF-8', $text), "\n"; - } - } -} - -sub _expand_filter_args { - my $arg = shift || ''; - - my @filters = split(/,/, $arg); - my $color_override; - - for (my $i = 0; $i < @filters; ++$i) { - my $filter = $filters[$i] or next; - if ($filter =~ /^(?:nocolor|color:([0-9a-fA-F]{6}))$/) { - $color_override = $1 || ''; - splice(@filters, $i, 1); - redo; - } - } - - return (\@filters, $color_override); -} - -sub _colored { - my $text = shift; - my $rgb = shift or return $text; - - # ansifg honors NO_COLOR already, but ansi_reset does not. - return $text if $ENV{NO_COLOR}; - - my ($begin, $end) = (ansifg($rgb), ansi_reset); - return "${begin}${text}${end}"; -} - -sub _combine_headers_rows { - my $headers = shift; - my $rows = shift; - - my @new_rows; - - for my $row (@$rows) { - push @new_rows, (my $new_row = {}); - for (my $i = 0; $i < @$headers; ++$i) { - $new_row->{$headers->[$i]} = $row->[$i]; - } - } - - return \@new_rows; -} - -sub _stringify { - my $item = shift; - return ref($item) eq 'ARRAY' ? join(',', @$item) : $item; -} - 1;