#![crate_name = "docopt"]
#![doc(html_root_url = "http://burntsushi.net/rustdoc/docopt")]
#![deny(missing_docs)]
#![feature(collections, core, std_misc, io)]
extern crate libc;
extern crate regex;
extern crate "rustc-serialize" as rustc_serialize;
use std::borrow::IntoCow;
use std::collections::HashMap;
use std::error::Error as StdError;
use std::fmt;
use std::io::{self, Write};
use std::str::FromStr;
use std::num;
use std::num::NumCast;
use rustc_serialize::Decodable;
use parse::Parser;
use synonym::SynonymMap;
use Value::{Switch, Counted, Plain, List};
use Error::{Usage, Argv, NoMatch, Decode, WithProgramUsage, Help, Version};
macro_rules! werr(
($($arg:tt)*) => (
match write!(&mut io::stderr(), $($arg)*) {
Ok(_) => (),
Err(err) => panic!("{}", err),
}
)
);
macro_rules! regex(
($s:expr) => (regex::Regex::new($s).unwrap());
);
#[derive(Debug)]
pub enum Error {
Usage(String),
Argv(String),
NoMatch,
Decode(String),
WithProgramUsage(Box<Error>, String),
Help,
Version(String),
}
impl Error {
pub fn fatal(&self) -> bool {
match *self {
Help | Version(..) => false,
Usage(..) | Argv(..) | NoMatch | Decode(..) => true,
WithProgramUsage(ref b, _) => b.fatal(),
}
}
pub fn exit(&self) -> ! {
if self.fatal() {
werr!("{}\n", self);
exit(1)
} else {
println!("{}", self);
exit(0)
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
WithProgramUsage(ref other, ref usage) => {
let other = other.to_string();
if other.is_empty() {
write!(f, "{}", usage)
} else {
write!(f, "{}\n\n{}", other, usage)
}
}
Help => write!(f, ""),
NoMatch => write!(f, "Invalid arguments."),
Usage(ref s) | Argv(ref s) | Decode(ref s) | Version(ref s) => {
write!(f, "{}", s)
}
}
}
}
impl StdError for Error {
fn description(&self) -> &str {
match *self {
Usage(..) => "invalid usage string",
Argv(..) => "failed to parse specified argv",
NoMatch => "could not match specified argv",
Decode(..) => "failed to decode",
WithProgramUsage(..) => "failed to parse specified argv",
Help => "help message requested",
Version(..) => "version message requested",
}
}
fn cause(&self) -> Option<&StdError> {
match *self {
WithProgramUsage(ref cause, _) => Some(&**cause as &StdError),
_ => None,
}
}
}
#[derive(Clone, Debug)]
pub struct Docopt {
p: Parser,
argv: Option<Vec<String>>,
options_first: bool,
help: bool,
version: Option<String>,
}
impl Docopt {
pub fn new<S>(usage: S) -> Result<Docopt, Error>
where S: ::std::ops::Deref<Target=str> {
Parser::new(usage.deref())
.map_err(Usage)
.map(|p| Docopt {
p: p,
argv: None,
options_first: false,
help: true,
version: None,
})
}
pub fn decode<D>(&self) -> Result<D, Error> where D: Decodable {
self.parse().and_then(|vals| vals.decode())
}
pub fn parse(&self) -> Result<ArgvMap, Error> {
let argv = self.argv.clone().unwrap_or_else(|| Docopt::get_argv());
let vals = try!(
self.p.parse_argv(argv, self.options_first)
.map_err(|s| self.err_with_usage(Argv(s)))
.and_then(|argv|
match self.p.matches(&argv) {
Some(m) => Ok(ArgvMap { map: m }),
None => Err(self.err_with_usage(NoMatch)),
}));
if self.help && vals.get_bool("--help") {
return Err(self.err_with_full_doc(Help));
}
match self.version {
Some(ref v) if vals.get_bool("--version") => {
return Err(Version(v.clone()))
}
_ => {},
}
Ok(vals)
}
pub fn argv<'a, I, S>(mut self, argv: I) -> Docopt
where I: Iterator<Item=S>, S: IntoCow<'a, str> {
self.argv = Some(
argv.skip(1).map(|s| s.into_cow().into_owned()).collect()
);
self
}
pub fn options_first(mut self, yes: bool) -> Docopt {
self.options_first = yes;
self
}
pub fn help(mut self, yes: bool) -> Docopt {
self.help = yes;
self
}
pub fn version(mut self, version: Option<String>) -> Docopt {
self.version = version;
self
}
#[doc(hidden)]
pub fn parser<'a>(&'a self) -> &'a Parser {
&self.p
}
fn err_with_usage(&self, e: Error) -> Error {
WithProgramUsage(
Box::new(e), self.p.usage.trim().to_string())
}
fn err_with_full_doc(&self, e: Error) -> Error {
WithProgramUsage(
Box::new(e), self.p.full_doc.trim().to_string())
}
fn get_argv() -> Vec<String> {
::std::env::args().skip(1).map(|v| v.to_string()).collect()
}
}
#[derive(Clone)]
pub struct ArgvMap {
map: SynonymMap<String, Value>,
}
impl ArgvMap {
pub fn decode<T: Decodable>(self) -> Result<T, Error> {
Decodable::decode(&mut Decoder { vals: self, stack: vec!() })
}
pub fn get_bool(&self, key: &str) -> bool {
self.find(key).map(|v| v.as_bool()).unwrap_or(false)
}
pub fn get_count(&self, key: &str) -> usize {
self.find(key).map(|v| v.as_count()).unwrap_or(0)
}
pub fn get_str<'a>(&'a self, key: &str) -> &'a str {
self.find(key).map(|v| v.as_str()).unwrap_or("")
}
pub fn get_vec<'a>(&'a self, key: &str) -> Vec<&'a str> {
self.find(key).map(|v| v.as_vec()).unwrap_or(vec!())
}
pub fn find<'a>(&'a self, key: &str) -> Option<&'a Value> {
self.map.find(&key.to_string())
}
pub fn len(&self) -> usize {
self.map.len()
}
#[doc(hidden)]
pub fn key_to_struct_field(name: &str) -> String {
fn sanitize(name: &str) -> String {
name.replace("-", "_")
}
let r = regex!(r"^(?:--?(?P<flag>\S+)|(?:(?P<argu>\p{Lu}+)|<(?P<argb>[^>]+)>)|(?P<cmd>\S+))$");
r.replace(name, |cap: ®ex::Captures| {
let (flag, cmd) = (
cap.name("flag").unwrap_or(""),
cap.name("cmd").unwrap_or(""),
);
let (argu, argb) = (
cap.name("argu").unwrap_or(""),
cap.name("argb").unwrap_or(""),
);
let (prefix, name) =
if !flag.is_empty() {
("flag_", flag)
} else if !argu.is_empty() {
("arg_", argu)
} else if !argb.is_empty() {
("arg_", argb)
} else if !cmd.is_empty() {
("cmd_", cmd)
} else {
panic!("Unknown ArgvMap key: '{}'", name)
};
let mut prefix = prefix.to_string();
prefix.push_str(&sanitize(name));
prefix
})
}
#[doc(hidden)]
pub fn struct_field_to_key(field: &str) -> String {
fn desanitize(name: &str) -> String {
name.replace("_", "-")
}
let name =
if field.starts_with("flag_") {
let name = regex!(r"^flag_").replace(field, "");
let mut pre_name = (if name.len() == 1 { "-" } else { "--" })
.to_string();
pre_name.push_str(&*name);
pre_name
} else if field.starts_with("arg_") {
let name = regex!(r"^arg_").replace(field, "");
if regex!(r"^\p{Lu}+$").is_match(&name) {
name
} else {
let mut pre_name = "<".to_string();
pre_name.push_str(&*name);
pre_name.push('>');
pre_name
}
} else if field.starts_with("cmd_") {
{ regex!(r"^cmd_") }.replace(field, "")
} else {
panic!("Unrecognized struct field: '{}'", field)
};
desanitize(&*name)
}
}
impl fmt::Debug for ArgvMap {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.len() == 0 {
return write!(f, "{{EMPTY}}");
}
let reverse: HashMap<&String, &String> =
self.map.synonyms().map(|(from, to)| (to, from)).collect();
let mut keys: Vec<&String> = self.map.keys().collect();
keys.sort();
let mut first = true;
for &k in keys.iter() {
if !first { try!(write!(f, "\n")); } else { first = false; }
match reverse.get(&k) {
None => {
try!(write!(f, "{} => {:?}", k, self.map.get(k)))
}
Some(s) => {
try!(write!(f, "{}, {} => {:?}", s, k, self.map.get(k)))
}
}
}
Ok(())
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Switch(bool),
Counted(usize),
Plain(Option<String>),
List(Vec<String>),
}
impl Value {
pub fn as_bool(&self) -> bool {
match *self {
Switch(b) => b,
Counted(n) => n > 0,
Plain(None) => false,
Plain(Some(_)) => true,
List(ref vs) => !vs.is_empty(),
}
}
pub fn as_count(&self) -> usize {
match *self {
Switch(b) => if b { 1 } else { 0 },
Counted(n) => n,
Plain(None) => 0,
Plain(Some(_)) => 1,
List(ref vs) => vs.len(),
}
}
pub fn as_str<'a>(&'a self) -> &'a str {
match *self {
Switch(_) | Counted(_) | Plain(None) | List(_) => "",
Plain(Some(ref s)) => &**s,
}
}
pub fn as_vec<'a>(&'a self) -> Vec<&'a str> {
match *self {
Switch(_) | Counted(_) | Plain(None) => vec![],
Plain(Some(ref s)) => vec![&**s],
List(ref vs) => vs.iter().map(|s| &**s).collect(),
}
}
}
pub struct Decoder {
vals: ArgvMap,
stack: Vec<DecoderItem>,
}
#[derive(Debug)]
struct DecoderItem {
key: String,
struct_field: String,
val: Option<Value>,
}
macro_rules! derr(
($($arg:tt)*) => (return Err(Decode(format!($($arg)*))))
);
impl Decoder {
fn push(&mut self, struct_field: &str) {
let key = ArgvMap::struct_field_to_key(struct_field);
self.stack.push(DecoderItem {
key: key.clone(),
struct_field: struct_field.to_string(),
val: self.vals.find(&*key).map(|v| v.clone()),
});
}
fn pop(&mut self) -> Result<DecoderItem, Error> {
match self.stack.pop() {
None => derr!("Could not decode value into unknown key."),
Some(it) => Ok(it),
}
}
fn pop_key_val(&mut self) -> Result<(String, Value), Error> {
let it = try!(self.pop());
match it.val {
None => derr!("Could not find argument '{}' (from struct \
field '{}').", it.key, it.struct_field),
Some(v) => Ok((it.key, v)),
}
}
fn pop_val(&mut self) -> Result<Value, Error> {
let (_, v) = try!(self.pop_key_val());
Ok(v)
}
fn to_number<T: FromStr + NumCast>
(&mut self, expect: &str) -> Result<T, Error> {
let (k, v) = try!(self.pop_key_val());
match v {
Counted(n) => Ok(num::cast(n).unwrap()),
_ => {
match v.as_str().parse() {
Err(_) => derr!("Could not decode '{}' to {} for '{}'.",
v.as_str(), expect, k),
Ok(v) => Ok(v),
}
}
}
}
}
impl rustc_serialize::Decoder for Decoder {
type Error = Error;
fn error(&mut self, err: &str) -> Error {
Decode(err.to_string())
}
fn read_nil(&mut self) -> Result<(), Error> {
panic!("I don't know how to read into a nil value.")
}
fn read_usize(&mut self) -> Result<usize, Error> {
self.to_number("usize")
}
fn read_u64(&mut self) -> Result<u64, Error> {
self.to_number("u64")
}
fn read_u32(&mut self) -> Result<u32, Error> {
self.to_number("u32")
}
fn read_u16(&mut self) -> Result<u16, Error> {
self.to_number("u16")
}
fn read_u8(&mut self) -> Result<u8, Error> {
self.to_number("u8")
}
fn read_isize(&mut self) -> Result<isize, Error> {
self.to_number("isize")
}
fn read_i64(&mut self) -> Result<i64, Error> {
self.to_number("i64")
}
fn read_i32(&mut self) -> Result<i32, Error> {
self.to_number("i32")
}
fn read_i16(&mut self) -> Result<i16, Error> {
self.to_number("i16")
}
fn read_i8(&mut self) -> Result<i8, Error> {
self.to_number("i8")
}
fn read_bool(&mut self) -> Result<bool, Error> {
self.pop_val().map(|v| v.as_bool())
}
fn read_f64(&mut self) -> Result<f64, Error> {
self.to_number("f64")
}
fn read_f32(&mut self) -> Result<f32, Error> {
self.to_number("f32")
}
fn read_char(&mut self) -> Result<char, Error> {
let (k, v) = try!(self.pop_key_val());
let vstr = v.as_str();
match vstr.chars().count() {
1 => Ok(vstr.char_at(0)),
_ => derr!("Could not decode '{}' into char for '{}'.", vstr, k),
}
}
fn read_str(&mut self) -> Result<String, Error> {
self.pop_val().map(|v| v.as_str().to_string())
}
fn read_enum<T, F>(&mut self, _: &str, f: F) -> Result<T, Error>
where F: FnOnce(&mut Decoder) -> Result<T, Error> {
f(self)
}
fn read_enum_variant<T, F>(&mut self, names: &[&str], mut f: F)
-> Result<T, Error>
where F: FnMut(&mut Decoder, usize) -> Result<T, Error> {
let v = try!(self.pop_val()).as_str().to_lowercase();
let i =
match names.iter().map(|n| n.to_lowercase()).position(|n| n == v) {
Some(i) => i,
None => {
derr!("Could not match '{}' with any of \
the allowed variants: {:?}", v, names)
}
};
f(self, i)
}
fn read_enum_variant_arg<T, F>(&mut self, _: usize, _: F)
-> Result<T, Error>
where F: FnOnce(&mut Decoder) -> Result<T, Error> {
unimplemented!()
}
fn read_enum_struct_variant<T, F>(&mut self, _: &[&str], _: F)
-> Result<T, Error>
where F: FnMut(&mut Decoder, usize) -> Result<T, Error> {
unimplemented!()
}
fn read_enum_struct_variant_field<T, F>(&mut self, _: &str, _: usize, _: F)
-> Result<T, Error>
where F: FnOnce(&mut Decoder) -> Result<T, Error> {
unimplemented!()
}
fn read_struct<T, F>(&mut self, _: &str, _: usize, f: F) -> Result<T, Error>
where F: FnOnce(&mut Decoder) -> Result<T, Error> {
f(self)
}
fn read_struct_field<T, F>(&mut self, f_name: &str, _: usize, f: F)
-> Result<T, Error>
where F: FnOnce(&mut Decoder) -> Result<T, Error> {
self.push(f_name);
f(self)
}
fn read_tuple<T, F>(&mut self, _: usize, _: F) -> Result<T, Error>
where F: FnOnce(&mut Decoder) -> Result<T, Error> {
unimplemented!()
}
fn read_tuple_arg<T, F>(&mut self, _: usize, _: F) -> Result<T, Error>
where F: FnOnce(&mut Decoder) -> Result<T, Error> {
unimplemented!()
}
fn read_tuple_struct<T, F>(&mut self, _: &str, _: usize, _: F)
-> Result<T, Error>
where F: FnOnce(&mut Decoder) -> Result<T, Error> {
unimplemented!()
}
fn read_tuple_struct_arg<T, F>(&mut self, _: usize, _: F)
-> Result<T, Error>
where F: FnOnce(&mut Decoder) -> Result<T, Error> {
unimplemented!()
}
fn read_option<T, F>(&mut self, mut f: F) -> Result<T, Error>
where F: FnMut(&mut Decoder, bool) -> Result<T, Error> {
let option =
match self.stack.last() {
None => derr!("Could not decode value into unknown key."),
Some(it) => it.val.as_ref()
.map(|v| v.as_bool())
.unwrap_or(false),
};
f(self, option)
}
fn read_seq<T, F>(&mut self, f: F) -> Result<T, Error>
where F: FnOnce(&mut Decoder, usize) -> Result<T, Error> {
let it = try!(self.pop());
let list = it.val.unwrap_or(List(vec!()));
let vals = list.as_vec();
for val in vals.iter().rev() {
self.stack.push(DecoderItem {
key: it.key.clone(),
struct_field: it.struct_field.clone(),
val: Some(Plain(Some(val.to_string()))),
})
}
f(self, vals.len())
}
fn read_seq_elt<T, F>(&mut self, _: usize, f: F) -> Result<T, Error>
where F: FnOnce(&mut Decoder) -> Result<T, Error> {
f(self)
}
fn read_map<T, F>(&mut self, _: F) -> Result<T, Error>
where F: FnOnce(&mut Decoder, usize) -> Result<T, Error> {
unimplemented!()
}
fn read_map_elt_key<T, F>(&mut self, _: usize, _: F) -> Result<T, Error>
where F: FnOnce(&mut Decoder) -> Result<T, Error> {
unimplemented!()
}
fn read_map_elt_val<T, F>(&mut self, _: usize, _: F) -> Result<T, Error>
where F: FnOnce(&mut Decoder) -> Result<T, Error> {
unimplemented!()
}
}
fn exit(code: usize) -> ! {
unsafe { libc::exit(code as libc::c_int) }
}
#[doc(hidden)]
pub mod parse;
mod synonym;
#[cfg(test)]
mod test;