///|
/// Returns a flag's value, or false when it was not provided.
///
/// ```mbt check
/// test {
/// let app = @cmder.cli(name="app", options=[@cmder.bool("verbose")])
/// guard app.parse(argv=["--verbose"]) is Parsed(call) else { fail("parse") }
/// assert_true(call.context.get_bool("verbose"))
/// }
/// ```
pub fn Context::get_bool(self : Context, name : String) -> Bool {
self.flags.get(name).unwrap_or(false)
}
///|
/// Returns the first value, or `None` if the name is absent.
///
/// ```mbt check
/// test {
/// let app = @cmder.cli(name="app", options=[
/// @cmder.string("name", default="Ada"),
/// ])
/// guard app.parse(argv=[]) is Parsed(call) else { fail("parse") }
/// assert_true(call.context.get_string("name") == Some("Ada"))
/// }
/// ```
pub fn Context::get_string(self : Context, name : String) -> String? {
match self.values.get(name) {
Some(arr) => if arr.length() > 0 { Some(arr[0]) } else { None }
None => None
}
}
///|
/// Reads declared text, raising MissingOption/MissingPositional if absent,
/// UnknownArgument for an unknown name, or ArgumentTypeMismatch for a flag.
///
/// ```mbt check
/// test {
/// let app = @cmder.cli(name="app", options=[
/// @cmder.string("name", default="Ada"),
/// ])
/// guard app.parse(argv=[]) is Parsed(call) else { fail("parse") }
/// inspect(call.context.get_string_required("name"), content="Ada")
/// }
/// ```
pub fn Context::get_string_required(
self : Context,
name : String,
) -> String raise CliError {
match self.get_string_checked(name) {
Some(v) => v
None => raise self.missing_value(name)
}
}
///|
/// Reads a signed decimal Int32. Only absence returns None; malformed input
/// raises InvalidInteger and overflow raises IntegerOutOfRange. Leading zeroes
/// have no length limit. Declared integer options are already checked by parse.
/// Unknown names and Boolean flags raise UnknownArgument/ArgumentTypeMismatch.
///
/// ```mbt check
/// test {
/// let app = @cmder.cli(name="app", options=[@cmder.int("count", default=3)])
/// guard app.parse(argv=[]) is Parsed(call) else { fail("parse") }
/// assert_true(call.context.get_int("count") == Some(3))
/// }
/// ```
pub fn Context::get_int(self : Context, name : String) -> Int32? raise CliError {
guard self.get_string_checked(name) is Some(value) else { return None }
Some(parse_integer_value(name, value))
}
///|
/// Parses required declared text as an integer. MissingOption/MissingPositional
/// distinguish absent values; name, kind and integer errors remain checked.
/// No application range is imposed.
///
/// ```mbt check
/// test {
/// let app = @cmder.cli(name="app", options=[@cmder.int("count", default=3)])
/// guard app.parse(argv=[]) is Parsed(call) else { fail("parse") }
/// inspect(call.context.get_int_required("count"), content="3")
/// }
/// ```
pub fn Context::get_int_required(
self : Context,
name : String,
) -> Int32 raise CliError {
match self.get_int(name) {
Some(v) => v
None => raise self.missing_value(name)
}
}
///|
// Shared by getters and parse-time validation.
fn parse_integer_value(name : String, value : String) -> Int32 raise CliError {
let negative = value.has_prefix("-")
let start = if negative { 1 } else { 0 }
if start == value.length() {
raise CliError::InvalidInteger(name, value)
}
// Validate the complete spelling before classifying overflow.
for index in start.. '9' {
raise CliError::InvalidInteger(name, value)
}
}
let limit = if negative { 2147483648L } else { 2147483647L }
let mut magnitude = 0L
for index in start.. (limit - digit) / 10L {
raise CliError::IntegerOutOfRange(name, value)
}
magnitude = magnitude * 10L + digit
}
(if negative { -magnitude } else { magnitude }).to_int()
}
///|
/// Returns a fresh copy of all values associated with a name, or an empty array.
///
/// ```mbt check
/// test {
/// let app = @cmder.cli(name="app", positionals=[@cmder.positional("files")])
/// guard app.parse(argv=["a", "b"]) is Parsed(call) else { fail("parse") }
/// inspect(call.context.get_strings("files").length(), content="2")
/// }
/// ```
pub fn Context::get_strings(self : Context, name : String) -> Array[String] {
self.values.get(name).map(values => values.copy()).unwrap_or([])
}
///|
/// Returns the current value's source, including explicit host overrides.
///
/// ```mbt check
/// test {
/// let app = @cmder.cli(name="app", options=[@cmder.int("count", default=1)])
/// guard app.parse(argv=[]) is Parsed(call) else { fail("parse") }
/// assert_true(
/// call.context.source_of("count") == Some(@cmder.ValueSource::Default),
/// )
/// }
/// ```
pub fn Context::source_of(self : Context, name : String) -> ValueSource? {
self.sources.get(name)
}