CLI.pm 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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;
  9. use Carp qw{croak};
  10. use Fcm::CLI::Config;
  11. use Fcm::CLI::Exception;
  12. use Fcm::Util::ClassLoader;
  13. use File::Basename qw{basename};
  14. use Getopt::Long qw{GetOptions};
  15. use Scalar::Util qw{blessed};
  16. ################################################################################
  17. # Invokes the FCM command line interface
  18. sub invoke {
  19. local(@ARGV) = @ARGV;
  20. my $config = Fcm::CLI::Config->instance();
  21. my $subcommand_name = @ARGV ? shift(@ARGV) : q{};
  22. my $subcommand = $config->get_subcommand_of($subcommand_name);
  23. eval {
  24. if (!$subcommand) {
  25. croak(Fcm::CLI::Exception->new({message => 'unknown command'}));
  26. }
  27. my ($opts_ref, $args_ref, $is_help) = _parse_argv_using($subcommand);
  28. my ($invoker_class, $invoker);
  29. if ($is_help) {
  30. $invoker_class
  31. = _load_invoker_class_of($config->get_subcommand_of(q{}));
  32. $invoker = $invoker_class->new({
  33. command => $subcommand_name,
  34. arguments => [$subcommand_name],
  35. });
  36. }
  37. else {
  38. $invoker_class = _load_invoker_class_of($subcommand);
  39. $invoker = $invoker_class->new({
  40. command => $subcommand_name,
  41. options => $opts_ref,
  42. arguments => $args_ref,
  43. (
  44. $subcommand->get_invoker_config()
  45. ? %{$subcommand->get_invoker_config()}
  46. : ()
  47. ),
  48. });
  49. }
  50. $invoker->invoke();
  51. };
  52. if ($@) {
  53. if (Fcm::CLI::Exception->caught($@)) {
  54. die(sprintf(
  55. qq{%s%s: %s\nType "%s help%s" for usage\n},
  56. basename($0),
  57. ($subcommand_name ? qq{ $subcommand_name} : q{}),
  58. $@->get_message(),
  59. basename($0),
  60. defined($subcommand) ? qq{ $subcommand_name} : q{},
  61. ));
  62. }
  63. else {
  64. die($@);
  65. }
  66. }
  67. }
  68. ################################################################################
  69. # Parses options in @ARGV using the options settings of a subcommand
  70. sub _parse_argv_using {
  71. my ($subcommand) = @_;
  72. my %options = ();
  73. my $is_help = undef;
  74. if (($subcommand->get_options())) {
  75. my $problem = q{};
  76. local($SIG{__WARN__}) = sub {
  77. ($problem) = @_;
  78. };
  79. my $success = GetOptions(
  80. \%options,
  81. (map {$_->get_arg_for_getopt_long()} ($subcommand->get_options())),
  82. );
  83. if (!$success) {
  84. croak(Fcm::CLI::Exception->new({message => sprintf(
  85. "option parse failed: %s", $problem,
  86. )}));
  87. }
  88. OPTION:
  89. for my $option ($subcommand->get_options()) {
  90. if (!exists($options{$option->get_name()})) {
  91. next OPTION;
  92. }
  93. if ($option->is_help()) {
  94. $is_help = 1;
  95. }
  96. if (
  97. $option->has_arg() == $option->ARRAY_ARG
  98. && $option->get_delimiter()
  99. ) {
  100. $options{$option->get_name()} = [split(
  101. $option->get_delimiter(),
  102. join(
  103. $option->get_delimiter(),
  104. @{$options{$option->get_name()}},
  105. ),
  106. )];
  107. }
  108. }
  109. }
  110. return (\%options, [@ARGV], $is_help);
  111. }
  112. ################################################################################
  113. # Loads and returns the invoker class of a subcommand
  114. sub _load_invoker_class_of {
  115. my ($subcommand) = @_;
  116. my $invoker_class
  117. = $subcommand->get_invoker_class() ? $subcommand->get_invoker_class()
  118. : 'Fcm::CLI::Invoker'
  119. ;
  120. return Fcm::Util::ClassLoader::load($invoker_class);
  121. }
  122. 1;
  123. __END__
  124. =head1 NAME
  125. Fcm::CLI
  126. =head1 SYNOPSIS
  127. use Fcm::CLI
  128. Fcm::CLI::invoke();
  129. =head1 DESCRIPTION
  130. Invokes the FCM command line interface.
  131. =head1 FUNCTIONS
  132. =over 4
  133. =item invoke()
  134. Invokes the FCM command line interface.
  135. =back
  136. =head1 TO DO
  137. Move option/argument parsing to L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>?
  138. Use an OO interface?
  139. =head1 SEE ALSO
  140. L<Fcm::CLI::Config|Fcm::CLI::Config>,
  141. L<Fcm::CLI::Invoker|Fcm::CLI::Invoker>,
  142. L<Fcm::CLI::Subcommand|Fcm::CLI::Subcommand>,
  143. L<Fcm::CLI::Option|Fcm::CLI::Option>
  144. =head1 COPYRIGHT
  145. E<169> Crown copyright Met Office. All rights reserved.
  146. =cut