1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! Declarative merge trait and JSON merge mechanics.
use ;
use crateOrthoResult;
use MergeLayer;
/// Trait implemented by derive-generated merge state machines.
///
/// # Feature Requirements
///
/// Declarative merging requires the `serde_json` feature (enabled by default).
/// The derive-generated implementations use [`serde_json::Value`] for layer
/// composition and will not compile without this feature.
///
/// # Example
///
/// ```rust
/// use ortho_config::declarative::{from_value, merge_value, MergeComposer, MergeLayer};
/// use ortho_config::DeclarativeMerge;
/// use serde::Deserialize;
/// use serde_json::json;
///
/// #[derive(Debug, Deserialize, PartialEq)]
/// struct AppSettings {
/// port: u16,
/// }
///
/// #[derive(Default)]
/// struct AppSettingsMerge {
/// buffer: serde_json::Value,
/// }
///
/// impl DeclarativeMerge for AppSettingsMerge {
/// type Output = AppSettings;
///
/// fn merge_layer(&mut self, layer: MergeLayer<'_>) -> ortho_config::OrthoResult<()> {
/// merge_value(&mut self.buffer, layer.into_value());
/// Ok(())
/// }
///
/// fn finish(self) -> ortho_config::OrthoResult<Self::Output> {
/// from_value(self.buffer)
/// }
/// }
///
/// let mut composer = MergeComposer::new();
/// composer.push_defaults(json!({"port": 3000}));
/// composer.push_cli(json!({"port": 4000}));
///
/// let mut merge = AppSettingsMerge::default();
/// for layer in composer.layers() {
/// merge.merge_layer(layer)?;
/// }
/// let settings = merge.finish()?;
/// assert_eq!(settings.port, 4000);
/// # Ok::<(), std::sync::Arc<ortho_config::OrthoError>>(())
/// ```
/// Overlay `layer` onto `target`, updating `target` in place.
///
/// Behaviour:
/// - When merging an object into a non-object target, target is initialized to
/// `{}` first.
/// - Objects are merged recursively (keys are added or overwritten, and nested
/// objects are overlaid).
/// - Arrays and scalars replace `target` wholesale (no deep merge for arrays).
///
/// # Examples
///
/// ```rust
/// use ortho_config::declarative::merge_value;
/// use serde_json::json;
///
/// let mut acc = json!({"a": 1, "b": {"x": 1}});
/// merge_value(&mut acc, json!({"b": {"y": 2}, "c": 3}));
/// assert_eq!(acc, json!({"a": 1, "b": {"x": 1, "y": 2}, "c": 3}));
///
/// // Arrays replace existing values.
/// merge_value(&mut acc, json!({"b": [1, 2, 3]}));
/// assert_eq!(acc["b"], json!([1, 2, 3]));
/// ```
/// Merge the provided JSON object `map` into `target`.
///
/// Behaviour mirrors [`merge_value`]: non-object targets are converted to empty
/// objects, nested objects merge recursively, and other types replace existing
/// entries. Library users normally experience these semantics via
/// [`merge_value`]; the example below demonstrates the helper's behaviour using
/// that public entrypoint so the doctest compiles against the crate surface.
///
/// # Examples
///
/// ```rust
/// use ortho_config::declarative::merge_value;
/// use serde_json::json;
///
/// let mut target = json!({"greeting": "hi"});
/// merge_value(&mut target, json!({"audience": "world"}));
/// assert_eq!(target, json!({"greeting": "hi", "audience": "world"}));
///
/// // Nested objects merge recursively.
/// merge_value(
/// &mut target,
/// json!({"nested": {"emphasis": "wave"}}),
/// );
/// assert_eq!(target["nested"], json!({"emphasis": "wave"}));
/// ```