Skip to main content

qubit_metadata/
lib.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//! # qubit-metadata
9//!
10//! A general-purpose, typed-value, extensible metadata model for Rust.
11//!
12//! This crate provides a [`Metadata`] type — a structured key-value store
13//! designed for any domain that needs to attach typed annotations to its data
14//! models. It is not a plain `HashMap` — it is a structured extensibility point
15//! with strict typed access, explicit conversion helpers,
16//! [`qubit_value::Value`] backing, and first-class `serde` support.
17//!
18//! ## Design Goals
19//!
20//! - **Typed Values**: Strict typed reads, explicit conversion helpers,
21//!   chainable set, and replacement-aware insert APIs backed by
22//!   [`qubit_value::Value`]
23//! - **Generality**: No domain-specific assumptions — usable in any Rust
24//!   project
25//! - **Schema Support**: Optional schema validation for metadata and filters
26//!   through the `schema` feature
27//! - **Serialization**: First-class `serde` support for JSON interchange
28//! - **Filtering**: Composable query conditions through the `filter` feature
29//!
30//! ## Features
31//!
32//! - Core type: [`Metadata`] — an ordered key-value store with typed accessors
33//! - Enable `filter` for composable filter expressions and their public types
34//! - Enable `schema` (which includes `filter`) for field definitions and
35//!   validation APIs
36//! - Error type: [`MetadataError`] — structured failure reporting for reads,
37//!   conversions, validation, and wire boundaries
38//! - `schema` also provides aggregate validation errors
39//!
40//! ## Example
41//!
42//! ```rust
43//! use qubit_metadata::Metadata;
44//!
45//! let meta = Metadata::new()
46//!     .with("author", "alice")
47//!     .with("priority", 3_i64);
48//!
49//! // Optional strict reads distinguish absence from conversion/type failures.
50//! let author = meta.get_optional::<String>("author").unwrap();
51//! assert_eq!(author.as_deref(), Some("alice"));
52//!
53//! // Explicit API: preserve failure reasons for diagnostics.
54//! let priority = meta.convert::<i64>("priority").unwrap();
55//! assert_eq!(priority, 3);
56//! ```
57//!
58//! The `filter` feature uses fail-closed three-valued logic: a missing or unset
59//! value stays unknown through negation and does not match at the public API
60//! boundary.
61//!
62//! `Metadata`'s `Debug` and `Display` implementations are bounded and rendered
63//! through the default redaction policy. They are useful diagnostic views, not
64//! a confidentiality boundary for arbitrary user-defined keys or error text.
65
66#![deny(missing_docs)]
67
68mod constants;
69#[cfg(feature = "filter")]
70mod filter;
71mod internal;
72mod metadata;
73mod metadata_error;
74#[cfg(feature = "json")]
75mod metadata_limits;
76#[cfg(feature = "json")]
77mod metadata_limits_builder;
78mod metadata_result;
79#[cfg(feature = "schema")]
80mod metadata_validation_error;
81#[cfg(feature = "schema")]
82mod metadata_validation_result;
83#[cfg(feature = "json")]
84mod metadata_wire_decode_error;
85#[cfg(feature = "json")]
86mod metadata_wire_encode_error;
87mod metadata_wire_limit_kind;
88#[cfg(feature = "schema")]
89mod schema;
90mod wire;
91
92#[cfg(feature = "filter")]
93pub use filter::Condition;
94#[cfg(feature = "filter")]
95pub use filter::FilterExpression;
96#[cfg(feature = "filter")]
97pub use filter::FilterExpressionBuilder;
98#[cfg(feature = "filter")]
99pub use filter::FilterExpressionView;
100#[cfg(feature = "filter")]
101pub use filter::FilterLimitKind;
102#[cfg(feature = "filter")]
103pub use filter::FilterLimits;
104#[cfg(feature = "filter")]
105pub use filter::FilterLimitsBuilder;
106#[cfg(feature = "filter")]
107pub use filter::FilterMatchOptions;
108#[cfg(feature = "filter")]
109pub use filter::FilterMatchOptionsBuilder;
110#[cfg(feature = "filter")]
111pub use filter::MetadataFilter;
112#[cfg(feature = "filter")]
113pub use filter::MetadataFilterBuilder;
114pub use metadata::Metadata;
115pub use metadata_error::MetadataError;
116#[cfg(feature = "json")]
117pub use metadata_limits::DEFAULT_MAX_JSON_BYTES;
118#[cfg(feature = "json")]
119pub use metadata_limits::DEFAULT_MAX_KEY_BYTES;
120#[cfg(feature = "json")]
121pub use metadata_limits::DEFAULT_MAX_METADATA_ENTRIES;
122#[cfg(feature = "json")]
123pub use metadata_limits::DEFAULT_MAX_SCHEMA_FIELDS;
124#[cfg(feature = "json")]
125pub use metadata_limits::MetadataLimits;
126#[cfg(feature = "json")]
127pub use metadata_limits::default_json_decode_limits;
128#[cfg(feature = "json")]
129pub use metadata_limits::default_json_encode_limits;
130#[cfg(feature = "json")]
131pub use metadata_limits::default_json_value_limits;
132#[cfg(feature = "json")]
133pub use metadata_limits_builder::MetadataLimitsBuilder;
134pub use metadata_result::MetadataResult;
135#[cfg(feature = "schema")]
136pub use metadata_validation_error::MetadataValidationError;
137#[cfg(feature = "schema")]
138pub use metadata_validation_result::MetadataValidationResult;
139#[cfg(feature = "json")]
140pub use metadata_wire_decode_error::MetadataWireDecodeError;
141#[cfg(feature = "json")]
142pub use metadata_wire_encode_error::MetadataWireEncodeError;
143pub use metadata_wire_limit_kind::MetadataWireLimitKind;
144#[cfg(feature = "schema")]
145pub use schema::MetadataField;
146#[cfg(feature = "schema")]
147pub use schema::MetadataSchema;
148#[cfg(feature = "schema")]
149pub use schema::MetadataSchemaBuilder;
150#[cfg(feature = "schema")]
151pub use schema::UnknownFilterFieldPolicy;
152#[cfg(feature = "schema")]
153pub use schema::UnknownMetadataFieldPolicy;