Skip to main content

qubit_metadata/
metadata.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Provides the [`Metadata`] type — a structured, key-sorted, typed key-value
9//! store.
10
11#[cfg(feature = "json")]
12use std::cell::RefCell;
13use std::collections::BTreeMap;
14use std::fmt;
15#[cfg(feature = "json")]
16use std::io::Write;
17#[cfg(feature = "json")]
18use std::rc::Rc;
19
20#[cfg(feature = "json")]
21use qubit_budget::json::JsonDecodeSession;
22#[cfg(feature = "json")]
23use qubit_budget::json::JsonEncodeLimits;
24#[cfg(feature = "json")]
25use qubit_budget::json::JsonEncodeSession;
26use qubit_datatype::ConversionLimits;
27use qubit_datatype::ConversionPolicy;
28use qubit_datatype::DataConversionTarget;
29use qubit_datatype::DataType;
30#[cfg(feature = "json")]
31use qubit_json::decode::JsonDecoder;
32#[cfg(feature = "json")]
33use qubit_json::encode::JsonEncoder;
34use qubit_redact::Redact;
35use qubit_redact::RedactionWriter;
36use qubit_redact::Redactor;
37use qubit_value::IntoValueDefault;
38use qubit_value::StrictValueRead;
39use qubit_value::Value;
40use qubit_value::ValueError;
41#[cfg(feature = "json")]
42use qubit_value::ValueWireEncodePreflight;
43use qubit_value::ValueWirePayloadV1;
44use qubit_value::ValueWirePayloadV1Seed;
45use serde::Deserialize;
46use serde::Deserializer;
47use serde::Serialize;
48use serde::Serializer;
49use serde::de;
50use serde::de::DeserializeSeed;
51#[cfg(feature = "json")]
52use serde::de::Error as DeError;
53use serde::ser::Error as SerError;
54
55use crate::MetadataError;
56use crate::MetadataResult;
57#[cfg(feature = "schema")]
58use crate::MetadataSchema;
59use crate::constants::STRICT_STRING_MAP_MAX_ENTRIES;
60use crate::constants::STRICT_STRING_MAP_MAX_KEY_BYTES;
61use crate::internal::MetadataValues;
62#[cfg(feature = "json")]
63use crate::metadata_limits::MetadataLimits;
64use crate::wire::METADATA_WIRE_VERSION_V1;
65use crate::wire::MetadataWireV1;
66use crate::wire::MetadataWireV1Seed;
67use crate::wire::MetadataWireValuesRef;
68use crate::wire::StrictStringMap;
69use crate::wire::StrictStringMapValueSeed;
70
71/// A structured, key-sorted, typed key-value store for metadata fields.
72///
73/// `Metadata` stores values as [`qubit_value::Value`], preserving concrete Rust
74/// scalar types such as `i64`, `u32`, `f64`, `String`, and `bool`.  This avoids
75/// the ambiguity of a single JSON number type while still allowing callers to
76/// store explicit `Value::Json` values when they really need JSON payloads.
77/// [`Value::Unset`] retains a declared type but represents no concrete metadata
78/// value: typed reads report [`MetadataError::ValueAccess`]. When the optional
79/// `schema` or `filter` features are enabled, their validation and matching
80/// APIs treat it as a missing concrete value.
81///
82/// Use [`Metadata::with`] for fluent construction and [`Metadata::set`] when
83/// mutating an existing object. The typed [`Metadata::get`] and
84/// [`Metadata::get_ref`] accessors read strictly; [`Metadata::convert`]
85/// explicitly converts stored values. Use [`Metadata::get_raw`] when
86/// the stored runtime [`qubit_value::Value`] must be inspected without
87/// conversion.
88///
89/// # Examples
90///
91/// ```
92/// use qubit_metadata::Metadata;
93///
94/// # fn main() -> qubit_metadata::MetadataResult<()> {
95/// let metadata = Metadata::new().with("tenant", "acme");
96/// assert_eq!(metadata.get_ref::<str>("tenant")?, "acme");
97/// # Ok(())
98/// # }
99/// ```
100#[derive(Clone, PartialEq, Default)]
101pub struct Metadata(
102    /// Stored values indexed by metadata key.
103    BTreeMap<String, Value>,
104);
105
106impl Metadata {
107    /// Creates an empty metadata object.
108    ///
109    /// # Returns
110    ///
111    /// An empty metadata object.
112    #[inline]
113    #[must_use]
114    pub fn new() -> Self {
115        Self(BTreeMap::new())
116    }
117
118    /// Checks the metadata wire contract and all values against JSON limits.
119    ///
120    /// # Parameters
121    ///
122    /// * `limits` - Output and JSON-value budgets for this operation.
123    ///
124    /// # Returns
125    ///
126    /// `Ok(())` when the wire contract and every value pass preflight.
127    ///
128    /// # Errors
129    ///
130    /// Returns the wire-contract error before checking JSON value budgets.
131    /// Values are checked in key order with one shared preflight, so value
132    /// budgets accumulate across all metadata values.
133    #[cfg(feature = "json")]
134    fn preflight_json_encoding(&self, limits: JsonEncodeLimits) -> Result<(), crate::MetadataWireEncodeError> {
135        self.validate_wire_contract()
136            .map_err(crate::MetadataWireEncodeError::Domain)?;
137        let mut preflight = ValueWireEncodePreflight::new_value_limits(*limits.value_limits());
138        for value in self.0.values() {
139            preflight
140                .check_value(value)
141                .map_err(crate::MetadataWireEncodeError::from)?;
142        }
143        Ok(())
144    }
145
146    /// Decodes a strict metadata JSON envelope using the metadata profile.
147    ///
148    /// # Parameters
149    ///
150    /// * `input` - Complete untrusted JSON input.
151    ///
152    /// # Returns
153    ///
154    /// The decoded metadata object.
155    ///
156    /// # Errors
157    ///
158    /// Returns [`crate::MetadataWireDecodeError::Budget`] or
159    /// [`crate::MetadataWireDecodeError::Quantity`] when shared JSON traversal
160    /// exceeds a configured limit, [`crate::MetadataWireDecodeError::Syntax`]
161    /// for empty input or lexical JSON errors, and
162    /// [`crate::MetadataWireDecodeError::Decode`] for shared decoder failures
163    /// such as invalid UTF-8, unexpected top-level types, or deserialization
164    /// failures. Domain limits return
165    /// [`crate::MetadataWireDecodeError::Domain`]; invalid metadata
166    /// limit configuration returns
167    /// [`crate::MetadataWireDecodeError::InvalidLimits`]; unsupported
168    /// versions return
169    /// [`crate::MetadataWireDecodeError::UnsupportedVersion`]. A metadata
170    /// value conversion failure after wire materialization returns
171    /// [`crate::MetadataWireDecodeError::InvalidJson`]; such a failure may no
172    /// longer have source coordinates.
173    #[cfg(feature = "json")]
174    #[inline]
175    pub fn decode_json_slice(input: &[u8]) -> Result<Self, crate::MetadataWireDecodeError> {
176        Self::decode_json_slice_with_limits(input, MetadataLimits::default())
177    }
178
179    /// Decodes a strict metadata JSON envelope after applying `limits`.
180    ///
181    /// # Parameters
182    ///
183    /// * `input` - Complete untrusted JSON input.
184    /// * `limits` - Shared JSON limits for this decoding session.
185    ///
186    /// # Returns
187    ///
188    /// The decoded metadata object.
189    ///
190    /// # Errors
191    ///
192    /// Returns `Budget` or `Quantity` when shared JSON traversal exceeds a
193    /// configured limit, `Domain` for entry/key limits, `UnsupportedVersion`
194    /// for a version mismatch, and `InvalidLimits` for invalid metadata limits.
195    /// Empty input and lexical JSON errors return `Syntax`; shared decoder
196    /// failures such as invalid UTF-8, unexpected top-level types, or
197    /// deserialization failures return `Decode`. A metadata value conversion
198    /// failure after wire materialization returns `InvalidJson` and may no
199    /// longer have source coordinates.
200    #[cfg(feature = "json")]
201    pub fn decode_json_slice_with_limits(
202        input: &[u8],
203        limits: MetadataLimits,
204    ) -> Result<Self, crate::MetadataWireDecodeError> {
205        limits
206            .validate()
207            .map_err(crate::MetadataWireDecodeError::InvalidLimits)?;
208        let mut decoder = JsonDecoder::new(JsonDecodeSession::from_limits(limits.json_decode()));
209        let error_slot = Rc::new(RefCell::new(None));
210        let wire = decoder
211            .decode_unique_seed_utf8(
212                MetadataWireV1Seed::new(
213                    StrictStringMapValueSeed::new(
214                        limits.max_metadata_entries(),
215                        limits.max_key_bytes(),
216                        ValueWirePayloadV1Seed::new(),
217                    )
218                    .with_error_slot(Rc::clone(&error_slot)),
219                ),
220                input,
221            )
222            .map_err(|error| {
223                error_slot.borrow_mut().take().map_or_else(
224                    || Into::<crate::MetadataWireDecodeError>::into(error),
225                    crate::MetadataWireDecodeError::Domain,
226                )
227            })?;
228        if wire.version != METADATA_WIRE_VERSION_V1 {
229            return Err(crate::MetadataWireDecodeError::UnsupportedVersion {
230                expected: METADATA_WIRE_VERSION_V1,
231                actual: wire.version,
232            });
233        }
234        let metadata = Self(Self::from_wire(wire).map_err(|error| {
235            crate::MetadataWireDecodeError::InvalidJson(<serde_json::Error as DeError>::custom(error))
236        })?);
237        Ok(metadata)
238    }
239
240    /// Encodes this metadata object with the default JSON budget profile.
241    ///
242    /// # Returns
243    ///
244    /// Compact JSON bytes accepted by the strict metadata wire format.
245    ///
246    /// # Errors
247    ///
248    /// Returns [`crate::MetadataWireEncodeError`] when the JSON value or
249    /// metadata wire contract rejects the value, output exceeds a configured
250    /// budget, serialization fails, or the destination writer rejects bytes.
251    /// Wire-contract failures preserve their
252    /// [`MetadataError::WireLimitExceeded`] source in the `Domain` variant.
253    #[cfg(feature = "json")]
254    pub fn to_json_vec(&self) -> Result<Vec<u8>, crate::MetadataWireEncodeError> {
255        self.to_json_vec_with_limits(crate::metadata_limits::default_json_encode_limits())
256    }
257
258    /// Encodes this metadata object with caller-provided JSON budgets.
259    ///
260    /// # Parameters
261    ///
262    /// * `limits` - Output and JSON-value budgets for this operation.
263    ///
264    /// # Returns
265    ///
266    /// Compact JSON bytes accepted by the strict metadata wire format.
267    ///
268    /// # Errors
269    ///
270    /// Returns [`crate::MetadataWireEncodeError`] when the metadata wire
271    /// contract rejects the value, the JSON value or output exceeds a
272    /// configured budget, or serialization fails. Wire-contract failures
273    /// preserve their [`MetadataError::WireLimitExceeded`] source in the
274    /// `Domain` variant. The outer metadata wire contract is checked before
275    /// value budgets.
276    #[cfg(feature = "json")]
277    pub fn to_json_vec_with_limits(&self, limits: JsonEncodeLimits) -> Result<Vec<u8>, crate::MetadataWireEncodeError> {
278        self.preflight_json_encoding(limits)?;
279        let session = JsonEncodeSession::from_limits(limits);
280        JsonEncoder::new(session).to_vec(self).map_err(Into::into)
281    }
282
283    /// Validates that this metadata object can be encoded within
284    /// caller-provided JSON budgets.
285    ///
286    /// This performs the same wire-contract, value-preflight, serialization,
287    /// and output-budget checks as [`Self::to_json_vec_with_limits`]
288    /// without retaining the encoded JSON bytes. Use it when validating
289    /// metadata before another operation encodes it.
290    ///
291    /// # Parameters
292    ///
293    /// * `limits` - Output and JSON-value budgets for this operation.
294    ///
295    /// # Returns
296    ///
297    /// `Ok(())` when the metadata can be encoded within the supplied budgets.
298    ///
299    /// # Errors
300    ///
301    /// Returns [`crate::MetadataWireEncodeError`] with the same structured
302    /// error categories and validation ordering as
303    /// [`Self::to_json_vec_with_limits`]. The metadata V1 wire contract
304    /// is checked before JSON value budgets; output byte limits apply to
305    /// compact encoded JSON.
306    #[cfg(feature = "json")]
307    #[inline]
308    pub fn validate_json_with_limits(&self, limits: JsonEncodeLimits) -> Result<(), crate::MetadataWireEncodeError> {
309        self.preflight_json_encoding(limits)?;
310        JsonEncoder::new(JsonEncodeSession::from_limits(limits))
311            .write_incremental(&mut std::io::sink(), self)
312            .map_err(Into::into)
313    }
314
315    /// Encodes this metadata object to a writer with the default JSON budget
316    /// profile ([`crate::default_json_encode_limits`]).
317    ///
318    /// # Parameters
319    ///
320    /// * `writer` - Destination receiving the complete compact JSON document.
321    ///
322    /// The complete compact JSON document is buffered and validated before
323    /// the first write to `writer`. A budget or serialization failure leaves
324    /// the destination untouched by this operation. A destination I/O error
325    /// may leave a written prefix; this method does not provide atomic writes
326    /// or rollback. Applications own persistence and retry semantics.
327    ///
328    /// Output budgets use the default encoding profile. Decode limits do not
329    /// affect them; use [`Self::to_json_writer_with_limits`] to provide
330    /// explicit output budgets.
331    ///
332    /// # Returns
333    ///
334    /// `Ok(())` after the buffered document is written successfully.
335    ///
336    /// # Errors
337    ///
338    /// Returns [`crate::MetadataWireEncodeError`] when the metadata wire
339    /// contract rejects the value, encoding exceeds a budget, serialization
340    /// fails, or `writer` rejects the output. Wire-contract failures
341    /// preserve their [`MetadataError::WireLimitExceeded`] source in the
342    /// `Domain` variant. The outer metadata wire contract is checked before
343    /// value budgets.
344    ///
345    /// # Type Parameters
346    ///
347    /// * `W` - Writer that receives the encoded document.
348    #[cfg(feature = "json")]
349    pub fn to_json_writer<W>(&self, writer: W) -> Result<(), crate::MetadataWireEncodeError>
350    where
351        W: Write,
352    {
353        self.to_json_writer_with_limits(writer, crate::metadata_limits::default_json_encode_limits())
354    }
355
356    /// Encodes this metadata object to a writer with caller-provided budgets.
357    ///
358    /// # Parameters
359    ///
360    /// * `writer` - Destination receiving the complete compact JSON document.
361    /// * `limits` - Explicit output and JSON-value budgets for this operation.
362    ///
363    /// The complete compact JSON document is buffered and validated before
364    /// the first write to `writer`. A budget or serialization failure leaves
365    /// the destination untouched by this operation. A destination I/O error
366    /// may leave a written prefix; this method does not provide atomic writes
367    /// or rollback. Applications own persistence and retry semantics. Output
368    /// budgets come only from the explicitly supplied `limits`; decode limits
369    /// do not affect them.
370    ///
371    /// # Returns
372    ///
373    /// `Ok(())` after the buffered document is written successfully.
374    ///
375    /// # Errors
376    ///
377    /// Returns [`crate::MetadataWireEncodeError`] when the metadata wire
378    /// contract rejects the value, encoding exceeds a budget, serialization
379    /// fails, or `writer` rejects the output. Wire-contract failures
380    /// preserve their [`MetadataError::WireLimitExceeded`] source in the
381    /// `Domain` variant.
382    ///
383    /// # Type Parameters
384    ///
385    /// * `W` - Writer that receives the encoded document.
386    #[cfg(feature = "json")]
387    pub fn to_json_writer_with_limits<W>(
388        &self,
389        mut writer: W,
390        limits: JsonEncodeLimits,
391    ) -> Result<(), crate::MetadataWireEncodeError>
392    where
393        W: Write,
394    {
395        self.preflight_json_encoding(limits)?;
396        let session = JsonEncodeSession::from_limits(limits);
397        JsonEncoder::new(session)
398            .write_buffered(&mut writer, self)
399            .map_err(Into::into)
400    }
401
402    /// Returns `true` if there are no entries.
403    ///
404    /// # Returns
405    ///
406    /// `true` when this object contains no entries.
407    #[inline]
408    #[must_use]
409    pub fn is_empty(&self) -> bool {
410        self.0.is_empty()
411    }
412
413    /// Returns the number of key-value pairs.
414    ///
415    /// # Returns
416    ///
417    /// The number of stored entries.
418    #[inline]
419    #[must_use]
420    pub fn len(&self) -> usize {
421        self.0.len()
422    }
423
424    /// Returns `true` if the given key exists, including when it stores
425    /// [`Value::Unset`].
426    ///
427    /// # Parameters
428    ///
429    /// * `key` - Metadata key to inspect.
430    ///
431    /// # Returns
432    ///
433    /// `true` when an entry exists for `key`.
434    #[inline]
435    #[must_use]
436    pub fn contains_key(&self, key: &str) -> bool {
437        self.0.contains_key(key)
438    }
439
440    /// Strictly reads `key` as `T` without coercing the stored runtime type.
441    /// Use [`Self::convert`] when the stored value must be explicitly
442    /// converted.
443    ///
444    /// # Type Parameters
445    ///
446    /// * `T` - The requested type, which must support strict value reads.
447    ///
448    /// # Parameters
449    ///
450    /// * `key` - The metadata key to read.
451    ///
452    /// # Returns
453    ///
454    /// The owned value read as `T`.
455    ///
456    /// # Errors
457    ///
458    /// Returns [`MetadataError::MissingKey`] when `key` is absent, or
459    /// [`MetadataError::ValueAccess`] when the stored value is unset or its
460    /// runtime type does not match `T`. The value access error preserves the
461    /// underlying type and unset details.
462    ///
463    /// # Examples
464    ///
465    /// ```
466    /// use qubit_metadata::Metadata;
467    /// use qubit_metadata::MetadataError;
468    ///
469    /// let metadata = Metadata::new().with("port", "8080");
470    /// assert!(matches!(metadata.get::<u16>("port"),
471    ///     Err(MetadataError::ValueAccess { .. })));
472    /// assert_eq!(metadata.convert::<u16>("port").unwrap(), 8080);
473    /// assert_eq!(metadata.get_ref::<str>("port").unwrap(), "8080");
474    /// ```
475    pub fn get<T: StrictValueRead>(&self, key: &str) -> MetadataResult<T> {
476        T::read_scalar(self.entry(key)?).map_err(|source| Self::map_access_error(key, source))
477    }
478
479    /// Borrows the concrete payload under `key` without copying it.
480    ///
481    /// # Type Parameters
482    ///
483    /// * `T` - The borrowed payload type. Its reference conversion must accept
484    ///   a borrow of the stored value.
485    ///
486    /// # Parameters
487    ///
488    /// * `key` - The metadata key to read.
489    ///
490    /// # Returns
491    ///
492    /// A reference to the payload. Its lifetime is tied to the borrow of
493    /// `self`, not to `key`.
494    ///
495    /// # Errors
496    ///
497    /// Returns [`MetadataError::MissingKey`] when `key` is absent, or
498    /// [`MetadataError::ValueAccess`] when the stored value is unset or cannot
499    /// be borrowed as `T`.
500    pub fn get_ref<'a, T: ?Sized>(&'a self, key: &str) -> MetadataResult<&'a T>
501    where
502        &'a T: TryFrom<&'a Value, Error = ValueError>,
503    {
504        self.entry(key)?
505            .get_ref::<T>()
506            .map_err(|source| Self::map_access_error(key, source))
507    }
508
509    /// Strictly reads `key`, returning `None` only for an absent key or unset
510    /// storage declared as `T`.
511    ///
512    /// # Type Parameters
513    ///
514    /// * `T` - The requested type, which must support strict value reads.
515    ///
516    /// # Parameters
517    ///
518    /// * `key` - The metadata key to read.
519    ///
520    /// # Returns
521    ///
522    /// `Some(value)` when a concrete value is read as `T`; `None` when the key
523    /// is absent or stores an unset value declared as `T`.
524    ///
525    /// # Errors
526    ///
527    /// Returns [`MetadataError::ValueAccess`] when the stored concrete value
528    /// has a different runtime type, or when unset storage is declared as a
529    /// different type than `T`.
530    ///
531    /// # Examples
532    ///
533    /// ```
534    /// use qubit_datatype::DataType;
535    /// use qubit_metadata::Metadata;
536    /// use qubit_value::Value;
537    ///
538    /// let metadata = Metadata::new().with("count", Value::Unset(DataType::Int64));
539    /// assert_eq!(metadata.get_optional::<i64>("absent").unwrap(), None);
540    /// assert_eq!(metadata.get_optional::<i64>("count").unwrap(), None);
541    /// assert!(metadata.get_optional::<i32>("count").is_err());
542    /// ```
543    pub fn get_optional<T: StrictValueRead>(&self, key: &str) -> MetadataResult<Option<T>> {
544        match self.get(key) {
545            Ok(value) => Ok(Some(value)),
546            Err(error) if Self::is_defaultable(&error, false) => Ok(None),
547            Err(error) => Err(error),
548        }
549    }
550
551    /// Strictly reads `key`, adapting `default` only for an absent key or
552    /// unset storage declared as `T`.
553    ///
554    /// The default adapter is invoked only when this read permits fallback.
555    /// Rust still evaluates the argument expression before calling this method.
556    ///
557    /// # Type Parameters
558    ///
559    /// * `T` - The requested type, which must support strict value reads.
560    ///
561    /// # Parameters
562    ///
563    /// * `key` - The metadata key to read.
564    /// * `default` - A value or adapter that can provide a default `T`.
565    ///
566    /// # Returns
567    ///
568    /// The stored value read as `T`, or the adapted default when the key is
569    /// absent or contains matching unset storage.
570    ///
571    /// # Errors
572    ///
573    /// Returns [`MetadataError::ValueAccess`] for a type mismatch, including
574    /// unset storage declared as a different type. Such errors are propagated
575    /// without using the default.
576    ///
577    /// # Examples
578    ///
579    /// ```
580    /// use qubit_datatype::DataType;
581    /// use qubit_metadata::Metadata;
582    /// use qubit_value::Value;
583    ///
584    /// let metadata = Metadata::new()
585    ///     .with("count", Value::Unset(DataType::Int64))
586    ///     .with("text", "invalid");
587    /// assert_eq!(metadata.get_or::<i64>("count", 7_i64).unwrap(), 7);
588    /// assert_eq!(metadata.get_or::<i64>("absent", 7_i64).unwrap(), 7);
589    /// assert!(metadata.get_or::<i64>("text", 7_i64).is_err());
590    /// ```
591    pub fn get_or<T: StrictValueRead>(&self, key: &str, default: impl IntoValueDefault<T>) -> MetadataResult<T> {
592        self.get_optional(key)
593            .map(|value| value.unwrap_or_else(|| default.into_value_default()))
594    }
595
596    /// Converts the stored value under `key` to `T` using the default
597    /// conversion policy and limits. For strict reads, see [`Self::get`].
598    ///
599    /// # Type Parameters
600    ///
601    /// * `T` - The conversion target type.
602    ///
603    /// # Parameters
604    ///
605    /// * `key` - The metadata key to convert.
606    ///
607    /// # Returns
608    ///
609    /// The stored value converted to `T`.
610    ///
611    /// # Errors
612    ///
613    /// Returns [`MetadataError::MissingKey`] when `key` is absent, or
614    /// [`MetadataError::ValueAccess`] when conversion fails. The latter retains
615    /// the underlying missing, invalid, unsupported, precision-loss, or
616    /// resource-limit error and its source chain.
617    pub fn convert<T: DataConversionTarget>(&self, key: &str) -> MetadataResult<T> {
618        self.convert_with(key, ConversionPolicy::default_ref(), ConversionLimits::default_ref())
619    }
620
621    /// Converts the stored value under `key` to `T` with an explicit
622    /// [`ConversionPolicy`] and [`ConversionLimits`]. Each call has an
623    /// independent conversion budget and leaves the stored value unchanged.
624    /// For default policy and limits, see [`Self::convert`].
625    ///
626    /// # Type Parameters
627    ///
628    /// * `T` - The conversion target type.
629    ///
630    /// # Parameters
631    ///
632    /// * `key` - The metadata key to convert.
633    /// * `policy` - The policy controlling which conversions are allowed.
634    /// * `limits` - The per-call resource limits and budget.
635    ///
636    /// # Returns
637    ///
638    /// The stored value converted to `T`.
639    ///
640    /// # Errors
641    ///
642    /// Returns [`MetadataError::MissingKey`] when `key` is absent, or
643    /// [`MetadataError::ValueAccess`] when conversion fails. The latter
644    /// preserves the underlying conversion facts and any exceeded limits.
645    pub fn convert_with<T: DataConversionTarget>(
646        &self,
647        key: &str,
648        policy: &ConversionPolicy,
649        limits: &ConversionLimits,
650    ) -> MetadataResult<T> {
651        self.entry(key)?
652            .to_with(policy, limits)
653            .map_err(|source| Self::map_access_error(key, source))
654    }
655
656    /// Converts the value under `key`, returning `None` for an absent key,
657    /// unset storage, or a scalar classified as missing by `policy`.
658    ///
659    /// # Type Parameters
660    ///
661    /// * `T` - The conversion target type.
662    ///
663    /// # Parameters
664    ///
665    /// * `key` - The metadata key to convert.
666    /// * `policy` - The policy controlling conversion and missing-value
667    ///   handling.
668    /// * `limits` - The per-call resource limits and budget.
669    ///
670    /// # Returns
671    ///
672    /// `Some(value)` when conversion succeeds; `None` for an absent key, unset
673    /// storage, or a policy-missing scalar.
674    ///
675    /// # Errors
676    ///
677    /// Propagates all other errors as [`MetadataError::ValueAccess`], retaining
678    /// the key and the underlying conversion error. See [`Self::convert_with`]
679    /// for explicit policy and budget behavior.
680    pub fn convert_optional_with<T: DataConversionTarget>(
681        &self,
682        key: &str,
683        policy: &ConversionPolicy,
684        limits: &ConversionLimits,
685    ) -> MetadataResult<Option<T>> {
686        match self.convert_with(key, policy, limits) {
687            Ok(value) => Ok(Some(value)),
688            Err(error) if Self::is_defaultable(&error, true) => Ok(None),
689            Err(error) => Err(error),
690        }
691    }
692
693    /// Converts the value under `key`, adapting `default` only when the result
694    /// is a defaultable missing value. See [`Self::convert_optional_with`] for
695    /// the conditions that produce `None`.
696    ///
697    /// The default adapter is invoked only when this conversion permits
698    /// fallback. Rust still evaluates the argument expression before calling
699    /// this method.
700    ///
701    /// # Type Parameters
702    ///
703    /// * `T` - The conversion target type.
704    ///
705    /// # Parameters
706    ///
707    /// * `key` - The metadata key to convert.
708    /// * `default` - A value or adapter that can provide a default `T`.
709    /// * `policy` - The policy controlling conversion and missing-value
710    ///   handling.
711    /// * `limits` - The per-call resource limits and budget.
712    ///
713    /// # Returns
714    ///
715    /// The converted value, or the adapted default when the conversion result
716    /// is a defaultable missing value.
717    ///
718    /// # Errors
719    ///
720    /// Errors other than defaultable missing values propagate as
721    /// [`MetadataError::ValueAccess`]. Invalid values, unsupported conversions,
722    /// precision loss, and resource-limit errors never use the default.
723    pub fn convert_or_with<T: DataConversionTarget>(
724        &self,
725        key: &str,
726        default: impl IntoValueDefault<T>,
727        policy: &ConversionPolicy,
728        limits: &ConversionLimits,
729    ) -> MetadataResult<T> {
730        self.convert_optional_with(key, policy, limits)
731            .map(|value| value.unwrap_or_else(|| default.into_value_default()))
732    }
733
734    /// Returns a reference to the stored [`Value`] for `key`, or `None` if
735    /// absent.
736    ///
737    /// # Parameters
738    ///
739    /// * `key` - Metadata key to retrieve.
740    ///
741    /// # Returns
742    ///
743    /// The stored value, or `None` when `key` is absent.
744    #[inline]
745    pub fn get_raw(&self, key: &str) -> Option<&Value> {
746        self.0.get(key)
747    }
748
749    /// Returns the concrete data type of the value stored under `key`.
750    ///
751    /// # Parameters
752    ///
753    /// * `key` - Metadata key to inspect.
754    ///
755    /// # Returns
756    ///
757    /// The stored value's data type, or `None` when `key` is absent.
758    #[inline]
759    pub fn data_type(&self, key: &str) -> Option<DataType> {
760        self.0.get(key).map(Value::data_type)
761    }
762
763    /// Inserts a typed value and returns the previous value.
764    ///
765    /// # Parameters
766    ///
767    /// * `key` - Metadata key to replace.
768    /// * `value` - Typed value to store.
769    ///
770    /// # Returns
771    ///
772    /// The previous value when the key was already present, or `None`.
773    ///
774    /// # Type Parameters
775    ///
776    /// * `T` - Input type convertible into a stored [`Value`].
777    #[inline]
778    pub fn insert<T>(&mut self, key: &str, value: T) -> Option<Value>
779    where
780        T: Into<Value>,
781    {
782        self.0.insert(key.to_string(), value.into())
783    }
784
785    /// Sets a typed value and returns this metadata object for chaining.
786    ///
787    /// # Parameters
788    ///
789    /// * `key` - Metadata key to replace.
790    /// * `value` - Typed value to store.
791    ///
792    /// # Returns
793    ///
794    /// A mutable reference to this metadata object.
795    ///
796    /// # Type Parameters
797    ///
798    /// * `T` - Input type convertible into a stored [`Value`].
799    #[inline]
800    pub fn set<T>(&mut self, key: &str, value: T) -> &mut Self
801    where
802        T: Into<Value>,
803    {
804        let _ = self.insert(key, value);
805        self
806    }
807
808    /// Returns a new metadata object with `key` set to `value`.
809    ///
810    /// # Parameters
811    ///
812    /// * `key` - Metadata key to replace.
813    /// * `value` - Typed value to store.
814    ///
815    /// # Returns
816    ///
817    /// This metadata object after inserting the value.
818    ///
819    /// # Type Parameters
820    ///
821    /// * `T` - Input type convertible into a stored [`Value`].
822    #[inline]
823    #[must_use]
824    pub fn with<T>(mut self, key: &str, value: T) -> Self
825    where
826        T: Into<Value>,
827    {
828        self.set(key, value);
829        self
830    }
831
832    /// Inserts a typed value after validating it against `schema` and returns
833    /// the previous value.
834    ///
835    /// # Parameters
836    ///
837    /// * `schema` - Schema used to validate the entry.
838    /// * `key` - Metadata key to replace.
839    /// * `value` - Typed value to validate and store.
840    ///
841    /// # Returns
842    ///
843    /// The previous value when the key was already present, or `None`.
844    ///
845    /// # Errors
846    ///
847    /// Returns [`MetadataError::UnknownField`] when `key` is rejected by the
848    /// schema, [`MetadataError::MissingRequiredField`] when a required field is
849    /// assigned [`Value::Unset`], or [`MetadataError::TypeMismatch`] when the
850    /// constructed value's concrete type does not match the schema field type.
851    ///
852    /// # Type Parameters
853    ///
854    /// * `T` - Input type convertible into a stored [`Value`].
855    #[cfg(feature = "schema")]
856    #[inline]
857    pub fn insert_checked<T>(&mut self, schema: &MetadataSchema, key: &str, value: T) -> MetadataResult<Option<Value>>
858    where
859        T: Into<Value>,
860    {
861        let value = value.into();
862        schema.validate_entry(key, &value)?;
863        Ok(self.insert(key, value))
864    }
865
866    /// Sets a typed value after schema validation and returns this metadata
867    /// object for chaining.
868    ///
869    /// # Parameters
870    ///
871    /// * `schema` - Schema used to validate the entry.
872    /// * `key` - Metadata key to replace.
873    /// * `value` - Typed value to validate and store.
874    ///
875    /// # Returns
876    ///
877    /// A mutable reference to this metadata object.
878    ///
879    /// # Errors
880    ///
881    /// Returns [`MetadataError::UnknownField`] when `key` is rejected by the
882    /// schema, [`MetadataError::MissingRequiredField`] when a required field is
883    /// assigned [`Value::Unset`], or [`MetadataError::TypeMismatch`] when the
884    /// constructed value's concrete type does not match the schema field type.
885    ///
886    /// # Type Parameters
887    ///
888    /// * `T` - Input type convertible into a stored [`Value`].
889    #[cfg(feature = "schema")]
890    #[inline]
891    pub fn set_checked<T>(&mut self, schema: &MetadataSchema, key: &str, value: T) -> MetadataResult<&mut Self>
892    where
893        T: Into<Value>,
894    {
895        let _ = self.insert_checked(schema, key, value)?;
896        Ok(self)
897    }
898
899    /// Returns a new metadata object with a typed value validated and inserted.
900    ///
901    /// # Parameters
902    ///
903    /// * `schema` - Schema used to validate the entry.
904    /// * `key` - Metadata key to replace.
905    /// * `value` - Typed value to validate and store.
906    ///
907    /// # Returns
908    ///
909    /// This metadata object after inserting the validated value.
910    ///
911    /// # Errors
912    ///
913    /// Returns [`MetadataError::UnknownField`] when `key` is rejected by the
914    /// schema, [`MetadataError::MissingRequiredField`] when a required field is
915    /// assigned [`Value::Unset`], or [`MetadataError::TypeMismatch`] when the
916    /// constructed value's concrete type does not match the schema field type.
917    ///
918    /// # Type Parameters
919    ///
920    /// * `T` - Input type convertible into a stored [`Value`].
921    #[cfg(feature = "schema")]
922    #[inline]
923    pub fn with_checked<T>(mut self, schema: &MetadataSchema, key: &str, value: T) -> MetadataResult<Self>
924    where
925        T: Into<Value>,
926    {
927        self.set_checked(schema, key, value)?;
928        Ok(self)
929    }
930
931    /// Removes the entry for `key` and returns the stored [`Value`] if it
932    /// existed.
933    ///
934    /// # Parameters
935    ///
936    /// * `key` - Metadata key to remove.
937    ///
938    /// # Returns
939    ///
940    /// The removed value, or `None` when `key` was absent.
941    #[inline]
942    pub fn remove(&mut self, key: &str) -> Option<Value> {
943        self.0.remove(key)
944    }
945
946    /// Removes all entries.
947    #[inline]
948    pub fn clear(&mut self) {
949        self.0.clear();
950    }
951
952    /// Returns an iterator over `(&str, &Value)` pairs in key-sorted order.
953    ///
954    /// # Returns
955    ///
956    /// A borrowing iterator over entries in key order.
957    #[inline]
958    #[must_use = "the metadata iterator must be consumed to inspect entries"]
959    pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
960        self.0.iter().map(|(key, value)| (key.as_str(), value))
961    }
962
963    /// Returns an iterator over the keys in sorted order.
964    ///
965    /// # Returns
966    ///
967    /// A borrowing iterator over keys in sorted order.
968    #[inline]
969    #[must_use = "the metadata key iterator must be consumed to inspect keys"]
970    pub fn keys(&self) -> impl Iterator<Item = &str> {
971        self.0.keys().map(String::as_str)
972    }
973
974    /// Returns an iterator over the values in key-sorted order.
975    ///
976    /// # Returns
977    ///
978    /// A borrowing iterator over values in key order.
979    #[inline]
980    #[must_use = "the metadata value iterator must be consumed to inspect values"]
981    pub fn values(&self) -> impl Iterator<Item = &Value> {
982        self.0.values()
983    }
984
985    /// Merges all entries from `other` into `self`, overwriting existing keys.
986    ///
987    /// # Parameters
988    ///
989    /// * `other` - Metadata entries to consume and merge.
990    pub fn merge(&mut self, mut other: Metadata) {
991        self.0.append(&mut other.0);
992    }
993
994    /// Returns a new `Metadata` that contains entries from `self` and `other`.
995    ///
996    /// Entries from `other` take precedence on key conflicts.
997    ///
998    /// # Parameters
999    ///
1000    /// * `other` - Metadata entries to merge.
1001    ///
1002    /// # Returns
1003    ///
1004    /// A merged copy without modifying either input.
1005    #[must_use]
1006    pub fn merged(&self, other: &Metadata) -> Metadata {
1007        let mut result = self.clone();
1008        let mut right = other.0.clone();
1009        result.0.append(&mut right);
1010        result
1011    }
1012
1013    /// Retains only the entries for which `predicate` returns `true`.
1014    ///
1015    /// # Parameters
1016    ///
1017    /// * `predicate` - Callback invoked for each key and value; returning
1018    ///   `false` removes that entry.
1019    ///
1020    /// # Type Parameters
1021    ///
1022    /// * `F` - Callback type used to decide which entries to retain.
1023    #[inline]
1024    pub fn retain<F>(&mut self, mut predicate: F)
1025    where
1026        F: FnMut(&str, &Value) -> bool,
1027    {
1028        self.0.retain(|key, value| predicate(key.as_str(), value));
1029    }
1030
1031    /// Converts this metadata object into its underlying map.
1032    ///
1033    /// # Returns
1034    ///
1035    /// The owned, key-sorted map of metadata values.
1036    #[inline]
1037    #[must_use]
1038    pub fn into_inner(self) -> BTreeMap<String, Value> {
1039        self.0
1040    }
1041
1042    /// Validates that this metadata object fits the strict V1 wire contract.
1043    ///
1044    /// This preflight checks the same entry-count and key-byte limits enforced
1045    /// by [`Serialize::serialize`], allowing callers to reject invalid metadata
1046    /// at the write boundary instead of discovering the error during encoding.
1047    ///
1048    /// # Returns
1049    ///
1050    /// `Ok(())` when every entry can satisfy the metadata map limits.
1051    ///
1052    /// # Errors
1053    ///
1054    /// Returns [`MetadataError::WireLimitExceeded`] when the entry count or a
1055    /// key exceeds the strict V1 limit.
1056    #[inline]
1057    pub fn validate_wire_contract(&self) -> MetadataResult<()> {
1058        if self.0.len() > STRICT_STRING_MAP_MAX_ENTRIES {
1059            return Err(MetadataError::WireLimitExceeded {
1060                kind: crate::MetadataWireLimitKind::Entries,
1061                value: self.0.len(),
1062                maximum: STRICT_STRING_MAP_MAX_ENTRIES,
1063            });
1064        }
1065        if let Some(key) = self.0.keys().find(|key| key.len() > STRICT_STRING_MAP_MAX_KEY_BYTES) {
1066            return Err(MetadataError::WireLimitExceeded {
1067                kind: crate::MetadataWireLimitKind::KeyBytes,
1068                value: key.len(),
1069                maximum: STRICT_STRING_MAP_MAX_KEY_BYTES,
1070            });
1071        }
1072        Ok(())
1073    }
1074
1075    /// Looks up storage, leaving unset and type classification to the value
1076    /// layer.
1077    ///
1078    /// # Parameters
1079    ///
1080    /// * `key` - Metadata key to look up.
1081    ///
1082    /// # Returns
1083    ///
1084    /// The stored value for the key.
1085    ///
1086    /// # Errors
1087    ///
1088    /// Returns [`MetadataError::MissingKey`] when the key is absent.
1089    fn entry(&self, key: &str) -> MetadataResult<&Value> {
1090        self.get_raw(key)
1091            .ok_or_else(|| MetadataError::MissingKey(key.to_owned()))
1092    }
1093
1094    /// Wraps a value access error with its metadata key while retaining the
1095    /// original error as the source.
1096    ///
1097    /// # Parameters
1098    ///
1099    /// * `key` - Metadata key associated with the access error.
1100    /// * `source` - Original value access error.
1101    ///
1102    /// # Returns
1103    ///
1104    /// A metadata value access error containing `key` and `source`.
1105    fn map_access_error(key: &str, source: ValueError) -> MetadataError {
1106        MetadataError::ValueAccess {
1107            key: key.to_owned(),
1108            source: Box::new(source),
1109        }
1110    }
1111
1112    /// Determines whether a metadata error represents a defaultable missing
1113    /// value under the selected read policy.
1114    ///
1115    /// # Parameters
1116    ///
1117    /// * `error` - Metadata error to classify.
1118    /// * `conversion` - Whether conversion fallback rules apply.
1119    ///
1120    /// # Returns
1121    ///
1122    /// `true` when the error represents a missing value eligible for the
1123    /// selected fallback; otherwise, `false`.
1124    fn is_defaultable(error: &MetadataError, conversion: bool) -> bool {
1125        match error {
1126            MetadataError::MissingKey(_) => true,
1127            MetadataError::ValueAccess { source, .. } => source.missing().is_some_and(|missing| {
1128                if conversion {
1129                    missing.is_defaultable_for_conversion()
1130                } else {
1131                    missing.is_defaultable_for_strict_read()
1132                }
1133            }),
1134            _ => false,
1135        }
1136    }
1137
1138    /// Converts a decoded wire envelope into scalar metadata values.
1139    ///
1140    /// # Parameters
1141    ///
1142    /// * `wire` - Decoded metadata envelope containing scalar value payloads.
1143    ///
1144    /// # Returns
1145    ///
1146    /// The metadata values indexed by key.
1147    ///
1148    /// # Errors
1149    ///
1150    /// Returns an error message when an envelope value is not a scalar V1
1151    /// payload.
1152    fn from_wire(
1153        wire: MetadataWireV1<StrictStringMap<ValueWirePayloadV1>>,
1154    ) -> Result<BTreeMap<String, Value>, &'static str> {
1155        wire.values
1156            .into_inner()
1157            .into_iter()
1158            .map(|(key, value)| {
1159                value
1160                    .into_container()
1161                    .into_scalar()
1162                    .map(|value| (key, value))
1163                    .map_err(|_| "metadata values must use scalar V1 payloads")
1164            })
1165            .collect()
1166    }
1167}
1168
1169impl Redact for Metadata {
1170    /// Writes a policy-redacted metadata representation.
1171    ///
1172    /// Metadata is pure domain structure, so this traversal consumes nodes,
1173    /// collection items, and output bytes but no diagnostic input bytes. The
1174    /// metadata node and its map field are admitted before the stored map is
1175    /// accessed. The map writer then enters exactly one map node and admits
1176    /// each exact remaining entry before iterator advancement. Its admitted-
1177    /// item path classifies entries without charging duplicate keyed root and
1178    /// field nodes; pass-through values still enter their legitimate nested
1179    /// value scopes.
1180    ///
1181    /// # Parameters
1182    ///
1183    /// * `writer` - Redaction writer receiving the metadata representation.
1184    ///
1185    /// # Returns
1186    ///
1187    /// Returns `()` after writing through the redaction writer.
1188    fn write_redacted(&self, writer: &mut RedactionWriter<'_>) {
1189        writer.record("Metadata", |fields| {
1190            fields.nested("values", &MetadataValues(&self.0));
1191        });
1192    }
1193}
1194
1195impl fmt::Debug for Metadata {
1196    /// Writes the strict-policy redacted representation.
1197    ///
1198    /// # Parameters
1199    ///
1200    /// * `formatter` - Formatter receiving the redacted representation.
1201    #[inline]
1202    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1203        let output = Redactor::strict().redact_text(self);
1204        let text = output.text_or_marker("<redaction incomplete>");
1205        formatter.write_str(text.as_ref())
1206    }
1207}
1208
1209impl fmt::Display for Metadata {
1210    /// Writes a bounded, strict-policy redacted representation as
1211    /// single-line diagnostic text.
1212    ///
1213    /// The strict policy redacts values according to the diagnostic policy, but
1214    /// this output is not a confidentiality boundary for arbitrary user-defined
1215    /// keys or error text.
1216    ///
1217    /// # Parameters
1218    ///
1219    /// * `formatter` - Formatter receiving the redacted representation.
1220    #[inline]
1221    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1222        let output = Redactor::strict().redact_text(self);
1223        let text = output.text_or_marker("<redaction incomplete>");
1224        formatter.write_str(text.as_ref())
1225    }
1226}
1227
1228impl Serialize for Metadata {
1229    /// Serializes metadata as the strict v1 envelope.
1230    ///
1231    /// # Type Parameters
1232    ///
1233    /// * `S` - Serializer used to produce the wire envelope.
1234    ///
1235    /// # Parameters
1236    ///
1237    /// * `serializer` - Serializer receiving the metadata envelope.
1238    ///
1239    /// # Returns
1240    ///
1241    /// The serializer's result containing the encoded envelope.
1242    ///
1243    /// # Errors
1244    ///
1245    /// Returns the serializer's error if wire validation or serialization
1246    /// fails.
1247    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1248    where
1249        S: Serializer,
1250    {
1251        self.validate_wire_contract().map_err(<S::Error as SerError>::custom)?;
1252        MetadataWireV1 {
1253            version: METADATA_WIRE_VERSION_V1,
1254            values: MetadataWireValuesRef(&self.0),
1255        }
1256        .serialize(serializer)
1257    }
1258}
1259
1260impl<'de> Deserialize<'de> for Metadata {
1261    /// Deserializes only the strict v1 envelope.
1262    ///
1263    /// # Type Parameters
1264    ///
1265    /// * `D` - Deserializer providing the wire envelope.
1266    ///
1267    /// # Parameters
1268    ///
1269    /// * `deserializer` - Source of the serialized metadata envelope.
1270    ///
1271    /// # Returns
1272    ///
1273    /// The decoded metadata object.
1274    ///
1275    /// # Errors
1276    ///
1277    /// Returns the deserializer's error if the envelope is malformed,
1278    /// unsupported, or contains invalid metadata values.
1279    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1280    where
1281        D: Deserializer<'de>,
1282    {
1283        let wire: MetadataWireV1<StrictStringMap<ValueWirePayloadV1>> =
1284            MetadataWireV1Seed::new(StrictStringMapValueSeed::new(
1285                STRICT_STRING_MAP_MAX_ENTRIES,
1286                STRICT_STRING_MAP_MAX_KEY_BYTES,
1287                ValueWirePayloadV1Seed::new(),
1288            ))
1289            .deserialize(deserializer)?;
1290        if wire.version != METADATA_WIRE_VERSION_V1 {
1291            return Err(de::Error::custom("unsupported Metadata wire format version"));
1292        }
1293        let values = Self::from_wire(wire).map_err(de::Error::custom)?;
1294        Ok(Self(values))
1295    }
1296}
1297
1298impl From<BTreeMap<String, Value>> for Metadata {
1299    /// Takes ownership of a sorted map without validating schema or wire
1300    /// limits. Wire encoding can still reject keys or entry counts outside
1301    /// the V1 contract.
1302    ///
1303    /// # Parameters
1304    ///
1305    /// * `map` - Sorted metadata map to take ownership of.
1306    ///
1307    /// # Returns
1308    ///
1309    /// Metadata containing the supplied entries.
1310    #[inline]
1311    fn from(map: BTreeMap<String, Value>) -> Self {
1312        Self(map)
1313    }
1314}
1315
1316impl From<Metadata> for BTreeMap<String, Value> {
1317    /// Consumes metadata and returns its owned sorted map.
1318    ///
1319    /// # Parameters
1320    ///
1321    /// * `meta` - Metadata object to consume.
1322    ///
1323    /// # Returns
1324    ///
1325    /// The owned sorted map of metadata values.
1326    #[inline]
1327    fn from(meta: Metadata) -> Self {
1328        meta.0
1329    }
1330}
1331
1332impl FromIterator<(String, Value)> for Metadata {
1333    /// Collects entries into metadata using `BTreeMap` duplicate-key
1334    /// replacement. The resulting keys are sorted; collection does not
1335    /// validate schema or wire limits.
1336    ///
1337    /// # Type Parameters
1338    ///
1339    /// * `I` - Iterator type yielding owned metadata key-value pairs.
1340    ///
1341    /// # Parameters
1342    ///
1343    /// * `iter` - Source of metadata key-value pairs.
1344    ///
1345    /// # Returns
1346    ///
1347    /// Metadata containing the collected entries.
1348    #[inline]
1349    fn from_iter<I: IntoIterator<Item = (String, Value)>>(iter: I) -> Self {
1350        Self(iter.into_iter().collect())
1351    }
1352}
1353
1354impl IntoIterator for Metadata {
1355    /// Owning B-tree iterator, yielding entries in key order.
1356    type IntoIter = std::collections::btree_map::IntoIter<String, Value>;
1357    /// Owned key-value pair moved from the consumed metadata.
1358    type Item = (String, Value);
1359
1360    /// Consumes metadata and moves its sorted entries without cloning.
1361    ///
1362    /// # Parameters
1363    ///
1364    /// * `self` - Metadata object whose entries are moved into the iterator.
1365    ///
1366    /// # Returns
1367    ///
1368    /// An owning iterator over sorted metadata entries.
1369    #[inline]
1370    fn into_iter(self) -> Self::IntoIter {
1371        self.0.into_iter()
1372    }
1373}
1374
1375impl<'a> IntoIterator for &'a Metadata {
1376    /// Borrowed B-tree iterator tied to the metadata borrow.
1377    type IntoIter = std::collections::btree_map::Iter<'a, String, Value>;
1378    /// References to an entry's key and value; neither is cloned.
1379    type Item = (&'a String, &'a Value);
1380
1381    /// Borrows sorted entries for the lifetime of the metadata borrow without
1382    /// cloning. This yields `(&String, &Value)`, unlike [`Metadata::iter`]
1383    /// which yields `(&str, &Value)`.
1384    ///
1385    /// # Parameters
1386    ///
1387    /// * `self` - Borrowed metadata whose entries are exposed by the iterator.
1388    ///
1389    /// # Returns
1390    ///
1391    /// A borrowed iterator over sorted metadata entries.
1392    #[inline]
1393    fn into_iter(self) -> Self::IntoIter {
1394        self.0.iter()
1395    }
1396}
1397
1398impl Extend<(String, Value)> for Metadata {
1399    /// Adds entries, replacing duplicate keys according to `BTreeMap` behavior.
1400    /// The resulting keys remain sorted; no input iteration order is promised.
1401    ///
1402    /// # Type Parameters
1403    ///
1404    /// * `I` - Iterator type yielding owned metadata key-value pairs.
1405    ///
1406    /// # Parameters
1407    ///
1408    /// * `iter` - Source of metadata key-value pairs to add.
1409    #[inline]
1410    fn extend<I: IntoIterator<Item = (String, Value)>>(&mut self, iter: I) {
1411        self.0.extend(iter);
1412    }
1413}