Help.pm 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. # ------------------------------------------------------------------------------
  2. # (C) Crown copyright Met Office. All rights reserved.
  3. # For further details please refer to the file COPYRIGHT.txt
  4. # which you should have received as part of this distribution.
  5. # ------------------------------------------------------------------------------
  6. use strict;
  7. use warnings;
  8. package Fcm::CLI::Invoker::Help;
  9. use base qw{Fcm::CLI::Invoker};
  10. use Carp qw{croak};
  11. use Fcm::CLI::Exception;
  12. use Fcm::CLI::Config;
  13. use Fcm::Config;
  14. use Fcm::Util qw{run_command};
  15. use IO::File;
  16. ################################################################################
  17. # Invokes the sub-system
  18. sub invoke {
  19. my ($self) = @_;
  20. my @subcommand_names = $self->get_arguments();
  21. if (@subcommand_names) {
  22. for my $subcommand_name (@subcommand_names) {
  23. my $help_string = $self->_get_help_for($subcommand_name);
  24. if (!defined($help_string)) {
  25. croak(Fcm::CLI::Exception->new({message => sprintf(
  26. "%s: unknown command", $subcommand_name,
  27. )}));
  28. }
  29. print($help_string, "\n");
  30. }
  31. }
  32. else {
  33. print($self->_get_help());
  34. }
  35. }
  36. ################################################################################
  37. # Returns the help string for a subcommand matching $subcommand_name
  38. sub _get_help_for {
  39. my ($self, $subcommand_name) = @_;
  40. my $subcommand
  41. = Fcm::CLI::Config->instance()->get_subcommand_of($subcommand_name);
  42. if (!$subcommand) {
  43. return;
  44. }
  45. if ($subcommand->is_vc()) {
  46. my $invoker = $subcommand->get_invoker($subcommand_name);
  47. local(@ARGV) = '--help';
  48. $invoker->invoke();
  49. return q{};
  50. }
  51. my $prog = Fcm::Config->instance()->setting('FCM_COMMAND');
  52. # FIXME: can do with using Text::Template or Perl6::Form
  53. my $help = sprintf(
  54. "%s %s: %s\n",
  55. $prog,
  56. $subcommand->as_string(),
  57. $subcommand->get_synopsis(),
  58. );
  59. $help .= sprintf(
  60. "usage: %s %s %s\n",
  61. $prog, $subcommand->get_names()->[0], $subcommand->get_usage(),
  62. );
  63. if ($subcommand->get_description()) {
  64. my @lines = (q{}, split("\n", $subcommand->get_description()), q{});
  65. $help .= join(qq{\n }, @lines) . "\n";
  66. }
  67. if ($subcommand->get_options()) {
  68. $help .= "Valid options:\n";
  69. my $max_length_of_name = 0;
  70. my @option_names;
  71. for my $option ($subcommand->get_options()) {
  72. if (length($option->get_name()) > $max_length_of_name) {
  73. $max_length_of_name = length($option->get_name());
  74. }
  75. }
  76. for my $option ($subcommand->get_options()) {
  77. $help .= sprintf(
  78. " --%s%s%s%s : %s\n",
  79. $option->get_name(),
  80. q{ } x ($max_length_of_name - length($option->get_name())),
  81. (
  82. $option->get_letter()
  83. ? q{ [-} . $option->get_letter() . q{]} : q{ }
  84. ),
  85. ($option->has_arg() ? q{ arg} : q{ } x 4),
  86. $option->get_description(),
  87. );
  88. }
  89. }
  90. return $help;
  91. }
  92. ################################################################################
  93. # Returns the general help string
  94. sub _get_help {
  95. my ($self) = @_;
  96. my $release = $self->_get_release();
  97. # FIXME: can do with using Text::Template or Perl6::Form
  98. my $prog = Fcm::Config->instance()->setting('FCM_COMMAND');
  99. my $return = sprintf(
  100. qq{usage: %s <subcommand> [options] [args]\n}
  101. . qq{Flexible configuration management system, release %s.\n}
  102. . qq{Type "%s help <subcommand>" for help on a specific subcommand\n}
  103. . qq{\n}
  104. . qq{Available subcommands:\n}
  105. ,
  106. $prog, $release, $prog,
  107. );
  108. for my $subcommand (Fcm::CLI::Config->instance()->get_core_subcommands()) {
  109. $return .= sprintf(qq{ %s\n}, $subcommand->as_string());
  110. }
  111. my @lines = run_command(
  112. [qw/svn help/], DEVNULL => 1, METHOD => 'qx', ERROR => 'ignore',
  113. );
  114. if (@lines) {
  115. for my $subcommand (Fcm::CLI::Config->instance()->get_vc_subcommands()) {
  116. if (defined($subcommand->get_synopsis())) {
  117. $return .= sprintf(qq{ %s\n}, $subcommand->as_string());
  118. }
  119. else {
  120. $return .= qq{ <version control system commands, see below>\n};
  121. }
  122. }
  123. $return .= "\n=> svn help\n". join(q{}, @lines);
  124. }
  125. return $return;
  126. }
  127. ################################################################################
  128. # Returns the release number of the current program
  129. sub _get_release {
  130. my ($self) = @_;
  131. my $release = Fcm::Config->instance()->setting('FCM_RELEASE');
  132. my $rev_file = Fcm::Config->instance()->setting('FCM_REV_FILE');
  133. if (-r $rev_file) {
  134. my $handle = IO::File->new($rev_file, 'r');
  135. if ($handle) {
  136. my $rev = $handle->getline();
  137. $handle->close();
  138. chomp($rev);
  139. if ($rev) {
  140. $release .= qq{ (r$rev)};
  141. }
  142. }
  143. }
  144. return $release;
  145. }
  146. 1;
  147. __END__
  148. =head1 NAME
  149. Fcm::CLI::Invoker::Help
  150. =head1 SYNOPSIS
  151. use Fcm::CLI::Invoker::Help;
  152. $invoker = Fcm::CLI::Invoker::Help->new({
  153. command => $command,
  154. options => \%options,
  155. arguments => $arguments,
  156. });
  157. $invoker->invoke();
  158. =head1 DESCRIPTION
  159. This class extends L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> an inherits all its
  160. methods. An object of this class is used to provide help on the command line
  161. interface.
  162. =head1 METHODS
  163. See L<Fcm::CLI::Invoker|Fcm::CLI::Invoker> for a list of inherited methods.
  164. =over 4
  165. =item invoke()
  166. Provides help. If a subcommand name is specified in the argument, provides help
  167. for the specified subcommand. If a subcommand name is not specified, provides
  168. general CLI help.
  169. =back
  170. =head1 DIAGNOSTICS
  171. =over 4
  172. =item L<Fcm::CLI::Exception|Fcm::CLI::Exception>
  173. The invoke() method can croak() with this exception if the specified subcommand
  174. cannot be identified.
  175. =back
  176. =head1 TO DO
  177. Unit tests.
  178. Separate logic in this module with that of L<Fcm::CLI::Config|Fcm::CLI::Config>.
  179. Decouples help formatter with this invoker.
  180. =head1 SEE ALSO
  181. L<Fcm::CLI::Exception|Fcm::CLI::Exception>,
  182. L<Fcm::CLI::Subcommand|Fcm::CLI::Subcommand>
  183. =head1 COPYRIGHT
  184. E<169> Crown copyright Met Office. All rights reserved.
  185. =cut