Developer-Dashboard

 view release on metacpan or  search on metacpan

lib/Developer/Dashboard/CLI/Progress.pm  view on Meta::CPAN

package Developer::Dashboard::CLI::Progress;

use strict;
use warnings;

our $VERSION = '3.14';

# new(%args)
# Constructs a terminal progress renderer for restart/stop lifecycle commands.
# Input: title string, arrayref of task hashes, output stream handle, and dynamic flag.
# Output: Developer::Dashboard::CLI::Progress object.
sub new {
    my ( $class, %args ) = @_;
    my $tasks = $args{tasks} || [];
    die 'Progress tasks must be an array reference' if ref($tasks) ne 'ARRAY';
    my @order = map { $_->{id} } @{$tasks};
    my %task_lookup = map {
        my $task = $_;
        my $id   = $task->{id} || die 'Progress task missing id';
        $id => {
            id     => $id,
            label  => $task->{label} || $id,
            status => 'pending',
        }
    } @{$tasks};
    my $stream = $args{stream} || \*STDERR;
    my $self = bless {
        title    => $args{title} || 'dashboard progress',
        order    => \@order,
        tasks    => \%task_lookup,
        stream   => $stream,
        dynamic  => $args{dynamic} ? 1 : 0,
        color    => $args{color} ? 1 : 0,
        rendered => 0,
    }, $class;
    $self->render;
    return $self;
}

# callback()
# Returns a callback that updates task state from runtime lifecycle events.
# Input: none.
# Output: coderef that accepts one hash reference event.
sub callback {
    my ($self) = @_;
    return sub {
        my ($event) = @_;
        $self->update($event);
    };
}

# update($event)
# Applies one lifecycle progress event to the tracked task board.
# Input: hash reference with task_id, status, and optional label.
# Output: true value.
sub update {
    my ( $self, $event ) = @_;
    return 1 if !$event || ref($event) ne 'HASH';
    my $id = $event->{task_id} || return 1;
    my $task = $self->{tasks}{$id} || return 1;
    $task->{status} = $event->{status} if defined $event->{status} && $event->{status} ne '';
    $task->{label}  = $event->{label}  if defined $event->{label}  && $event->{label} ne '';
    $self->render;
    return 1;
}

# finish()
# Finalizes the rendered board by ensuring a trailing newline after dynamic redraws.
# Input: none.
# Output: true value.
sub finish {
    my ($self) = @_;
    return 1 if !$self->{dynamic} || !$self->{rendered};
    my $stream = $self->{stream};
    print {$stream} "\n";
    return 1;
}

# render()
# Renders the full task board to the configured output stream.
# Input: none.
# Output: true value.
sub render {
    my ($self) = @_;
    my $stream = $self->{stream};
    my $board  = $self->render_text;
    if ( $self->{dynamic} && $self->{rendered} ) {
        my $line_count = scalar( split /\n/, $board );
        for ( 1 .. $line_count ) {
            print {$stream} "\e[1A\e[2K";
        }
    }
    print {$stream} $board;
    $self->{rendered} = 1;
    return 1;
}

# render_text()
# Builds the current task board text for terminal output.
# Input: none.
# Output: multi-line string.
sub render_text {
    my ($self) = @_;
    my @lines = ( $self->{title} );
    for my $id ( @{ $self->{order} } ) {
        my $task   = $self->{tasks}{$id} || next;
        my $prefix = $self->_status_prefix( $task->{status} );
        push @lines, sprintf '%s %s', $self->_colorize( $prefix, $task->{status} ), $task->{label};
    }
    return join( "\n", @lines ) . "\n";
}

# _status_prefix($status)
# Maps one task status to the terminal marker shown beside the task label.
# Input: status string.
# Output: short ASCII marker string.



( run in 2.142 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )