Initial commit
This commit is contained in:
157
src/command.cr
Normal file
157
src/command.cr
Normal file
@@ -0,0 +1,157 @@
|
||||
require "colorize"
|
||||
require "time"
|
||||
|
||||
require "./command/parser"
|
||||
|
||||
module CliGenerator
|
||||
|
||||
annotation CommandPreRun
|
||||
end
|
||||
|
||||
annotation CommandInfo
|
||||
end
|
||||
|
||||
annotation CommandArgument
|
||||
end
|
||||
|
||||
annotation SubCommand
|
||||
end
|
||||
|
||||
|
||||
class Command
|
||||
extend CliGenerator::Parser
|
||||
|
||||
@@action : String = ""
|
||||
|
||||
macro inherited
|
||||
macro finished
|
||||
define_actions
|
||||
define_header
|
||||
define_runner
|
||||
define_parser
|
||||
end
|
||||
end
|
||||
|
||||
macro define_actions
|
||||
ACTIONS : Array(String) = {{@type.class.methods.select(&.annotation(::CliGenerator::SubCommand)).map(&.name.stringify)}}
|
||||
end
|
||||
|
||||
macro define_header
|
||||
{% name = @type.name.split("::").last.downcase.id %}
|
||||
{% info_annos = @type.class.methods.select(&.annotation(::CliGenerator::SubCommand)).map(&.annotation(::CliGenerator::SubCommand)) %}
|
||||
HEADER = [
|
||||
"#{PROGRAM_NAME} {{name}} [[flags]]",
|
||||
"",
|
||||
{% unless info_annos.empty? %}
|
||||
"Examples:".colorize(:green),
|
||||
"-------------------------------------------------------------------------------".colorize(:blue),
|
||||
{% for anno in info_annos %}
|
||||
{% examples = anno[:examples].resolve %}
|
||||
{% for example in examples %}
|
||||
{{example}},
|
||||
{% end %}
|
||||
{% 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 %}
|
||||
|
||||
@[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 = [
|
||||
CliGenerator::Regex::INPUT_DATETIME_REGEX,
|
||||
CliGenerator::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, CliGenerator::Format::INPUT_DATETIME_FORMAT)
|
||||
elsif var =~ formats[1]
|
||||
@@{{variable}} = Time.parse_local(var, CliGenerator::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(::CliGenerator::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(::CliGenerator::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
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user