1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//! Shared body input for every endpoint subcommand under `api/`.
//!
//! Each `api <...> {post,get,delete}` subcommand that carries a request body
//! flattens a `BodySource` so the same four flags work uniformly:
//!
//! - `--body-inline <json>`
//! - `--body-file <path>`
//! - `--body-python-inline <code>`
//! - `--body-python-file <path>`
//!
//! Errors from the JSON paths route through `serde_path_to_error` so a malformed
//! body points at the exact field that mismatched the typed request struct.
//! The python paths use `crate::python::exec_{code,file}`, which internally do
//! the same `serde_path_to_error` deserialize.
use clap::Args;
#[derive(Args)]
#[group(required = true, multiple = false)]
pub struct BodySource {
/// Inline JSON body
#[arg(long)]
body_inline: Option<String>,
/// Path to a JSON file
#[arg(long)]
body_file: Option<std::path::PathBuf>,
/// Inline Python code that produces the JSON body
#[arg(long)]
body_python_inline: Option<String>,
/// Path to a Python file that produces the JSON body
#[arg(long)]
body_python_file: Option<std::path::PathBuf>,
}
impl BodySource {
pub fn resolve<T: serde::de::DeserializeOwned>(self) -> Result<T, crate::error::Error> {
if let Some(inline) = self.body_inline {
let mut de = serde_json::Deserializer::from_str(&inline);
return serde_path_to_error::deserialize(&mut de)
.map_err(crate::error::Error::InlineDeserialize);
}
if let Some(path) = self.body_file {
let contents = std::fs::read_to_string(&path)
.map_err(|e| crate::error::Error::PythonFileRead(path, e))?;
let mut de = serde_json::Deserializer::from_str(&contents);
return serde_path_to_error::deserialize(&mut de)
.map_err(crate::error::Error::InlineDeserialize);
}
if let Some(code) = self.body_python_inline {
return crate::python::exec_code(&code);
}
if let Some(path) = self.body_python_file {
return crate::python::exec_file(&path);
}
unreachable!("clap group ensures one is set")
}
}