The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
#####################################################################
# This program is not guaranteed to work at all, and by using this  #
# program you release the author of any and all liability.          #
#                                                                   #
# You may use this code as long as you are in compliance with the   #
# license (see the LICENSE file) and this notice, disclaimer and    #
# comment box remain intact and unchanged.                          #
#                                                                   #
# Package:     Term::RouterCLI                                      #
# Class:       Help                                                 #
# Description: Methods for building a Router (Stanford) style CLI   #
#                                                                   #
# Written by:  Bret Jordan (jordan at open1x littledot org)         #
# Created:     2011-02-21                                           #
##################################################################### 
#
#
#
#
package Term::RouterCLI::Help;

use 5.8.8;
use strict;
use warnings;
use Term::RouterCLI::Debugger;
use Log::Log4perl;

use parent qw(Exporter);
our @EXPORT      = qw();
our @EXPORT_OK   = qw( PrintHelp _GetCommandHelp _GetCommandSummaries _GetCommandSummary);
our %EXPORT_TAGS = ( 'all' => [ @EXPORT_OK ] );
our $VERSION     = '1.00';
$VERSION = eval $VERSION;


my $oDebugger = new Term::RouterCLI::Debugger();


# ----------------------------------------
# Public Methods 
# ----------------------------------------

sub PrintHelp
{
    # This method will print out a short description or long description depending on whether 
    # or not an argument "topic" is passed in.  If there is a command argument, then we will
    # print the detailed help topics.
    # Required:
    #   array_ref ($self->{'_aCommandArguments'})
    my $self = shift;
    my $logger = $oDebugger->GetLogger($self);
    my $OUT = $self->{OUT};

    $logger->debug("$self->{'_sName'} - ", '### Entering Method ###');

    # If there is an argument passed to the help function, then lets process those arguments 
    # finding the corresponding command and its help directives.  If there is no argument
    # passed in, then we will just print out all of the help summaries
    my $iNumberOfArguments =  @{$self->{'_aCommandArguments'}};
    $logger->debug("$self->{'_sName'} - ", "iNumberOfArguments: $iNumberOfArguments");
    if ($iNumberOfArguments > 0) 
    {
        $logger->debug("$self->{'_sName'} - ", "Step 2");
        my $sHelpAboutACommand = $self->_GetCommandHelp();
        $logger->debug("$self->{'_sName'} - ", "sHelpAboutACommand: $$sHelpAboutACommand");

        print $OUT $$sHelpAboutACommand;
        print $OUT "\n";
    } 
    else 
    {
        $logger->debug("$self->{'_sName'} - ", "Step 3");
        unless (exists($self->{'_hCommandDirectives'}->{cmds})) {$self->{'_hCommandTreeAtLevel'} = $self->GetFullCommandTree();}
        print $OUT $self->_GetCommandSummaries();
    }
    $logger->debug("$self->{'_sName'} - ", '### Leaving Method ###');
}


# ----------------------------------------
# Private Methods 
# ----------------------------------------

sub _GetCommandHelp
{
    # This method will get the command details from the help directive 
    # Required:
    #   hash_ref ($self->{'_hCommandTreeAtLevel'})   
    #   hash_ref ($self->{'_hCommandDirectives'})
    # Return:
    #   string_ref(help details for the command in question)
    my $self = shift;
    my $logger = $oDebugger->GetLogger($self);
    my $sHelpDetails = "";

    $logger->debug("$self->{'_sName'} - ", '### Entering Method ###');
    # Lets get the current data for the tree location that we are now at
    $self->_FindCommandInCommandTree(); 

    # If their are no command directives then lets look for a default command
    if (!$self->{'_hCommandDirectives'}) 
    {
        $logger->debug("$self->{'_sName'} - ", "Step 1");
        if (exists $self->{'_hCommandTreeAtLevel'}->{''}) { $self->{'_hCommandDirectives'} = $self->{'_hCommandTreeAtLevel'}->{''}; } 
        else 
        { 
            my ($sCommandName) = $self->_GetFullCommandName();
            $sHelpDetails = "$sCommandName doesn't exist.\n"; 
        }
    }
    else
    {
        $logger->debug("$self->{'_sName'} - ", "Step 2");
        if ($self->{display_summary_in_help}) 
        {
            my ($sCommand) = $self->_GetFullCommandName();
            $logger->debug("$self->{'_sName'} - ", "Step 2.1");
            $logger->debug("$self->{'_sName'} - ", "sCommand: $sCommand");

            # We need to take in to account if the desc or help is not in the translated lanugage pack
            if (exists($self->{'_hCommandDirectives'}->{'desc'}) && (defined $self->{'_hCommandDirectives'}->{'desc'})) 
            {
                $sHelpDetails = "$sCommand: " . $self->{'_hCommandDirectives'}->{'desc'} . "\n"; 
            } 
            else { $sHelpDetails = "$sCommand: Command description not found\n"; }
        }
        
        if (exists($self->{'_hCommandDirectives'}->{'help'}) && (defined $self->{'_hCommandDirectives'}->{'help'})) 
        { 
            $sHelpDetails .= $self->{'_hCommandDirectives'}->{'help'}; 
            $sHelpDetails .= "\n";
        } 
        else { $sHelpDetails = "No additional help found\n"; }

        if ($self->{'display_subcommands_in_help'} && exists($self->{'_hCommandDirectives'}->{'cmds'})) 
        { 
            $sHelpDetails .= "\nSubcommands available:\n";
            $sHelpDetails .= $self->_GetCommandSummaries(); 
        }
    }
    $logger->debug("$self->{'_sName'} - ", '### Leaving Method ###');
    return \$sHelpDetails;
}

sub _GetCommandSummaries
{
    # This method will return the command summaries for all commands at the current level of a command tree
    # Required:
    #   hash_ref ($self->{'_hCommandTreeAtLevel'}) 
    # Optional:
    #   array_ref (commands)
    my $self = shift;
    my $aCommands = shift;
    my $logger = $oDebugger->GetLogger($self);
    $logger->debug("$self->{'_sName'} - ", '### Entering Method ###');
    
    # This was added to support "?" mark tab completion when there is nothing yet entered on the command prompt
    unless (exists $aCommands->[0]) 
    {
        unless ( defined $self->{'_hCommandTreeAtLevel'} ) { $self->{'_hCommandTreeAtLevel'} = $self->GetCurrentCommandTree(); }
        foreach (sort(keys(%{$self->{'_hCommandTreeAtLevel'}})))
        {
            push @$aCommands, $_;
        }
    }

    $logger->debug("$self->{'_sName'} - ", "_hCommandTreeAtLevel: ", ${$oDebugger->DumpHashKeys($self->{'_hCommandTreeAtLevel'})});
    $logger->debug("$self->{'_sName'} - ", "aCommands: ", ${$oDebugger->DumpArray($aCommands)});    

    my $sAllCommandSummaries = "";

    # We need to push values in to this string for the following use cases:
    # 1) There is a code directive found on the command, meaning it can be ran by itself
    #    ()example "show interface" and "show interface brief")
    # 2) An actual argument is possible, we should print out some helper text so that the user will know what
    #    they should be entering
    
    if ( exists $self->{'_hCommandDirectives'}->{'maxargs'} && $self->{'_hCommandDirectives'}->{'maxargs'} >= 1 ) 
    {
        my $sArgDescription = "unknown";
        if (exists $self->{'_hCommandDirectives'}->{'argdesc'} && defined $self->{'_hCommandDirectives'}->{'argdesc'}) { $sArgDescription = $self->{'_hCommandDirectives'}->{'argdesc'}; } 
        $sAllCommandSummaries .= sprintf("  %-20s $sArgDescription\n", "WORD"); 
    }
    
    foreach (sort(@$aCommands)) 
    {
        # We now exclude synonyms from the command summaries.
        next if exists $self->{'_hCommandTreeAtLevel'}->{$_}->{'alias'} || exists $self->{'_hCommandTreeAtLevel'}->{$_}->{'syn'};
        # Lets not show the default command in any summaries
        next if $_ eq '';
        # Lets not show "hidden" options in any summaries
        next if exists $self->{'_hCommandTreeAtLevel'}->{$_}->{'hidden'};

        $sAllCommandSummaries .= $self->_GetCommandSummary("$_");
    }
    if ( exists $self->{'_hCommandDirectives'}->{'code'} ) { $sAllCommandSummaries .= sprintf("  %-20s\n", "<cr>"); }

    $logger->debug("$self->{'_sName'} - ", '### Leaving Method ###');
    return $sAllCommandSummaries;
}

sub _GetCommandSummary
{
    # This method returns the command summary for a specific command at a certain command tree level
    # Required:
    #   hash_ref ($self->{'_hCommandTreeAtLevel'})
    #   string (command name)
    # Return:
    #   string (command summary line)
    my $self = shift;
    my $sCommandName = shift;
    my $logger = $oDebugger->GetLogger($self);
    my $sCommandSummary;

    $logger->debug("$self->{'_sName'} - ", '### Entering Method ###');
    $sCommandSummary = $self->{'_hCommandTreeAtLevel'}->{$sCommandName}->{'desc'} || "(no description)";
    
    $logger->debug("$self->{'_sName'} - ", '### Leaving Method ###');
    return sprintf("  %-20s $sCommandSummary\n", $sCommandName);
}

return 1;