Skip to main content

iqdb_types/
metadata.rs

1//! Scalar metadata attached to a stored vector.
2//!
3//! [`Metadata`] is an immutable, ordered map from string keys to scalar
4//! [`Value`]s. It carries the structured attributes a query filters on (an
5//! author, a timestamp encoded as an integer, a published flag). Construct it
6//! once from a map or an iterator; it has no in-place mutators.
7
8use std::collections::{BTreeMap, btree_map};
9
10/// A scalar metadata value.
11///
12/// A closed set of JSON-like scalars. It deliberately has no nesting — metadata
13/// is a flat map of scalars, which keeps filtering simple and predictable. It
14/// holds an `f64`, so it is [`PartialEq`] but not [`Eq`].
15///
16/// # Examples
17///
18/// ```
19/// use iqdb_types::Value;
20///
21/// let title = Value::String("intro".to_string());
22/// let year = Value::Int(2026);
23/// let score = Value::Float(0.5);
24/// let published = Value::Bool(true);
25/// let missing = Value::Null;
26///
27/// assert_eq!(year, Value::Int(2026));
28/// assert_ne!(title, missing);
29/// let _ = (score, published);
30/// ```
31#[derive(Debug, Clone, PartialEq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33pub enum Value {
34    /// A UTF-8 string value.
35    String(String),
36    /// A signed 64-bit integer value.
37    Int(i64),
38    /// A 64-bit floating-point value.
39    Float(f64),
40    /// A boolean value.
41    Bool(bool),
42    /// The absence of a value.
43    Null,
44}
45
46/// An immutable, ordered map of metadata keys to [`Value`]s.
47///
48/// Build one from a [`BTreeMap`] with [`From`], or collect it from an iterator
49/// of `(String, Value)` pairs. Read it with [`get`](Metadata::get),
50/// [`len`](Metadata::len), [`is_empty`](Metadata::is_empty), and
51/// [`iter`](Metadata::iter). There are no setters — to change metadata, build a
52/// new value.
53///
54/// # Examples
55///
56/// ```
57/// use iqdb_types::{Metadata, Value};
58///
59/// let meta: Metadata = [
60///     ("title".to_string(), Value::String("intro".to_string())),
61///     ("year".to_string(), Value::Int(2026)),
62/// ]
63/// .into_iter()
64/// .collect();
65///
66/// assert_eq!(meta.len(), 2);
67/// assert_eq!(meta.get("year"), Some(&Value::Int(2026)));
68/// assert_eq!(meta.get("missing"), None);
69/// ```
70#[derive(Debug, Clone, Default, PartialEq)]
71#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
72pub struct Metadata(BTreeMap<String, Value>);
73
74impl Metadata {
75    /// Returns the value for `key`, or `None` if the key is absent.
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// use iqdb_types::{Metadata, Value};
81    ///
82    /// let meta: Metadata =
83    ///     [("year".to_string(), Value::Int(2026))].into_iter().collect();
84    /// assert_eq!(meta.get("year"), Some(&Value::Int(2026)));
85    /// assert_eq!(meta.get("nope"), None);
86    /// ```
87    #[inline]
88    #[must_use]
89    pub fn get(&self, key: &str) -> Option<&Value> {
90        self.0.get(key)
91    }
92
93    /// Returns the number of entries.
94    ///
95    /// # Examples
96    ///
97    /// ```
98    /// use iqdb_types::{Metadata, Value};
99    ///
100    /// let meta: Metadata =
101    ///     [("a".to_string(), Value::Null)].into_iter().collect();
102    /// assert_eq!(meta.len(), 1);
103    /// ```
104    #[inline]
105    #[must_use]
106    pub fn len(&self) -> usize {
107        self.0.len()
108    }
109
110    /// Returns `true` if there are no entries.
111    ///
112    /// # Examples
113    ///
114    /// ```
115    /// use iqdb_types::Metadata;
116    ///
117    /// assert!(Metadata::default().is_empty());
118    /// ```
119    #[inline]
120    #[must_use]
121    pub fn is_empty(&self) -> bool {
122        self.0.is_empty()
123    }
124
125    /// Returns an iterator over the entries in key order.
126    ///
127    /// # Examples
128    ///
129    /// ```
130    /// use iqdb_types::{Metadata, Value};
131    ///
132    /// let meta: Metadata = [
133    ///     ("b".to_string(), Value::Int(2)),
134    ///     ("a".to_string(), Value::Int(1)),
135    /// ]
136    /// .into_iter()
137    /// .collect();
138    ///
139    /// // BTreeMap iterates in key order.
140    /// let keys: Vec<&String> = meta.iter().map(|(key, _)| key).collect();
141    /// assert_eq!(keys, vec!["a", "b"]);
142    /// ```
143    #[inline]
144    pub fn iter(&self) -> btree_map::Iter<'_, String, Value> {
145        self.0.iter()
146    }
147}
148
149impl From<BTreeMap<String, Value>> for Metadata {
150    #[inline]
151    fn from(map: BTreeMap<String, Value>) -> Self {
152        Self(map)
153    }
154}
155
156impl FromIterator<(String, Value)> for Metadata {
157    #[inline]
158    fn from_iter<I: IntoIterator<Item = (String, Value)>>(iter: I) -> Self {
159        Self(iter.into_iter().collect())
160    }
161}