Skip to main content

qubit_metadata/
metadata.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! Provides the [`Metadata`] type — a structured, ordered, typed key-value store.
10
11use std::collections::BTreeMap;
12
13use qubit_common::{DataType, DataTypeOf};
14use qubit_value::{Value, ValueConstructor, ValueConverter};
15use serde::{Deserialize, Serialize};
16
17use crate::{MetadataError, MetadataResult, MetadataSchema};
18
19/// A structured, ordered, typed key-value store for metadata fields.
20///
21/// `Metadata` stores values as [`qubit_value::Value`], preserving concrete Rust
22/// scalar types such as `i64`, `u32`, `f64`, `String`, and `bool`.  This avoids
23/// the ambiguity of a single JSON number type while still allowing callers to
24/// store explicit [`Value::Json`] values when they really need JSON payloads.
25///
26/// Use [`Metadata::with`] for fluent construction and [`Metadata::set`] when
27/// mutating an existing object.
28#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
29pub struct Metadata(BTreeMap<String, Value>);
30
31impl Metadata {
32    /// Creates an empty metadata object.
33    #[inline]
34    #[must_use]
35    pub fn new() -> Self {
36        Self(BTreeMap::new())
37    }
38
39    /// Returns `true` if there are no entries.
40    #[inline]
41    #[must_use]
42    pub fn is_empty(&self) -> bool {
43        self.0.is_empty()
44    }
45
46    /// Returns the number of key-value pairs.
47    #[inline]
48    #[must_use]
49    pub fn len(&self) -> usize {
50        self.0.len()
51    }
52
53    /// Returns `true` if the given key exists.
54    #[inline]
55    #[must_use]
56    pub fn contains_key(&self, key: &str) -> bool {
57        self.0.contains_key(key)
58    }
59
60    /// Retrieves the value associated with `key` and converts it to `T`.
61    ///
62    /// This convenience method returns `None` when the key is absent or when the
63    /// stored [`Value`] cannot be converted to `T`.
64    #[inline]
65    pub fn get<T>(&self, key: &str) -> Option<T>
66    where
67        T: DataTypeOf,
68        Value: ValueConverter<T>,
69    {
70        self.try_get(key).ok()
71    }
72
73    /// Retrieves the value associated with `key` and converts it to `T`.
74    ///
75    /// # Errors
76    ///
77    /// Returns [`MetadataError::MissingKey`] when the key is absent, or
78    /// [`MetadataError::TypeMismatch`] when the stored value cannot be converted
79    /// to the requested type.
80    pub fn try_get<T>(&self, key: &str) -> MetadataResult<T>
81    where
82        T: DataTypeOf,
83        Value: ValueConverter<T>,
84    {
85        let value = self
86            .0
87            .get(key)
88            .ok_or_else(|| MetadataError::MissingKey(key.to_string()))?;
89        value
90            .to::<T>()
91            .map_err(|error| MetadataError::conversion_error(key, T::DATA_TYPE, value, error))
92    }
93
94    /// Returns a reference to the stored [`Value`] for `key`, or `None` if absent.
95    #[inline]
96    #[must_use]
97    pub fn get_raw(&self, key: &str) -> Option<&Value> {
98        self.0.get(key)
99    }
100
101    /// Returns the concrete data type of the value stored under `key`.
102    #[inline]
103    #[must_use]
104    pub fn data_type(&self, key: &str) -> Option<DataType> {
105        self.0.get(key).map(Value::data_type)
106    }
107
108    /// Retrieves and converts the value associated with `key`, or returns
109    /// `default` if lookup or conversion fails.
110    #[inline]
111    #[must_use]
112    pub fn get_or<T>(&self, key: &str, default: T) -> T
113    where
114        T: DataTypeOf,
115        Value: ValueConverter<T>,
116    {
117        self.try_get(key).unwrap_or(default)
118    }
119
120    /// Inserts a typed value under `key` and returns the previous value if present.
121    #[inline]
122    pub fn set<T>(&mut self, key: &str, value: T) -> Option<Value>
123    where
124        Value: ValueConstructor<T>,
125    {
126        self.0.insert(key.to_string(), to_value(value))
127    }
128
129    /// Inserts a typed value after validating it against `schema`.
130    ///
131    /// # Errors
132    ///
133    /// Returns [`MetadataError::UnknownField`] when `key` is rejected by the
134    /// schema, or [`MetadataError::TypeMismatch`] when the constructed value's
135    /// concrete type does not match the schema field type.
136    #[inline]
137    pub fn set_checked<T>(
138        &mut self,
139        schema: &MetadataSchema,
140        key: &str,
141        value: T,
142    ) -> MetadataResult<Option<Value>>
143    where
144        Value: ValueConstructor<T>,
145    {
146        let value = to_value(value);
147        schema.validate_entry(key, &value)?;
148        Ok(self.set_raw(key, value))
149    }
150
151    /// Returns a new metadata object with a typed value validated and inserted.
152    ///
153    /// # Errors
154    ///
155    /// Returns [`MetadataError::UnknownField`] when `key` is rejected by the
156    /// schema, or [`MetadataError::TypeMismatch`] when the constructed value's
157    /// concrete type does not match the schema field type.
158    #[inline]
159    pub fn with_checked<T>(
160        mut self,
161        schema: &MetadataSchema,
162        key: &str,
163        value: T,
164    ) -> MetadataResult<Self>
165    where
166        Value: ValueConstructor<T>,
167    {
168        self.set_checked(schema, key, value)?;
169        Ok(self)
170    }
171
172    /// Returns a new metadata object with `key` set to `value`.
173    #[inline]
174    #[must_use]
175    pub fn with<T>(mut self, key: &str, value: T) -> Self
176    where
177        Value: ValueConstructor<T>,
178    {
179        self.set(key, value);
180        self
181    }
182
183    /// Inserts a raw [`Value`] directly and returns the previous value if present.
184    #[inline]
185    pub fn set_raw(&mut self, key: &str, value: Value) -> Option<Value> {
186        self.0.insert(key.to_string(), value)
187    }
188
189    /// Returns a new metadata object with a raw [`Value`] inserted.
190    #[inline]
191    #[must_use]
192    pub fn with_raw(mut self, key: &str, value: Value) -> Self {
193        self.set_raw(key, value);
194        self
195    }
196
197    /// Removes the entry for `key` and returns the stored [`Value`] if it existed.
198    #[inline]
199    pub fn remove(&mut self, key: &str) -> Option<Value> {
200        self.0.remove(key)
201    }
202
203    /// Removes all entries.
204    #[inline]
205    pub fn clear(&mut self) {
206        self.0.clear();
207    }
208
209    /// Returns an iterator over `(&str, &Value)` pairs in key-sorted order.
210    #[inline]
211    pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
212        self.0.iter().map(|(key, value)| (key.as_str(), value))
213    }
214
215    /// Returns an iterator over the keys in sorted order.
216    #[inline]
217    pub fn keys(&self) -> impl Iterator<Item = &str> {
218        self.0.keys().map(String::as_str)
219    }
220
221    /// Returns an iterator over the values in key-sorted order.
222    #[inline]
223    pub fn values(&self) -> impl Iterator<Item = &Value> {
224        self.0.values()
225    }
226
227    /// Merges all entries from `other` into `self`, overwriting existing keys.
228    pub fn merge(&mut self, other: Metadata) {
229        for (key, value) in other.0 {
230            self.0.insert(key, value);
231        }
232    }
233
234    /// Returns a new `Metadata` that contains entries from `self` and `other`.
235    ///
236    /// Entries from `other` take precedence on key conflicts.
237    #[must_use]
238    pub fn merged(&self, other: &Metadata) -> Metadata {
239        let mut result = self.clone();
240        for (key, value) in &other.0 {
241            result.0.insert(key.clone(), value.clone());
242        }
243        result
244    }
245
246    /// Retains only the entries for which `predicate` returns `true`.
247    #[inline]
248    pub fn retain<F>(&mut self, mut predicate: F)
249    where
250        F: FnMut(&str, &Value) -> bool,
251    {
252        self.0.retain(|key, value| predicate(key.as_str(), value));
253    }
254
255    /// Converts this metadata object into its underlying map.
256    #[inline]
257    #[must_use]
258    pub fn into_inner(self) -> BTreeMap<String, Value> {
259        self.0
260    }
261}
262
263#[inline]
264fn to_value<T>(value: T) -> Value
265where
266    Value: ValueConstructor<T>,
267{
268    <Value as ValueConstructor<T>>::from_type(value)
269}
270
271impl From<BTreeMap<String, Value>> for Metadata {
272    #[inline]
273    fn from(map: BTreeMap<String, Value>) -> Self {
274        Self(map)
275    }
276}
277
278impl From<Metadata> for BTreeMap<String, Value> {
279    #[inline]
280    fn from(meta: Metadata) -> Self {
281        meta.0
282    }
283}
284
285impl FromIterator<(String, Value)> for Metadata {
286    #[inline]
287    fn from_iter<I: IntoIterator<Item = (String, Value)>>(iter: I) -> Self {
288        Self(iter.into_iter().collect())
289    }
290}
291
292impl IntoIterator for Metadata {
293    type IntoIter = std::collections::btree_map::IntoIter<String, Value>;
294    type Item = (String, Value);
295
296    #[inline]
297    fn into_iter(self) -> Self::IntoIter {
298        self.0.into_iter()
299    }
300}
301
302impl<'a> IntoIterator for &'a Metadata {
303    type IntoIter = std::collections::btree_map::Iter<'a, String, Value>;
304    type Item = (&'a String, &'a Value);
305
306    #[inline]
307    fn into_iter(self) -> Self::IntoIter {
308        self.0.iter()
309    }
310}
311
312impl Extend<(String, Value)> for Metadata {
313    #[inline]
314    fn extend<I: IntoIterator<Item = (String, Value)>>(&mut self, iter: I) {
315        self.0.extend(iter);
316    }
317}