///|
/// Introspection for editor, completion and embedded-console integrations.
pub struct ParameterInfo {
key : String
kind : String
repeated : Bool
aliases : Array[String]
default_value : Value
}
///|
pub fn ParameterInfo::to_json(self : ParameterInfo) -> Json {
{
"key": Json(self.key),
"kind": Json(self.kind),
"repeated": Json(self.repeated),
"aliases": Json(self.aliases),
"default": self.default_value.to_json(),
}
}
///|
/// Ordered metadata is reconstructed, never shared with the compiled parser.
pub fn Parser::parameters(
self : Parser,
) -> Array[ParameterInfo] raise DocoptError {
let defaults = self.defaults()
let out = []
for key in self.keys() {
let spec = find_spec(self.specs, key).map(fn(i) { self.specs[i] })
let aliases = []
let kind = match spec {
Some(s) => {
if s.short is Some(n) {
aliases.push(n)
}
if s.long is Some(n) {
aliases.push(n)
}
if s.takes_value {
"option-value"
} else {
"option-flag"
}
}
None => if is_argument(key) { "argument" } else { "command" }
}
let default_value = defaults.get(key).unwrap_or(Missing)
let repeated = match default_value {
Texts(_) | Count(_) => true
_ => false
}
out.push({ key, kind, repeated, aliases, default_value, })
}
out
}
///|
/// Machine-readable description; not a code-first schema or parser definition.
pub fn Parser::describe(self : Parser) -> Json raise DocoptError {
{
"program": Json(self.program()),
"usage": Json(self.usage()),
"parameters": Json::array(self.parameters().map(fn(p) { p.to_json() })),
}
}
///|
/// Lexical suggestions only. They do not promise that a grammar branch matches.
pub fn Parser::suggest(
self : Parser,
prefix : String,
) -> Array[String] raise DocoptError {
let candidates = Map([])
for info in self.parameters() {
if info.kind == "command" && info.key.has_prefix(prefix) {
candidates[info.key] = true
}
for spelling in info.aliases {
if spelling.has_prefix(prefix) {
candidates[spelling] = true
}
}
}
let result = candidates.keys().to_array()
result.sort_by(fn(a, b) { a[:].lexical_compare(b[:]) })
result
}