use std::collections::HashMap;
use std::sync::Arc;
use crate::domain::registry::primitive::{PrimitiveId, PrimitiveKind, Registered};
#[derive(Debug, Clone, Default)]
struct Table {
order: Vec<Arc<Registered>>,
index: HashMap<PrimitiveId, Arc<Registered>>,
}
impl Table {
fn get(&self, id: &PrimitiveId) -> Option<&Arc<Registered>> {
self.index.get(id)
}
fn contains(&self, id: &PrimitiveId) -> bool {
self.index.contains_key(id)
}
fn order(&self) -> &[Arc<Registered>] {
&self.order
}
const fn len(&self) -> usize {
self.order.len()
}
const fn is_empty(&self) -> bool {
self.order.is_empty()
}
fn insert(&mut self, primitive: Arc<Registered>) {
let id = primitive.id().clone();
let replaced = self.index.insert(id, Arc::clone(&primitive));
if let Some(previous) = replaced
&& let Some(slot) = self
.order
.iter()
.position(|held| Arc::ptr_eq(held, &previous))
&& let Some(target) = self.order.get_mut(slot)
{
*target = primitive;
return;
}
self.order.push(primitive);
}
fn remove(&mut self, id: &PrimitiveId) -> Option<Arc<Registered>> {
let mut removed = None;
let taken = self
.order
.extract_if(.., |held: &mut Arc<Registered>| {
if held.id() == id {
removed = Some(Arc::clone(held));
true
} else {
false
}
})
.count();
debug_assert_eq!(
taken,
usize::from(removed.is_some()),
"the filter ran once per match"
);
self.index.remove(id).or(removed)
}
}
#[derive(Debug, Clone, Default)]
pub struct Snapshot {
generation: u64,
tools: Table,
resources: Table,
templates: Table,
prompts: Table,
}
impl Snapshot {
#[must_use]
pub fn empty() -> Self {
Self::default()
}
#[must_use]
pub fn to_builder(&self) -> SnapshotBuilder {
SnapshotBuilder {
base: self.generation,
tools: self.tools.clone(),
resources: self.resources.clone(),
templates: self.templates.clone(),
prompts: self.prompts.clone(),
}
}
#[must_use]
pub fn builder() -> SnapshotBuilder {
SnapshotBuilder::default()
}
#[must_use]
pub const fn generation(&self) -> u64 {
self.generation
}
#[must_use]
pub fn list(&self, kind: PrimitiveKind) -> &[Arc<Registered>] {
self.table(kind).order()
}
#[must_use]
pub fn get(&self, kind: PrimitiveKind, id: &PrimitiveId) -> Option<&Arc<Registered>> {
self.table(kind).get(id)
}
#[must_use]
pub fn contains(&self, kind: PrimitiveKind, id: &PrimitiveId) -> bool {
self.table(kind).contains(id)
}
#[must_use]
pub const fn len(&self, kind: PrimitiveKind) -> usize {
self.table(kind).len()
}
#[must_use]
pub const fn is_empty(&self, kind: PrimitiveKind) -> bool {
self.table(kind).is_empty()
}
#[must_use]
pub fn total(&self) -> usize {
PrimitiveKind::ALL
.into_iter()
.map(|kind| self.len(kind))
.sum()
}
#[must_use]
pub fn is_blank(&self) -> bool {
PrimitiveKind::ALL
.into_iter()
.all(|kind| self.is_empty(kind))
}
const fn table(&self, kind: PrimitiveKind) -> &Table {
match kind {
PrimitiveKind::Tool => &self.tools,
PrimitiveKind::Resource => &self.resources,
PrimitiveKind::ResourceTemplate => &self.templates,
PrimitiveKind::Prompt => &self.prompts,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct SnapshotBuilder {
base: u64,
tools: Table,
resources: Table,
templates: Table,
prompts: Table,
}
impl SnapshotBuilder {
pub fn insert(&mut self, primitive: impl Into<Registered>) {
self.insert_arc(Arc::new(primitive.into()));
}
pub fn insert_arc(&mut self, primitive: Arc<Registered>) {
self.table_mut(primitive.kind()).insert(primitive);
}
pub fn remove(&mut self, kind: PrimitiveKind, id: &PrimitiveId) -> Option<Arc<Registered>> {
self.table_mut(kind).remove(id)
}
#[must_use]
pub const fn len(&self, kind: PrimitiveKind) -> usize {
self.table(kind).len()
}
#[must_use]
pub fn build(self) -> Snapshot {
let next = self.base.saturating_add(1);
self.build_at(next)
}
#[must_use]
pub fn build_at(self, generation: u64) -> Snapshot {
Snapshot {
generation,
tools: self.tools,
resources: self.resources,
templates: self.templates,
prompts: self.prompts,
}
}
const fn table(&self, kind: PrimitiveKind) -> &Table {
match kind {
PrimitiveKind::Tool => &self.tools,
PrimitiveKind::Resource => &self.resources,
PrimitiveKind::ResourceTemplate => &self.templates,
PrimitiveKind::Prompt => &self.prompts,
}
}
const fn table_mut(&mut self, kind: PrimitiveKind) -> &mut Table {
match kind {
PrimitiveKind::Tool => &mut self.tools,
PrimitiveKind::Resource => &mut self.resources,
PrimitiveKind::ResourceTemplate => &mut self.templates,
PrimitiveKind::Prompt => &mut self.prompts,
}
}
}
impl From<Snapshot> for SnapshotBuilder {
fn from(snapshot: Snapshot) -> Self {
snapshot.to_builder()
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::Snapshot;
use crate::domain::ir::JsonObjectError;
use crate::domain::registry::TestError;
use crate::domain::registry::primitive::{
Primitive, PrimitiveId, PrimitiveKind, Prompt, Registered, Tool,
};
fn tool(name: &str) -> Result<Registered, JsonObjectError> {
Ok(Registered::from(Primitive::from(Tool::new(
name,
r#"{"type":"object"}"#,
)?)))
}
#[test]
fn a_published_snapshot_does_not_change_under_a_reader() -> Result<(), TestError> {
let mut builder = Snapshot::builder();
builder.insert(tool("first")?);
let first = builder.build();
let mut next = first.to_builder();
next.insert(tool("second")?);
let second = next.build();
assert_eq!(first.len(PrimitiveKind::Tool), 1);
assert_eq!(second.len(PrimitiveKind::Tool), 2);
assert!(first.get(PrimitiveKind::Tool, &"first".into()).is_some());
assert!(
first.get(PrimitiveKind::Tool, &"second".into()).is_none(),
"the reader must not see a tool published after it"
);
Ok(())
}
#[test]
fn listing_order_is_registration_order() -> Result<(), TestError> {
let mut builder = Snapshot::builder();
for name in ["c", "a", "b", "d"] {
builder.insert(tool(name)?);
}
let snapshot = builder.build();
let listed: Vec<&str> = snapshot
.list(PrimitiveKind::Tool)
.iter()
.map(|p| p.id().as_str())
.collect();
assert_eq!(listed, vec!["c", "a", "b", "d"]);
Ok(())
}
#[test]
fn a_replacement_keeps_its_position() -> Result<(), TestError> {
let mut builder = Snapshot::builder();
for name in ["a", "b", "c"] {
builder.insert(tool(name)?);
}
builder.insert(Registered::from(Primitive::from(
Tool::new("b", r#"{"type":"object"}"#)?.described("now documented"),
)));
let snapshot = builder.build();
let listed: Vec<&str> = snapshot
.list(PrimitiveKind::Tool)
.iter()
.map(|p| p.id().as_str())
.collect();
assert_eq!(listed, vec!["a", "b", "c"]);
assert_eq!(
snapshot.len(PrimitiveKind::Tool),
3,
"a replace is not an add"
);
let Some(Primitive::Tool(replaced)) = snapshot
.get(PrimitiveKind::Tool, &"b".into())
.map(|held| held.primitive())
else {
return Err("replacement lost from the index".into());
};
assert_eq!(replaced.description.as_deref(), Some("now documented"));
Ok(())
}
#[test]
fn removal_reports_the_removed_primitive() -> Result<(), TestError> {
let mut builder = Snapshot::builder();
for name in ["a", "b", "c"] {
builder.insert(tool(name)?);
}
let removed = builder.remove(PrimitiveKind::Tool, &PrimitiveId::from("b"));
let Some(Primitive::Tool(tool)) = removed.as_deref().map(Registered::primitive) else {
return Err("removal reported the wrong kind".into());
};
assert_eq!(tool.name.as_str(), "b");
let snapshot = builder.build();
let listed: Vec<&str> = snapshot
.list(PrimitiveKind::Tool)
.iter()
.map(|p| p.id().as_str())
.collect();
assert_eq!(listed, vec!["a", "c"]);
assert!(!snapshot.contains(PrimitiveKind::Tool, &"b".into()));
Ok(())
}
#[test]
fn removing_an_absent_primitive_reports_nothing() {
let mut builder = Snapshot::builder();
assert!(
builder
.remove(PrimitiveKind::Tool, &"ghost".into())
.is_none()
);
assert!(builder.build().is_blank());
}
#[test]
fn the_kinds_do_not_leak_into_each_other() -> Result<(), TestError> {
let mut builder = Snapshot::builder();
builder.insert(tool("t")?);
builder.insert(Registered::from(Primitive::from(Prompt::new("p"))));
let snapshot = builder.build();
assert_eq!(snapshot.len(PrimitiveKind::Tool), 1);
assert_eq!(snapshot.len(PrimitiveKind::Prompt), 1);
assert_eq!(snapshot.len(PrimitiveKind::Resource), 0);
assert_eq!(snapshot.len(PrimitiveKind::ResourceTemplate), 0);
assert_eq!(snapshot.total(), 2);
assert!(!snapshot.is_blank());
assert!(
snapshot.get(PrimitiveKind::Prompt, &"t".into()).is_none(),
"a tool id must not resolve a prompt"
);
Ok(())
}
#[test]
fn an_unheld_kind_is_empty_not_missing() {
let snapshot = Snapshot::empty();
for kind in PrimitiveKind::ALL {
assert!(snapshot.is_empty(kind));
assert_eq!(snapshot.list(kind).len(), 0);
assert!(snapshot.get(kind, &"anything".into()).is_none());
}
assert!(snapshot.is_blank());
assert_eq!(snapshot.total(), 0);
assert_eq!(snapshot.generation(), 0);
}
#[test]
fn the_copy_shares_its_primitives() -> Result<(), TestError> {
let mut builder = Snapshot::builder();
builder.insert_arc(Arc::new(tool("shared")?));
let snapshot = builder.build();
let Some(held) = snapshot.get(PrimitiveKind::Tool, &"shared".into()) else {
return Err("tool missing".into());
};
let next = snapshot.to_builder();
let rebuilt = next.build();
let Some(again) = rebuilt.get(PrimitiveKind::Tool, &"shared".into()) else {
return Err("tool lost in the copy".into());
};
assert!(Arc::ptr_eq(held, again));
Ok(())
}
#[test]
fn the_generation_advances_by_one() -> Result<(), TestError> {
let mut builder = Snapshot::builder();
builder.insert(tool("a")?);
let first = builder.build_at(1);
let second = first.to_builder().build_at(first.generation() + 1);
assert_eq!(first.generation(), 1);
assert_eq!(second.generation(), 2);
Ok(())
}
#[test]
fn a_copied_builder_stamps_the_next_generation() -> Result<(), TestError> {
let mut builder = Snapshot::builder();
builder.insert(tool("a")?);
let first = builder.build();
assert_eq!(first.generation(), 1);
let mut second = first.to_builder();
second.insert(tool("b")?);
assert_eq!(second.build().generation(), 2);
let at_max: Snapshot = first.to_builder().build_at(u64::MAX);
assert_eq!(at_max.to_builder().build().generation(), u64::MAX);
Ok(())
}
}