This repository has been archived on 2026-05-05. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
engram-retired/engrams/engram-projection/src/registry.rs
T
Will Anderson d1ec384b27 rename crates/ to engrams/, bindings/ to receptors/
- crates/ → engrams/ (Rust engrams live here)
- bindings/ → receptors/ (cross-language access points into the graph)
- Cargo.toml workspace paths updated
2026-04-29 03:27:33 -05:00

134 lines
4.2 KiB
Rust

/// In-memory registry of named projection schemas.
///
/// The registry is the store of all registered projections. In a running server,
/// one registry instance is shared (behind a Mutex or RwLock). Projections are
/// looked up by name to execute queries.
use std::collections::HashMap;
use crate::error::{ProjectionError, ProjectionResult};
use crate::schema::ProjectionSchema;
/// Holds all registered `ProjectionSchema`s, keyed by name.
#[derive(Default)]
pub struct ProjectionRegistry {
schemas: HashMap<String, ProjectionSchema>,
}
impl ProjectionRegistry {
pub fn new() -> Self {
Self {
schemas: HashMap::new(),
}
}
/// Register a new schema. Fails if one with the same name already exists.
pub fn register(&mut self, schema: ProjectionSchema) -> ProjectionResult<()> {
if self.schemas.contains_key(&schema.name) {
return Err(ProjectionError::AlreadyExists(schema.name.clone()));
}
if schema.name.is_empty() {
return Err(ProjectionError::InvalidSchema("name must not be empty".into()));
}
self.schemas.insert(schema.name.clone(), schema);
Ok(())
}
/// Replace an existing schema (upsert). Creates if not present.
pub fn upsert(&mut self, schema: ProjectionSchema) -> ProjectionResult<()> {
if schema.name.is_empty() {
return Err(ProjectionError::InvalidSchema("name must not be empty".into()));
}
self.schemas.insert(schema.name.clone(), schema);
Ok(())
}
/// Retrieve a schema by name.
pub fn get(&self, name: &str) -> ProjectionResult<&ProjectionSchema> {
self.schemas
.get(name)
.ok_or_else(|| ProjectionError::NotFound(name.to_string()))
}
/// List all schema names.
pub fn list(&self) -> Vec<&ProjectionSchema> {
let mut schemas: Vec<&ProjectionSchema> = self.schemas.values().collect();
schemas.sort_by(|a, b| a.name.cmp(&b.name));
schemas
}
/// Remove a schema by name. Returns true if it existed.
pub fn remove(&mut self, name: &str) -> bool {
self.schemas.remove(name).is_some()
}
/// Number of registered schemas.
pub fn len(&self) -> usize {
self.schemas.len()
}
/// True if no schemas are registered.
pub fn is_empty(&self) -> bool {
self.schemas.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::schema::{NodeFilter, ProjectionType};
fn make_schema(name: &str) -> ProjectionSchema {
ProjectionSchema {
name: name.to_string(),
description: None,
projection_type: ProjectionType::Relational,
node_filter: NodeFilter::All,
field_mappings: vec![],
}
}
#[test]
fn test_register_and_get() {
let mut reg = ProjectionRegistry::new();
reg.register(make_schema("users")).unwrap();
let s = reg.get("users").unwrap();
assert_eq!(s.name, "users");
}
#[test]
fn test_register_duplicate_fails() {
let mut reg = ProjectionRegistry::new();
reg.register(make_schema("events")).unwrap();
assert!(reg.register(make_schema("events")).is_err());
}
#[test]
fn test_upsert_replaces() {
let mut reg = ProjectionRegistry::new();
reg.register(make_schema("s1")).unwrap();
let mut updated = make_schema("s1");
updated.description = Some("updated".into());
reg.upsert(updated).unwrap();
assert_eq!(reg.get("s1").unwrap().description.as_deref(), Some("updated"));
}
#[test]
fn test_list_sorted() {
let mut reg = ProjectionRegistry::new();
reg.register(make_schema("zoo")).unwrap();
reg.register(make_schema("alpha")).unwrap();
reg.register(make_schema("mango")).unwrap();
let names: Vec<&str> = reg.list().iter().map(|s| s.name.as_str()).collect();
assert_eq!(names, vec!["alpha", "mango", "zoo"]);
}
#[test]
fn test_remove() {
let mut reg = ProjectionRegistry::new();
reg.register(make_schema("temp")).unwrap();
assert!(reg.remove("temp"));
assert!(!reg.remove("temp"));
assert!(reg.get("temp").is_err());
}
}