olai-store 0.0.1

Generic, TAO-inspired object and association store with field-role enforcement
Documentation

olai-store

Generic, TAO-inspired resource store for typed objects and associations.

olai-store provides the async storage abstractions used by services built with the Trestle framework. It defines the core traits and types for a graph-based resource store: objects (nodes), associations (edges), field-role enforcement, and secret management.

Add to your project

[dependencies]
olai-store = "0.1"

# Enable sqlx::FromRow derives on Object<L>:
# olai-store = { version = "0.1", features = ["sqlx"] }

Key concepts

Label

A Label is a type-safe discriminant for resource types — typically an enum generated by olai-codegen from google.api.resource proto annotations:

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MyLabel { Catalog, Schema, Table }

impl olai_store::Label for MyLabel {}

Object<L> and Association<L>

Object<L> is the untyped interchange format between your store backend and typed resource structs: a UUID, a label (resource type), a hierarchical ResourceName, and a JSON properties blob.

Association<L> is a directed edge between two objects — a UUID, source from_id, edge label string, target to_id, and optional JSON properties.

ResourceName

A slash-separated hierarchical identifier, e.g. "catalogs/my-catalog/schemas/my-schema". The store uses ResourceName as the stable human-readable key for each object.

Store traits

The crate exposes read/write split interfaces:

// Read-only
ObjectStoreReader<L>   — get, get_by_name, list
AssociationStoreReader<L> — list

// Read-write (extends the reader traits)
ObjectStore<L>         — create, update, delete
AssociationStore<L>    — add, remove

A minimal in-memory implementation:

use olai_store::{Label, Object, ObjectStore, ObjectStoreReader, ResourceName, Result};
use async_trait::async_trait;
use uuid::Uuid;

struct InMemoryStore<L> { /* ... */ }

#[async_trait]
impl<L: Label> ObjectStoreReader<L> for InMemoryStore<L> {
    async fn get(&self, id: &Uuid) -> Result<Object<L>> { /* ... */ }
    async fn get_by_name(&self, label: L, name: &ResourceName) -> Result<Object<L>> { /* ... */ }
    async fn list(
        &self, label: L, namespace: Option<&ResourceName>,
        max_results: Option<usize>, page_token: Option<String>,
    ) -> Result<(Vec<Object<L>>, Option<String>)> { /* ... */ }
}

ManagedObjectStore — field-role enforcement

ManagedObjectStore<L, S, M> wraps any ObjectStore<S> and enforces field roles derived from proto annotations:

FieldRole Source annotation Behaviour
Data (default) Stored in properties JSON, returned as-is
Identifier field_behavior = IDENTIFIER Stripped on write; mapped to Object.id
Managed OUTPUT_ONLY + known name Stripped on write; injected on read (created_at, updated_at, etc.)
Sensitive debug_redact = true Routed to SecretManager on write; redacted on read
use olai_store::{ManagedObjectStore, NoSecrets, ResourceRegistry};

let registry = ResourceRegistry::from_static(&RESOURCE_DESCRIPTORS);
let store = ManagedObjectStore::new(backend, registry);

// With secret management:
let store = ManagedObjectStore::with_secrets(backend, my_secret_manager, registry);

ResourceRegistry and FieldRole

ResourceRegistry<L> is a runtime map from label → ResourceTypeDescriptor, holding the field roles for every resource type. It is typically constructed from a RESOURCE_DESCRIPTORS static generated by olai-codegen:

let registry = ResourceRegistry::from_static(&RESOURCE_DESCRIPTORS);
let descriptor = registry.descriptor(MyLabel::Catalog).unwrap();
for field in descriptor.fields {
    println!("{}: {:?}", field.name, field.role);
}

SecretManager

SecretManager is a trait for encrypted storage of sensitive fields. Implement it to route Sensitive-role field values to a key management service or vault:

use olai_store::SecretManager;
use async_trait::async_trait;
use uuid::Uuid;

struct MyVault { /* ... */ }

#[async_trait]
impl SecretManager for MyVault {
    async fn put(&self, object_id: Uuid, field: &str, value: String) -> olai_store::Result<()> { /* ... */ }
    async fn get(&self, object_id: Uuid, field: &str) -> olai_store::Result<Option<String>> { /* ... */ }
    async fn delete(&self, object_id: Uuid, field: &str) -> olai_store::Result<()> { /* ... */ }
}

Use NoSecrets (the default) when you do not need secret management — sensitive fields are stripped from properties but not stored anywhere.

Optional sqlx feature

olai-store = { version = "0.1", features = ["sqlx"] }

Enables sqlx::FromRow on Object<L>, making it straightforward to read rows from a SQL database directly into the store's interchange type.

Integration with olai-codegen

In a typical Trestle project, olai-codegen (the proto-gen CLI) generates:

  • An ObjectLabel enum that implements Label
  • A RESOURCE_DESCRIPTORS static slice of ResourceTypeDescriptor<ObjectLabel>

Feed these directly into ResourceRegistry::from_static and you get full field-role enforcement with zero boilerplate.

See the olai-codegen crate for the full code-generation pipeline.

License

Apache-2.0