use std::collections::HashMap;
use crate::errors::{ParseDiagnostic, RenderError};
use crate::format::OutputFormat;
use crate::payload::Diagram;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiagramFamily {
Graph,
Timeline,
}
pub type DiagramDetector = fn(&str) -> bool;
pub type DiagramFactory = fn() -> Box<dyn DiagramInstance>;
pub struct DiagramDefinition {
pub id: &'static str,
pub family: DiagramFamily,
pub detector: DiagramDetector,
pub factory: DiagramFactory,
pub supported_formats: &'static [OutputFormat],
}
pub struct DiagramRegistry {
diagrams: HashMap<&'static str, DiagramDefinition>,
detection_order: Vec<&'static str>,
}
impl DiagramRegistry {
pub fn new() -> Self {
Self {
diagrams: HashMap::new(),
detection_order: Vec::new(),
}
}
pub fn register(&mut self, definition: DiagramDefinition) {
let id = definition.id;
self.diagrams.insert(id, definition);
self.detection_order.push(id);
}
#[must_use]
pub fn detect(&self, input: &str) -> Option<&'static str> {
for id in &self.detection_order {
if let Some(def) = self.diagrams.get(id)
&& (def.detector)(input)
{
return Some(def.id);
}
}
None
}
#[must_use]
pub fn get(&self, id: &str) -> Option<&DiagramDefinition> {
self.diagrams.get(id)
}
#[must_use]
pub fn supports_format(&self, id: &str, format: OutputFormat) -> bool {
self.diagrams
.get(id)
.is_some_and(|def| def.supported_formats.contains(&format))
}
pub fn diagram_ids(&self) -> impl Iterator<Item = &'static str> + '_ {
self.detection_order.iter().copied()
}
#[must_use]
pub fn create(&self, id: &str) -> Option<Box<dyn DiagramInstance>> {
self.diagrams.get(id).map(|def| (def.factory)())
}
#[must_use]
pub fn resolve(&self, input: &str) -> Option<ResolvedDiagram<'_>> {
let id = self.detect(input)?;
let definition = self.diagrams.get(id)?;
Some(ResolvedDiagram { definition })
}
}
impl Default for DiagramRegistry {
fn default() -> Self {
Self::new()
}
}
pub struct ResolvedDiagram<'a> {
definition: &'a DiagramDefinition,
}
impl ResolvedDiagram<'_> {
#[must_use]
pub fn diagram_id(&self) -> &'static str {
self.definition.id
}
#[must_use]
pub fn family(&self) -> DiagramFamily {
self.definition.family
}
#[must_use]
pub fn supported_formats(&self) -> &'static [OutputFormat] {
self.definition.supported_formats
}
}
pub trait DiagramInstance: Send + Sync {
fn parse(
self: Box<Self>,
input: &str,
) -> Result<Box<dyn ParsedDiagram>, Box<dyn std::error::Error + Send + Sync>>;
fn validation_warnings(&self, _input: &str) -> Vec<ParseDiagnostic> {
Vec::new()
}
}
pub trait ParsedDiagram: Send + Sync {
fn into_payload(self: Box<Self>) -> Result<Diagram, RenderError>;
}