Skip to content

Instantly share code, notes, and snippets.

@yorickpeterse
Created March 30, 2023 01:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yorickpeterse/9051a6259fd550f7d6ff29a22d03938b to your computer and use it in GitHub Desktop.
Save yorickpeterse/9051a6259fd550f7d6ff29a22d03938b to your computer and use it in GitHub Desktop.
import std::fmt::(Formatter, Format)
import std::test::Tests
import std::cmp::Equal
let DASH = 45
class pub enum Argument {
case Positional(String)
case Long(String, Option[String])
case Short(String, Option[String])
}
impl Format for Argument {
fn pub fmt(formatter: mut Formatter) {
match self {
case Positional(val) -> {
formatter.write('Positional(')
val.fmt(formatter)
formatter.write(')')
}
case Long(name, val) -> {
formatter.write('Long(')
name.fmt(formatter)
formatter.write(', ')
val.fmt(formatter)
formatter.write(')')
}
case Short(name, val) -> {
formatter.write('Short(')
name.fmt(formatter)
formatter.write(', ')
val.fmt(formatter)
formatter.write(')')
}
}
}
}
impl Equal for Argument {
fn pub ==(other: ref Argument) -> Bool {
match (self, other) {
case (Positional(a), Positional(b)) -> a == b
case (Long(name1, val1), Long(name2, val2)) -> {
name1 == name2 and val1 == val2
}
case (Short(name1, val1), Short(name2, val2)) -> {
name1 == name2 and val1 == val2
}
case _ -> false
}
}
}
class pub Parser {
let @arguments: Array[String]
fn pub static new(arguments: Array[String]) -> Parser {
Parser { @arguments = arguments }
}
fn pub parse -> Array[Argument] {
@arguments
.iter
.map fn (raw) {
match raw.byte(0) {
case DASH -> option(raw)
case _ -> Argument.Positional(raw)
}
}
.to_array
}
fn option(raw: String) -> Argument {
if raw.size == 1 or (raw.size == 2 and raw.byte(1) == DASH) {
return Argument.Positional(raw)
}
let equal = match raw.byte_index(of: '=', starting_at: 1) {
case Some(index) -> index
case _ -> 0
}
let value = if equal > 0 {
Option.Some(raw.slice_bytes(start: equal + 1, length: raw.size))
} else {
Option.None
}
let name_size = if equal > 0 { equal } else { raw.size }
if raw.byte(1) == DASH {
Argument.Long(raw.slice_bytes(start: 2, length: name_size - 2), value)
} else {
Argument.Short(raw.slice_bytes(start: 1, length: name_size - 1), value)
}
}
}
fn parse(values: Array[String]) -> Array[Argument] {
Parser.new(values).parse
}
fn tests(t: mut Tests) {
t.test('Positional arguments') fn (t) {
t.equal(parse([]), [])
t.equal(parse(['foo']), [Argument.Positional('foo')])
t.equal(
parse(['foo', 'bar']),
[Argument.Positional('foo'), Argument.Positional('bar')]
)
t.equal(
parse(['-', '--']),
[Argument.Positional('-'), Argument.Positional('--')]
)
}
t.test('Short options') fn (t) {
t.equal(parse(['-n']), [Argument.Short('n', Option.None)])
t.equal(parse(['-n=4']), [Argument.Short('n', Option.Some('4'))])
t.equal(parse(['-n=']), [Argument.Short('n', Option.Some(''))])
t.equal(parse(['-=']), [Argument.Short('', Option.Some(''))])
}
t.test('Long options') fn (t) {
t.equal(parse(['--n']), [Argument.Long('n', Option.None)])
t.equal(parse(['--n=4']), [Argument.Long('n', Option.Some('4'))])
t.equal(parse(['--n=']), [Argument.Long('n', Option.Some(''))])
t.equal(parse(['--=']), [Argument.Long('', Option.Some(''))])
}
}
class async Main {
fn async main {
let t = Tests.new
tests(t)
t.run
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment