有没有办法让用户提示括号内的字节并用逗号或类似的东西分隔?
./main bytes [0, 1, 2, 3, 4, 5]
我设法让它看起来像这样:
./main bytes 0 1 2 3 4 5
这是我的代码:
extern crate docopt;
#[macro_use]
extern crate serde_derive;
use docopt::Docopt;
const USAGE: &'static str = "
Puzzle Solver.
Usage:
puzzle_solver string <text>
puzzle_solver bytes [<bin>...]
puzzle_solver (-h | --help)
puzzle_solver --version
Options:
-h --help Show this screen.
--version Show version.
";
#[derive(Debug, Deserialize)]
struct Args {
cmd_string: bool,
arg_text: Option<String>,
cmd_bytes: bool,
arg_bin: Option<Vec<u8>>,
}
fn main() {
let args: Args = Docopt::new(USAGE)
.and_then(|d| d.deserialize())
.unwrap_or_else(|e| e.exit());
println!("ARGS: {:?}", args);
}
答案 0 :(得分:2)
这是可能的,但您必须手动实施Deserialize
。
Vec<u8>
已经实现Deserialize
,并且该实现不知道包含逗号分隔的括号列表的字符串,docopt::Deserializer
也不知道,因为传递列表的正常方法在命令行上是逐个元素。因此,您必须创建一个将从您想要的格式反序列化的新类型。
当然,如果您想将其视为Deref<Target = Vec<u8>>
,您还可以为DerefMut
实施Bytes
和Vec<u8>
。 Some people might consider this a slight misuse of Deref
,但在这种情况下可能会很好。
extern crate docopt;
extern crate serde;
#[macro_use]
extern crate serde_derive;
use docopt::Docopt;
use serde::de;
use std::fmt;
const USAGE: &'static str = "
Puzzle Solver.
Usage:
puzzle_solver string <text>
puzzle_solver bytes <bin>
puzzle_solver (-h | --help)
puzzle_solver --version
Options:
-h --help Show this screen.
--version Show version.
";
#[derive(Debug, Deserialize)]
struct Args {
cmd_string: bool,
arg_text: Option<String>,
cmd_bytes: bool,
arg_bin: Option<Bytes>,
}
#[derive(Debug)]
struct Bytes(Vec<u8>);
impl<'de> de::Deserialize<'de> for Bytes {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct BytesVisitor;
impl<'de> de::Visitor<'de> for BytesVisitor {
type Value = Bytes;
fn expecting(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(formatter, "a bracketed, comma-delimited string")
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
let v = if v.starts_with('[') && v.ends_with(']') {
&v[1..v.len() - 1]
} else {
return Err(E::custom(format!("expected a bracketed list, got {:?}", v)));
};
let values: Result<Vec<u8>, _> = v.split(",").map(|s| s.trim().parse()).collect();
Ok(Bytes(values.map_err(E::custom)?))
}
}
deserializer.deserialize_str(BytesVisitor)
}
}
Here it is in the playground.以下是我为实现这一目标所做的更改:
[<bin>...]
替换为<bin>
,以便docopt知道要查找单个内容而不是一系列......内容。 (如果你不这样做,docopt实际上只会抛出一个空字符串。)Bytes
。Vec<u8>
包装
serde::de::Deserialize
代表Bytes
。这需要创建一个实现serde::de::Visitor
特征的结构,将代码放入其visit_str
方法中的代码,并将访问者传递给deserialize_str
,告诉{{1}期望一个字符串并将其传递给访问者Deserializer
。我几乎没有意识到这一点,但你可以实现visit_str
,并使其解析visit_seq
(不引用列表)。但我不会,因为这违反了命令行惯例;如果您仍然使用shell来分割参数,那么您应该全力以赴并接受bytes [1, 2, 3]
。