160 lines
5.3 KiB
Crystal
160 lines
5.3 KiB
Crystal
require "colorize"
|
|
require "time"
|
|
|
|
require "./command/parser"
|
|
|
|
module CliGen
|
|
|
|
annotation CommandPreRun
|
|
end
|
|
|
|
annotation CommandInfo
|
|
end
|
|
|
|
annotation CommandArgument
|
|
end
|
|
|
|
annotation SubCommand
|
|
end
|
|
|
|
|
|
class Command
|
|
extend CliGen::Parser
|
|
|
|
|
|
macro inherited
|
|
macro finished
|
|
define_actions
|
|
define_header
|
|
define_runner
|
|
define_parser
|
|
define_action_setter
|
|
end
|
|
end
|
|
|
|
macro define_actions
|
|
@@action : String = ""
|
|
ACTIONS = {{@type.class.methods.select(&.annotation(::CliGen::SubCommand)).map(&.name.stringify)}} of String
|
|
end
|
|
|
|
macro define_header
|
|
{% name = @type.name.split("::").last.downcase.id %}
|
|
{% info_annos = @type.class.methods.select(&.annotation(::CliGen::SubCommand)).map(&.annotation(::CliGen::SubCommand)) %}
|
|
{% examples = [] of StringLiteral %}
|
|
{% info_annos.select(&.[](:examples)).map(&.[](:examples).resolve).each(&.each{|example| examples << example}) %}
|
|
HEADER = [
|
|
"#{CliGen::APPNAME} {{name}} [[flags]]",
|
|
"",
|
|
{% unless examples.empty? %}
|
|
"Examples:".colorize(:green),
|
|
"-------------------------------------------------------------------------------".colorize(:blue),
|
|
{% for example in examples %}
|
|
{{example}},
|
|
{% end %}
|
|
{% end %}
|
|
].join("\n")
|
|
end
|
|
|
|
macro define_argument(arg_name, variable = nil, type = String, short = "", long = "", description = nil, subtype = Nil, format = nil, check = nil, def_getter = false, default = nil, logger = nil)
|
|
{% raise "define_argument : You must provide a format or set it to \"auto\" to use builtin formats when defining a Time argument" if type.id.stringify == "Time" && format == nil %}
|
|
{% raise "define_argument : You MUST provide a description" unless description %}
|
|
{% variable = arg_name unless variable %}
|
|
{% _type = type.id.stringify %}
|
|
{% _subtype = subtype.id.stringify %}
|
|
{% if _type == "String" %}
|
|
@@{{variable}} : {{type}} = ""
|
|
{% elsif _type == "Bool" %}
|
|
@@{{variable}} : {{type}} = false
|
|
{% elsif _type == "Int32" %}
|
|
@@{{variable}} : {{type}} = 0
|
|
{% elsif _type == "Array" %}
|
|
{% raise "define_argument : subtype can only be Int32 or String" unless %w[ Int32 String ].includes?(_subtype) %}
|
|
@@{{variable}} : {{type}}({{subtype}}) = [] of {{subtype}}
|
|
{% elsif _type == "Time" %}
|
|
@@{{variable}} : {{type}} = Time.unix(seconds: 0)
|
|
{% end %}
|
|
{% if def_getter %}
|
|
def self.get_{{arg_name}} : {{type}}
|
|
@@{{variable}}
|
|
end
|
|
{% end %}
|
|
|
|
@[::CliGen::CommandArgument(type: {{type}}, short: {{short}}, long: {{long}}, description: {{description}})]
|
|
def self.{{arg_name}}(var : String) : Nil
|
|
{% if check %}
|
|
## If the check exists go ahead and call it
|
|
{{check}}(var)
|
|
{% end %}
|
|
|
|
{% if format %}
|
|
abort "ERROR : Action : {{arg_name}} : Incorrect format for provided data #{var}" unless var =~ {{format}}
|
|
{% end %}
|
|
## If a logger method has been provided by the user
|
|
{% if logger %}
|
|
{{logger.id}} "Command : subclass : argument_setter : {{arg_name}} : Entered with #{var}"
|
|
{% end %}
|
|
{% if _type == "Time" %}
|
|
formats = [
|
|
CliGen::Regex::INPUT_DATETIME_REGEX,
|
|
CliGen::Regex::INPUT_DATE_REGEX
|
|
]
|
|
abort "ERROR : Command : {{arg_name}} : Incorrect format for provided data #{var}" unless formats.any?{|f| var.match(f)}
|
|
if var =~ formats[0]
|
|
@@{{variable}} = Time.parse_local(var, CliGen::Format::INPUT_DATETIME_FORMAT)
|
|
elsif var =~ formats[1]
|
|
@@{{variable}} = Time.parse_local(var, CliGen::Format::INPUT_DATE_FORMAT)
|
|
end
|
|
{% elsif _type == "Array" %}
|
|
{% if _subtype == "Int32" %}
|
|
var : Int32 = var.to_i
|
|
{% elsif _subtype == "String" %}
|
|
{% else %}
|
|
var = {{subtype}}.new(var)
|
|
{% end %}
|
|
@@{{variable}} << var
|
|
{% elsif _type == "Bool" %}
|
|
@@{{variable}} = true
|
|
{% elsif _type == "Int32" %}
|
|
@@{{variable}} = var.to_i
|
|
{% elsif _type == "String" %}
|
|
@@{{variable}} = var
|
|
{% end %}
|
|
end
|
|
end
|
|
|
|
macro define_runner
|
|
def self.run
|
|
{% pre_run_commands = @type.class.methods.select(&.annotation(::CliGen::CommandPreRun)) %}
|
|
{% unless pre_run_commands.empty? %}
|
|
## Ensuring that all runs that are tagged with the the CommandPreRun annotation are run
|
|
{% for method in pre_run_commands %}
|
|
{{method.name}}
|
|
{% end %}
|
|
{% end %}
|
|
|
|
{% methods = @type.class.methods.select(&.annotation(::CliGen::SubCommand)) %}
|
|
{% raise "ERROR : No commands defined for #{@type.name}" if methods.empty? %}
|
|
{% begin %}
|
|
case @@action
|
|
{% for method in methods %}
|
|
when {{method.name.stringify}}
|
|
{{method.name}}
|
|
{% end %}
|
|
else
|
|
abort "ERROR : No action provided to command {{@type.name}}"
|
|
end
|
|
{% end %}
|
|
end
|
|
end
|
|
|
|
macro define_action_setter
|
|
def self.action=(action : String)
|
|
abort "ERROR : Action(#{action}) is not valid. Only #{ACTIONS.join(", ")} are acceptable" unless ACTIONS.includes?(action)
|
|
@@action = action
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
end
|