Skip to main content

mmdb_writer/
value.rs

1//! The [`Value`] data model — one MMDB data-section value.
2//!
3//! Every piece of data an MMDB file stores (a record payload, a map key, an array element,
4//! the metadata itself) reduces to a `Value`. Construct them directly with [`Value::map`],
5//! [`Value::array`], and the many [`From`] conversions, or — with the `serde` feature — let
6//! [`Writer::insert`] build them from any `serde::Serialize` type.
7//!
8//! [`Writer::insert`]: crate::Writer::insert
9
10use std::collections::BTreeMap;
11
12/// One MMDB data-section value.
13///
14/// The variants correspond one-to-one with the data types defined by the [MMDB format].
15/// There is deliberately no null/unit variant — MMDB has no null type; absence is modeled
16/// by omitting a map entry.
17///
18/// # Equality and hashing
19///
20/// Floating-point variants ([`Value::Float`], [`Value::Double`]) compare and hash by their
21/// raw bit pattern, so bit-identical values (including matching `NaN` payloads) are treated
22/// as equal. This is what lets the writer deduplicate repeated values in the data section.
23///
24/// # Construction
25///
26/// ```
27/// use mmdb_writer::Value;
28///
29/// let scalar = Value::from(42_u32);
30/// let list = Value::array([Value::from("a"), Value::from("b")]);
31/// let map = Value::map([
32///     ("count", Value::from(2_u32)),
33///     ("items", list),
34/// ]);
35/// assert!(matches!(map, Value::Map(_)));
36/// ```
37///
38/// [MMDB format]: https://maxmind.github.io/MaxMind-DB/
39#[derive(Debug, Clone)]
40#[non_exhaustive]
41pub enum Value {
42    /// UTF-8 string (type 2).
43    String(String),
44    /// IEEE 754 binary64 (type 3).
45    Double(f64),
46    /// Arbitrary byte blob (type 4).
47    Bytes(Vec<u8>),
48    /// Unsigned 16-bit integer (type 5).
49    U16(u16),
50    /// Unsigned 32-bit integer (type 6).
51    U32(u32),
52    /// String-keyed map (type 7). Backed by a [`BTreeMap`] so the encoded byte order — and
53    /// therefore the output — is deterministic.
54    Map(BTreeMap<String, Value>),
55    /// Signed 32-bit integer (extended type 8).
56    I32(i32),
57    /// Unsigned 64-bit integer (extended type 9).
58    U64(u64),
59    /// Unsigned 128-bit integer (extended type 10).
60    U128(u128),
61    /// Ordered array (extended type 11).
62    Array(Vec<Value>),
63    /// Boolean (extended type 14).
64    Bool(bool),
65    /// IEEE 754 binary32 (extended type 15).
66    Float(f32),
67}
68
69impl Value {
70    /// Build a [`Value::Map`] from key/value pairs.
71    ///
72    /// Keys convert via [`Into<String>`] and values via [`Into<Value>`], so string literals
73    /// and scalars can be passed directly.
74    ///
75    /// ```
76    /// use mmdb_writer::Value;
77    ///
78    /// let v = Value::map([
79    ///     ("asn", Value::from(64_512_u32)),
80    ///     ("org", Value::from("Example, Inc.")),
81    /// ]);
82    /// ```
83    pub fn map<K, V, I>(entries: I) -> Self
84    where
85        K: Into<String>,
86        V: Into<Value>,
87        I: IntoIterator<Item = (K, V)>,
88    {
89        Self::Map(
90            entries
91                .into_iter()
92                .map(|(k, v)| (k.into(), v.into()))
93                .collect(),
94        )
95    }
96
97    /// Build a [`Value::Array`] from a sequence of values.
98    ///
99    /// Elements convert via [`Into<Value>`].
100    ///
101    /// ```
102    /// use mmdb_writer::Value;
103    ///
104    /// let v = Value::array([Value::from(1_u32), Value::from(2_u32)]);
105    /// ```
106    pub fn array<V, I>(items: I) -> Self
107    where
108        V: Into<Value>,
109        I: IntoIterator<Item = V>,
110    {
111        Self::Array(items.into_iter().map(Into::into).collect())
112    }
113
114    /// Merge `new` onto `existing`, combining only the top level of two maps.
115    ///
116    /// If both are [`Value::Map`], the result contains every key from both; on a key present
117    /// in both, `new`'s value wins (no recursion into nested maps). If either side is not a
118    /// map, `new` is returned unchanged. This is the equivalent of the Go writer's
119    /// `TopLevelMergeWith` inserter.
120    #[must_use]
121    pub fn merge_top_level(existing: &Value, new: &Value) -> Value {
122        match (existing, new) {
123            (Value::Map(old), Value::Map(new_map)) => {
124                let mut merged = old.clone();
125                for (k, v) in new_map {
126                    merged.insert(k.clone(), v.clone());
127                }
128                Value::Map(merged)
129            }
130            _ => new.clone(),
131        }
132    }
133
134    /// Merge `new` onto `existing`, recursing into nested maps and concatenating arrays.
135    ///
136    /// - Two maps merge key by key, recursing on keys present in both.
137    /// - Two arrays concatenate (`existing` elements followed by `new` elements).
138    /// - Any other combination yields `new`.
139    ///
140    /// This is the equivalent of the Go writer's `DeepMergeWith` inserter.
141    #[must_use]
142    pub fn merge_deep(existing: &Value, new: &Value) -> Value {
143        match (existing, new) {
144            (Value::Map(old), Value::Map(new_map)) => {
145                let mut merged = old.clone();
146                for (k, v) in new_map {
147                    let combined = match merged.get(k) {
148                        Some(prev) => Value::merge_deep(prev, v),
149                        None => v.clone(),
150                    };
151                    merged.insert(k.clone(), combined);
152                }
153                Value::Map(merged)
154            }
155            (Value::Array(old), Value::Array(new_arr)) => {
156                let mut merged = old.clone();
157                merged.extend(new_arr.iter().cloned());
158                Value::Array(merged)
159            }
160            _ => new.clone(),
161        }
162    }
163}
164
165impl PartialEq for Value {
166    fn eq(&self, other: &Self) -> bool {
167        match (self, other) {
168            (Self::String(a), Self::String(b)) => a == b,
169            (Self::Double(a), Self::Double(b)) => a.to_bits() == b.to_bits(),
170            (Self::Bytes(a), Self::Bytes(b)) => a == b,
171            (Self::U16(a), Self::U16(b)) => a == b,
172            (Self::U32(a), Self::U32(b)) => a == b,
173            (Self::Map(a), Self::Map(b)) => a == b,
174            (Self::I32(a), Self::I32(b)) => a == b,
175            (Self::U64(a), Self::U64(b)) => a == b,
176            (Self::U128(a), Self::U128(b)) => a == b,
177            (Self::Array(a), Self::Array(b)) => a == b,
178            (Self::Bool(a), Self::Bool(b)) => a == b,
179            (Self::Float(a), Self::Float(b)) => a.to_bits() == b.to_bits(),
180            _ => false,
181        }
182    }
183}
184
185impl Eq for Value {}
186
187impl std::hash::Hash for Value {
188    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
189        std::mem::discriminant(self).hash(state);
190        match self {
191            Self::String(v) => v.hash(state),
192            Self::Double(v) => v.to_bits().hash(state),
193            Self::Bytes(v) => v.hash(state),
194            Self::U16(v) => v.hash(state),
195            Self::U32(v) => v.hash(state),
196            Self::Map(v) => v.hash(state),
197            Self::I32(v) => v.hash(state),
198            Self::U64(v) => v.hash(state),
199            Self::U128(v) => v.hash(state),
200            Self::Array(v) => v.hash(state),
201            Self::Bool(v) => v.hash(state),
202            Self::Float(v) => v.to_bits().hash(state),
203        }
204    }
205}
206
207// --- From conversions -------------------------------------------------------------------
208
209impl From<&str> for Value {
210    fn from(v: &str) -> Self {
211        Self::String(v.to_owned())
212    }
213}
214
215impl From<String> for Value {
216    fn from(v: String) -> Self {
217        Self::String(v)
218    }
219}
220
221impl From<bool> for Value {
222    fn from(v: bool) -> Self {
223        Self::Bool(v)
224    }
225}
226
227impl From<f32> for Value {
228    fn from(v: f32) -> Self {
229        Self::Float(v)
230    }
231}
232
233impl From<f64> for Value {
234    fn from(v: f64) -> Self {
235        Self::Double(v)
236    }
237}
238
239// Unsigned integers map to the narrowest MMDB type that holds them; `u8`/`u16` share the
240// 16-bit type, matching how the serde serializer widens them.
241impl From<u8> for Value {
242    fn from(v: u8) -> Self {
243        Self::U16(u16::from(v))
244    }
245}
246
247impl From<u16> for Value {
248    fn from(v: u16) -> Self {
249        Self::U16(v)
250    }
251}
252
253impl From<u32> for Value {
254    fn from(v: u32) -> Self {
255        Self::U32(v)
256    }
257}
258
259impl From<u64> for Value {
260    fn from(v: u64) -> Self {
261        Self::U64(v)
262    }
263}
264
265impl From<u128> for Value {
266    fn from(v: u128) -> Self {
267        Self::U128(v)
268    }
269}
270
271// Signed integers narrower than 64-bit share the single MMDB signed type (int32).
272impl From<i8> for Value {
273    fn from(v: i8) -> Self {
274        Self::I32(i32::from(v))
275    }
276}
277
278impl From<i16> for Value {
279    fn from(v: i16) -> Self {
280        Self::I32(i32::from(v))
281    }
282}
283
284impl From<i32> for Value {
285    fn from(v: i32) -> Self {
286        Self::I32(v)
287    }
288}
289
290impl From<Vec<u8>> for Value {
291    fn from(v: Vec<u8>) -> Self {
292        Self::Bytes(v)
293    }
294}
295
296impl From<Vec<Value>> for Value {
297    fn from(v: Vec<Value>) -> Self {
298        Self::Array(v)
299    }
300}
301
302impl From<BTreeMap<String, Value>> for Value {
303    fn from(v: BTreeMap<String, Value>) -> Self {
304        Self::Map(v)
305    }
306}
307
308impl FromIterator<Value> for Value {
309    fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
310        Self::Array(iter.into_iter().collect())
311    }
312}
313
314impl FromIterator<(String, Value)> for Value {
315    fn from_iter<I: IntoIterator<Item = (String, Value)>>(iter: I) -> Self {
316        Self::Map(iter.into_iter().collect())
317    }
318}
319
320#[cfg(feature = "serde")]
321mod serde_impls;