Compare commits
5 Commits
71e10f10ea
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3217750714 | ||
|
|
613e059fb7 | ||
|
|
771934b486 | ||
|
|
37ea36cfdd | ||
|
|
3775096cc3 |
17
README.md
17
README.md
@@ -2,11 +2,24 @@
|
|||||||
|
|
||||||
This is a crystal project to manage setting up OptionParser objects based around "Command" objects and arguments you define inside them.
|
This is a crystal project to manage setting up OptionParser objects based around "Command" objects and arguments you define inside them.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Add the dependency to your `shard.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
cligen:
|
||||||
|
git: https://git.arcanium.tech/tristan/cligen
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Run `shards install`
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
```crystal
|
```crystal
|
||||||
require "cli_generator"
|
require "cligen"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
- [Tristan Ancelet](https://github.com/your-github-user) - creator and maintainer
|
- [Tristan Ancelet](https://git.arcanium.tech/tristan) - creator and maintainer
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
require "option_parser"
|
require "option_parser"
|
||||||
require "./regex"
|
require "./cligen/regex"
|
||||||
require "./format"
|
require "./cligen/format"
|
||||||
|
|
||||||
module CliGen
|
module CliGen
|
||||||
VERSION = "0.1.0"
|
VERSION = "0.1.0"
|
||||||
@@ -44,21 +44,21 @@ module CliGen
|
|||||||
{{parser}}.invalid_option{|opt|
|
{{parser}}.invalid_option{|opt|
|
||||||
case opt
|
case opt
|
||||||
when /^-+[a-z-]+/
|
when /^-+[a-z-]+/
|
||||||
Logger.debug "#{CliGen::APPNAME} : ERROR : {{parser}} : invalid_option : Inavlid option provided #{opt}"
|
STDERR.puts "#{CliGen::APPNAME} : ERROR : {{@type.name}} : {{parser}} : invalid_option : Inavlid option provided #{opt}"
|
||||||
else
|
else
|
||||||
abort "#{CliGen::APPNAME} : ERROR : {{parser}} : invalid_option : Inavlid option provided #{opt}"
|
abort "#{CliGen::APPNAME} : ERROR : {{@type.name}} : {{parser}} : invalid_option : Inavlid option provided #{opt}"
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
{{parser}}.missing_option{|opt|
|
{{parser}}.missing_option{|opt|
|
||||||
abort "ERROR : {{parser}} : missing_option : Argument was not provided to #{opt}"
|
abort "ERROR : {{@type.name}} : {{parser}} : missing_option : Argument was not provided to #{opt}"
|
||||||
}
|
}
|
||||||
{{parser}}.unknown_args{|opt|
|
{{parser}}.unknown_args{|opt|
|
||||||
unless opt.empty?
|
unless opt.empty?
|
||||||
case opt.first
|
case opt.first
|
||||||
when /^-+[a-z-]+$/
|
when /^-+[a-z-]+$/
|
||||||
Logger.debug "ERROR : {{parser}} : unknown_args : #{opt} not a configured argument"
|
STDERR.puts "ERROR : {{@type.name}} : {{parser}} : unknown_args : #{opt} not a configured argument"
|
||||||
else
|
else
|
||||||
abort "ERROR : {{parser}} : unknown_args : #{opt} not a configured argument"
|
abort "ERROR : {{@type.name}} : {{parser}} : unknown_args : #{opt} not a configured argument"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
@@ -100,4 +100,4 @@ module CliGen
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
require "./command"
|
require "./cligen/command"
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ module CliGen
|
|||||||
annotation CommandArgument
|
annotation CommandArgument
|
||||||
end
|
end
|
||||||
|
|
||||||
|
annotation CommandSelection
|
||||||
|
end
|
||||||
|
|
||||||
annotation SubCommand
|
annotation SubCommand
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -23,13 +26,18 @@ module CliGen
|
|||||||
|
|
||||||
|
|
||||||
macro inherited
|
macro inherited
|
||||||
|
{% verbatim do %}
|
||||||
macro finished
|
macro finished
|
||||||
define_actions
|
define_actions
|
||||||
define_header
|
define_header
|
||||||
|
{% anno = @type.annotation(::CliGen::CommandInfo) %}
|
||||||
|
{% unless anno[:def_runner] == false %}
|
||||||
define_runner
|
define_runner
|
||||||
|
{% end %}
|
||||||
define_parser
|
define_parser
|
||||||
define_action_setter
|
define_action_setter
|
||||||
end
|
end
|
||||||
|
{% end %}
|
||||||
end
|
end
|
||||||
|
|
||||||
macro define_actions
|
macro define_actions
|
||||||
@@ -40,6 +48,7 @@ module CliGen
|
|||||||
macro define_header
|
macro define_header
|
||||||
{% name = @type.name.split("::").last.downcase.id %}
|
{% name = @type.name.split("::").last.downcase.id %}
|
||||||
{% info_annos = @type.class.methods.select(&.annotation(::CliGen::SubCommand)).map(&.annotation(::CliGen::SubCommand)) %}
|
{% info_annos = @type.class.methods.select(&.annotation(::CliGen::SubCommand)).map(&.annotation(::CliGen::SubCommand)) %}
|
||||||
|
{% info_annos += @type.class.methods.select(&.annotation(::CliGen::CommandSelection)).map(&.annotation(::CliGen::CommandSelection)) %}
|
||||||
{% examples = [] of StringLiteral %}
|
{% examples = [] of StringLiteral %}
|
||||||
{% info_annos.select(&.[](:examples)).map(&.[](:examples).resolve).each(&.each{|example| examples << example}) %}
|
{% info_annos.select(&.[](:examples)).map(&.[](:examples).resolve).each(&.each{|example| examples << example}) %}
|
||||||
HEADER = [
|
HEADER = [
|
||||||
@@ -55,9 +64,28 @@ module CliGen
|
|||||||
].join("\n")
|
].join("\n")
|
||||||
end
|
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)
|
macro define_selection(selection_name, short = "", long = "", description = nil, default = "", values = [] of StringLiteral)
|
||||||
{% 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 %}
|
{% if values.is_a? Path %}
|
||||||
{% raise "define_argument : You MUST provide a description" unless description %}
|
{% values = values.resolve %}
|
||||||
|
{% end %}
|
||||||
|
{% raise "ERROR : define_selection : You MUST provide a description" unless description %}
|
||||||
|
{% raise "ERROR : define_selection : You must provide a valid set of values in Array(String) format" if values.empty? %}
|
||||||
|
{% description = "#{description.id} (valid: #{values.join(", ").id})" %}
|
||||||
|
{% unless default.empty? %}
|
||||||
|
{% description = "#{description.id} (default: #{default.id})" %}
|
||||||
|
{% end %}
|
||||||
|
@@{{selection_name}} : String = {{default}}
|
||||||
|
|
||||||
|
@[::CliGen::CommandArgument(short: {{short}}, long: {{long}}, description: {{description}} )]
|
||||||
|
def self.{{selection_name}}= (selection : String)
|
||||||
|
raise "ERROR : {{selection_name}} : Invalid Selection(#{selection}) (valid: {{values.map(&.id).join(", ").id}})" unless {{values}}.includes?(selection)
|
||||||
|
@@{{selection_name}} = selection
|
||||||
|
end
|
||||||
|
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, &block?)
|
||||||
|
{% raise "ERROR : 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 "ERROR : define_argument : You MUST provide a description" unless description %}
|
||||||
{% variable = arg_name unless variable %}
|
{% variable = arg_name unless variable %}
|
||||||
{% _type = type.id.stringify %}
|
{% _type = type.id.stringify %}
|
||||||
{% _subtype = subtype.id.stringify %}
|
{% _subtype = subtype.id.stringify %}
|
||||||
@@ -86,9 +114,6 @@ module CliGen
|
|||||||
{{check}}(var)
|
{{check}}(var)
|
||||||
{% end %}
|
{% 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 a logger method has been provided by the user
|
||||||
{% if logger %}
|
{% if logger %}
|
||||||
{{logger.id}} "Command : subclass : argument_setter : {{arg_name}} : Entered with #{var}"
|
{{logger.id}} "Command : subclass : argument_setter : {{arg_name}} : Entered with #{var}"
|
||||||
@@ -132,13 +157,22 @@ module CliGen
|
|||||||
{% end %}
|
{% end %}
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
|
{% selections = @type.class.methods.select(&.annotation(::CliGen::CommandSelection)) %}
|
||||||
{% methods = @type.class.methods.select(&.annotation(::CliGen::SubCommand)) %}
|
{% methods = @type.class.methods.select(&.annotation(::CliGen::SubCommand)) %}
|
||||||
{% raise "ERROR : No commands defined for #{@type.name}" if methods.empty? %}
|
{% raise "ERROR : No commands or selections defined for #{@type.name}" if methods.empty? && selections.empty? %}
|
||||||
|
{% raise "ERROR : Can't define both selections and commands for work in #{@type.name}" if ! methods.empty? && ! selections.empty? %}
|
||||||
|
{% var = methods.empty? ? nil : "action" %}
|
||||||
|
{% targets = methods.empty? ? selections : methods %}
|
||||||
|
{% if targets == selections %}
|
||||||
|
{% selectors = selections.map(&.annotation(::CliGen::CommandSelection)).map(&.[:selector]).uniq %}
|
||||||
|
{% raise "ERROR : Can't provide more than a single selector for runner" if selectors.size > 1 %}
|
||||||
|
{% var = selectors.first %}
|
||||||
|
{% end %}
|
||||||
{% begin %}
|
{% begin %}
|
||||||
case @@action
|
case @@{{var.id}}
|
||||||
{% for method in methods %}
|
{% for target in targets %}
|
||||||
when {{method.name.stringify}}
|
when {{target.name.stringify}}
|
||||||
{{method.name}}
|
{{target.name}}
|
||||||
{% end %}
|
{% end %}
|
||||||
else
|
else
|
||||||
abort "ERROR : No action provided to command {{@type.name}}"
|
abort "ERROR : No action provided to command {{@type.name}}"
|
||||||
@@ -7,11 +7,24 @@ module CliGen::Parser
|
|||||||
{% puts "#{@type.name} OptionParser is being generated" %}
|
{% puts "#{@type.name} OptionParser is being generated" %}
|
||||||
{% name = @type.name.split("::").last %}
|
{% name = @type.name.split("::").last %}
|
||||||
{% var = name.downcase %}
|
{% var = name.downcase %}
|
||||||
|
{% methods = @type.class.methods %}
|
||||||
{% info = @type.annotation(::CliGen::CommandInfo) %}
|
{% info = @type.annotation(::CliGen::CommandInfo) %}
|
||||||
{% raise "ERROR : No CommandInfo annotation provided to {{@type.name}}" unless info %}
|
{% raise "ERROR : No CommandInfo annotation provided to {{@type.name}}" unless info %}
|
||||||
subparser = OptionParser.new do |parser|
|
subparser = OptionParser.new do |parser|
|
||||||
parser.banner = {{@type.name}}::HEADER
|
parser.banner = {{@type.name}}::HEADER
|
||||||
{% subcommands = @type.class.methods.select(&.annotation(::CliGen::SubCommand)) %}
|
|
||||||
|
{% selections = methods.select(&.annotation(::CliGen::CommandSelection)) %}
|
||||||
|
{% if selections.size > 0 %}
|
||||||
|
CliGen.define_section("Selections", parser)
|
||||||
|
{% for selection in selections %}
|
||||||
|
{% selection_anno = selection.annotation(::CliGen::CommandSelection) %}
|
||||||
|
parser.on({{selection.name.stringify}}, {{selection_anno[:description]}}){
|
||||||
|
{{@type.name}}.{{selection_anno[:selector]}}= {{selection.name.stringify}}
|
||||||
|
}
|
||||||
|
{% end %}
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
{% subcommands = methods.select(&.annotation(::CliGen::SubCommand)) %}
|
||||||
{% if subcommands.size > 0 %}
|
{% if subcommands.size > 0 %}
|
||||||
CliGen.define_section("Subcommands", parser)
|
CliGen.define_section("Subcommands", parser)
|
||||||
{% for subcommand in subcommands %}
|
{% for subcommand in subcommands %}
|
||||||
@@ -22,7 +35,7 @@ module CliGen::Parser
|
|||||||
{% end %}
|
{% end %}
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
{% arguments = @type.class.methods.select(&.annotation(::CliGen::CommandArgument)) %}
|
{% arguments = methods.select(&.annotation(::CliGen::CommandArgument)) %}
|
||||||
{% if arguments.size > 0 %}
|
{% if arguments.size > 0 %}
|
||||||
CliGen.define_section("Provide Arguments", parser)
|
CliGen.define_section("Provide Arguments", parser)
|
||||||
{% for argument in arguments %}
|
{% for argument in arguments %}
|
||||||
@@ -41,7 +54,10 @@ module CliGen::Parser
|
|||||||
CliGen.define_default_flags(parser)
|
CliGen.define_default_flags(parser)
|
||||||
end
|
end
|
||||||
parent_parser.on({{var}}, {{info[:description]}}){
|
parent_parser.on({{var}}, {{info[:description]}}){
|
||||||
ARGV.delete({{var}})
|
raise "ERROR : {{var.id}} not found in ARGV" unless i = ARGV.index({{var}})
|
||||||
|
## Removing all preceeding arguments so that only the actual required
|
||||||
|
## args for the next command are present
|
||||||
|
ARGV.shift(i+1)
|
||||||
abort subparser if ARGV.empty?
|
abort subparser if ARGV.empty?
|
||||||
subparser.parse
|
subparser.parse
|
||||||
{{@type.name}}.run
|
{{@type.name}}.run
|
||||||
Reference in New Issue
Block a user