conflate/
lib.rs

1// SPDX-FileCopyrightText: 2020 Robin Krahl <robin.krahl@ireas.org>
2// SPDX-License-Identifier: Apache-2.0 or MIT
3
4//! Provides [`Merge`][], a trait for objects that can be merged.
5//!
6//! # Usage
7//!
8//! ```
9//! trait Merge {
10//!     fn merge(&mut self, other: Self);
11//! }
12//! ```
13//!
14//! The [`Merge`][] trait can be used to merge two objects of the same type into one.  The intended
15//! use case is merging configuration from different sources, for example environment variables,
16//! multiple configuration files and command-line arguments, see the [`args.rs`][] example.
17//!
18//! This crate does not provide any `Merge` implementations, but `Merge` can be derived for
19//! structs.  When deriving the `Merge` trait for a struct, you can provide custom merge strategies
20//! for the fields that don’t implement `Merge`.  A merge strategy is a function with the signature
21//! `fn merge<T>(left: &mut T, right: T)` that merges `right` into `left`.  The submodules of this
22//! crate provide strategies for the most common types, but you can also define your own
23//! strategies.
24//!
25//! ## Features
26//!
27//! This crate has the following features:
28//!
29//! - `derive` (default):  Enables the derive macro for the `Merge` trait using the `conflate_derive`
30//!   crate.
31//! - `num` (default): Enables the merge strategies in the `num` module that require the
32//!   `num_traits` crate.
33//! - `std` (default): Enables the merge strategies in the `hashmap`, `btreemap` and `vec` modules
34//!    that require the standard library. If this feature is not set, `conflate` is a `no_std`
35//!    library.
36//!
37//! # Example
38//!
39//! ```
40//! use conflate::Merge;
41//!
42//! #[derive(Merge)]
43//! struct User {
44//!     // Fields with the skip attribute are skipped by Merge
45//!     #[merge(skip)]
46//!     pub name: &'static str,
47//!
48//!     // The strategy attribute is used to customize the merge behavior
49//!     #[merge(strategy = conflate::option::overwrite_none)]
50//!     pub location: Option<&'static str>,
51//!
52//!     #[merge(strategy = conflate::vec::append)]
53//!     pub groups: Vec<&'static str>,
54//! }
55//!
56//! let defaults = User {
57//!     name: "",
58//!     location: Some("Internet"),
59//!     groups: vec!["rust"],
60//! };
61//! let mut ferris = User {
62//!     name: "Ferris",
63//!     location: None,
64//!     groups: vec!["mascot"],
65//! };
66//! ferris.merge(defaults);
67//!
68//! assert_eq!("Ferris", ferris.name);
69//! assert_eq!(Some("Internet"), ferris.location);
70//! assert_eq!(vec!["mascot", "rust"], ferris.groups);
71//! ```
72//!
73//! [`Merge`]: trait.Merge.html
74//! [`args.rs`]: https://git.sr.ht/~ireas/merge-rs/tree/master/examples/args.rs
75
76#![cfg_attr(not(feature = "std"), no_std)]
77
78#[cfg(feature = "derive")]
79pub use conflate_derive::*;
80
81pub mod bool;
82#[cfg(feature = "std")]
83pub mod btreemap;
84#[cfg(feature = "std")]
85pub mod hashmap;
86#[cfg(feature = "num")]
87pub mod num;
88pub mod option;
89pub mod ord;
90#[cfg(feature = "std")]
91pub mod vec;
92
93/// A trait for objects that can be merged.
94///
95/// # Deriving
96///
97/// `Merge` can be derived for structs if the `derive` feature is enabled.  The generated
98/// implementation calls the `merge` method for all fields, or the merge strategy function if set.
99/// You can use these field attributes to configure the generated implementation:
100/// - `skip`: Skip this field in the `merge` method.
101/// - `strategy = f`: Call `f(self.field, other.field)` instead of calling the `merge` function for
102///    this field.
103///
104/// You can also set a default strategy for all fields by setting the `strategy` attribute for the
105/// struct.
106///
107/// # Examples
108///
109/// Deriving `Merge` for a struct:
110///
111/// ```
112/// use conflate::Merge;
113///
114/// #[derive(Debug, PartialEq, Merge)]
115/// struct S {
116///     #[merge(strategy = conflate::option::overwrite_none)]
117///     option: Option<usize>,
118///
119///     #[merge(skip)]
120///     s: String,
121///
122///     #[merge(strategy = conflate::bool::overwrite_false)]
123///     flag: bool,
124/// }
125///
126/// let mut val = S {
127///     option: None,
128///     s: "some ignored value".to_owned(),
129///     flag: false,
130/// };
131///
132/// val.merge(S {
133///     option: Some(42),
134///     s: "some other ignored value".to_owned(),
135///     flag: true,
136/// });
137///
138/// assert_eq!(S {
139///     option: Some(42),
140///     s: "some ignored value".to_owned(),
141///     flag: true,
142/// }, val);
143/// ```
144///
145/// Setting a default merge strategy:
146///
147/// ```
148/// use conflate::Merge;
149///
150/// #[derive(Debug, PartialEq, Merge)]
151/// #[merge(strategy = conflate::option::overwrite_none)]
152/// struct S {
153///     option1: Option<usize>,
154///     option2: Option<usize>,
155///     option3: Option<usize>,
156/// }
157///
158/// let mut val = S {
159///     option1: None,
160///     option2: Some(1),
161///     option3: None,
162/// };
163///
164/// val.merge(S {
165///     option1: Some(2),
166///     option2: Some(2),
167///     option3: None,
168/// });
169///
170/// assert_eq!(S {
171///     option1: Some(2),
172///     option2: Some(1),
173///     option3: None,
174/// }, val);
175/// ```
176pub trait Merge {
177    /// Merge another object into this object.
178    fn merge(&mut self, other: Self);
179}
180
181/// A trait for objects that can be merged from another object
182/// of the same type creating a new object.
183///
184/// Using a builder like pattern to merge two objects
185/// with the `option::overwrite_none` strategy.
186///
187/// ```
188/// use conflate::{Merge, MergeFrom};
189///
190/// #[derive(Debug, PartialEq, Merge)]
191/// #[merge(strategy = conflate::option::overwrite_none)]
192/// struct S {
193///     option1: Option<usize>,
194///     option2: Option<usize>,
195///     option3: Option<usize>,
196///     option4: Option<usize>,
197/// }
198///
199/// let cli = S {
200///     option1: None,
201///     option2: Some(1),
202///     option3: None,
203///     option4: None,
204/// };
205///
206/// let config = S {
207///     option1: Some(2),
208///     option2: Some(2),
209///     option3: None,
210///     option4: None,
211/// };
212///
213/// impl Default for S {
214///    fn default() -> Self {
215///       S {
216///         option1: None,
217///         option2: None,
218///         option3: Some(4),
219///         option4: None,
220///       }
221///    }
222/// }
223///
224/// let val = cli
225///           .merge_from(config)
226///           .merge_from(S::default());  
227///
228/// assert_eq!(S {
229///     option1: Some(2),
230///     option2: Some(1),
231///     option3: Some(4),
232///     option4: None,
233/// }, val);
234/// ```
235///
236/// Using a builder like pattern to merge two objects
237/// with the `option::overwrite_with_some` strategy.
238///
239/// ```
240/// use conflate::{Merge, MergeFrom};
241///
242/// #[derive(Debug, PartialEq, Merge)]
243/// #[merge(strategy = conflate::option::overwrite_with_some)]
244/// struct S {
245///     option1: Option<usize>,
246///     option2: Option<usize>,
247///     option3: Option<usize>,
248///     option4: Option<usize>,
249/// }
250///
251/// impl Default for S {
252///    fn default() -> Self {
253///       S {
254///         option1: None,
255///         option2: None,
256///         option3: Some(4),
257///         option4: None,
258///       }
259///    }
260/// }
261///
262/// let config = S {
263///     option1: Some(2),
264///     option2: Some(2),
265///     option3: None,
266///     option4: None,
267/// };
268///
269/// let cli = S {
270///     option1: None,
271///     option2: Some(1),
272///     option3: None,
273///     option4: None,
274/// };
275///
276/// let val = S::default()
277///           .merge_from(config)
278///           .merge_from(cli);  
279///
280/// assert_eq!(S {
281///     option1: Some(2),
282///     option2: Some(1),
283///     option3: Some(4),
284///     option4: None,
285/// }, val);
286/// ```
287pub trait MergeFrom: Merge {
288    /// Merges two instances of a type into a new instance.
289    ///
290    /// The method merges `self` with `other`.
291    ///
292    /// # Arguments
293    ///
294    /// * `self` - The instance to merge into.
295    /// * `other` - The instance to merge into `self`.
296    ///
297    /// # Returns
298    ///
299    /// A new instance that is the result of merging `self`, `other`.
300    fn merge_from(mut self, other: Self) -> Self
301    where
302        Self: Sized,
303    {
304        self.merge(other);
305        self
306    }
307}
308
309// Blanket implementation for all types that implement `Merge`.
310impl<T: Merge> MergeFrom for T {}
311
312/// A trait that defines a merge precedence strategy for merging multiple instances of a type.
313///
314/// This trait extends the `MergeFrom` trait and provides a method to merge three instances
315/// of a type, with the precedence order being `self`, `medium`, and `low`.
316///
317/// This trait is useful when merging configuration values from different sources with different
318/// precedence levels. For example, a configuration value can be defined in multiple places, such as
319/// command-line arguments, environment variables, and configuration files. The default implementation
320/// is to merge the first two values and then merge the result with the third value.
321///
322/// # Example
323///
324/// ```rust
325/// use conflate::{Merge, MergeFrom, MergePrecedence};
326///
327/// #[derive(Debug, PartialEq, Merge)]
328/// #[merge(strategy = conflate::option::overwrite_none)]
329/// struct MyConfig {
330///     a: Option<u8>,
331///     b: Option<u8>,
332///     c: Option<u8>,
333/// }
334///
335/// let cli = MyConfig { a: Some(1), b: None, c: None };
336/// let config = MyConfig { a: None, b: Some(2), c: None };
337/// let defaults = MyConfig { a: None, b: Some(1), c: Some(3) };
338///
339/// let merged = cli.merge_precedence(config, defaults);
340///
341/// assert_eq!(MyConfig { a: Some(1), b: Some(2), c: Some(3) }, merged);
342/// ```
343pub trait MergePrecedence: MergeFrom {
344    /// Merges three instances of a type, with the precedence order being `self`, `medium`, and `low`.
345    ///
346    /// The method first merges `self` with `medium`, and then merges the result with `low`.
347    ///
348    /// # Arguments
349    ///
350    /// * `self` - The instance with the highest precedence.
351    /// * `medium` - The instance with medium precedence.
352    /// * `low` - The instance with the lowest precedence.
353    ///
354    /// # Returns
355    ///
356    /// A new instance that is the result of merging `self`, `medium`, and `low` in the specified order.
357    fn merge_precedence(self, medium: Self, low: Self) -> Self
358    where
359        Self: Sized,
360    {
361        let merged = self.merge_from(medium);
362        merged.merge_from(low)
363    }
364}
365
366// Blanket implementation for all types that implement `Merge`.
367impl<T: MergeFrom> MergePrecedence for T {}