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 {}