class Cli

class Cli

lib/cli.tya:2

Cli provides the cli/Cli standard library API.

Source
# Cli provides the cli/Cli standard library API.
class Cli
  # Cli.value stores instance state.
  # @type Nil
  value: nil

  # Cli.initialize stores command-line args or a command name.
  # @param value String value value.
  # @return Self the initialized object.
  initialize: value ->
    self.value = value

  # Cli.apply_defaults provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @return Any the resulting value.
  apply_defaults: state ->
    options = Cli(nil).spec_options(state["spec"])
    names = options.keys()
    for name in names
      opt = options[name]
      if opt.has("default")
        state["options"][name] = opt["default"]
      else
        typ = Cli(nil).option_type(opt)
        if typ == "bool"
          state["options"][name] = false
        elseif typ == "array"
          state["options"][name] = []

  # Cli.assign_value provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @param name String name value.
  # @param opt Any opt value.
  # @param value String value value.
  # @return Any the resulting value.
  assign_value: state, name, opt, value ->
    typ = Cli(nil).option_type(opt)
    if typ == "string"
      state["options"][name] = value
    elseif typ == "int"
      try
        state["options"][name] = value.to_i()
      catch _
        Cli(nil).error(state, "invalid_value", name, "--{name} expects int")
    else
      if typ == "float"
        try
          state["options"][name] = value.to_f()
        catch _
          Cli(nil).error(state, "invalid_value", name, "--{name} expects float")
      elseif typ == "array"
        if not state["options"].has(name)
          state["options"][name] = []
        state["options"][name].push(value)
      else
        Cli(nil).error(
          state,
          "invalid_type",
          name,
          "unsupported option type {typ}"
        )

  # Cli.check_required provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @return Any the resulting value.
  check_required: state ->
    options = Cli(nil).spec_options(state["spec"])
    names = options.keys().sort()
    for name in names
      opt = options[name]
      if (
        Cli(nil).spec_bool(opt, "required", false) and not state["options"].has(name)
      )
        Cli(nil).error(state, "required", name, "--{name} is required")

  # Cli.error provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @param kind Any kind value.
  # @param name String name value.
  # @param message String message value.
  # @return Any the resulting value.
  error: state, kind, name, message ->
    state["errors"].push({ kind: kind, option: name, message: message })

  # Cli.index_of provides the cli/Cli standard library operation.
  # @param text String text value.
  # @param needle Any needle value.
  # @return Any the resulting value.
  index_of: text, needle ->
    i = 0
    while i < text.byte_len()
      if text[i] == needle
        return i
      i = i + 1
    -1

  # Cli.name_for_alias provides the cli/Cli standard library operation.
  # @param spec Any spec value.
  # @param alias Any alias value.
  # @return Any the resulting value.
  name_for_alias: spec, alias ->
    options = Cli(nil).spec_options(spec)
    names = options.keys()
    for name in names
      opt = options[name]
      if Cli(nil).spec_string(opt, "alias", "") == alias
        return name
    ""

  # Cli.option_type provides the cli/Cli standard library operation.
  # @param opt Any opt value.
  # @return Any the resulting value.
  option_type: opt ->
    Cli(nil).spec_string(opt, "type", "string")

  # Cli.parse provides the cli/Cli standard library operation.
  # @param args Array args value.
  # @param spec Any spec value.
  # @return Any the resulting value.
  parse: args, spec = nil ->
    if self.value != nil
      spec = args
      args = self.value
    state =
      args: args
      spec: spec
      options: {}
      positionals: []
      rest: []
      errors: []
      stop_options: false
    Cli(nil).apply_defaults(state)
    i = 0
    while i < args.len()
      arg = args[i]
      if state["stop_options"]
        state["rest"].push(arg)
      elseif arg == "--"
        state["stop_options"] = true
        if Cli(nil).spec_bool(spec, "stop_at_double_dash", true)
          j = i + 1
          while j < args.len()
            state["rest"].push(args[j])
            j = j + 1
          i = args.len()
      else
        if arg.starts_with("--") and arg.byte_len() > 2
          i = Cli(nil).parse_long(state, i)
        elseif arg.starts_with("-") and arg != "-"
          i = Cli(nil).parse_short(state, i)
        else
          state["positionals"].push(arg)
      i = i + 1
    Cli(nil).check_required(state)
    result =
      options: state["options"]
      positionals: state["positionals"]
      rest: state["rest"]
      errors: state["errors"]
    result

  # Cli.parse_bool_value provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @param name String name value.
  # @param value String value value.
  # @return Any the resulting value.
  parse_bool_value: state, name, value ->
    if value == "true" or value == "1" or value == "yes"
      return true
    if value == "false" or value == "0" or value == "no"
      return false
    Cli(nil).error(state, "invalid_value", name, "--{name} expects bool")
    false

  # Cli.parse_long provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @param i Int i value.
  # @return Any the resulting value.
  parse_long: state, i ->
    arg = state["args"][i]
    raw = arg.slice(2, arg.len())
    eq = Cli(nil).index_of(raw, "=")
    name = raw
    value = nil
    has_value = false
    if eq >= 0
      name = raw.slice(0, eq)
      value = raw.slice(eq + 1, raw.len())
      has_value = true
    negated = false
    if name.starts_with("no-")
      negated = true
      name = name.slice(3, name.len())
    options = Cli(nil).spec_options(state["spec"])
    if not options.has(name)
      Cli(nil).unknown(state, arg)
      return i
    opt = options[name]
    typ = Cli(nil).option_type(opt)
    if typ == "bool"
      if negated
        state["options"][name] = false
      elseif has_value
        state["options"][name] = Cli(nil).parse_bool_value(state, name, value)
      else
        state["options"][name] = true
      return i
    if negated
      Cli(nil).error(
        state,
        "invalid_option",
        name,
        "--no-{name} is only valid for bool options"
      )
      return i
    if not has_value
      if i + 1 >= state["args"].len()
        Cli(nil).error(
          state,
          "missing_value",
          name,
          "--{name} requires a value"
        )
        return i
      i = i + 1
      value = state["args"][i]
    Cli(nil).assign_value(state, name, opt, value)
    i

  # Cli.parse_or_exit provides the cli/Cli standard library operation.
  # @param args Array args value.
  # @param spec Any spec value.
  # @return Any the resulting value.
  parse_or_exit: args, spec = nil ->
    if self.value != nil
      spec = args
      args = self.value
    result = Cli(args).parse(spec)
    if result["errors"].len() > 0
      command = Cli(nil).spec_string(spec, "command", "usage")
      println(Cli(nil).usage(command, spec))
      for err in result["errors"]
        println(err["message"])
      exit(1)
    result

  # Cli.parse_short provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @param i Int i value.
  # @return Any the resulting value.
  parse_short: state, i ->
    arg = state["args"][i]
    raw = arg.slice(1, arg.len())
    j = 0
    while j < raw.byte_len()
      alias = raw[j]
      name = Cli(nil).name_for_alias(state["spec"], alias)
      if name == ""
        Cli(nil).unknown(state, "-{alias}")
        j = j + 1
      else
        opt = Cli(nil).spec_options(state["spec"])[name]
        typ = Cli(nil).option_type(opt)
        if typ == "bool"
          state["options"][name] = true
          j = j + 1
        else
          if j + 1 < raw.byte_len()
            value = raw.slice(j + 1, raw.len())
            Cli(nil).assign_value(state, name, opt, value)
            return i
          if i + 1 >= state["args"].len()
            Cli(nil).error(
              state,
              "missing_value",
              name,
              "-{alias} requires a value"
            )
            return i
          i = i + 1
          Cli(nil).assign_value(state, name, opt, state["args"][i])
          return i
    i

  # Cli.spec_bool provides the cli/Cli standard library operation.
  # @param spec Any spec value.
  # @param name String name value.
  # @param fallback Any fallback value.
  # @return Any the resulting value.
  spec_bool: spec, name, fallback ->
    if spec.has(name)
      return spec[name]
    fallback

  # Cli.spec_options provides the cli/Cli standard library operation.
  # @param spec Any spec value.
  # @return Dict the resulting value.
  spec_options: spec ->
    if spec.has("options")
      return spec["options"]
    {}

  # Cli.spec_string provides the cli/Cli standard library operation.
  # @param spec Any spec value.
  # @param name String name value.
  # @param fallback Any fallback value.
  # @return String the resulting value.
  spec_string: spec, name, fallback ->
    if spec.has(name)
      return spec[name]
    fallback

  # Cli.unknown provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @param raw Any raw value.
  # @return Any the resulting value.
  unknown: state, raw ->
    if Cli(nil).spec_bool(state["spec"], "allow_unknown", false)
      state["rest"].push(raw)
    else
      Cli(nil).error(state, "unknown_option", raw, "unknown option {raw}")

  # Cli.usage provides the cli/Cli standard library operation.
  # @param command Any command value.
  # @param spec Any spec value.
  # @return String the resulting value.
  usage: command, spec = nil ->
    if self.value != nil
      spec = command
      command = self.value
    parts = ["Usage: {command}"]
    options = Cli(nil).spec_options(spec)
    names = options.keys().sort()
    if names.len() > 0
      parts.push("[options]")
    if spec.has("positionals")
      for pos in spec["positionals"]
        if pos.class == String
          parts.push("<{pos}>")
        else
          name = Cli(nil).spec_string(pos, "name", "arg")
          required = Cli(nil).spec_bool(pos, "required", true)
          if required
            parts.push("<{name}>")
          else
            parts.push("[{name}]")
    out = parts.join(" ")
    if names.len() > 0
      out = out + "\nOptions:"
      for name in names
        opt = options[name]
        line = "  --{name}"
        alias = Cli(nil).spec_string(opt, "alias", "")
        if alias != ""
          line = "  -{alias}, --{name}"
        typ = Cli(nil).option_type(opt)
        if typ != "bool"
          line = line + " <{typ}>"
        markers = []
        if Cli(nil).spec_bool(opt, "required", false)
          markers.push("required")
        if opt.has("default")
          default_value = opt["default"]
          markers.push("default: {default_value}")
        if markers.len() > 0
          line = line + " (" + markers.join(", ") + ")"
        if opt.has("help")
          line = line + "  " + opt["help"]
        out = out + "\n" + line
    out

Instance Variables

value

Cli.value

lib/cli.tya:5

Cli.value stores instance state.

Source
  # Cli.value stores instance state.
  # @type Nil
  value: nil

Methods

apply_defaults

Cli.apply_defaults(state)

lib/cli.tya:16

Cli.apply_defaults provides the cli/Cli standard library operation.

Source
  # Cli.apply_defaults provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @return Any the resulting value.
  apply_defaults: state ->
    options = Cli(nil).spec_options(state["spec"])
    names = options.keys()
    for name in names
      opt = options[name]
      if opt.has("default")
        state["options"][name] = opt["default"]
      else
        typ = Cli(nil).option_type(opt)
        if typ == "bool"
          state["options"][name] = false
        elseif typ == "array"
          state["options"][name] = []

assign_value

Cli.assign_value(state, name, opt, value)

lib/cli.tya:36

Cli.assign_value provides the cli/Cli standard library operation.

Source
  # Cli.assign_value provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @param name String name value.
  # @param opt Any opt value.
  # @param value String value value.
  # @return Any the resulting value.
  assign_value: state, name, opt, value ->
    typ = Cli(nil).option_type(opt)
    if typ == "string"
      state["options"][name] = value
    elseif typ == "int"
      try
        state["options"][name] = value.to_i()
      catch _
        Cli(nil).error(state, "invalid_value", name, "--{name} expects int")
    else
      if typ == "float"
        try
          state["options"][name] = value.to_f()
        catch _
          Cli(nil).error(state, "invalid_value", name, "--{name} expects float")
      elseif typ == "array"
        if not state["options"].has(name)
          state["options"][name] = []
        state["options"][name].push(value)
      else
        Cli(nil).error(
          state,
          "invalid_type",
          name,
          "unsupported option type {typ}"
        )

check_required

Cli.check_required(state)

lib/cli.tya:66

Cli.check_required provides the cli/Cli standard library operation.

Source
  # Cli.check_required provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @return Any the resulting value.
  check_required: state ->
    options = Cli(nil).spec_options(state["spec"])
    names = options.keys().sort()
    for name in names
      opt = options[name]
      if (
        Cli(nil).spec_bool(opt, "required", false) and not state["options"].has(name)
      )
        Cli(nil).error(state, "required", name, "--{name} is required")

error

Cli.error(state, kind, name, message)

lib/cli.tya:82

Cli.error provides the cli/Cli standard library operation.

Source
  # Cli.error provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @param kind Any kind value.
  # @param name String name value.
  # @param message String message value.
  # @return Any the resulting value.
  error: state, kind, name, message ->
    state["errors"].push({ kind: kind, option: name, message: message })

index_of

Cli.index_of(text, needle)

lib/cli.tya:89

Cli.index_of provides the cli/Cli standard library operation.

Source
  # Cli.index_of provides the cli/Cli standard library operation.
  # @param text String text value.
  # @param needle Any needle value.
  # @return Any the resulting value.
  index_of: text, needle ->
    i = 0
    while i < text.byte_len()
      if text[i] == needle
        return i
      i = i + 1
    -1

initialize

Cli.initialize(value)

lib/cli.tya:10

Cli.initialize stores command-line args or a command name.

Source
  # Cli.initialize stores command-line args or a command name.
  # @param value String value value.
  # @return Self the initialized object.
  initialize: value ->
    self.value = value

name_for_alias

Cli.name_for_alias(spec, alias)

lib/cli.tya:101

Cli.name_for_alias provides the cli/Cli standard library operation.

Source
  # Cli.name_for_alias provides the cli/Cli standard library operation.
  # @param spec Any spec value.
  # @param alias Any alias value.
  # @return Any the resulting value.
  name_for_alias: spec, alias ->
    options = Cli(nil).spec_options(spec)
    names = options.keys()
    for name in names
      opt = options[name]
      if Cli(nil).spec_string(opt, "alias", "") == alias
        return name
    ""

option_type

Cli.option_type(opt)

lib/cli.tya:113

Cli.option_type provides the cli/Cli standard library operation.

Source
  # Cli.option_type provides the cli/Cli standard library operation.
  # @param opt Any opt value.
  # @return Any the resulting value.
  option_type: opt ->
    Cli(nil).spec_string(opt, "type", "string")

parse

Cli.parse(args, spec = nil)

lib/cli.tya:120

Cli.parse provides the cli/Cli standard library operation.

Source
  # Cli.parse provides the cli/Cli standard library operation.
  # @param args Array args value.
  # @param spec Any spec value.
  # @return Any the resulting value.
  parse: args, spec = nil ->
    if self.value != nil
      spec = args
      args = self.value
    state =
      args: args
      spec: spec
      options: {}
      positionals: []
      rest: []
      errors: []
      stop_options: false
    Cli(nil).apply_defaults(state)
    i = 0
    while i < args.len()
      arg = args[i]
      if state["stop_options"]
        state["rest"].push(arg)
      elseif arg == "--"
        state["stop_options"] = true
        if Cli(nil).spec_bool(spec, "stop_at_double_dash", true)
          j = i + 1
          while j < args.len()
            state["rest"].push(args[j])
            j = j + 1
          i = args.len()
      else
        if arg.starts_with("--") and arg.byte_len() > 2
          i = Cli(nil).parse_long(state, i)
        elseif arg.starts_with("-") and arg != "-"
          i = Cli(nil).parse_short(state, i)
        else
          state["positionals"].push(arg)
      i = i + 1
    Cli(nil).check_required(state)
    result =
      options: state["options"]
      positionals: state["positionals"]
      rest: state["rest"]
      errors: state["errors"]
    result

parse_bool_value

Cli.parse_bool_value(state, name, value)

lib/cli.tya:167

Cli.parse_bool_value provides the cli/Cli standard library operation.

Source
  # Cli.parse_bool_value provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @param name String name value.
  # @param value String value value.
  # @return Any the resulting value.
  parse_bool_value: state, name, value ->
    if value == "true" or value == "1" or value == "yes"
      return true
    if value == "false" or value == "0" or value == "no"
      return false
    Cli(nil).error(state, "invalid_value", name, "--{name} expects bool")
    false

parse_long

Cli.parse_long(state, i)

lib/cli.tya:179

Cli.parse_long provides the cli/Cli standard library operation.

Source
  # Cli.parse_long provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @param i Int i value.
  # @return Any the resulting value.
  parse_long: state, i ->
    arg = state["args"][i]
    raw = arg.slice(2, arg.len())
    eq = Cli(nil).index_of(raw, "=")
    name = raw
    value = nil
    has_value = false
    if eq >= 0
      name = raw.slice(0, eq)
      value = raw.slice(eq + 1, raw.len())
      has_value = true
    negated = false
    if name.starts_with("no-")
      negated = true
      name = name.slice(3, name.len())
    options = Cli(nil).spec_options(state["spec"])
    if not options.has(name)
      Cli(nil).unknown(state, arg)
      return i
    opt = options[name]
    typ = Cli(nil).option_type(opt)
    if typ == "bool"
      if negated
        state["options"][name] = false
      elseif has_value
        state["options"][name] = Cli(nil).parse_bool_value(state, name, value)
      else
        state["options"][name] = true
      return i
    if negated
      Cli(nil).error(
        state,
        "invalid_option",
        name,
        "--no-{name} is only valid for bool options"
      )
      return i
    if not has_value
      if i + 1 >= state["args"].len()
        Cli(nil).error(
          state,
          "missing_value",
          name,
          "--{name} requires a value"
        )
        return i
      i = i + 1
      value = state["args"][i]
    Cli(nil).assign_value(state, name, opt, value)
    i

parse_or_exit

Cli.parse_or_exit(args, spec = nil)

lib/cli.tya:234

Cli.parse_or_exit provides the cli/Cli standard library operation.

Source
  # Cli.parse_or_exit provides the cli/Cli standard library operation.
  # @param args Array args value.
  # @param spec Any spec value.
  # @return Any the resulting value.
  parse_or_exit: args, spec = nil ->
    if self.value != nil
      spec = args
      args = self.value
    result = Cli(args).parse(spec)
    if result["errors"].len() > 0
      command = Cli(nil).spec_string(spec, "command", "usage")
      println(Cli(nil).usage(command, spec))
      for err in result["errors"]
        println(err["message"])
      exit(1)
    result

parse_short

Cli.parse_short(state, i)

lib/cli.tya:251

Cli.parse_short provides the cli/Cli standard library operation.

Source
  # Cli.parse_short provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @param i Int i value.
  # @return Any the resulting value.
  parse_short: state, i ->
    arg = state["args"][i]
    raw = arg.slice(1, arg.len())
    j = 0
    while j < raw.byte_len()
      alias = raw[j]
      name = Cli(nil).name_for_alias(state["spec"], alias)
      if name == ""
        Cli(nil).unknown(state, "-{alias}")
        j = j + 1
      else
        opt = Cli(nil).spec_options(state["spec"])[name]
        typ = Cli(nil).option_type(opt)
        if typ == "bool"
          state["options"][name] = true
          j = j + 1
        else
          if j + 1 < raw.byte_len()
            value = raw.slice(j + 1, raw.len())
            Cli(nil).assign_value(state, name, opt, value)
            return i
          if i + 1 >= state["args"].len()
            Cli(nil).error(
              state,
              "missing_value",
              name,
              "-{alias} requires a value"
            )
            return i
          i = i + 1
          Cli(nil).assign_value(state, name, opt, state["args"][i])
          return i
    i

spec_bool

Cli.spec_bool(spec, name, fallback)

lib/cli.tya:290

Cli.spec_bool provides the cli/Cli standard library operation.

Source
  # Cli.spec_bool provides the cli/Cli standard library operation.
  # @param spec Any spec value.
  # @param name String name value.
  # @param fallback Any fallback value.
  # @return Any the resulting value.
  spec_bool: spec, name, fallback ->
    if spec.has(name)
      return spec[name]
    fallback

spec_options

Cli.spec_options(spec)

lib/cli.tya:298

Cli.spec_options provides the cli/Cli standard library operation.

Source
  # Cli.spec_options provides the cli/Cli standard library operation.
  # @param spec Any spec value.
  # @return Dict the resulting value.
  spec_options: spec ->
    if spec.has("options")
      return spec["options"]
    {}

spec_string

Cli.spec_string(spec, name, fallback)

lib/cli.tya:308

Cli.spec_string provides the cli/Cli standard library operation.

Source
  # Cli.spec_string provides the cli/Cli standard library operation.
  # @param spec Any spec value.
  # @param name String name value.
  # @param fallback Any fallback value.
  # @return String the resulting value.
  spec_string: spec, name, fallback ->
    if spec.has(name)
      return spec[name]
    fallback

unknown

Cli.unknown(state, raw)

lib/cli.tya:317

Cli.unknown provides the cli/Cli standard library operation.

Source
  # Cli.unknown provides the cli/Cli standard library operation.
  # @param state Any state value.
  # @param raw Any raw value.
  # @return Any the resulting value.
  unknown: state, raw ->
    if Cli(nil).spec_bool(state["spec"], "allow_unknown", false)
      state["rest"].push(raw)
    else
      Cli(nil).error(state, "unknown_option", raw, "unknown option {raw}")

usage

Cli.usage(command, spec = nil)

lib/cli.tya:327

Cli.usage provides the cli/Cli standard library operation.

Source
  # Cli.usage provides the cli/Cli standard library operation.
  # @param command Any command value.
  # @param spec Any spec value.
  # @return String the resulting value.
  usage: command, spec = nil ->
    if self.value != nil
      spec = command
      command = self.value
    parts = ["Usage: {command}"]
    options = Cli(nil).spec_options(spec)
    names = options.keys().sort()
    if names.len() > 0
      parts.push("[options]")
    if spec.has("positionals")
      for pos in spec["positionals"]
        if pos.class == String
          parts.push("<{pos}>")
        else
          name = Cli(nil).spec_string(pos, "name", "arg")
          required = Cli(nil).spec_bool(pos, "required", true)
          if required
            parts.push("<{name}>")
          else
            parts.push("[{name}]")
    out = parts.join(" ")
    if names.len() > 0
      out = out + "\nOptions:"
      for name in names
        opt = options[name]
        line = "  --{name}"
        alias = Cli(nil).spec_string(opt, "alias", "")
        if alias != ""
          line = "  -{alias}, --{name}"
        typ = Cli(nil).option_type(opt)
        if typ != "bool"
          line = line + " <{typ}>"
        markers = []
        if Cli(nil).spec_bool(opt, "required", false)
          markers.push("required")
        if opt.has("default")
          default_value = opt["default"]
          markers.push("default: {default_value}")
        if markers.len() > 0
          line = line + " (" + markers.join(", ") + ")"
        if opt.has("help")
          line = line + "  " + opt["help"]
        out = out + "\n" + line
    out