require 'thor' module Eco module Common class BaseCLI < MethaThor PIPE = "!" CUSTOM_HELP_MAPPINGS = ["?", "help"] ALL_HELP_MAPPINGS = Thor::HELP_MAPPINGS + CUSTOM_HELP_MAPPINGS class_option :simulate, type: :boolean, aliases: :s, desc: "do not launch updates or rewrite files" class_option :verbosity, type: :numeric, default: 0, desc: "defines the level of verbosity to show feedback" class_option :input, type: :hash, default: {json: nil}, required: false, desc: "--input=json:STRING" class_option :pipe, type: :boolean, default: false, required: false, desc: "--pipe subcommand" def self.start(given_args = ARGV, config = {}) #given_args = splat_namespaces(given_args) given_args = BaseCLI.parse_help(given_args) super(given_args, config) end # incompatible with options / arguments of Hash type # see: parse_hash here: https://github.com/erikhuda/thor/blob/master/lib/thor/parser/arguments.rb def self.splat_namespaces(args) args.reduce([]) do |done, arg| done.concat(arg.split(":")) end end def self.parse_help(args) # Help enhancement. Adapted from: https://stackoverflow.com/a/49044225/4352306 last = args.last if args.length > 1 && help?(last) # switch last and second last position last = "help" if custom_help?(last) args.insert(args.length - 2, last).pop puts "> #{args.join(" ")}" end args end def self.help?(value) case value when String ALL_HELP_MAPPINGS.include?(value) when Symbol help?(value.to_s) when Array value.any? { |v| help?(v) } when Hash value.keys.any { |k| help?(v) } end end def self.custom_help?(value) CUSTOM_HELP_MAPPINGS.include?(value) end protected no_commands do def input?(options) if options options.key?("input") || options.key?(:input) || options.key?("json") || options.key?(:json) end end def input_object(input) Eco::CLI::Input.new(input) end def input(value) case when value.is_a?(Eco::CLI::Input) value.value when value.is_a?(Eco::CLI::MultiInput) value.to_h when value.is_a?(String) input(JSON.parse(value)) when value&.key?("input") input(value["input"]) when value&.key?(:input) input(value[:input]) when value&.key?("json") JSON.parse(value["json"]) when value&.key?(:json) JSON.parse(value[:json]) end end def pipe(input, args) command, args, piped = parse_pipe(args) invoke command, args, {input: input_object(input)} if piped piped end def parse_pipe(args) args.shift if piped = (args.first == PIPE) subcommand = "" if piped raise "Error: bad usage of pipe ('#{PIPE}'). Expected: '#{PIPE} command' " unless subcommand = args.slice!(0) if help?(subcommand) # Idea adapted from: https://stackoverflow.com/a/46167300/4352306 self.class.command_help(Thor::Base.shell.new, args.first) exit end end [subcommand, args, piped] end def help?(value) BaseCLI.help?(value) end end end end end