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}