///|
/// Require a present scalar; unknown keys and absent optional values differ.
pub fn Values::require_text(
self : Values,
key : String,
) -> String raise DocoptError {
match self.text(key) {
Some(v) => v
None => raise Conversion(key + " has no value")
}
}
///|
/// Return an owned repeated argument/option vector.
pub fn Values::texts(
self : Values,
key : String,
) -> Array[String] raise DocoptError {
match self.items.get(key) {
Some(Texts(xs)) => xs.copy()
_ => raise Conversion(key + " is not repeated text")
}
}
///|
pub fn Values::count(self : Values, key : String) -> Int32 raise DocoptError {
match self.items.get(key) {
Some(Count(n)) => n
_ => raise Conversion(key + " is not a repetition count")
}
}
///|
/// Strict ASCII decimal, accepting one leading sign but no spaces or suffix.
/// Int64 is only an accumulator; every multiplication is bounded beforehand.
fn decimal_int(text : String, key : String) -> Int32 raise DocoptError {
guard text != "" else {
raise Conversion(key + " is empty, expected an integer")
}
let negative = text[0] == 45
let mut i = if negative || text[0] == 43 { 1 } else { 0 }
guard i < text.length() else {
raise Conversion(key + " has a sign without digits")
}
let ceiling : Int64 = if negative { 2147483648L } else { 2147483647L }
let mut n = 0L
while i < text.length() {
let c = text[i].to_int()
guard c >= 48 && c <= 57 else {
raise Conversion(key + " is not decimal at offset \{i}")
}
let digit = (c - 48).to_int64()
guard n <= (ceiling - digit) / 10L else {
raise Conversion(key + " exceeds Int32 range")
}
n = n * 10L + digit
i += 1
}
(if negative { -n } else { n }).to_int()
}
///|
/// Decode a scalar with explicit inclusive domain bounds.
pub fn Values::integer(
self : Values,
key : String,
minimum~ : Int32 = -2147483648,
maximum~ : Int32 = 2147483647,
) -> Int32 raise DocoptError {
guard minimum <= maximum else { raise Conversion("invalid integer bounds") }
let n = decimal_int(self.require_text(key), key)
guard n >= minimum && n <= maximum else {
raise Conversion(key + " outside permitted range")
}
n
}
///|
/// Validate enum-like options without silently falling back to a default.
pub fn Values::choice(
self : Values,
key : String,
allowed : Array[String],
) -> String raise DocoptError {
let v = self.require_text(key)
guard allowed.contains(v) else {
raise Conversion(key + " has an unsupported value")
}
v
}
///|
/// Decode repeated scalar integers with the same overflow rules.
pub fn Values::integers(
self : Values,
key : String,
) -> Array[Int32] raise DocoptError {
self.texts(key).map(s => decimal_int(s, key))
}