provcfg/sources/cli.rs
1use crate::{Category, Source};
2
3/// A [`Source`] wrapping a pre-built `*Partial`, typically produced by a CLI
4/// argument parser (clap via the `provcfg-clap` `ClapArgs` derive, or by hand).
5/// Reports [`Category::Cli`].
6///
7/// Internally serializes the partial to JSON, then re-deserializes through the
8/// same path as every other source. The round-trip keeps [`Source`] strictly
9/// format-agnostic.
10///
11/// ```
12/// use provcfg::{Category, Config, Configurable};
13/// use provcfg::sources::CliSource;
14///
15/// # #[derive(Configurable, serde::Deserialize, Clone, Default)]
16/// # struct Settings { host: String }
17/// // `SettingsPartial` is generated by `#[derive(Configurable)]`; a CLI parser
18/// // fills in the fields it saw on the command line.
19/// let from_cli = SettingsPartial { host: Some("cli.example".to_string()) };
20///
21/// let settings = Config::new()
22/// .add_source(CliSource::new("cli", from_cli))
23/// .build::<SettingsProv>()
24/// .unwrap();
25///
26/// assert_eq!(settings.host.value(), "cli.example");
27/// assert_eq!(settings.host.source().category(), Category::Cli);
28/// ```
29pub struct CliSource<P> {
30 name: String,
31 partial: P,
32}
33
34impl<P> CliSource<P>
35where
36 P: serde::Serialize + Send + Sync + 'static,
37{
38 /// Create a CLI source. `name` is a human-readable label used in error
39 /// messages; `partial` is the pre-built `*Partial` to layer in.
40 pub fn new(name: impl Into<String>, partial: P) -> Self {
41 Self {
42 name: name.into(),
43 partial,
44 }
45 }
46}
47
48impl<P> Source for CliSource<P>
49where
50 P: serde::Serialize + Send + Sync + 'static,
51{
52 fn name(&self) -> &str {
53 &self.name
54 }
55
56 fn category(&self) -> Category {
57 Category::Cli
58 }
59
60 fn deserialize(
61 &self,
62 seed: &mut dyn for<'de> FnMut(
63 &mut dyn erased_serde::Deserializer<'de>,
64 ) -> Result<(), erased_serde::Error>,
65 ) -> Result<(), erased_serde::Error> {
66 let bytes = serde_json::to_vec(&self.partial)
67 .map_err(<erased_serde::Error as serde::de::Error>::custom)?;
68 let mut json_de = serde_json::Deserializer::from_slice(&bytes);
69 let mut erased = <dyn erased_serde::Deserializer>::erase(&mut json_de);
70 seed(&mut erased)
71 }
72}