yellowstone_vixen/
config.rs

1//! Configuration types for the Vixen runtime.
2use clap::Args;
3use serde::Deserialize;
4/// A helper trait for types that may or may not have a default value,
5/// determined at runtime.
6pub trait MaybeDefault: Sized {
7    /// Get the default value for this type, if it exists.
8    fn default_opt() -> Option<Self>;
9}
10
11impl<T: Default> MaybeDefault for T {
12    #[inline]
13    fn default_opt() -> Option<Self> { Some(Self::default()) }
14}
15
16/// Root configuration for [the Vixen runtime](crate::Runtime).
17#[derive(Debug, Args)]
18pub struct VixenConfig<S>
19where S: Args
20{
21    /// The source configuration.
22    #[command(flatten)]
23    pub source: S,
24
25    /// The buffer configuration.
26    #[command(flatten)]
27    pub buffer: BufferConfig,
28}
29
30impl<'de, S> Deserialize<'de> for VixenConfig<S>
31where S: Args + Deserialize<'de>
32{
33    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
34    where D: serde::Deserializer<'de> {
35        #[derive(Deserialize)]
36        #[serde(bound = "S: Deserialize<'de>")]
37        struct Inner<S> {
38            source: S,
39            #[serde(default)]
40            buffer: BufferConfig,
41        }
42
43        let Inner { source, buffer } = Inner::<S>::deserialize(deserializer)?;
44
45        Ok(Self { source, buffer })
46    }
47}
48
49/// Job scheduler configuration.
50#[derive(Debug, Clone, Copy, clap::Args, serde::Deserialize)]
51#[serde(rename_all = "kebab-case")]
52pub struct BufferConfig {
53    /// The maximum number of concurrent jobs to run.  If unset, defaults to
54    /// the number of CPUs.
55    #[arg(long, env)]
56    pub jobs: Option<usize>,
57    /// The maximum number of concurrent sources to run.
58    /// Defaults to 100.
59    #[arg(long, env)]
60    pub sources_channel_size: usize,
61}
62
63impl Default for BufferConfig {
64    fn default() -> Self {
65        Self {
66            jobs: None,
67            sources_channel_size: 100,
68        }
69    }
70}
71
72/// Helper type for blank configuration sections.
73#[derive(
74    Default,
75    Debug,
76    Clone,
77    Copy,
78    PartialEq,
79    Eq,
80    PartialOrd,
81    Ord,
82    Hash,
83    clap::Args,
84    serde::Deserialize,
85)]
86pub struct NullConfig;
87
88impl From<Option<NullConfig>> for NullConfig {
89    #[inline]
90    fn from(value: Option<NullConfig>) -> Self { value.unwrap_or_default() }
91}
92
93/// Helper type for optional configuration sections.
94#[derive(Debug, serde::Deserialize)]
95#[repr(transparent)]
96pub struct OptConfig<T>(Option<T>);
97
98impl<T> Default for OptConfig<T> {
99    #[inline]
100    fn default() -> Self { Self(None) }
101}
102
103impl<T> OptConfig<T> {
104    /// Get the underlying `Option`.
105    #[inline]
106    pub fn opt(self) -> Option<T> { self.into() }
107}
108
109impl<T> From<T> for OptConfig<T> {
110    #[inline]
111    fn from(value: T) -> Self { Some(value).into() }
112}
113
114impl<T> From<Option<T>> for OptConfig<T> {
115    #[inline]
116    fn from(value: Option<T>) -> Self { Self(value) }
117}
118
119impl<T> From<OptConfig<T>> for Option<T> {
120    #[inline]
121    fn from(OptConfig(value): OptConfig<T>) -> Self { value }
122}
123
124impl<T> std::ops::Deref for OptConfig<T> {
125    type Target = Option<T>;
126
127    #[inline]
128    fn deref(&self) -> &Self::Target { &self.0 }
129}
130
131impl<T> std::ops::DerefMut for OptConfig<T> {
132    #[inline]
133    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
134}
135
136impl<T: clap::FromArgMatches> clap::FromArgMatches for OptConfig<T> {
137    fn from_arg_matches(matches: &clap::ArgMatches) -> Result<Self, clap::Error> {
138        T::from_arg_matches(matches).map(Into::into)
139    }
140
141    fn from_arg_matches_mut(matches: &mut clap::ArgMatches) -> Result<Self, clap::Error> {
142        T::from_arg_matches(matches).map(Into::into)
143    }
144
145    fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error> {
146        self.0
147            .as_mut()
148            .expect("Cannot update empty OptConfig")
149            .update_from_arg_matches(matches)
150    }
151
152    fn update_from_arg_matches_mut(
153        &mut self,
154        matches: &mut clap::ArgMatches,
155    ) -> Result<(), clap::Error> {
156        self.0
157            .as_mut()
158            .expect("Cannot update empty OptConfig")
159            .update_from_arg_matches_mut(matches)
160    }
161}
162
163impl<T: clap::Args> clap::Args for OptConfig<T> {
164    fn group_id() -> Option<clap::Id> { T::group_id() }
165
166    fn augment_args(cmd: clap::Command) -> clap::Command { T::augment_args(cmd) }
167
168    fn augment_args_for_update(cmd: clap::Command) -> clap::Command {
169        T::augment_args_for_update(cmd)
170    }
171}
172
173// Used for clap hacks below
174#[allow(dead_code)] // Currently unused if feature 'prometheus' is disabled
175fn update_clone<T: ToOwned, U: Into<T>, F: FnOnce(&mut U) -> V, V>(t: &mut T, f: F) -> V
176where T::Owned: Into<U> {
177    let mut u = t.to_owned().into();
178    let ret = f(&mut u);
179    *t = u.into();
180    ret
181}