Skip to main content

protocol_droid/domain/registry/
mod.rs

1//! The registry's pure half: what a primitive _is_, who may see it, and the
2//! immutable set a publish hands to readers.
3//!
4//! Nothing here touches an `ArcSwap`, a lock, or a clock. The types in this
5//! module are the values that get stored, the rule that decides visibility, and
6//! the snapshot they are packed into — the decision half of the problem, with
7//! the concurrency half left to
8//! [`application::registry`](crate::application::registry).
9//!
10//! The split is load-bearing in one direction and cosmetic in the other.
11//! [`is_visible`] must be pure because the whole feature-gating capability is
12//! a claim that a filter is a function of its inputs, and a function that can
13//! observe a lock's state is not one. `Snapshot` has no mutating method
14//! because the publish is the transaction boundary, and a value that can be
15//! edited in place has no boundary at all.
16//!
17//! # Examples
18//!
19//! ```
20//! use protocol_droid::domain::registry::{
21//!     AuthContext, Primitive, Registered, Scope, ScopeSet, Snapshot, Tool, is_visible,
22//! };
23//! use protocol_droid::domain::FeatureProfile;
24//!
25//! let mut builder = Snapshot::builder();
26//! builder.insert(Primitive::from(Tool::new("echo", r#"{"type":"object"}"#)?));
27//! let snapshot = builder.build();
28//!
29//! // A session holding every scope sees a tool that asks for none.
30//! let alice = AuthContext::unrestricted("alice");
31//! let registered = Registered::from(Primitive::from(
32//!     Tool::new("echo", r#"{"type":"object"}"#)?,
33//! ));
34//! assert!(is_visible(&alice, &FeatureProfile::all(), &registered.meta()));
35//!
36//! // A session holding no scope does not see a tool that asks for one.
37//! let restricted = AuthContext::new("bob", ScopeSet::empty());
38//! let guarded = registered
39//!     .clone()
40//!     .requiring_scope(Scope::Admin)
41//!     .requiring(FeatureProfile::all().as_set());
42//! assert!(!is_visible(&restricted, &FeatureProfile::all(), &guarded.meta()));
43//! # Ok::<(), protocol_droid::domain::ir::JsonObjectError>(())
44//! ```
45
46pub mod auth;
47pub mod primitive;
48pub mod snapshot;
49pub mod visibility;
50
51/// What a test in this module reports when it is the one complaining.
52///
53/// The fallible calls here answer two unrelated error types —
54/// [`JsonObjectError`](crate::domain::ir::JsonObjectError) from parsing a tool
55/// schema, and a test's own `&str` complaint. A test that has to choose one
56/// type can only report half its failures, so it takes a `Box` and lets `?`
57/// convert.
58#[cfg(test)]
59pub(crate) type TestError = Box<dyn core::error::Error>;
60
61pub use auth::{AuthContext, Scope, ScopeSet};
62pub use primitive::{
63    Primitive, PrimitiveId, PrimitiveKind, Prompt, PromptArgument, Registered, Resource,
64    ResourceTemplate, Tool,
65};
66pub use snapshot::{Snapshot, SnapshotBuilder};
67pub use visibility::{is_visible, missing_features, missing_scopes};