pavex/blueprint/
config.rs

1use super::reflection::AnnotationCoordinates;
2use crate::blueprint::{CloningPolicy, conversions::cloning2cloning};
3use pavex_bp_schema::{Blueprint as BlueprintSchema, Component, ConfigType};
4
5/// The input type for [`Blueprint::config`].
6///
7/// Check out [`Blueprint::config`] for more information on how to manage configuration types
8/// in Pavex.
9///
10/// # Stability guarantees
11///
12/// Use the [`config`](macro@crate::config) attribute macro to create instances of `Config`.\
13/// `Config`'s fields are an implementation detail of Pavex's macros and should not be relied upon:
14/// newer versions of Pavex may add, remove or modify its fields.
15///
16/// [`Blueprint::config`]: crate::Blueprint::config
17pub struct Config {
18    #[doc(hidden)]
19    pub coordinates: AnnotationCoordinates,
20}
21
22/// A configuration type registered via [`Blueprint::config`].
23///
24/// # Example
25///
26/// You can use the methods exposed by [`RegisteredConfig`] to tune the behaviour
27/// of the registered configuration type.
28/// For example, instruct Pavex to use the `Default` implementation if the user configuration
29/// doesn't specify a value for `pool`:
30///
31/// ```rust
32/// use pavex::{config, Blueprint};
33///
34/// #[config(key = "pool")]
35/// #[derive(serde::Deserialize, Debug, Clone)]
36/// pub struct PoolConfig {
37///     pub max_n_connections: u32,
38///     pub min_n_connections: u32,
39/// }
40///
41/// impl Default for PoolConfig {
42///     fn default() -> Self {
43///         Self {
44///             max_n_connections: 10,
45///             min_n_connections: 2,
46///         }
47///     }
48/// }
49///
50/// let mut bp = Blueprint::new();
51/// // This is equivalent to `#[config(key = "pool", default_if_missing)]`
52/// bp.config(POOL_CONFIG).default_if_missing();
53/// ```
54///
55/// # Example: override the annotation
56///
57/// You can also override the behaviour specified via the [`config`](macro@crate::config) attribute.
58///
59/// ```rust
60/// use pavex::{config, Blueprint};
61///
62/// #[config(key = "pool", default_if_missing)]
63/// #[derive(serde::Deserialize, Debug, Clone)]
64/// pub struct PoolConfig {
65///     pub max_n_connections: u32,
66///     pub min_n_connections: u32,
67/// }
68///
69/// # impl Default for PoolConfig {
70/// #     fn default() -> Self {
71/// #         Self {
72/// #             max_n_connections: 10,
73/// #             min_n_connections: 2,
74/// #         }
75/// #     }
76/// # }
77/// #
78/// let mut bp = Blueprint::new();
79/// // Using `required`, we are overriding the `default_if_missing` flag
80/// // specified via the `config` attribute.
81/// // This is equivalent to `#[config(key = "pool")]`, thus restoring
82/// // the default behaviour.
83/// bp.config(POOL_CONFIG).required();
84/// ```
85///
86/// [`Blueprint::config`]: crate::Blueprint::config
87pub struct RegisteredConfig<'a> {
88    #[allow(unused)]
89    pub(crate) blueprint: &'a mut BlueprintSchema,
90    /// The index of the registered config type in the blueprint's `components` vector.
91    #[allow(unused)]
92    pub(crate) component_id: usize,
93}
94
95impl RegisteredConfig<'_> {
96    /// Set the cloning strategy for this configuration type.
97    ///
98    /// By default, Pavex will clone a configuration type if it's necessary to generate code
99    /// that satisfies Rust's borrow checker.
100    /// You can change the default behaviour by invoking [`never_clone`](Self::never_clone).
101    ///
102    /// Regardless of the chosen strategy, configuration types *must* implement `Clone`,
103    /// since the code-generated `ApplicationConfig` type will need to derive it.
104    pub fn cloning(mut self, strategy: CloningPolicy) -> Self {
105        self.config().cloning_policy = Some(cloning2cloning(strategy));
106        self
107    }
108
109    /// Set the cloning strategy to [`CloningPolicy::CloneIfNecessary`].
110    /// Check out [`cloning`](Self::cloning) method for more details.
111    pub fn clone_if_necessary(self) -> Self {
112        self.cloning(CloningPolicy::CloneIfNecessary)
113    }
114
115    /// Set the cloning strategy to [`CloningPolicy::NeverClone`].
116    /// Check out [`cloning`](Self::cloning) method for more details.
117    pub fn never_clone(self) -> Self {
118        self.cloning(CloningPolicy::NeverClone)
119    }
120
121    /// Use the default configuration values returned by [`Default::default`]
122    /// if the user has not specified its own configuration for this type.
123    ///
124    /// # Requirements
125    ///
126    /// The configuration type *must* implement the [`Default`] trait
127    /// to support this option.
128    ///
129    /// # Implementation notes
130    ///
131    /// `default_if_missing` adds a `#[serde(default)]` attribute on the corresponding
132    /// configuration key in the code-generated `ApplicationConfig` struct.
133    pub fn default_if_missing(mut self) -> Self {
134        self.config().default_if_missing = Some(true);
135        self
136    }
137
138    /// Force the user to specify a value for this configuration entry.
139    ///
140    /// It's the opposite of [`default_if_missing`](Self::default_if_missing).
141    pub fn required(mut self) -> Self {
142        self.config().default_if_missing = Some(false);
143        self
144    }
145
146    /// Include this configuration entry in the generated `ApplicationConfig` struct
147    /// even if the type is never used by the application.
148    pub fn include_if_unused(mut self) -> Self {
149        self.config().include_if_unused = Some(true);
150        self
151    }
152
153    fn config(&mut self) -> &mut ConfigType {
154        let component = &mut self.blueprint.components[self.component_id];
155        let Component::ConfigType(s) = component else {
156            unreachable!("The component should be a config type")
157        };
158        s
159    }
160}