Subcommand.pm 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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::Subcommand;
  9. use Carp qw{croak};
  10. use Fcm::CLI::Exception;
  11. use Fcm::Util::ClassLoader;
  12. ################################################################################
  13. # Constructor
  14. sub new {
  15. my ($class, $args_ref) = @_;
  16. return bless({%{$args_ref}}, $class);
  17. }
  18. ################################################################################
  19. # Methods: get_*
  20. for my $key (
  21. # Returns the long description of this subcommand
  22. 'description',
  23. # Returns the class of the invoker of this subcommand
  24. 'invoker_class',
  25. # Returns the extra config to be given to the invoker of this subcommand
  26. 'invoker_config',
  27. # Returns the names of this subcommand
  28. 'names',
  29. # Returns the options of this subcommand
  30. 'options',
  31. # Returns the synopsis of this subcommand
  32. 'synopsis',
  33. # Returns the usage of this subcommand
  34. 'usage',
  35. ) {
  36. no strict qw{refs};
  37. my $getter = "get_$key";
  38. *$getter = sub {
  39. my ($self) = @_;
  40. if (defined($self->{$key})) {
  41. if (ref($self->{$key}) eq 'ARRAY') {
  42. return (wantarray() ? @{$self->{$key}} : $self->{$key});
  43. }
  44. else {
  45. return $self->{$key};
  46. }
  47. }
  48. else {
  49. return;
  50. }
  51. }
  52. }
  53. ################################################################################
  54. # Returns true if this subcommand represents a command in the CM sub-system
  55. sub is_vc {
  56. my ($self) = @_;
  57. return $self->{is_vc};
  58. }
  59. ################################################################################
  60. # Returns true if $string matches a name of this subcommand
  61. sub has_a_name {
  62. my ($self, $string) = @_;
  63. if ($self->get_names() && ref($self->get_names()) eq 'ARRAY') {
  64. my %name_of = map {$_, 1} @{$self->get_names()};
  65. return exists($name_of{$string});
  66. }
  67. else {
  68. return;
  69. }
  70. }
  71. ################################################################################
  72. # Invokes this subcommand based on current @ARGV
  73. sub get_invoker {
  74. my ($self, $command) = @_;
  75. my %options = ();
  76. if (($self->get_options())) {
  77. my $problem = q{};
  78. local($SIG{__WARN__}) = sub {
  79. ($problem) = @_;
  80. };
  81. my $success = GetOptions(
  82. \%options,
  83. (map {$_->get_arg_for_getopt_long()} ($self->get_options())),
  84. );
  85. if (!$success) {
  86. croak(Fcm::CLI::Exception->new({message => sprintf(
  87. "%s: option parse failed: %s", $command, $problem,
  88. )}));
  89. }
  90. }
  91. my $invoker_class
  92. = $self->get_invoker_class() ? $self->get_invoker_class()
  93. : 'Fcm::CLI::Invoker'
  94. ;
  95. Fcm::Util::ClassLoader::load($invoker_class);
  96. my $invoker = $invoker_class->new({
  97. command => $command,
  98. options => \%options,
  99. arguments => [@ARGV],
  100. });
  101. return $invoker;
  102. }
  103. ################################################################################
  104. # Returns a simple string representation of this subcommand
  105. sub as_string {
  106. my ($self) = @_;
  107. # FIXME: can do with using Text::Template or Perl6::Form
  108. if (
  109. $self->get_names()
  110. && ref($self->get_names()) eq 'ARRAY'
  111. && @{$self->get_names()}
  112. ) {
  113. my @names = $self->get_names();
  114. my $return = $names[0];
  115. for my $i (1 .. $#names) {
  116. if ($names[$i]) {
  117. $return
  118. .= $i == 1 ? q{ (} . $names[$i]
  119. : q{, } . $names[$i]
  120. ;
  121. }
  122. if ($i == $#names) {
  123. $return .= q{)};
  124. }
  125. }
  126. return $return;
  127. }
  128. else {
  129. return q{};
  130. }
  131. }
  132. 1;
  133. __END__
  134. =head1 NAME
  135. Fcm::CLI::Subcommand
  136. =head1 SYNOPSIS
  137. use Fcm::CLI::Subcommand;
  138. $subcommand = Fcm::CLI::Subcommand->new({
  139. names => ['build', 'bld'],
  140. options => [
  141. Fcm::CLI::Option->new(
  142. # ... some arguments ...
  143. ),
  144. # ... more options
  145. ],
  146. synopsis => 'invokes the build system',
  147. description => $description,
  148. usage => '[OPTIONS] [CONFIG]',
  149. invoker_class => $invoker_class,
  150. invoker_config => {
  151. option1 => $option1,
  152. # ... more options
  153. },
  154. });
  155. $boolean = $subcommand->has_a_name($string);
  156. $invoker_class = $subcommand->get_invoker_class();
  157. =head1 DESCRIPTION
  158. An object of this class is used to store the configuration of a subcommand of
  159. the FCM CLI.
  160. =head1 METHODS
  161. =over 4
  162. =item new($args_ref)
  163. Constructor.
  164. =item get_description()
  165. Returns the long description of this subcommand.
  166. =item get_invoker_class()
  167. Returns the invoker class of this subcommand, which should be a sub-class of
  168. L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>.
  169. =item get_invoker_cconfig()
  170. Returns a reference to a hash containing the extra configuration to be given to
  171. the constructor of the invoker of this subcommand.
  172. =item get_names()
  173. Returns an array containing the names of this subcommand.
  174. =item get_options()
  175. Returns an array containing the options of this subcommand. Each element of
  176. the array should be a L<Fcm::CLI::Option|Fcm::CLI::Option> object.
  177. =item get_synopsis()
  178. Returns a short synopsis of this subcommand.
  179. =item get_usage()
  180. Returns a short usage statement of this subcommand.
  181. =item is_vc()
  182. Returns true if this subcommand represents commands in the underlying VC system.
  183. =item has_a_name($string)
  184. Returns true if a name in C<$self-E<gt>get_names()> matches $string.
  185. =back
  186. =head1 DIAGNOSTICS
  187. =over 4
  188. =item L<Fcm::CLI::Exception|Fcm::CLI::Exception>
  189. The invoke() method may croak() with this exception.
  190. =back
  191. =head1 SEE ALSO
  192. L<Fcm::CLI::Exception|Fcm::CLI::Exception>,
  193. L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>,
  194. L<Fcm::CLI::Option|Fcm::CLI::Option>
  195. =head1 COPYRIGHT
  196. E<169> Crown copyright Met Office. All rights reserved.
  197. =cut