mod directory; use std::fmt::Debug; use std::sync::Arc; use anyhow::{anyhow, Context as _}; use async_trait::async_trait; use serde::{Deserialize, Serialize}; use tera::{Context, Tera}; use thiserror::Error; use crate::document::{Document, MetaError}; use crate::domains::common::*; pub use self::directory::DirectoryRegistry; #[derive(Debug, Error)] pub enum RegistryError { #[error("config error: {0}")] Config(anyhow::Error), #[error("failed to parse document {0}: {1}")] Document(DomainId, MetaError), #[error("domain id {0} not found")] NotFound(DomainId), #[error("{0}")] Other(#[from] anyhow::Error), } impl From for RegistryError { fn from(err: std::io::Error) -> Self { Self::Other(err.into()) } } #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] pub struct RegistryConfig { edit_link: Option, } impl RegistryConfig { pub fn edit_link(&self, domain_id: DomainId) -> Result { let edit_link = self .edit_link .as_deref() .ok_or_else(|| RegistryError::Config(anyhow!("edit-link option not set")))?; let (domain, model) = domain_id.kind.parts(); let mut context = Context::new(); context.insert("id", &domain_id); context.insert("domain", domain); context.insert("model", model); context.insert("model_id", &domain_id.model_id); Tera::one_off(edit_link, &context, false) .context("error rendering edit link") .map_err(RegistryError::Config) } } #[async_trait] pub trait Registry: Send + Sync { async fn get_config(&self) -> Result, RegistryError>; async fn get_document(&self, model_id: ModelId) -> Result, RegistryError> where M: DomainModel; async fn get_documents(&self) -> Result)>, RegistryError> where M: DomainModel; async fn get_document_ids(&self, kind: DomainModelKind) -> Result, RegistryError>; async fn put_document( &self, model_id: ModelId, document: Document, ) -> Result<(), RegistryError> where M: DomainModel; } #[async_trait] impl Registry for &T where T: Registry, { async fn get_config(&self) -> Result, RegistryError> { (**self).get_config().await } async fn get_document(&self, model_id: ModelId) -> Result, RegistryError> where M: DomainModel, { (**self).get_document(model_id).await } async fn get_documents(&self) -> Result)>, RegistryError> where M: DomainModel, { (**self).get_documents().await } async fn get_document_ids( &self, kind: DomainModelKind, ) -> Result, RegistryError> { (**self).get_document_ids(kind).await } async fn put_document( &self, model_id: ModelId, document: Document, ) -> Result<(), RegistryError> where M: DomainModel, { (**self).put_document(model_id, document).await } } #[derive(Debug)] pub enum SupportedRegistry { Directory(DirectoryRegistry), } #[async_trait] impl Registry for SupportedRegistry { async fn get_config(&self) -> Result, RegistryError> { match self { Self::Directory(r) => r.get_config().await, } } async fn get_document(&self, model_id: ModelId) -> Result, RegistryError> where M: DomainModel, { match self { Self::Directory(r) => r.get_document(model_id).await, } } async fn get_documents(&self) -> Result)>, RegistryError> where M: DomainModel, { match self { Self::Directory(r) => r.get_documents().await, } } async fn get_document_ids( &self, kind: DomainModelKind, ) -> Result, RegistryError> { match self { Self::Directory(r) => r.get_document_ids(kind).await, } } async fn put_document( &self, model_id: ModelId, document: Document, ) -> Result<(), RegistryError> where M: DomainModel, { match self { Self::Directory(r) => r.put_document(model_id, document).await, } } }