App-Project-Doctor
view release on metacpan or search on metacpan
lib/App/Project/Doctor/Fixer.pm view on Meta::CPAN
? $self->_apply_all(\@fixable)
: $self->_interactive_loop(\@fixable);
}
# ---------------------------------------------------------------------------
# Private helpers
# ---------------------------------------------------------------------------
# Purpose: Print a numbered list of fixes to STDOUT.
# Entry: $fixable is a non-empty arrayref of Finding objects.
# Exit: Returns nothing; side-effect is printed output only.
# Side effects: Writes to STDOUT.
sub _print_fix_list {
my $fixable = shift;
print "\nSuggested fixes:\n";
# Number each finding starting at 1 so the user can reference them by number.
my $i = 0;
printf " [%d] %s\n", ++$i, $_->message for @{$fixable};
return;
}
# Purpose: Read the user's choice from STDIN and apply the selected fixes.
# Entry: $fixable is a non-empty arrayref of Finding objects.
# Exit: Integer count of fixes successfully applied.
# Side effects: Reads STDIN, writes STDOUT, may modify the filesystem via fix coderefs.
sub _interactive_loop {
my ($self, $fixable) = @_;
# Show the numbered list so the user knows what choices are available.
_print_fix_list($fixable);
print "\nWould you like me to apply them? [Y/n/1,3] ";
# Read one line from the user; return 0 cleanly if STDIN is closed (e.g. in a pipe).
my $answer = <STDIN>;
return 0 unless defined $answer;
chomp $answer; # Remove the trailing newline before comparing.
# Empty input or "yes" means apply everything.
return $self->_apply_all($fixable)
if $answer eq '' || $answer =~ /^y(?:es)?$/i;
# Explicit "no" -- tell the user we skipped and return.
if ($answer =~ /^n(?:o)?$/i) {
print "No fixes applied.\n";
return 0;
}
# A comma/space-separated list of numbers selects individual fixes.
if ($answer =~ /^[\d,\s]+$/) {
my $max = scalar @{$fixable}; # The highest valid index.
my %seen;
# Parse the numbers, clamp to valid range, and deduplicate.
my @indices = grep { $_ >= 1 && $_ <= $max && !$seen{$_}++ }
map { int($_) }
split /[\s,]+/, $answer;
# Convert 1-based user indices to 0-based array indices.
my @selected = map { $fixable->[$_ - 1] } @indices;
return $self->_apply_all(\@selected);
}
# Anything else is unrecognised; be explicit rather than guessing.
print "Unrecognised input -- no fixes applied.\n";
return 0;
}
# Purpose: Call every fix coderef in the list and count the successes.
# Entry: $fixable is an arrayref of Finding objects (may be empty).
# Exit: Integer count of fixes that ran without throwing.
# Side effects: Calls fix coderefs (may create/modify files), writes to STDOUT on
# success, calls carp for each failing fix.
sub _apply_all {
my ($self, $fixable) = @_;
my $count = 0;
for my $f (@{$fixable}) {
# Wrap the fix in eval so a single failure doesn't abort all remaining fixes.
my $ok = eval { $f->fix->($self->context); 1 };
if ($ok) {
# Print confirmation so the user can see what changed.
printf " Applied: %s\n", $f->message;
$count++;
} else {
# Report the failure but continue with the next fix.
carp "Fix failed for '" . $f->message . "': $@";
}
}
# Summary line always prints, even when count is 0.
printf "\n%d fix(es) applied.\n", $count;
return $count;
}
1;
__END__
=head1 NAME
App::Project::Doctor::Fixer - Interactive fix application loop
=head1 VERSION
0.02
=head1 SYNOPSIS
use App::Project::Doctor::Fixer;
my $fixer = App::Project::Doctor::Fixer->new(
report => $report,
context => $ctx,
);
my $count = $fixer->run;
=head1 DESCRIPTION
Presents fixable findings from a report, reads the user's choice from STDIN
(C<Y> all, C<n> none, or C<1,3> index list), and calls each selected
finding's C<fix> coderef with the current context.
Set C<non_interactive =E<gt> 1> to apply all fixes without prompting
(C<--fix> mode).
( run in 1.897 second using v1.01-cache-2.11-cpan-7fcb06a456a )