Compare commits

..

5 Commits
v0.1.1 ... main

Author SHA1 Message Date
Chris Watson 89516cc699
Merge pull request #2 from taupiqueur/fix-typo
Fix typo
2023-01-29 16:39:15 -07:00
Mathieu Ablasou 13aa2b3bae Fix typo 2023-01-29 17:51:24 +01:00
Chris Watson e6f7413674 fix for boolean flag at end 2023-01-28 14:37:38 -07:00
Chris Watson ef7a9f76dc add --error-trace to specs 2023-01-27 22:50:04 -07:00
Chris Watson 781b275d3e fix an issue related to parsing unions; add more tests 2023-01-27 22:36:53 -07:00
7 changed files with 77 additions and 22 deletions

View File

@ -21,4 +21,4 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: shards install run: shards install
- name: Run tests - name: Run tests
run: crystal spec run: crystal spec --error-trace

View File

@ -67,7 +67,7 @@ which implements `from_arg` as a proxy for that type.
## Converters ## Converters
Converers are simply modules which have a `self.from_arg` method which takes Converters are simply modules which have a `self.from_arg` method which takes
a value string, and returns the converted value. For Example: a value string, and returns the converted value. For Example:
``` ```

View File

@ -44,3 +44,16 @@ struct TestConverters
@[ArgParser::Field(converter: ArgParser::EnumValueConverter(Color))] @[ArgParser::Field(converter: ArgParser::EnumValueConverter(Color))]
getter ncolor : Color getter ncolor : Color
end end
struct TestValidators
include ArgParser
@[ArgParser::Validate::Format(/^[a-zA-Z]+$/)]
getter first_name : String
@[ArgParser::Validate::Format(/^[a-zA-Z]+$/)]
getter last_name : String?
@[ArgParser::Validate::InRange(1, 100)]
getter age : Int32
end

View File

@ -0,0 +1,7 @@
require "../spec_helper"
Spectator.describe "ArgParser from_arg" do
it "should parse a union" do
expect(Union(String, Nil).from_arg("foo")).to eq("foo")
end
end

View File

@ -0,0 +1,28 @@
require "../spec_helper"
Spectator.describe "ArgParser validators" do
let(valid_args) { ["positional stuff", "--first_name", "John", "--age", "32"] }
let(invalid_args) { ["positional stuff", "--first_name", "John^", "--last_name", "Doe1", "--age", "101"] }
context "valid arguments" do
it "should not raise an error" do
expect { TestValidators.new(valid_args) }.not_to raise_error
end
it "should set the name" do
opts = TestValidators.new(valid_args)
expect(opts.first_name).to eq("John")
end
it "should set the age" do
opts = TestValidators.new(valid_args)
expect(opts.age).to eq(32)
end
end
context "invalid arguments" do
it "should raise an error" do
expect { TestValidators.new(invalid_args) }.to raise_error(ArgParser::ValidationError)
end
end
end

View File

@ -65,34 +65,34 @@ module ArgParser
i = 0 i = 0
while !%args.empty? while !%args.empty?
arg = %args.shift arg = %args.shift
key = parse_key(arg) next unless key = parse_key(arg)
next unless key
# TODO: Find a different way to handle this
value = %args.shift rescue "true" value = %args.shift rescue "true"
if value.starts_with?("-") if value && parse_key(value)
%args.unshift(value) %args.unshift(value)
value = "true" value = "true"
end end
next unless value
case key case key
{% for name, value in properties %} {% for name, value in properties %}
when {{ value[:key].id.stringify }}{% if value[:alias] %}, {{ value[:alias].id.stringify }}{% end %} when {{ value[:key].id.stringify }}{% if value[:alias] %}, {{ value[:alias].id.stringify }}{% end %}
%found{name} = true %found{name} = true
begin begin
{% if value[:type] == String %} {% if value[:type] < Array %}
%var{name} = value
{% elsif value[:type] < Array %}
%var{name} ||= [] of {{value[:type].type_vars[0]}} %var{name} ||= [] of {{value[:type].type_vars[0]}}
{% if value[:converter] %} {% if value[:converter] %}
%var{name}.concat {{ value[:converter] }}.from_arg(value) %var{name}.concat {{ value[:converter] }}.from_arg(value)
{% else %} {% else %}
%var{name} << ::Union({{value[:type].type_vars[0]}}).from_arg(value) %var{name} << {{value[:type].type_vars[0]}}.from_arg(value)
{% end %} {% end %}
{% else %} {% else %}
{% if value[:converter] %} {% if value[:converter] %}
%var{name} = {{ value[:converter] }}.from_arg(value) %var{name} = {{ value[:converter] }}.from_arg(value)
{% else %} {% else %}
%var{name} = ::Union({{value[:type]}}).from_arg(value) %var{name} = {{value[:type]}}.from_arg(value)
{% end %} {% end %}
{% end %} {% end %}
rescue rescue
@ -106,7 +106,7 @@ module ArgParser
{% for name, value in properties %} {% for name, value in properties %}
{% unless value[:nilable] || value[:has_default] %} {% unless value[:nilable] || value[:has_default] %}
if %var{name}.nil? && !%found{name} && !::Union({{value[:type]}}).nilable? if %var{name}.nil? && !%found{name} && !{{value[:type]}}.nilable?
on_missing_attribute({{value[:key].id.stringify}}) on_missing_attribute({{value[:key].id.stringify}})
end end
{% end %} {% end %}
@ -145,8 +145,8 @@ module ArgParser
{% end %} {% end %}
%validator{name} = {{ validator.name(generic_args: false) }}.new({{ args.join(", ").id }}) %validator{name} = {{ validator.name(generic_args: false) }}.new({{ args.join(", ").id }})
if %found{name} && !%validator{name}.validate({{name.id.stringify}}, @{{name}}) if %found{name} && !%var{name}.nil? && !%validator{name}.validate({{name.id.stringify}}, %var{name})
on_validation_error({{name.stringify}}, @{{name}}, %validator{name}.errors) on_validation_error({{name.stringify}}, %var{name}, %validator{name}.errors)
end end
{% end %} {% end %}
{% end %} {% end %}
@ -154,8 +154,10 @@ module ArgParser
end end
# Parse the argument key. # Parse the argument key.
# Standard arg names start with a `--` # Standard arg names start with a `--.
# Aliases start with a single `-` # Aliases start with a single `-`.
#
# Arguments without a value become boolean true values.
# #
# Note: You can override this method to change the way keys are parsed. # Note: You can override this method to change the way keys are parsed.
def parse_key(arg : String) : String? def parse_key(arg : String) : String?

View File

@ -133,14 +133,19 @@ end
def Union.from_arg(arg : String) def Union.from_arg(arg : String)
{% begin %} {% begin %}
{% for type in T %} {% for type in T %}
{% if type != Nil %}
begin begin
%val = {{type}}.from_arg(arg) %val = {{type}}.from_arg(arg)
return %val if %val.is_a?({{type}}) return %val if %val.is_a?({{type}})
rescue rescue
end end
{% end %}
{% end %} {% end %}
{% if T.includes?(Nil) %}
nil
{% else %}
raise ArgParser::Error.new("Argument '#{arg}' cannot be converted to any of the union types: {{T}}") raise ArgParser::Error.new("Argument '#{arg}' cannot be converted to any of the union types: {{T}}")
{% end %} {% end %}
{% end %}
end end