use crate::events::GraphEvent;
use std::fmt;
pub mod dot;
pub mod plantuml;
#[derive(Debug)]
#[allow(dead_code)] pub enum SourceError {
UnknownFormat,
InvalidInput(String),
IoError(std::io::Error),
ParseError(String),
}
impl fmt::Display for SourceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnknownFormat => write!(f, "Unable to determine diagram format"),
Self::InvalidInput(msg) => write!(f, "Invalid input: {msg}"),
Self::IoError(err) => write!(f, "IO error: {err}"),
Self::ParseError(msg) => write!(f, "Parse error: {msg}"),
}
}
}
impl std::error::Error for SourceError {}
impl From<std::io::Error> for SourceError {
fn from(err: std::io::Error) -> Self {
Self::IoError(err)
}
}
pub trait GraphEventSource: Send + Sync {
fn source_name(&self) -> &'static str;
fn events(&self) -> Result<Vec<GraphEvent>, SourceError>;
#[allow(dead_code)] fn is_live(&self) -> bool {
false
}
}
#[allow(dead_code)] pub struct SourceRegistry {
sources: Vec<Box<dyn GraphEventSource>>,
}
#[allow(dead_code)] impl SourceRegistry {
pub fn new() -> Self {
Self {
sources: Vec::new(),
}
}
pub fn register(&mut self, source: Box<dyn GraphEventSource>) {
self.sources.push(source);
}
pub fn len(&self) -> usize {
self.sources.len()
}
pub fn is_empty(&self) -> bool {
self.sources.is_empty()
}
pub fn get_source(&self, name: &str) -> Option<&dyn GraphEventSource> {
self.sources
.iter()
.find(|s| s.source_name().eq_ignore_ascii_case(name))
.map(std::convert::AsRef::as_ref)
}
}
impl Default for SourceRegistry {
fn default() -> Self {
Self::new()
}
}
pub fn detect_format(content: &str) -> Option<&'static str> {
let trimmed = content.trim();
if trimmed.contains("@startuml") || trimmed.contains("@startsequence") {
return Some("plantuml");
}
if trimmed.contains("digraph") || trimmed.contains("graph") || trimmed.contains("->") {
return Some("dot");
}
if trimmed.contains('[') && trimmed.contains(']') {
return Some("dot");
}
None
}