///|
/// An immutable option declaration. Canonical key prefers the long name.
pub struct OptionSpec {
short : String?
long : String?
takes_value : Bool
default_value : String?
}
///|
pub fn OptionSpec::key(self : OptionSpec) -> String {
self.long.unwrap_or(self.short.unwrap_or(""))
}
///|
fn default_text(description : String) -> String? {
match description.to_lower().find("[default:") {
None => None
Some(start) => {
let tail = description[start + 9:]
match tail.find("]") {
None => None
Some(end) => Some(tail[:end].trim().to_owned())
}
}
}
}
///|
/// Parse the declaration, not the prose after the first double space.
pub fn OptionSpec::parse(source : String) -> OptionSpec raise DocoptError {
let text = source.trim().to_owned()
let end = text.find(" ").unwrap_or(text.length())
let syntax = text[:end]
.to_owned()
.replace_all(old=",", new=" ")
.replace_all(old="=", new=" ")
let mut short = None
let mut long = None
let mut takes_value = false
for token in words(syntax) {
if token.has_prefix("--") {
long = Some(token)
} else if token.has_prefix("-") {
short = Some(token)
} else {
takes_value = true
}
}
guard short != None || long != None else {
raise Language("option declaration has no name")
}
{
short,
long,
takes_value,
default_value: if takes_value {
default_text(text[end:].to_owned())
} else {
None
},
}
}
///|
/// Collect multiline descriptions, preserving upstream declaration order.
pub fn Document::options(
self : Document,
) -> Array[OptionSpec] raise DocoptError {
let out = []
for section in self.option_sections {
let mut pending = ""
for raw in section[8:].split("\n") {
let line = raw.trim().to_owned()
if line.has_prefix("-") {
if pending != "" {
out.push(OptionSpec::parse(pending))
}
pending = line
} else if pending != "" {
pending = pending + "\n" + line
}
}
if pending != "" {
out.push(OptionSpec::parse(pending))
}
}
out
}