clap_adapters/
json.rs

1use serde::de::DeserializeOwned;
2
3use crate::prelude::FromReader;
4
5/// An adapter for deserializing a Json document from a buffered reader
6#[derive(Debug, Clone)]
7pub struct JsonOf<T>(pub T);
8
9impl<T: DeserializeOwned> FromReader for JsonOf<T> {
10    type Error = serde_json::Error;
11    fn from_reader(reader: &mut impl std::io::BufRead) -> Result<Self, Self::Error> {
12        let json = serde_json::from_reader::<_, T>(reader)?;
13        Ok(JsonOf(json))
14    }
15}
16
17impl<T> crate::fs::PathTo<JsonOf<T>> {
18    /// Returns reference to the inner JSON datatype
19    ///
20    /// # Example
21    ///
22    /// ```
23    /// # fn main() -> anyhow::Result<()> {
24    /// use clap::Parser;
25    /// use clap_adapters::prelude::*;
26    ///
27    /// #[derive(Debug, Parser)]
28    /// struct Cli {
29    ///     #[clap(long)]
30    ///     config: PathTo<JsonOf<serde_json::Value>>,
31    /// }
32    ///
33    /// // Create a config file in a temporary directory
34    /// let config_dir = tempfile::tempdir()?;
35    /// let config_path = config_dir.path().join("config.json");
36    /// let config_path_string = config_path.display().to_string();
37    ///
38    /// // Write a test config of {"hello":"world"} to the config file
39    /// let config = serde_json::json!({"hello": "world"});
40    /// let config_string = serde_json::to_string(&config)?;
41    /// std::fs::write(&config_path, &config_string)?;
42    ///
43    /// // Parse our CLI, passing our config file path to --config
44    /// let cli = Cli::parse_from(["app", "--config", &config_path_string]);
45    /// let data = cli.config.data();
46    ///
47    /// // We should expect the value we get to match what we wrote to the config
48    /// assert_eq!(data, &serde_json::json!({"hello":"world"}));
49    /// # Ok(())
50    /// # }
51    /// ```
52    pub fn data(&self) -> &T {
53        &self.data.0
54    }
55
56    /// Returns reference to the inner JSON datatype
57    ///
58    /// # Example
59    ///
60    /// ```
61    /// # fn main() -> anyhow::Result<()> {
62    /// use clap::Parser;
63    /// use clap_adapters::prelude::*;
64    ///
65    /// #[derive(Debug, Parser)]
66    /// struct Cli {
67    ///     #[clap(long)]
68    ///     config: PathTo<JsonOf<serde_json::Value>>,
69    /// }
70    ///
71    /// // Create a config file in a temporary directory
72    /// let config_dir = tempfile::tempdir()?;
73    /// let config_path = config_dir.path().join("config.json");
74    /// let config_path_string = config_path.display().to_string();
75    ///
76    /// // Write a test config of {"hello":"world"} to the config file
77    /// let config = serde_json::json!({"hello": "world"});
78    /// let config_string = serde_json::to_string(&config)?;
79    /// std::fs::write(&config_path, &config_string)?;
80    ///
81    /// // Parse our CLI, passing our config file path to --config
82    /// let cli = Cli::parse_from(["app", "--config", &config_path_string]);
83    /// let data = cli.config.into_data();
84    ///
85    /// // We should expect the value we get to match what we wrote to the config
86    /// assert_eq!(data, serde_json::json!({"hello":"world"}));
87    /// # Ok(())
88    /// # }
89    /// ```
90    pub fn into_data(self) -> T {
91        self.data.0
92    }
93}