cyberstorm/src/registry/mod.rs

199 lines
5.0 KiB
Rust

mod directory;
use std::collections::HashMap;
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<std::io::Error> for RegistryError {
fn from(err: std::io::Error) -> Self {
Self::Other(err.into())
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct EditLinks {
page: Option<String>,
document: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct RegistryConfig {
pub edit: EditLinks,
pub content: HashMap<String, String>,
}
fn build_edit_link(
name: &str,
template: Option<&str>,
context: Context,
) -> Result<String, RegistryError> {
let template =
template.ok_or_else(|| RegistryError::Config(anyhow!("link for `{}` not set", name)))?;
Tera::one_off(template, &context, false)
.context("error rendering edit link")
.map_err(RegistryError::Config)
}
impl RegistryConfig {
pub fn document_edit_link(&self, domain_id: DomainId) -> Result<String, RegistryError> {
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("instance", &domain_id.instance);
build_edit_link("document", self.edit.document.as_deref(), context)
}
pub fn page_edit_link(&self, page: &str) -> Result<String, RegistryError> {
let mut context = Context::new();
context.insert("page", &page);
build_edit_link("page", self.edit.page.as_deref(), context)
}
}
#[async_trait]
pub trait Registry: Send + Sync {
async fn get_config(&self) -> Result<Arc<RegistryConfig>, RegistryError>;
async fn get_document<M>(&self, instance: Instance) -> Result<Document<M>, RegistryError>
where
M: DomainModel;
async fn get_documents<M>(&self) -> Result<Vec<(DomainId, Document<M>)>, RegistryError>
where
M: DomainModel;
async fn get_document_ids(&self, kind: DomainModelKind)
-> Result<Vec<DomainId>, RegistryError>;
async fn put_document<M>(
&self,
instance: Instance,
document: Document<M>,
) -> Result<(), RegistryError>
where
M: DomainModel;
}
#[async_trait]
impl<T> Registry for &T
where
T: Registry,
{
async fn get_config(&self) -> Result<Arc<RegistryConfig>, RegistryError> {
(**self).get_config().await
}
async fn get_document<M>(&self, instance: Instance) -> Result<Document<M>, RegistryError>
where
M: DomainModel,
{
(**self).get_document(instance).await
}
async fn get_documents<M>(&self) -> Result<Vec<(DomainId, Document<M>)>, RegistryError>
where
M: DomainModel,
{
(**self).get_documents().await
}
async fn get_document_ids(
&self,
kind: DomainModelKind,
) -> Result<Vec<DomainId>, RegistryError> {
(**self).get_document_ids(kind).await
}
async fn put_document<M>(
&self,
instance: Instance,
document: Document<M>,
) -> Result<(), RegistryError>
where
M: DomainModel,
{
(**self).put_document(instance, document).await
}
}
#[derive(Debug)]
pub enum SupportedRegistry {
Directory(DirectoryRegistry),
}
#[async_trait]
impl Registry for SupportedRegistry {
async fn get_config(&self) -> Result<Arc<RegistryConfig>, RegistryError> {
match self {
Self::Directory(r) => r.get_config().await,
}
}
async fn get_document<M>(&self, instance: Instance) -> Result<Document<M>, RegistryError>
where
M: DomainModel,
{
match self {
Self::Directory(r) => r.get_document(instance).await,
}
}
async fn get_documents<M>(&self) -> Result<Vec<(DomainId, Document<M>)>, RegistryError>
where
M: DomainModel,
{
match self {
Self::Directory(r) => r.get_documents().await,
}
}
async fn get_document_ids(
&self,
kind: DomainModelKind,
) -> Result<Vec<DomainId>, RegistryError> {
match self {
Self::Directory(r) => r.get_document_ids(kind).await,
}
}
async fn put_document<M>(
&self,
instance: Instance,
document: Document<M>,
) -> Result<(), RegistryError>
where
M: DomainModel,
{
match self {
Self::Directory(r) => r.put_document(instance, document).await,
}
}
}