command.pony

use "collections"

class box Command
  """
  Command contains all of the information describing a command with its spec
  and effective options and arguments, ready to use.
  """
  let _spec: CommandSpec box
  let _fullname: String
  let _options: Map[String, Option] box
  let _args: Map[String, Arg] box

  new _create(
    spec': CommandSpec box,
    fullname': String,
    options': Map[String, Option] box,
    args': Map[String, Arg] box)
  =>
    _spec = spec'
    _fullname = fullname'
    _options = options'
    _args = args'

  fun string(): String iso^ =>
    """
    Returns a representational string for this command.
    """
    let s: String iso = fullname().clone()
    for o in _options.values() do
      s.append(" ")
      s.append(o.deb_string())
    end
    for a in _args.values() do
      s.append(" ")
      s.append(a.deb_string())
    end
    s

  fun spec() : CommandSpec box =>
    """
    Returns the spec for this command.
    """
    _spec

  fun fullname() : String =>
    """
    Returns the full name of this command, with its parents prefixed.
    """
    _fullname

  fun option(name: String): Option =>
    """
    Returns the Option by name, defaulting to a fake Option if unknown.
    """
    try _options(name)? else Option(OptionSpec.bool(name), false) end

  fun arg(name: String): Arg =>
    """
    Returns the Arg by name, defaulting to a fake Arg if unknown.
    """
    try _args(name)? else Arg(ArgSpec.bool(name), false) end

class val Option
  """
  Option contains a spec and an effective value for a given option.
  """
  let _spec: OptionSpec
  let _value: _Value

  new val create(spec': OptionSpec, value': _Value) =>
    _spec = spec'
    _value = value'

  fun _append(next: Option): Option =>
    Option(_spec, _spec._typ_p().append(_value, next._value))

  fun spec() : OptionSpec => _spec

  fun bool(): Bool =>
    """
    Returns the option value as a Bool, defaulting to false.
    """
    try _value as Bool else false end

  fun string(): String =>
    """
    Returns the option value as a String, defaulting to empty.
    """
    try _value as String else "" end

  fun i64(): I64 =>
    """
    Returns the option value as an I64, defaulting to 0.
    """
    try _value as I64 else I64(0) end

  fun u64(): U64 =>
    """
    Returns the option value as an U64, defaulting to 0.
    """
    try _value as U64 else U64(0) end

  fun f64(): F64 =>
    """
    Returns the option value as an F64, defaulting to 0.0.
    """
    try _value as F64 else F64(0) end

  fun string_seq(): ReadSeq[String] val =>
    """
    Returns the option value as a ReadSeq[String], defaulting to empty.
    """
    try
      _value as _StringSeq val
    else
      recover val Array[String]() end
    end

  fun deb_string(): String =>
    _spec.deb_string() + "=" + _value.string()

class val Arg
  """
  Arg contains a spec and an effective value for a given arg.
  """
  let _spec: ArgSpec
  let _value: _Value

  new val create(spec': ArgSpec, value': _Value) =>
    _spec = spec'
    _value = value'

  fun _append(next: Arg): Arg =>
    Arg(_spec, _spec._typ_p().append(_value, next._value))

  fun spec(): ArgSpec => _spec

  fun bool(): Bool =>
    """
    Returns the arg value as a Bool, defaulting to false.
    """
    try _value as Bool else false end

  fun string(): String =>
    """
    Returns the arg value as a String, defaulting to empty.
    """
    try _value as String else "" end

  fun i64(): I64 =>
    """
    Returns the arg value as an I64, defaulting to 0.
    """
    try _value as I64 else I64(0) end

  fun u64(): U64 =>
    """
    Returns the arg value as an U64, defaulting to 0.
    """
    try _value as U64 else U64(0) end

  fun f64(): F64 =>
    """
    Returns the arg value as an F64, defaulting to 0.0.
    """
    try _value as F64 else F64(0) end

  fun string_seq(): ReadSeq[String] val =>
    """
    Returns the arg value as a ReadSeq[String], defaulting to empty.
    """
    try
      _value as _StringSeq val
    else
      recover val Array[String]() end
    end

  fun deb_string(): String =>
    "(" + _spec.deb_string() + "=)" + _value.string()

class val SyntaxError
  """
  SyntaxError summarizes a syntax error in a given parsed command line.
  """
  let _token: String
  let _msg: String

  new val create(token': String, msg': String) =>
    _token = token'
    _msg = msg'

  fun token(): String => _token

  fun string(): String => "Error: " + _msg + " at: '" + _token + "'"