#![forbid(unsafe_code)]
pub mod baseline;
pub mod common;
pub mod common_db;
pub mod config;
pub mod detect;
pub mod diagram;
pub mod diagrams;
pub mod editor;
pub mod entities;
pub mod error;
mod family;
pub mod generated;
pub mod geom;
mod inline_config;
pub mod models;
mod parse_pipeline;
pub mod preprocess;
mod runtime;
pub mod sanitize;
mod theme;
pub mod time;
pub mod utils;
#[cfg(feature = "full-config")]
mod yaml_config;
pub use config::MermaidConfig;
pub use detect::{Detector, DetectorRegistry};
pub use diagram::{
BLOCK_WIDTH_WARNING_RULE_ID, DiagramRegistry, DiagramSemanticParser, DiagramWarningFact,
FLOWCHART_EXPLICIT_DIRECTION_WARNING_RULE_ID, FLOWCHART_UNKNOWN_STYLE_TARGET_WARNING_RULE_ID,
GIT_GRAPH_DUPLICATE_COMMIT_WARNING_RULE_ID, ParsedDiagram, ParsedDiagramRender,
ParsedDiagramWithEditorFacts, ParsedEditorFacts, RenderDiagramRegistry, RenderSemanticModel,
RenderSemanticParser,
};
pub use editor::{
EditorExpectedSyntax, EditorExpectedSyntaxKind, EditorSemanticCompleteness,
EditorSemanticDiagnostic, EditorSemanticDiagnosticKind, EditorSemanticFacts,
EditorSemanticKind, EditorSemanticRole, EditorSemanticSymbol, EditorSpanCoordinateSpace,
SourceSpan,
};
pub use error::{Error, ParseDiagnostic, ParseDiagnosticSpanKind, Result};
pub use family::{DiagramFamilyCapability, DiagramHeaderFact, diagram_type_family_kind};
pub use preprocess::{PreprocessResult, preprocess_diagram, preprocess_diagram_with_known_type};
pub const MAX_DIAGRAM_NESTING_DEPTH: usize = 256;
pub fn supported_themes() -> &'static [&'static str] {
theme::SUPPORTED_THEME_NAMES
}
pub fn supported_diagrams() -> &'static [&'static str] {
supported_diagrams_for_profile(selected_baseline_registry_profile())
}
pub fn supported_diagrams_for_profile(
profile: baseline::BaselineRegistryProfile,
) -> &'static [&'static str] {
family::supported_diagram_metadata_ids(profile)
}
pub fn diagram_family_capabilities() -> &'static [DiagramFamilyCapability] {
diagram_family_capabilities_for_profile(selected_baseline_registry_profile())
}
pub fn diagram_family_capabilities_for_profile(
profile: baseline::BaselineRegistryProfile,
) -> &'static [DiagramFamilyCapability] {
family::diagram_family_capabilities(profile)
}
pub fn diagram_header_facts() -> &'static [DiagramHeaderFact] {
diagram_header_facts_for_profile(selected_baseline_registry_profile())
}
pub fn diagram_header_facts_for_profile(
profile: baseline::BaselineRegistryProfile,
) -> &'static [DiagramHeaderFact] {
family::diagram_header_facts(profile)
}
pub fn selected_baseline_registry_profile() -> baseline::BaselineRegistryProfile {
family::selected_registry_profile()
}
fn build_default_effective_config(site_config: &MermaidConfig) -> MermaidConfig {
let mut effective_config = site_config.clone();
theme::apply_theme_defaults(&mut effective_config);
effective_config
}
fn generated_default_effective_config() -> MermaidConfig {
static DEFAULT_EFFECTIVE_CONFIG: std::sync::OnceLock<MermaidConfig> =
std::sync::OnceLock::new();
DEFAULT_EFFECTIVE_CONFIG
.get_or_init(|| build_default_effective_config(&generated::default_site_config()))
.clone()
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ParseOptions {
pub suppress_errors: bool,
}
impl ParseOptions {
pub fn strict() -> Self {
Self {
suppress_errors: false,
}
}
pub fn lenient() -> Self {
Self {
suppress_errors: true,
}
}
}
#[derive(Debug, Clone)]
pub struct ParseMetadata {
pub diagram_type: String,
pub config: MermaidConfig,
pub effective_config: MermaidConfig,
pub title: Option<String>,
}
#[derive(Debug, Clone)]
pub struct Engine {
registry: DetectorRegistry,
diagram_registry: DiagramRegistry,
render_diagram_registry: RenderDiagramRegistry,
site_config: MermaidConfig,
default_effective_config: MermaidConfig,
fixed_today_local: Option<chrono::NaiveDate>,
fixed_local_offset_minutes: Option<i32>,
}
impl Default for Engine {
fn default() -> Self {
let site_config = generated::default_site_config();
let default_effective_config = generated_default_effective_config();
Self {
registry: DetectorRegistry::for_pinned_mermaid_baseline(),
diagram_registry: DiagramRegistry::for_pinned_mermaid_baseline(),
render_diagram_registry: RenderDiagramRegistry::for_pinned_mermaid_baseline(),
site_config,
default_effective_config,
fixed_today_local: None,
fixed_local_offset_minutes: None,
}
}
}
impl Engine {
pub(crate) fn parse_timing_enabled() -> bool {
#[cfg(feature = "host-timing")]
{
Self::parse_timing_enabled_from_env()
}
#[cfg(not(feature = "host-timing"))]
false
}
#[cfg(feature = "host-timing")]
fn parse_timing_enabled_from_env() -> bool {
static ENABLED: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
*ENABLED.get_or_init(|| {
matches!(
std::env::var("MERMAN_PARSE_TIMING").as_deref(),
Ok("1") | Ok("true")
)
})
}
pub fn new() -> Self {
Self::default()
}
pub(crate) fn default_effective_config(&self) -> MermaidConfig {
self.default_effective_config.clone()
}
pub fn with_fixed_today(mut self, today: Option<chrono::NaiveDate>) -> Self {
self.fixed_today_local = today;
self
}
pub fn with_fixed_local_offset_minutes(mut self, offset_minutes: Option<i32>) -> Self {
self.fixed_local_offset_minutes = offset_minutes;
self
}
pub fn fixed_local_offset_minutes(&self) -> Option<i32> {
self.fixed_local_offset_minutes
}
pub fn with_site_config(mut self, mut site_config: MermaidConfig) -> Self {
config::mirror_legacy_font_family_into_theme_variables(&mut site_config);
self.site_config.deep_merge(site_config.as_value());
self.default_effective_config = build_default_effective_config(&self.site_config);
self
}
pub fn registry(&self) -> &DetectorRegistry {
&self.registry
}
pub fn registry_mut(&mut self) -> &mut DetectorRegistry {
&mut self.registry
}
pub fn diagram_registry(&self) -> &DiagramRegistry {
&self.diagram_registry
}
pub fn diagram_registry_mut(&mut self) -> &mut DiagramRegistry {
&mut self.diagram_registry
}
pub fn render_diagram_registry(&self) -> &RenderDiagramRegistry {
&self.render_diagram_registry
}
pub fn render_diagram_registry_mut(&mut self) -> &mut RenderDiagramRegistry {
&mut self.render_diagram_registry
}
pub fn parse_metadata_sync(
&self,
text: &str,
options: ParseOptions,
) -> Result<Option<ParseMetadata>> {
parse_pipeline::ParsePipeline::detect(self, text, options).metadata()
}
pub fn parse_metadata_with_type_sync(
&self,
diagram_type: &str,
text: &str,
options: ParseOptions,
) -> Result<Option<ParseMetadata>> {
parse_pipeline::ParsePipeline::known_type(self, diagram_type, text, options).metadata()
}
pub fn parse_editor_semantic_facts_with_type_sync(
&self,
diagram_type: &str,
text: &str,
options: ParseOptions,
) -> Result<Option<EditorSemanticFacts>> {
parse_pipeline::ParsePipeline::known_type(self, diagram_type, text, options)
.parse_editor_semantic_facts()
}
pub async fn parse_metadata(
&self,
text: &str,
options: ParseOptions,
) -> Result<Option<ParseMetadata>> {
self.parse_metadata_sync(text, options)
}
pub async fn parse_metadata_with_type(
&self,
diagram_type: &str,
text: &str,
options: ParseOptions,
) -> Result<Option<ParseMetadata>> {
self.parse_metadata_with_type_sync(diagram_type, text, options)
}
pub fn parse_diagram_sync(
&self,
text: &str,
options: ParseOptions,
) -> Result<Option<ParsedDiagram>> {
parse_pipeline::ParsePipeline::detect(self, text, options)
.parse_json(parse_pipeline::ParseTiming::Json)
}
pub fn parse_diagram_with_editor_facts_sync(
&self,
text: &str,
options: ParseOptions,
) -> Result<Option<ParsedDiagramWithEditorFacts>> {
parse_pipeline::ParsePipeline::detect(self, text, options)
.parse_json_with_editor_facts(parse_pipeline::ParseTiming::Json)
}
pub async fn parse_diagram(
&self,
text: &str,
options: ParseOptions,
) -> Result<Option<ParsedDiagram>> {
self.parse_diagram_sync(text, options)
}
pub fn parse_diagram_for_render_model_sync(
&self,
text: &str,
options: ParseOptions,
) -> Result<Option<ParsedDiagramRender>> {
parse_pipeline::ParsePipeline::detect(self, text, options).parse_render_model()
}
pub async fn parse_diagram_for_render_model(
&self,
text: &str,
options: ParseOptions,
) -> Result<Option<ParsedDiagramRender>> {
self.parse_diagram_for_render_model_sync(text, options)
}
pub fn parse_diagram_for_render_model_with_type_sync(
&self,
diagram_type: &str,
text: &str,
options: ParseOptions,
) -> Result<Option<ParsedDiagramRender>> {
parse_pipeline::ParsePipeline::known_type(self, diagram_type, text, options)
.parse_render_model()
}
pub async fn parse_diagram_for_render_model_with_type(
&self,
diagram_type: &str,
text: &str,
options: ParseOptions,
) -> Result<Option<ParsedDiagramRender>> {
self.parse_diagram_for_render_model_with_type_sync(diagram_type, text, options)
}
pub fn parse_diagram_with_type_sync(
&self,
diagram_type: &str,
text: &str,
options: ParseOptions,
) -> Result<Option<ParsedDiagram>> {
parse_pipeline::ParsePipeline::known_type(self, diagram_type, text, options)
.parse_json(parse_pipeline::ParseTiming::None)
}
pub async fn parse_diagram_with_type(
&self,
diagram_type: &str,
text: &str,
options: ParseOptions,
) -> Result<Option<ParsedDiagram>> {
self.parse_diagram_with_type_sync(diagram_type, text, options)
}
pub async fn parse(&self, text: &str, options: ParseOptions) -> Result<Option<ParseMetadata>> {
self.parse_metadata(text, options).await
}
}
#[cfg(test)]
mod tests;