cyberstorm/src/render/mod.rs

106 lines
2.7 KiB
Rust

#[cfg(feature = "mdbook-renderer")]
pub mod mdbook;
use async_trait::async_trait;
use thiserror::Error;
use crate::document::Document;
use crate::domains::common::{DomainId, DomainModel, ModelId};
use crate::domains::GenericDocument;
use crate::registry::{Registry, RegistryError};
#[derive(Debug, Error)]
pub enum RendererError {
#[error("{0}")]
Engine(#[from] anyhow::Error),
#[error("{0}")]
Registry(#[from] RegistryError),
}
pub struct Renderer<E, R> {
engine: E,
registry: R,
}
impl<E, R> Renderer<E, R>
where
E: Engine,
R: Registry,
{
pub fn new(engine: E, registry: R) -> Self {
Self { engine, registry }
}
pub async fn generate(mut self) -> Result<(), RendererError> {
log::info!("Rendering of content has started");
self.engine.start(&self.registry).await?;
macro_rules! push_documents {
($($kind:ident),+) => {
$(
for (id, doc) in self.get_documents().await? {
self.engine
.push_document(id.model_id, GenericDocument::$kind(doc), &self.registry)
.await?;
}
)*
};
}
push_documents!(
SourceIntelligence,
SourceRequirement,
SourceProvider,
ThreatTactic,
ThreatTechnique,
ThreatSoftware,
ThreatSimulation,
ObserveEvent,
ObserveDetection,
ObserveProvider,
ObserveConfiguration,
ReactStage,
ReactAction,
ReactPlaybook,
MitigateStrategy,
MitigatePlatform,
MitigateConfiguration
);
self.engine.finish(&self.registry).await?;
log::info!("Rendering of content has finished");
Ok(())
}
async fn get_documents<M>(&self) -> Result<Vec<(DomainId, Document<M>)>, RendererError>
where
M: DomainModel,
{
self.registry
.get_documents()
.await
.map(|mut docs| {
docs.sort_by_key(|(id, _)| *id);
docs
})
.map_err(RendererError::Registry)
}
}
#[async_trait]
pub trait Engine {
async fn start<R>(&mut self, registry: &R) -> Result<(), RendererError>
where
R: Registry;
async fn push_document<R>(
&mut self,
id: ModelId,
doc: GenericDocument,
registry: &R,
) -> Result<(), RendererError>
where
R: Registry;
async fn finish<R>(&mut self, registry: &R) -> Result<(), RendererError>
where
R: Registry;
}