Skip to main content

merman_core/
family.rs

1//! Diagram family facts for the pinned Mermaid baseline.
2//!
3//! This module owns release-facing Mermaid family facts and projects them into detector,
4//! parser, render-model, and metadata surfaces. Identity, detection, headers, and configuration
5//! facts remain available when an implementation is absent. Parser and typed-model lists are
6//! separate projections of the callbacks compiled into this crate.
7
8use crate::detect::DetectorFn;
9use crate::diagram::{
10    BuiltInDiagramSemanticParser, BuiltInRenderSemanticParser, RenderSemanticModel,
11    RenderSemanticParseOutput,
12};
13use crate::{
14    DiagramWarningFact, EditorFamilySemantics, EditorSemanticFacts, EditorSemanticKind, Error,
15    ManagedSemanticJson, OperationControl, OperationControlResult, ParseMetadata, Result,
16};
17use serde::Serialize;
18use serde_json::Value;
19use std::sync::OnceLock;
20
21pub(crate) type CombinedSemanticParser = fn(
22    code: &str,
23    meta: &ParseMetadata,
24    control: &OperationControl,
25) -> OperationControlResult<CombinedSemanticParse>;
26
27pub(crate) type WarningSemanticParser =
28    fn(code: &str, meta: &ParseMetadata) -> Result<WarningSemanticParse>;
29
30/// Built-in compatibility JSON paired with the typed warnings that produced its warning field.
31pub(crate) struct WarningSemanticParse {
32    model: ManagedSemanticJson,
33    warning_facts: Vec<DiagramWarningFact>,
34}
35
36#[allow(
37    dead_code,
38    reason = "Warning construction is used only by warning-producing diagram families."
39)]
40impl WarningSemanticParse {
41    pub(crate) fn new(model: Value, warning_facts: Vec<DiagramWarningFact>) -> Self {
42        Self {
43            model: model.into(),
44            warning_facts,
45        }
46    }
47
48    pub(crate) fn into_model(self) -> Value {
49        self.model.into_unmanaged_value()
50    }
51
52    pub(crate) fn into_parts(self) -> (ManagedSemanticJson, Vec<DiagramWarningFact>) {
53        (self.model, self.warning_facts)
54    }
55}
56
57/// Closed result of one family semantic construction.
58///
59/// A successful construction hands typed warnings directly to the operation snapshot. A failed
60/// construction still owns the parser-derived editor facts produced before the error. This
61/// prevents callers from invoking a second recovery parser over the same source.
62pub(crate) struct CombinedSemanticParse {
63    model: Result<ManagedSemanticJson>,
64    editor_facts: EditorSemanticFacts,
65    warning_facts: Vec<DiagramWarningFact>,
66}
67
68/// Closed failure handoff produced after a family has retained its recovery journal.
69#[allow(
70    dead_code,
71    reason = "Shared parser facilities have different consumers in each family selection."
72)]
73pub(crate) struct CombinedSemanticFailure {
74    error: Box<Error>,
75    editor_facts: Box<EditorSemanticFacts>,
76    recovery_parser: Option<&'static str>,
77}
78
79#[allow(
80    dead_code,
81    reason = "Shared parser facilities have different consumers in each family selection."
82)]
83impl CombinedSemanticFailure {
84    pub(crate) fn new(error: Error, editor_facts: EditorSemanticFacts) -> Self {
85        Self {
86            error: Box::new(error),
87            editor_facts: Box::new(editor_facts),
88            recovery_parser: None,
89        }
90    }
91
92    pub(crate) fn parser_recovery(
93        parser: &'static str,
94        error: Error,
95        editor_facts: EditorSemanticFacts,
96    ) -> Self {
97        Self {
98            error: Box::new(error),
99            editor_facts: Box::new(editor_facts),
100            recovery_parser: Some(parser),
101        }
102    }
103
104    pub(crate) fn into_parts(mut self) -> (Error, EditorSemanticFacts) {
105        if let Some(parser) = self.recovery_parser.take() {
106            let (message, span) = match self.error.as_ref() {
107                Error::DiagramParse { diagnostic, .. } => {
108                    (diagnostic.message().to_string(), diagnostic.span())
109                }
110                error => (error.to_string(), None),
111            };
112            self.editor_facts.mark_recovered_from_parse_error(
113                format!("{parser} parser recovered after parse error: {message}"),
114                span,
115            );
116        }
117        (*self.error, *self.editor_facts)
118    }
119
120    pub(crate) fn into_error(self) -> Error {
121        *self.error
122    }
123}
124
125#[cfg(test)]
126mod combined_semantic_failure_tests {
127    use super::*;
128    use crate::{EditorSemanticDiagnosticKind, ParseDiagnosticSpanKind, SourceSpan};
129
130    #[test]
131    fn parser_recovery_preserves_the_strict_error_and_exact_editor_diagnostic() {
132        let span = SourceSpan::new(17, 29);
133        let failure = CombinedSemanticFailure::parser_recovery(
134            "quadrant chart",
135            Error::diagram_parse_exact("quadrantChart", "expected point coordinates", span),
136            EditorSemanticFacts::new(),
137        );
138
139        let (error, facts) = failure.into_parts();
140        let Error::DiagramParse {
141            diagram_type,
142            diagnostic,
143        } = error
144        else {
145            panic!("expected diagram parse error");
146        };
147        assert_eq!(diagram_type, "quadrantChart");
148        assert_eq!(diagnostic.message(), "expected point coordinates");
149        assert_eq!(diagnostic.span(), Some(span));
150        assert_eq!(diagnostic.span_kind(), ParseDiagnosticSpanKind::Exact);
151        assert_eq!(facts.diagnostics.len(), 1);
152        assert_eq!(
153            facts.diagnostics[0].message,
154            "quadrant chart parser recovered after parse error: expected point coordinates"
155        );
156        assert_eq!(facts.diagnostics[0].span, Some(span));
157        assert_eq!(
158            facts.diagnostics[0].kind,
159            EditorSemanticDiagnosticKind::ParserRecovery
160        );
161    }
162}
163
164impl CombinedSemanticParse {
165    #[allow(
166        dead_code,
167        reason = "Shared parser facilities have different consumers in each family selection."
168    )]
169    pub(crate) fn from_construction<S, F>(
170        construction: std::result::Result<S, F>,
171        success: impl FnOnce(S) -> (Result<Value>, EditorSemanticFacts),
172        failure: impl FnOnce(F) -> (Error, EditorSemanticFacts),
173    ) -> Self {
174        match construction {
175            Ok(source) => {
176                let (model, editor_facts) = success(source);
177                Self {
178                    model: model.map(ManagedSemanticJson::from),
179                    editor_facts,
180                    warning_facts: Vec::new(),
181                }
182            }
183            Err(parse_failure) => {
184                let (error, editor_facts) = failure(parse_failure);
185                Self {
186                    model: Err(error),
187                    editor_facts,
188                    warning_facts: Vec::new(),
189                }
190            }
191        }
192    }
193
194    #[allow(
195        dead_code,
196        reason = "Shared parser facilities have different consumers in each family selection."
197    )]
198    pub(crate) fn from_construction_with_warning_facts<S, F>(
199        construction: std::result::Result<S, F>,
200        success: impl FnOnce(S) -> (Result<Value>, EditorSemanticFacts, Vec<DiagramWarningFact>),
201        failure: impl FnOnce(F) -> (Error, EditorSemanticFacts),
202    ) -> Self {
203        match construction {
204            Ok(source) => {
205                let (model, editor_facts, warning_facts) = success(source);
206                Self {
207                    model: model.map(ManagedSemanticJson::from),
208                    editor_facts,
209                    warning_facts,
210                }
211            }
212            Err(parse_failure) => {
213                let (error, editor_facts) = failure(parse_failure);
214                Self {
215                    model: Err(error),
216                    editor_facts,
217                    warning_facts: Vec::new(),
218                }
219            }
220        }
221    }
222
223    pub(crate) fn into_parts(
224        self,
225    ) -> (
226        Result<ManagedSemanticJson>,
227        EditorSemanticFacts,
228        Vec<DiagramWarningFact>,
229    ) {
230        (self.model, self.editor_facts, self.warning_facts)
231    }
232}
233
234#[cfg(test)]
235#[allow(
236    dead_code,
237    reason = "Test helpers serve different optional family suites."
238)]
239pub(crate) mod test_support {
240    use super::{CombinedSemanticParse, CombinedSemanticParser};
241    use crate::{
242        EditorSemanticFacts, Error, OperationControl, OperationControlResult, ParseMetadata,
243    };
244    use serde_json::Value;
245
246    pub(crate) fn into_result(
247        parsed: OperationControlResult<CombinedSemanticParse>,
248    ) -> std::result::Result<(Value, EditorSemanticFacts), Error> {
249        let (model, editor_facts, _) = parsed
250            .expect("a private parse control cannot be cancelled")
251            .into_parts();
252        model.map(|model| (model.into_unmanaged_value(), editor_facts))
253    }
254
255    pub(crate) fn editor_facts(
256        parser: CombinedSemanticParser,
257        code: &str,
258        meta: &ParseMetadata,
259    ) -> EditorSemanticFacts {
260        parser(code, meta, &OperationControl::new())
261            .expect("a private parse control cannot be cancelled")
262            .into_parts()
263            .1
264    }
265}
266
267#[derive(Clone, Copy)]
268pub(crate) struct DetectorFact {
269    pub(crate) id: &'static str,
270    pub(crate) detector: DetectorFn,
271}
272
273#[derive(Clone, Copy)]
274pub(crate) struct SemanticParserFact {
275    pub(crate) id: &'static str,
276    pub(crate) parser: BuiltInDiagramSemanticParser,
277}
278
279#[derive(Clone, Copy)]
280pub(crate) struct RenderParserFact {
281    pub(crate) id: &'static str,
282    pub(crate) metadata_id: Option<&'static str>,
283    pub(crate) model_kind: &'static str,
284    pub(crate) parser: BuiltInRenderSemanticParser,
285}
286
287#[derive(Clone, Copy)]
288pub(crate) struct CombinedParserFact {
289    pub(crate) id: &'static str,
290    pub(crate) parser: CombinedSemanticParser,
291}
292
293#[derive(Clone, Copy, Debug, PartialEq, Eq)]
294pub struct DiagramHeaderFact {
295    /// Mermaid diagram type id owned by this catalog entry.
296    pub diagram_type: &'static str,
297    /// Header text suggested to the user.
298    pub label: &'static str,
299    /// Short description shown in completion details.
300    pub detail: &'static str,
301}
302
303#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
304#[non_exhaustive]
305pub struct DiagramFamilyCapability {
306    /// Mermaid diagram type id used by the pinned detector and parser registries.
307    pub diagram_type: &'static str,
308    /// Logical diagram family. This does not change when a family reuses another render model.
309    pub logical_family_kind: &'static str,
310    /// Public metadata identity, retained even when its implementation is unavailable.
311    pub metadata_id: Option<&'static str>,
312    /// Known typed render-model kind, independent of whether its parser is compiled.
313    pub render_model_kind: Option<&'static str>,
314    /// Whether this id participates in automatic detection.
315    pub has_detector: bool,
316    /// Whether this build includes a semantic parser for this diagram type.
317    pub has_semantic_parser: bool,
318    /// Whether this build includes parser-backed editor facts.
319    pub has_editor_parser: bool,
320    /// Whether JSON and editor facts share one combined semantic construction.
321    pub has_combined_parser: bool,
322    /// Whether this build includes a typed render-model parser for this diagram type.
323    pub has_render_parser: bool,
324    /// Whether this known id has an authoring header, without promising parser availability.
325    pub has_header: bool,
326    /// Mermaid configuration namespace associated with this id.
327    pub config_namespace: Option<&'static str>,
328}
329
330/// Public Cargo selector for one logical family, regardless of this build's availability.
331///
332/// Aliases share the selector of their owning family. Infrastructure models have no selector.
333#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
334#[non_exhaustive]
335pub struct DiagramFamilySelector {
336    /// Logical identity shared by all parser aliases in this family.
337    pub logical_family_kind: &'static str,
338    /// Positive Cargo feature selecting this family's implementation.
339    pub feature: &'static str,
340}
341
342/// Canonical public identity for one compiled concrete built-in typed render family.
343///
344/// Parser aliases that share a render model contribute exactly one entry. Error and custom JSON
345/// models are infrastructure variants rather than concrete Mermaid families and are excluded.
346#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
347#[non_exhaustive]
348pub struct BuiltInTypedRenderFamily {
349    pub diagram_type: &'static str,
350    pub render_model_kind: &'static str,
351}
352
353/// Closed, catalog-owned identity for one logical Mermaid diagram family.
354///
355/// The inner value is private so editor facts cannot invent family ownership from an arbitrary
356/// string. Instances only come from the admitted family catalog and are cheap to copy into
357/// lexical provenance.
358#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)]
359#[serde(transparent)]
360pub struct DiagramFamilyId(&'static str);
361
362impl DiagramFamilyId {
363    /// Logical identity shared by all admitted Flowchart parser variants.
364    pub const FLOWCHART: Self = Self("flowchart");
365
366    pub fn as_str(self) -> &'static str {
367        self.0
368    }
369}
370
371struct FamilyCatalogProjection {
372    detector_facts: Vec<DetectorFact>,
373    semantic_parser_facts: Vec<SemanticParserFact>,
374    render_parser_facts: Vec<RenderParserFact>,
375    combined_parser_facts: Vec<CombinedParserFact>,
376    supported_diagram_metadata_ids: Vec<&'static str>,
377    diagram_header_facts: Vec<DiagramHeaderFact>,
378    diagram_family_capabilities: Vec<DiagramFamilyCapability>,
379    built_in_typed_render_families: Vec<BuiltInTypedRenderFamily>,
380}
381
382impl FamilyCatalogProjection {
383    fn build<'a>(
384        variants: impl IntoIterator<Item = (&'a DiagramFamilyDefinition, &'a FamilyVariantDefinition)>,
385    ) -> Self {
386        let mut detector_facts = Vec::<(u16, DetectorFact)>::new();
387        let mut semantic_parser_facts = Vec::<(u16, SemanticParserFact)>::new();
388        let mut render_parser_facts = Vec::<(u16, RenderParserFact)>::new();
389        let mut combined_parser_facts = Vec::<(u16, CombinedParserFact)>::new();
390        let mut metadata_facts = Vec::<(u16, &'static str)>::new();
391        let mut diagram_header_facts = Vec::<(u16, DiagramHeaderFact)>::new();
392        let mut diagram_family_capabilities = Vec::<(u16, DiagramFamilyCapability)>::new();
393
394        for (family, variant) in variants {
395            let identity = &variant.identity;
396            let implementation = &variant.implementation;
397            if let Some(ordered) = identity.detector {
398                detector_facts.push((
399                    ordered.order,
400                    DetectorFact {
401                        id: identity.id,
402                        detector: ordered.value,
403                    },
404                ));
405            }
406            if let Some(ordered) = implementation.semantic {
407                semantic_parser_facts.push((
408                    ordered.order,
409                    SemanticParserFact {
410                        id: identity.id,
411                        parser: ordered.value,
412                    },
413                ));
414            }
415            if let Some(ordered) = implementation.typed_render {
416                render_parser_facts.push((
417                    ordered.order,
418                    RenderParserFact {
419                        id: identity.id,
420                        metadata_id: identity.metadata.map(|metadata| metadata.id),
421                        model_kind: identity
422                            .render_model_kind
423                            .expect("typed render variants declare their model kind"),
424                        parser: ordered.value,
425                    },
426                ));
427            }
428            if let Some(ordered) = implementation.combined {
429                combined_parser_facts.push((
430                    ordered.order,
431                    CombinedParserFact {
432                        id: identity.id,
433                        parser: ordered.value,
434                    },
435                ));
436            }
437            if let Some((order, id)) = identity
438                .metadata
439                .and_then(|metadata| metadata.order.map(|order| (order, metadata.id)))
440            {
441                metadata_facts.push((order, id));
442            }
443            for header in identity.headers {
444                diagram_header_facts.push((
445                    header.order,
446                    DiagramHeaderFact {
447                        diagram_type: identity.id,
448                        label: header.label,
449                        detail: header.detail,
450                    },
451                ));
452            }
453            diagram_family_capabilities.push((
454                identity.catalog_order,
455                DiagramFamilyCapability {
456                    diagram_type: identity.id,
457                    logical_family_kind: family.logical_kind,
458                    metadata_id: identity.metadata.map(|metadata| metadata.id),
459                    render_model_kind: identity.render_model_kind,
460                    has_detector: identity.detector.is_some(),
461                    has_semantic_parser: implementation.semantic.is_some(),
462                    has_editor_parser: implementation.combined.is_some(),
463                    has_combined_parser: implementation.combined.is_some(),
464                    has_render_parser: implementation.typed_render.is_some(),
465                    has_header: !identity.headers.is_empty(),
466                    config_namespace: family.config.map(|config| config.namespace),
467                },
468            ));
469        }
470
471        let detector_facts = ordered_values(detector_facts);
472        let semantic_parser_facts = ordered_values(semantic_parser_facts);
473        let render_parser_facts = ordered_values(render_parser_facts);
474        let combined_parser_facts = ordered_values(combined_parser_facts);
475        let metadata_facts = ordered_values(metadata_facts);
476        let diagram_header_facts = ordered_values(diagram_header_facts);
477        let diagram_family_capabilities = ordered_values(diagram_family_capabilities);
478        let supported_diagram_metadata_ids = metadata_facts
479            .iter()
480            .copied()
481            .filter(|metadata_id| {
482                diagram_family_capabilities
483                    .iter()
484                    .any(|fact| fact.metadata_id == Some(*metadata_id) && fact.has_semantic_parser)
485            })
486            .collect::<Vec<&'static str>>();
487        let mut built_in_typed_render_families = Vec::<BuiltInTypedRenderFamily>::new();
488        for metadata_id in metadata_facts {
489            let Some(fact) = render_parser_facts
490                .iter()
491                .find(|fact| fact.metadata_id == Some(metadata_id))
492            else {
493                continue;
494            };
495            if !built_in_typed_render_families
496                .iter()
497                .any(|family| family.render_model_kind == fact.model_kind)
498            {
499                built_in_typed_render_families.push(BuiltInTypedRenderFamily {
500                    diagram_type: metadata_id,
501                    render_model_kind: fact.model_kind,
502                });
503            }
504        }
505
506        Self {
507            detector_facts,
508            semantic_parser_facts,
509            render_parser_facts,
510            combined_parser_facts,
511            supported_diagram_metadata_ids,
512            diagram_header_facts,
513            diagram_family_capabilities,
514            built_in_typed_render_families,
515        }
516    }
517}
518
519fn ordered_values<T>(mut values: Vec<(u16, T)>) -> Vec<T> {
520    values.sort_by_key(|(order, _)| *order);
521    values.into_iter().map(|(_, value)| value).collect()
522}
523
524fn family_catalog_projection() -> &'static FamilyCatalogProjection {
525    static CATALOG: OnceLock<FamilyCatalogProjection> = OnceLock::new();
526    CATALOG.get_or_init(|| FamilyCatalogProjection::build(variants()))
527}
528
529pub(crate) fn detector_facts() -> &'static [DetectorFact] {
530    family_catalog_projection().detector_facts.as_slice()
531}
532
533pub(crate) fn semantic_parser_facts() -> &'static [SemanticParserFact] {
534    family_catalog_projection().semantic_parser_facts.as_slice()
535}
536
537pub(crate) fn render_parser_facts() -> &'static [RenderParserFact] {
538    family_catalog_projection().render_parser_facts.as_slice()
539}
540
541pub(crate) fn combined_parser_facts() -> &'static [CombinedParserFact] {
542    family_catalog_projection().combined_parser_facts.as_slice()
543}
544
545pub(crate) fn combined_parser(diagram_type: &str) -> Option<CombinedSemanticParser> {
546    combined_parser_facts()
547        .iter()
548        .find_map(|fact| (fact.id == diagram_type).then_some(fact.parser))
549}
550
551pub(crate) fn warning_semantic_parser(diagram_type: &str) -> Option<WarningSemanticParser> {
552    find_variant(diagram_type).and_then(|(_, variant)| variant.implementation.warning_semantic)
553}
554
555pub(crate) fn supported_diagram_metadata_ids() -> &'static [&'static str] {
556    family_catalog_projection()
557        .supported_diagram_metadata_ids
558        .as_slice()
559}
560
561pub(crate) fn diagram_header_facts() -> &'static [DiagramHeaderFact] {
562    family_catalog_projection().diagram_header_facts.as_slice()
563}
564
565pub(crate) fn diagram_family_capabilities() -> &'static [DiagramFamilyCapability] {
566    family_catalog_projection()
567        .diagram_family_capabilities
568        .as_slice()
569}
570
571pub(crate) fn diagram_family_selectors() -> &'static [DiagramFamilySelector] {
572    static SELECTORS: OnceLock<Vec<DiagramFamilySelector>> = OnceLock::new();
573    SELECTORS.get_or_init(|| {
574        FAMILY_CATALOG
575            .iter()
576            .filter_map(|family| {
577                family.selector.map(|feature| DiagramFamilySelector {
578                    logical_family_kind: family.logical_kind,
579                    feature,
580                })
581            })
582            .collect()
583    })
584}
585
586pub(crate) fn built_in_typed_render_families() -> &'static [BuiltInTypedRenderFamily] {
587    family_catalog_projection()
588        .built_in_typed_render_families
589        .as_slice()
590}
591
592pub(crate) fn is_builtin_diagram_type(diagram_type: &str) -> bool {
593    find_variant(diagram_type).is_some()
594}
595
596pub(crate) fn render_model_kind_supports_diagram_type(
597    model_kind: &'static str,
598    diagram_type: &str,
599) -> bool {
600    render_parser_facts()
601        .iter()
602        .any(|fact| fact.id == diagram_type && fact.model_kind == model_kind)
603}
604
605pub fn diagram_type_family_kind(diagram_type: &str) -> Option<&'static str> {
606    diagram_type_family_id(diagram_type).map(DiagramFamilyId::as_str)
607}
608
609pub fn diagram_type_metadata_id(diagram_type: &str) -> Option<&'static str> {
610    find_variant(diagram_type)
611        .and_then(|(_, variant)| variant.identity.metadata.map(|metadata| metadata.id))
612}
613
614pub fn diagram_type_family_id(diagram_type: &str) -> Option<DiagramFamilyId> {
615    find_variant(diagram_type).map(|(family, _)| DiagramFamilyId(family.logical_kind))
616}
617
618pub(crate) fn diagram_type_editor_semantics(diagram_type: &str) -> Option<EditorFamilySemantics> {
619    find_variant(diagram_type).map(|(family, _)| family.editor_semantics)
620}
621
622pub fn diagram_type_render_model_kind(diagram_type: &str) -> Option<&'static str> {
623    find_variant(diagram_type).and_then(|(_, variant)| variant.identity.render_model_kind)
624}
625
626macro_rules! render_parser {
627    ($fn_name:ident, $parser:path, $variant:path) => {
628        fn $fn_name(
629            code: &str,
630            meta: &ParseMetadata,
631            control: &OperationControl,
632        ) -> OperationControlResult<Result<RenderSemanticParseOutput>> {
633            control.checkpoint()?;
634            let result = $parser(code, meta)
635                .map($variant)
636                .map(RenderSemanticParseOutput::new);
637            control.checkpoint()?;
638            Ok(result)
639        }
640    };
641}
642
643#[allow(
644    unused_macros,
645    reason = "No controlled family adapter is emitted in infrastructure-only builds."
646)]
647macro_rules! render_parser_controlled {
648    ($fn_name:ident, $parser:path, $variant:path) => {
649        fn $fn_name(
650            code: &str,
651            meta: &ParseMetadata,
652            control: &OperationControl,
653        ) -> OperationControlResult<Result<RenderSemanticParseOutput>> {
654            control.checkpoint()?;
655            let result = $parser(code, meta, control)?
656                .map($variant)
657                .map(RenderSemanticParseOutput::new);
658            control.checkpoint()?;
659            Ok(result)
660        }
661    };
662}
663
664render_parser!(
665    render_error,
666    crate::diagrams::error_diagram::parse_error_model_for_render,
667    RenderSemanticModel::Error
668);
669#[cfg(feature = "diagram-mindmap")]
670render_parser_controlled!(
671    render_mindmap,
672    crate::diagrams::mindmap::parse_mindmap_model_for_render_controlled,
673    RenderSemanticModel::Mindmap
674);
675#[cfg(feature = "diagram-state")]
676render_parser_controlled!(
677    render_state,
678    crate::diagrams::state::parse_state_model_for_render_controlled,
679    RenderSemanticModel::State
680);
681#[cfg(feature = "diagram-zenuml")]
682render_parser_controlled!(
683    render_zenuml,
684    crate::diagrams::zenuml::parse_zenuml_model_for_render_controlled,
685    RenderSemanticModel::Zenuml
686);
687#[cfg(feature = "diagram-sequence")]
688render_parser_controlled!(
689    render_sequence,
690    crate::diagrams::sequence::parse_sequence_model_for_render_controlled,
691    RenderSemanticModel::Sequence
692);
693#[cfg(any(feature = "diagram-flowchart", feature = "diagram-swimlane"))]
694fn render_flowchart(
695    code: &str,
696    meta: &ParseMetadata,
697    control: &OperationControl,
698) -> OperationControlResult<Result<RenderSemanticParseOutput>> {
699    let result = crate::diagrams::flowchart::parse_flowchart_model_with_render_context_controlled(
700        code, meta, control,
701    )?;
702    Ok(result
703        .map(|(model, label_sources)| RenderSemanticParseOutput::flowchart(model, label_sources)))
704}
705#[cfg(feature = "diagram-class")]
706render_parser_controlled!(
707    render_class,
708    crate::diagrams::class::parse_class_typed_controlled,
709    RenderSemanticModel::Class
710);
711#[cfg(feature = "diagram-c4")]
712render_parser_controlled!(
713    render_c4,
714    crate::diagrams::c4::parse_c4_model_for_render_controlled,
715    RenderSemanticModel::C4
716);
717#[cfg(feature = "diagram-cynefin")]
718render_parser_controlled!(
719    render_cynefin,
720    crate::diagrams::cynefin::parse_cynefin_model_for_render_controlled,
721    RenderSemanticModel::Cynefin
722);
723#[cfg(feature = "diagram-railroad")]
724render_parser_controlled!(
725    render_railroad,
726    crate::diagrams::railroad::parse_railroad_model_for_render_controlled,
727    RenderSemanticModel::Railroad
728);
729#[cfg(feature = "diagram-railroad")]
730render_parser_controlled!(
731    render_railroad_ebnf,
732    crate::diagrams::railroad::parse_railroad_ebnf_model_for_render_controlled,
733    RenderSemanticModel::Railroad
734);
735#[cfg(feature = "diagram-railroad")]
736render_parser_controlled!(
737    render_railroad_abnf,
738    crate::diagrams::railroad::parse_railroad_abnf_model_for_render_controlled,
739    RenderSemanticModel::Railroad
740);
741#[cfg(feature = "diagram-railroad")]
742render_parser_controlled!(
743    render_railroad_peg,
744    crate::diagrams::railroad::parse_railroad_peg_model_for_render_controlled,
745    RenderSemanticModel::Railroad
746);
747#[cfg(feature = "diagram-architecture")]
748render_parser_controlled!(
749    render_architecture,
750    crate::diagrams::architecture::parse_architecture_model_for_render_controlled,
751    RenderSemanticModel::Architecture
752);
753#[cfg(feature = "diagram-kanban")]
754render_parser_controlled!(
755    render_kanban,
756    crate::diagrams::kanban::parse_kanban_model_for_render_controlled,
757    RenderSemanticModel::Kanban
758);
759#[cfg(feature = "diagram-gantt")]
760render_parser_controlled!(
761    render_gantt,
762    crate::diagrams::gantt::parse_gantt_model_for_render_controlled,
763    RenderSemanticModel::Gantt
764);
765#[cfg(feature = "diagram-pie")]
766render_parser_controlled!(
767    render_pie,
768    crate::diagrams::pie::parse_pie_model_for_render_controlled,
769    RenderSemanticModel::Pie
770);
771#[cfg(feature = "diagram-packet")]
772render_parser_controlled!(
773    render_packet,
774    crate::diagrams::packet::parse_packet_model_for_render_controlled,
775    RenderSemanticModel::Packet
776);
777#[cfg(feature = "diagram-timeline")]
778render_parser_controlled!(
779    render_timeline,
780    crate::diagrams::timeline::parse_timeline_model_for_render_controlled,
781    RenderSemanticModel::Timeline
782);
783#[cfg(feature = "diagram-journey")]
784render_parser_controlled!(
785    render_journey,
786    crate::diagrams::journey::parse_journey_model_for_render_controlled,
787    RenderSemanticModel::Journey
788);
789#[cfg(feature = "diagram-requirement")]
790render_parser_controlled!(
791    render_requirement,
792    crate::diagrams::requirement::parse_requirement_model_for_render_controlled,
793    RenderSemanticModel::Requirement
794);
795#[cfg(feature = "diagram-sankey")]
796render_parser_controlled!(
797    render_sankey,
798    crate::diagrams::sankey::parse_sankey_model_for_render_controlled,
799    RenderSemanticModel::Sankey
800);
801#[cfg(feature = "diagram-radar")]
802render_parser_controlled!(
803    render_radar,
804    crate::diagrams::radar::parse_radar_model_for_render_controlled,
805    RenderSemanticModel::Radar
806);
807#[cfg(feature = "diagram-info")]
808render_parser_controlled!(
809    render_info,
810    crate::diagrams::info::parse_info_model_for_render_controlled,
811    RenderSemanticModel::Info
812);
813#[cfg(feature = "diagram-treemap")]
814render_parser_controlled!(
815    render_treemap,
816    crate::diagrams::treemap::parse_treemap_model_for_render_controlled,
817    RenderSemanticModel::Treemap
818);
819#[cfg(feature = "diagram-block")]
820render_parser_controlled!(
821    render_block,
822    crate::diagrams::block::parse_block_model_for_render_controlled,
823    RenderSemanticModel::Block
824);
825#[cfg(feature = "diagram-er")]
826render_parser_controlled!(
827    render_er,
828    crate::diagrams::er::parse_er_model_for_render_controlled,
829    RenderSemanticModel::Er
830);
831#[cfg(feature = "diagram-quadrant-chart")]
832render_parser_controlled!(
833    render_quadrant_chart,
834    crate::diagrams::quadrant_chart::parse_quadrant_chart_model_for_render_controlled,
835    RenderSemanticModel::QuadrantChart
836);
837#[cfg(feature = "diagram-xychart")]
838render_parser_controlled!(
839    render_xychart,
840    crate::diagrams::xychart::parse_xychart_model_for_render_controlled,
841    RenderSemanticModel::XyChart
842);
843#[cfg(feature = "diagram-git-graph")]
844render_parser_controlled!(
845    render_git_graph,
846    crate::diagrams::git_graph::parse_git_graph_model_for_render_controlled,
847    RenderSemanticModel::GitGraph
848);
849#[cfg(feature = "diagram-tree-view")]
850render_parser_controlled!(
851    render_tree_view,
852    crate::diagrams::tree_view::parse_tree_view_model_for_render_controlled,
853    RenderSemanticModel::TreeView
854);
855#[cfg(feature = "diagram-ishikawa")]
856render_parser_controlled!(
857    render_ishikawa,
858    crate::diagrams::ishikawa::parse_ishikawa_model_for_render_controlled,
859    RenderSemanticModel::Ishikawa
860);
861#[cfg(feature = "diagram-event-modeling")]
862render_parser_controlled!(
863    render_eventmodeling,
864    crate::diagrams::eventmodeling::parse_eventmodeling_model_for_render_controlled,
865    RenderSemanticModel::EventModeling
866);
867#[cfg(feature = "diagram-venn")]
868render_parser_controlled!(
869    render_venn,
870    crate::diagrams::venn::parse_venn_model_for_render_controlled,
871    RenderSemanticModel::Venn
872);
873#[cfg(feature = "diagram-wardley")]
874render_parser_controlled!(
875    render_wardley,
876    crate::diagrams::wardley::parse_wardley_model_for_render_controlled,
877    RenderSemanticModel::Wardley
878);
879#[cfg(feature = "diagram-usecase")]
880render_parser_controlled!(
881    render_usecase,
882    crate::diagrams::usecase::parse_usecase_model_for_render_controlled,
883    RenderSemanticModel::Usecase
884);
885#[cfg(feature = "diagram-agentflow")]
886render_parser_controlled!(
887    render_agentflow,
888    crate::diagrams::agentflow::parse_agentflow_model_for_render_controlled,
889    RenderSemanticModel::Agentflow
890);
891
892#[derive(Clone, Copy)]
893struct Ordered<T> {
894    order: u16,
895    value: T,
896}
897
898const fn ordered<T>(order: u16, value: T) -> Ordered<T> {
899    Ordered { order, value }
900}
901
902#[derive(Clone, Copy)]
903struct MetadataDefinition {
904    id: &'static str,
905    order: Option<u16>,
906}
907
908const fn metadata(id: &'static str, order: Option<u16>) -> MetadataDefinition {
909    MetadataDefinition { id, order }
910}
911
912#[derive(Clone, Copy)]
913struct HeaderDefinition {
914    order: u16,
915    label: &'static str,
916    detail: &'static str,
917}
918
919const fn header(order: u16, label: &'static str, detail: &'static str) -> HeaderDefinition {
920    HeaderDefinition {
921        order,
922        label,
923        detail,
924    }
925}
926
927#[derive(Clone, Copy)]
928struct FamilyVariantDefinition {
929    identity: FamilyVariantIdentity,
930    implementation: FamilyImplementation,
931}
932
933/// Lightweight pinned facts survive omission of their parser and model implementations.
934#[derive(Clone, Copy)]
935struct FamilyVariantIdentity {
936    id: &'static str,
937    catalog_order: u16,
938    detector: Option<Ordered<DetectorFn>>,
939    render_model_kind: Option<&'static str>,
940    metadata: Option<MetadataDefinition>,
941    headers: &'static [HeaderDefinition],
942    frontmatter_alias_order: Option<u16>,
943}
944
945/// Compiled entry points, bound beside the identity facts in the same catalog.
946///
947/// An absent callback makes only its executable projection unavailable; it never removes the
948/// known identity or releases its built-in identity reservation.
949#[derive(Clone, Copy, Default)]
950struct FamilyImplementation {
951    semantic: Option<Ordered<BuiltInDiagramSemanticParser>>,
952    warning_semantic: Option<WarningSemanticParser>,
953    combined: Option<Ordered<CombinedSemanticParser>>,
954    typed_render: Option<Ordered<BuiltInRenderSemanticParser>>,
955}
956
957#[derive(Clone, Copy)]
958struct FamilyConfigDefinition {
959    namespace: &'static str,
960    frontmatter_order: u16,
961}
962
963#[derive(Clone, Copy)]
964struct DiagramFamilyDefinition {
965    logical_kind: &'static str,
966    selector: Option<&'static str>,
967    editor_semantics: EditorFamilySemantics,
968    config: Option<FamilyConfigDefinition>,
969    variants: &'static [FamilyVariantDefinition],
970}
971
972const GENERIC_EDITOR_SEMANTICS: EditorFamilySemantics =
973    EditorFamilySemantics::new(EditorSemanticKind::Variable);
974const FLOWCHART_EDITOR_SEMANTICS: EditorFamilySemantics =
975    EditorFamilySemantics::new(EditorSemanticKind::Module);
976const SWIMLANE_EDITOR_SEMANTICS: EditorFamilySemantics =
977    EditorFamilySemantics::new(EditorSemanticKind::Variable);
978const MINDMAP_EDITOR_SEMANTICS: EditorFamilySemantics =
979    EditorFamilySemantics::new(EditorSemanticKind::Namespace);
980const SEQUENCE_EDITOR_SEMANTICS: EditorFamilySemantics =
981    EditorFamilySemantics::new(EditorSemanticKind::Event);
982const CLASS_EDITOR_SEMANTICS: EditorFamilySemantics =
983    EditorFamilySemantics::new(EditorSemanticKind::Class);
984const ER_EDITOR_SEMANTICS: EditorFamilySemantics =
985    EditorFamilySemantics::new(EditorSemanticKind::Struct);
986const CARDINAL_EDITOR_SEMANTICS: EditorFamilySemantics =
987    EditorFamilySemantics::new(EditorSemanticKind::Variable);
988const STATE_EDITOR_SEMANTICS: EditorFamilySemantics =
989    EditorFamilySemantics::new(EditorSemanticKind::Class);
990const BLOCK_EDITOR_SEMANTICS: EditorFamilySemantics =
991    EditorFamilySemantics::new(EditorSemanticKind::Object);
992
993macro_rules! variant {
994    (
995        $(feature: $feature:literal,)?
996        id: $id:literal,
997        catalog_order: $catalog_order:literal,
998        detector: $detector:expr,
999        semantic: $semantic:expr,
1000        $(warning_semantic: $warning_semantic:expr,)?
1001        combined: $combined:expr,
1002        typed: $typed:expr,
1003        render_kind: $render_kind:expr,
1004        metadata: $metadata:expr,
1005        headers: $headers:expr,
1006        config_alias_order: $config_alias_order:expr $(,)?
1007    ) => {
1008        FamilyVariantDefinition {
1009            identity: FamilyVariantIdentity {
1010                id: $id,
1011                catalog_order: $catalog_order,
1012                detector: $detector,
1013                render_model_kind: $render_kind,
1014                metadata: $metadata,
1015                headers: $headers,
1016                frontmatter_alias_order: $config_alias_order,
1017            },
1018            implementation: {
1019                $(#[cfg(feature = $feature)])?
1020                {
1021                    FamilyImplementation {
1022                        semantic: $semantic,
1023                        warning_semantic: variant!(@warning_semantic $($warning_semantic)?),
1024                        combined: $combined,
1025                        typed_render: $typed,
1026                    }
1027                }
1028                $(#[cfg(not(feature = $feature))]
1029                {
1030                    FamilyImplementation {
1031                        semantic: None,
1032                        warning_semantic: None,
1033                        combined: None,
1034                        typed_render: None,
1035                    }
1036                })?
1037            },
1038        }
1039    };
1040    (@warning_semantic) => {
1041        None
1042    };
1043    (@warning_semantic $warning_semantic:expr) => {
1044        Some($warning_semantic)
1045    };
1046}
1047
1048fn variants() -> impl Iterator<
1049    Item = (
1050        &'static DiagramFamilyDefinition,
1051        &'static FamilyVariantDefinition,
1052    ),
1053> {
1054    FAMILY_CATALOG
1055        .iter()
1056        .flat_map(|family| family.variants.iter().map(move |variant| (family, variant)))
1057}
1058
1059fn find_variant(
1060    diagram_type: &str,
1061) -> Option<(
1062    &'static DiagramFamilyDefinition,
1063    &'static FamilyVariantDefinition,
1064)> {
1065    FAMILY_CATALOG.iter().find_map(|family| {
1066        family
1067            .variants
1068            .iter()
1069            .find(|variant| variant.identity.id == diagram_type)
1070            .map(|variant| (family, variant))
1071    })
1072}
1073
1074#[derive(Clone, Copy)]
1075pub(crate) struct FrontmatterConfigAliasFact {
1076    pub(crate) source: &'static str,
1077    pub(crate) namespace: &'static str,
1078}
1079
1080pub(crate) fn frontmatter_config_aliases() -> &'static [FrontmatterConfigAliasFact] {
1081    static FACTS: OnceLock<Vec<FrontmatterConfigAliasFact>> = OnceLock::new();
1082    FACTS
1083        .get_or_init(|| {
1084            let mut facts: Vec<_> = FAMILY_CATALOG
1085                .iter()
1086                .flat_map(|family| {
1087                    family.variants.iter().filter_map(move |variant| {
1088                        variant.identity.frontmatter_alias_order.map(|order| {
1089                            let namespace = family
1090                                .config
1091                                .expect("config aliases require a family namespace")
1092                                .namespace;
1093                            (
1094                                order,
1095                                FrontmatterConfigAliasFact {
1096                                    source: variant.identity.id,
1097                                    namespace,
1098                                },
1099                            )
1100                        })
1101                    })
1102                })
1103                .collect();
1104            facts.sort_by_key(|(order, _)| *order);
1105            facts.into_iter().map(|(_, fact)| fact).collect()
1106        })
1107        .as_slice()
1108}
1109
1110pub(crate) fn frontmatter_config_namespaces() -> &'static [&'static str] {
1111    static NAMESPACES: OnceLock<Vec<&'static str>> = OnceLock::new();
1112    NAMESPACES
1113        .get_or_init(|| {
1114            let mut namespaces: Vec<_> = FAMILY_CATALOG
1115                .iter()
1116                .filter_map(|family| {
1117                    family
1118                        .config
1119                        .map(|config| (config.frontmatter_order, config.namespace))
1120                })
1121                .collect();
1122            namespaces.sort_by_key(|(order, _)| *order);
1123            namespaces
1124                .into_iter()
1125                .map(|(_, namespace)| namespace)
1126                .collect()
1127        })
1128        .as_slice()
1129}
1130
1131pub(crate) fn config_namespace_for_diagram_type(diagram_type: &str) -> Option<&'static str> {
1132    find_variant(diagram_type).and_then(|(family, _)| family.config.map(|config| config.namespace))
1133}
1134
1135const FLOWCHART_HEADERS: &[HeaderDefinition] = &[
1136    header(0, "flowchart TD", "flowchart header"),
1137    header(1, "graph TD", "flowchart alias"),
1138];
1139const SEQUENCE_HEADERS: &[HeaderDefinition] = &[header(2, "sequenceDiagram", "sequence header")];
1140const SWIMLANE_HEADERS: &[HeaderDefinition] = &[header(3, "swimlane-beta", "swimlane header")];
1141const CLASS_HEADERS: &[HeaderDefinition] = &[
1142    header(4, "classDiagram", "class header"),
1143    header(5, "classDiagram-v2", "class header"),
1144];
1145const STATE_HEADERS: &[HeaderDefinition] = &[
1146    header(6, "stateDiagram-v2", "state header"),
1147    header(7, "stateDiagram", "legacy state header"),
1148];
1149const ER_HEADERS: &[HeaderDefinition] = &[header(8, "erDiagram", "er header")];
1150const GANTT_HEADERS: &[HeaderDefinition] = &[header(9, "gantt", "gantt header")];
1151const MINDMAP_HEADERS: &[HeaderDefinition] = &[header(10, "mindmap", "mindmap header")];
1152const INFO_HEADERS: &[HeaderDefinition] = &[header(11, "info", "info header")];
1153const JOURNEY_HEADERS: &[HeaderDefinition] = &[header(12, "journey", "journey header")];
1154const TIMELINE_HEADERS: &[HeaderDefinition] = &[header(13, "timeline", "timeline header")];
1155const GIT_GRAPH_HEADERS: &[HeaderDefinition] = &[header(14, "gitGraph", "git graph header")];
1156const PIE_HEADERS: &[HeaderDefinition] = &[header(15, "pie", "pie header")];
1157const REQUIREMENT_HEADERS: &[HeaderDefinition] =
1158    &[header(16, "requirementDiagram", "requirement header")];
1159const SANKEY_HEADERS: &[HeaderDefinition] = &[header(17, "sankey", "sankey header")];
1160const PACKET_HEADERS: &[HeaderDefinition] = &[
1161    header(18, "packet", "packet header"),
1162    header(19, "packet-beta", "packet beta header"),
1163];
1164const XYCHART_HEADERS: &[HeaderDefinition] = &[
1165    header(20, "xychart", "xychart header"),
1166    header(21, "xychart-beta", "xychart beta header"),
1167];
1168const TREE_VIEW_HEADERS: &[HeaderDefinition] = &[header(22, "treeView-beta", "tree view header")];
1169const ISHIKAWA_HEADERS: &[HeaderDefinition] = &[header(23, "ishikawa-beta", "ishikawa header")];
1170const EVENTMODELING_HEADERS: &[HeaderDefinition] =
1171    &[header(24, "eventmodeling", "event modeling header")];
1172const QUADRANT_HEADERS: &[HeaderDefinition] =
1173    &[header(25, "quadrantChart", "quadrant chart header")];
1174const VENN_HEADERS: &[HeaderDefinition] = &[header(26, "venn-beta", "venn header")];
1175const ZENUML_HEADERS: &[HeaderDefinition] = &[header(27, "zenuml", "zenuml header")];
1176const C4_HEADERS: &[HeaderDefinition] = &[
1177    header(28, "C4Context", "c4 context header"),
1178    header(29, "C4Container", "c4 container header"),
1179    header(30, "C4Component", "c4 component header"),
1180    header(31, "C4Dynamic", "c4 dynamic header"),
1181    header(32, "C4Deployment", "c4 deployment header"),
1182];
1183const KANBAN_HEADERS: &[HeaderDefinition] = &[header(33, "kanban", "kanban header")];
1184const ARCHITECTURE_HEADERS: &[HeaderDefinition] =
1185    &[header(34, "architecture-beta", "architecture header")];
1186const BLOCK_HEADERS: &[HeaderDefinition] = &[header(35, "block-beta", "block header")];
1187const RADAR_HEADERS: &[HeaderDefinition] = &[header(36, "radar-beta", "radar header")];
1188const TREEMAP_HEADERS: &[HeaderDefinition] = &[header(37, "treemap-beta", "treemap header")];
1189const RAILROAD_HEADERS: &[HeaderDefinition] = &[header(38, "railroad-beta", "railroad header")];
1190const RAILROAD_EBNF_HEADERS: &[HeaderDefinition] =
1191    &[header(39, "railroad-ebnf-beta", "railroad ebnf header")];
1192const RAILROAD_ABNF_HEADERS: &[HeaderDefinition] =
1193    &[header(40, "railroad-abnf-beta", "railroad abnf header")];
1194const RAILROAD_PEG_HEADERS: &[HeaderDefinition] =
1195    &[header(41, "railroad-peg-beta", "railroad peg header")];
1196const WARDLEY_HEADERS: &[HeaderDefinition] = &[header(42, "wardley-beta", "wardley header")];
1197const CYNEFIN_HEADERS: &[HeaderDefinition] = &[header(43, "cynefin-beta", "cynefin header")];
1198const FLOWCHART_ELK_HEADERS: &[HeaderDefinition] =
1199    &[header(44, "flowchart-elk TD", "elk flowchart header")];
1200const USECASE_HEADERS: &[HeaderDefinition] =
1201    &[header(45, "usecase-beta", "usecase diagram header")];
1202const AGENTFLOW_HEADERS: &[HeaderDefinition] =
1203    &[header(46, "agentflow-beta", "agentflow diagram header")];
1204
1205const ERROR_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1206    id: "error",
1207    catalog_order: 0,
1208    detector: Some(ordered(0, crate::detect::detector_error)),
1209    semantic: Some(ordered(0, crate::diagrams::error_diagram::parse_error)),
1210    combined: None,
1211    typed: Some(ordered(38, render_error)),
1212    render_kind: Some("error"),
1213    metadata: None,
1214    headers: &[],
1215    config_alias_order: None,
1216}];
1217
1218const FLOWCHART_VARIANTS: &[FamilyVariantDefinition] = &[
1219    variant! {
1220        feature: "diagram-flowchart",
1221        id: "flowchart-elk",
1222        catalog_order: 2,
1223        detector: Some(ordered(2, crate::detect::detector_flowchart_elk)),
1224        semantic: Some(ordered(3, crate::diagrams::flowchart::parse_flowchart)),
1225        warning_semantic: crate::diagrams::flowchart::parse_flowchart_with_warning_facts,
1226        combined: Some(ordered(2, crate::diagrams::flowchart::parse_flowchart_json_and_editor_facts)),
1227        typed: Some(ordered(7, render_flowchart)),
1228        render_kind: Some("flowchart"),
1229        metadata: Some(metadata("flowchart", None)),
1230        headers: FLOWCHART_ELK_HEADERS,
1231        config_alias_order: Some(3),
1232    },
1233    variant! {
1234        feature: "diagram-flowchart",
1235        id: "flowchart-v2",
1236        catalog_order: 17,
1237        detector: Some(ordered(17, crate::detect::detector_flowchart_v2)),
1238        semantic: Some(ordered(1, crate::diagrams::flowchart::parse_flowchart)),
1239        warning_semantic: crate::diagrams::flowchart::parse_flowchart_with_warning_facts,
1240        combined: Some(ordered(0, crate::diagrams::flowchart::parse_flowchart_json_and_editor_facts)),
1241        typed: Some(ordered(5, render_flowchart)),
1242        render_kind: Some("flowchart"),
1243        metadata: Some(metadata("flowchart", Some(7))),
1244        headers: FLOWCHART_HEADERS,
1245        config_alias_order: Some(2),
1246    },
1247    variant! {
1248        feature: "diagram-flowchart",
1249        id: "flowchart",
1250        catalog_order: 18,
1251        detector: None,
1252        semantic: Some(ordered(2, crate::diagrams::flowchart::parse_flowchart)),
1253        warning_semantic: crate::diagrams::flowchart::parse_flowchart_with_warning_facts,
1254        combined: Some(ordered(1, crate::diagrams::flowchart::parse_flowchart_json_and_editor_facts)),
1255        typed: Some(ordered(6, render_flowchart)),
1256        render_kind: Some("flowchart"),
1257        metadata: Some(metadata("flowchart", None)),
1258        headers: &[],
1259        config_alias_order: None,
1260    },
1261];
1262
1263const SWIMLANE_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1264    feature: "diagram-swimlane",
1265    id: "swimlane",
1266    catalog_order: 16,
1267    detector: Some(ordered(16, crate::detect::detector_swimlane)),
1268    semantic: Some(ordered(9, crate::diagrams::flowchart::parse_flowchart)),
1269    warning_semantic: crate::diagrams::flowchart::parse_flowchart_with_warning_facts,
1270    combined: Some(ordered(3, crate::diagrams::flowchart::parse_flowchart_json_and_editor_facts)),
1271    typed: Some(ordered(39, render_flowchart)),
1272    render_kind: Some("flowchart"),
1273    metadata: Some(metadata("swimlane", Some(27))),
1274    headers: SWIMLANE_HEADERS,
1275    config_alias_order: None,
1276}];
1277
1278const MINDMAP_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1279    feature: "diagram-mindmap",
1280    id: "mindmap",
1281    catalog_order: 3,
1282    detector: Some(ordered(3, crate::detect::detector_mindmap)),
1283    semantic: Some(ordered(22, crate::diagrams::mindmap::parse_mindmap)),
1284    combined: Some(ordered(5, crate::diagrams::mindmap::parse_mindmap_json_and_editor_facts)),
1285    typed: Some(ordered(0, render_mindmap)),
1286    render_kind: Some("mindmap"),
1287    metadata: Some(metadata("mindmap", Some(14))),
1288    headers: MINDMAP_HEADERS,
1289    config_alias_order: None,
1290}];
1291
1292const ARCHITECTURE_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1293    feature: "diagram-architecture",
1294    id: "architecture",
1295    catalog_order: 4,
1296    detector: Some(ordered(4, crate::detect::detector_architecture)),
1297    semantic: Some(ordered(27, crate::diagrams::architecture::parse_architecture)),
1298    combined: Some(ordered(4, crate::diagrams::architecture::parse_architecture_json_and_editor_facts)),
1299    typed: Some(ordered(16, render_architecture)),
1300    render_kind: Some("architecture"),
1301    metadata: Some(metadata("architecture", Some(0))),
1302    headers: ARCHITECTURE_HEADERS,
1303    config_alias_order: None,
1304}];
1305
1306const ZENUML_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1307    feature: "diagram-zenuml",
1308    id: "zenuml",
1309    catalog_order: 5,
1310    detector: Some(ordered(5, crate::detect::detector_zenuml)),
1311    semantic: Some(ordered(15, crate::diagrams::zenuml::parse_zenuml)),
1312    combined: Some(ordered(36, crate::diagrams::zenuml::parse_zenuml_json_and_editor_facts)),
1313    typed: Some(ordered(3, render_zenuml)),
1314    render_kind: Some("zenuml"),
1315    metadata: Some(metadata("zenuml", Some(34))),
1316    headers: ZENUML_HEADERS,
1317    config_alias_order: None,
1318}];
1319
1320const SEQUENCE_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1321    feature: "diagram-sequence",
1322    id: "sequence",
1323    catalog_order: 15,
1324    detector: Some(ordered(15, crate::detect::detector_sequence)),
1325    semantic: Some(ordered(8, crate::diagrams::sequence::parse_sequence)),
1326    combined: Some(ordered(19, crate::diagrams::sequence::parse_sequence_json_and_editor_facts)),
1327    typed: Some(ordered(4, render_sequence)),
1328    render_kind: Some("sequence"),
1329    metadata: Some(metadata("sequence", Some(25))),
1330    headers: SEQUENCE_HEADERS,
1331    config_alias_order: None,
1332}];
1333
1334const C4_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1335    feature: "diagram-c4",
1336    id: "c4",
1337    catalog_order: 6,
1338    detector: Some(ordered(6, crate::detect::detector_c4)),
1339    semantic: Some(ordered(6, crate::diagrams::c4::parse_c4)),
1340    combined: Some(ordered(28, crate::diagrams::c4::parse_c4_json_and_editor_facts)),
1341    typed: Some(ordered(10, render_c4)),
1342    render_kind: Some("c4"),
1343    metadata: Some(metadata("c4", Some(2))),
1344    headers: C4_HEADERS,
1345    config_alias_order: None,
1346}];
1347
1348const KANBAN_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1349    feature: "diagram-kanban",
1350    id: "kanban",
1351    catalog_order: 7,
1352    detector: Some(ordered(7, crate::detect::detector_kanban)),
1353    semantic: Some(ordered(26, crate::diagrams::kanban::parse_kanban)),
1354    combined: Some(ordered(29, crate::diagrams::kanban::parse_kanban_json_and_editor_facts)),
1355    typed: Some(ordered(17, render_kanban)),
1356    render_kind: Some("kanban"),
1357    metadata: Some(metadata("kanban", Some(13))),
1358    headers: KANBAN_HEADERS,
1359    config_alias_order: None,
1360}];
1361
1362const CLASS_VARIANTS: &[FamilyVariantDefinition] = &[
1363    variant! {
1364        feature: "diagram-class",
1365        id: "classDiagram",
1366        catalog_order: 8,
1367        detector: Some(ordered(8, crate::detect::detector_class_v2)),
1368        semantic: Some(ordered(16, crate::diagrams::class::parse_class)),
1369        combined: Some(ordered(20, crate::diagrams::class::parse_class_json_and_editor_facts)),
1370        typed: Some(ordered(8, render_class)),
1371        render_kind: Some("class"),
1372        metadata: Some(metadata("class", Some(3))),
1373        headers: CLASS_HEADERS,
1374        config_alias_order: Some(0),
1375    },
1376    variant! {
1377        feature: "diagram-class",
1378        id: "class",
1379        catalog_order: 9,
1380        detector: None,
1381        semantic: Some(ordered(17, crate::diagrams::class::parse_class)),
1382        combined: Some(ordered(21, crate::diagrams::class::parse_class_json_and_editor_facts)),
1383        typed: Some(ordered(9, render_class)),
1384        render_kind: Some("class"),
1385        metadata: Some(metadata("class", None)),
1386        headers: &[],
1387        config_alias_order: None,
1388    },
1389];
1390
1391const ER_VARIANTS: &[FamilyVariantDefinition] = &[
1392    variant! {
1393        feature: "diagram-er",
1394        id: "er",
1395        catalog_order: 10,
1396        detector: Some(ordered(10, crate::detect::detector_er)),
1397        semantic: Some(ordered(18, crate::diagrams::er::parse_er)),
1398        combined: Some(ordered(17, crate::diagrams::er::parse_er_json_and_editor_facts)),
1399        typed: Some(ordered(29, render_er)),
1400        render_kind: Some("er"),
1401        metadata: Some(metadata("er", Some(5))),
1402        headers: ER_HEADERS,
1403        config_alias_order: None,
1404    },
1405    variant! {
1406        feature: "diagram-er",
1407        id: "erDiagram",
1408        catalog_order: 41,
1409        detector: None,
1410        semantic: Some(ordered(19, crate::diagrams::er::parse_er)),
1411        combined: Some(ordered(18, crate::diagrams::er::parse_er_json_and_editor_facts)),
1412        typed: Some(ordered(30, render_er)),
1413        render_kind: Some("er"),
1414        metadata: Some(metadata("er", None)),
1415        headers: &[],
1416        config_alias_order: Some(1),
1417    },
1418];
1419
1420const GANTT_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1421    feature: "diagram-gantt",
1422    id: "gantt",
1423    catalog_order: 11,
1424    detector: Some(ordered(11, crate::detect::detector_gantt)),
1425    semantic: Some(ordered(23, crate::diagrams::gantt::parse_gantt)),
1426    combined: Some(ordered(30, crate::diagrams::gantt::parse_gantt_json_and_editor_facts)),
1427    typed: Some(ordered(18, render_gantt)),
1428    render_kind: Some("gantt"),
1429    metadata: Some(metadata("gantt", Some(8))),
1430    headers: GANTT_HEADERS,
1431    config_alias_order: None,
1432}];
1433
1434const INFO_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1435    feature: "diagram-info",
1436    id: "info",
1437    catalog_order: 12,
1438    detector: Some(ordered(12, crate::detect::detector_info)),
1439    semantic: Some(ordered(4, crate::diagrams::info::parse_info)),
1440    combined: Some(ordered(10, crate::diagrams::info::parse_info_json_and_editor_facts)),
1441    typed: Some(ordered(26, render_info)),
1442    render_kind: Some("info"),
1443    metadata: Some(metadata("info", Some(10))),
1444    headers: INFO_HEADERS,
1445    config_alias_order: None,
1446}];
1447
1448const PIE_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1449    feature: "diagram-pie",
1450    id: "pie",
1451    catalog_order: 13,
1452    detector: Some(ordered(13, crate::detect::detector_pie)),
1453    semantic: Some(ordered(5, crate::diagrams::pie::parse_pie)),
1454    combined: Some(ordered(11, crate::diagrams::pie::parse_pie_json_and_editor_facts)),
1455    typed: Some(ordered(19, render_pie)),
1456    render_kind: Some("pie"),
1457    metadata: Some(metadata("pie", Some(16))),
1458    headers: PIE_HEADERS,
1459    config_alias_order: None,
1460}];
1461
1462const REQUIREMENT_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1463    feature: "diagram-requirement",
1464    id: "requirement",
1465    catalog_order: 14,
1466    detector: Some(ordered(14, crate::detect::detector_requirement)),
1467    semantic: Some(ordered(7, crate::diagrams::requirement::parse_requirement)),
1468    combined: Some(ordered(31, crate::diagrams::requirement::parse_requirement_json_and_editor_facts)),
1469    typed: Some(ordered(23, render_requirement)),
1470    render_kind: Some("requirement"),
1471    metadata: Some(metadata("requirement", Some(23))),
1472    headers: REQUIREMENT_HEADERS,
1473    config_alias_order: None,
1474}];
1475
1476const TIMELINE_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1477    feature: "diagram-timeline",
1478    id: "timeline",
1479    catalog_order: 19,
1480    detector: Some(ordered(19, crate::detect::detector_timeline)),
1481    semantic: Some(ordered(24, crate::diagrams::timeline::parse_timeline)),
1482    combined: Some(ordered(32, crate::diagrams::timeline::parse_timeline_json_and_editor_facts)),
1483    typed: Some(ordered(21, render_timeline)),
1484    render_kind: Some("timeline"),
1485    metadata: Some(metadata("timeline", Some(28))),
1486    headers: TIMELINE_HEADERS,
1487    config_alias_order: None,
1488}];
1489
1490const GIT_GRAPH_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1491    feature: "diagram-git-graph",
1492    id: "gitGraph",
1493    catalog_order: 20,
1494    detector: Some(ordered(20, crate::detect::detector_git_graph)),
1495    semantic: Some(ordered(29, crate::diagrams::git_graph::parse_git_graph)),
1496    warning_semantic: crate::diagrams::git_graph::parse_git_graph_with_warning_facts,
1497    combined: Some(ordered(15, crate::diagrams::git_graph::parse_git_graph_json_and_editor_facts)),
1498    typed: Some(ordered(33, render_git_graph)),
1499    render_kind: Some("gitGraph"),
1500    metadata: Some(metadata("gitgraph", Some(9))),
1501    headers: GIT_GRAPH_HEADERS,
1502    config_alias_order: None,
1503}];
1504
1505const STATE_VARIANTS: &[FamilyVariantDefinition] = &[
1506    variant! {
1507        feature: "diagram-state",
1508        id: "stateDiagram",
1509        catalog_order: 21,
1510        detector: Some(ordered(21, crate::detect::detector_state_v2)),
1511        semantic: Some(ordered(20, crate::diagrams::state::parse_state)),
1512        combined: Some(ordered(26, crate::diagrams::state::parse_state_json_and_editor_facts)),
1513        typed: Some(ordered(1, render_state)),
1514        render_kind: Some("state"),
1515        metadata: Some(metadata("state", Some(26))),
1516        headers: STATE_HEADERS,
1517        config_alias_order: Some(4),
1518    },
1519    variant! {
1520        feature: "diagram-state",
1521        id: "state",
1522        catalog_order: 22,
1523        detector: None,
1524        semantic: Some(ordered(21, crate::diagrams::state::parse_state)),
1525        combined: Some(ordered(27, crate::diagrams::state::parse_state_json_and_editor_facts)),
1526        typed: Some(ordered(2, render_state)),
1527        render_kind: Some("state"),
1528        metadata: Some(metadata("state", None)),
1529        headers: &[],
1530        config_alias_order: None,
1531    },
1532];
1533
1534const JOURNEY_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1535    feature: "diagram-journey",
1536    id: "journey",
1537    catalog_order: 23,
1538    detector: Some(ordered(23, crate::detect::detector_journey)),
1539    semantic: Some(ordered(25, crate::diagrams::journey::parse_journey)),
1540    combined: Some(ordered(33, crate::diagrams::journey::parse_journey_json_and_editor_facts)),
1541    typed: Some(ordered(22, render_journey)),
1542    render_kind: Some("journey"),
1543    metadata: Some(metadata("journey", Some(12))),
1544    headers: JOURNEY_HEADERS,
1545    config_alias_order: None,
1546}];
1547
1548const QUADRANT_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1549    feature: "diagram-quadrant-chart",
1550    id: "quadrantChart",
1551    catalog_order: 24,
1552    detector: Some(ordered(24, crate::detect::detector_quadrant)),
1553    semantic: Some(ordered(30, crate::diagrams::quadrant_chart::parse_quadrant_chart)),
1554    combined: Some(ordered(34, crate::diagrams::quadrant_chart::parse_quadrant_chart_json_and_editor_facts)),
1555    typed: Some(ordered(31, render_quadrant_chart)),
1556    render_kind: Some("quadrantChart"),
1557    metadata: Some(metadata("quadrantchart", Some(17))),
1558    headers: QUADRANT_HEADERS,
1559    config_alias_order: None,
1560}];
1561
1562const SANKEY_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1563    feature: "diagram-sankey",
1564    id: "sankey",
1565    catalog_order: 25,
1566    detector: Some(ordered(25, crate::detect::detector_sankey)),
1567    semantic: Some(ordered(38, crate::diagrams::sankey::parse_sankey)),
1568    combined: Some(ordered(16, crate::diagrams::sankey::parse_sankey_json_and_editor_facts)),
1569    typed: Some(ordered(24, render_sankey)),
1570    render_kind: Some("sankey"),
1571    metadata: Some(metadata("sankey", Some(24))),
1572    headers: SANKEY_HEADERS,
1573    config_alias_order: None,
1574}];
1575
1576const PACKET_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1577    feature: "diagram-packet",
1578    id: "packet",
1579    catalog_order: 26,
1580    detector: Some(ordered(26, crate::detect::detector_packet)),
1581    semantic: Some(ordered(31, crate::diagrams::packet::parse_packet)),
1582    combined: Some(ordered(12, crate::diagrams::packet::parse_packet_json_and_editor_facts)),
1583    typed: Some(ordered(20, render_packet)),
1584    render_kind: Some("packet"),
1585    metadata: Some(metadata("packet", Some(15))),
1586    headers: PACKET_HEADERS,
1587    config_alias_order: None,
1588}];
1589
1590const XYCHART_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1591    feature: "diagram-xychart",
1592    id: "xychart",
1593    catalog_order: 27,
1594    detector: Some(ordered(27, crate::detect::detector_xychart)),
1595    semantic: Some(ordered(39, crate::diagrams::xychart::parse_xychart)),
1596    combined: Some(ordered(38, crate::diagrams::xychart::parse_xychart_json_and_editor_facts)),
1597    typed: Some(ordered(32, render_xychart)),
1598    render_kind: Some("xychart"),
1599    metadata: Some(metadata("xychart", Some(33))),
1600    headers: XYCHART_HEADERS,
1601    config_alias_order: Some(5),
1602}];
1603
1604const BLOCK_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1605    feature: "diagram-block",
1606    id: "block",
1607    catalog_order: 28,
1608    detector: Some(ordered(28, crate::detect::detector_block)),
1609    semantic: Some(ordered(28, crate::diagrams::block::parse_block)),
1610    warning_semantic: crate::diagrams::block::parse_block_with_warning_facts,
1611    combined: Some(ordered(37, crate::diagrams::block::parse_block_json_and_editor_facts)),
1612    typed: Some(ordered(28, render_block)),
1613    render_kind: Some("block"),
1614    metadata: Some(metadata("block", Some(1))),
1615    headers: BLOCK_HEADERS,
1616    config_alias_order: None,
1617}];
1618
1619const EVENTMODELING_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1620    feature: "diagram-event-modeling",
1621    id: "eventmodeling",
1622    catalog_order: 29,
1623    detector: Some(ordered(29, crate::detect::detector_eventmodeling)),
1624    semantic: Some(ordered(35, crate::diagrams::eventmodeling::parse_eventmodeling)),
1625    combined: Some(ordered(22, crate::diagrams::eventmodeling::parse_eventmodeling_json_and_editor_facts)),
1626    typed: Some(ordered(36, render_eventmodeling)),
1627    render_kind: Some("eventmodeling"),
1628    metadata: Some(metadata("eventmodeling", Some(6))),
1629    headers: EVENTMODELING_HEADERS,
1630    config_alias_order: None,
1631}];
1632
1633const TREE_VIEW_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1634    feature: "diagram-tree-view",
1635    id: "treeView",
1636    catalog_order: 30,
1637    detector: Some(ordered(30, crate::detect::detector_tree_view)),
1638    semantic: Some(ordered(33, crate::diagrams::tree_view::parse_tree_view)),
1639    combined: Some(ordered(23, crate::diagrams::tree_view::parse_tree_view_json_and_editor_facts)),
1640    typed: Some(ordered(34, render_tree_view)),
1641    render_kind: Some("treeView"),
1642    metadata: Some(metadata("treeView", Some(29))),
1643    headers: TREE_VIEW_HEADERS,
1644    config_alias_order: None,
1645}];
1646
1647const RADAR_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1648    feature: "diagram-radar",
1649    id: "radar",
1650    catalog_order: 31,
1651    detector: Some(ordered(31, crate::detect::detector_radar)),
1652    semantic: Some(ordered(32, crate::diagrams::radar::parse_radar)),
1653    combined: Some(ordered(14, crate::diagrams::radar::parse_radar_json_and_editor_facts)),
1654    typed: Some(ordered(25, render_radar)),
1655    render_kind: Some("radar"),
1656    metadata: Some(metadata("radar", Some(18))),
1657    headers: RADAR_HEADERS,
1658    config_alias_order: None,
1659}];
1660
1661const ISHIKAWA_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1662    feature: "diagram-ishikawa",
1663    id: "ishikawa",
1664    catalog_order: 32,
1665    detector: Some(ordered(32, crate::detect::detector_ishikawa)),
1666    semantic: Some(ordered(34, crate::diagrams::ishikawa::parse_ishikawa)),
1667    combined: Some(ordered(24, crate::diagrams::ishikawa::parse_ishikawa_json_and_editor_facts)),
1668    typed: Some(ordered(35, render_ishikawa)),
1669    render_kind: Some("ishikawa"),
1670    metadata: Some(metadata("ishikawa", Some(11))),
1671    headers: ISHIKAWA_HEADERS,
1672    config_alias_order: None,
1673}];
1674
1675const TREEMAP_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1676    feature: "diagram-treemap",
1677    id: "treemap",
1678    catalog_order: 33,
1679    detector: Some(ordered(33, crate::detect::detector_treemap)),
1680    semantic: Some(ordered(36, crate::diagrams::treemap::parse_treemap)),
1681    combined: Some(ordered(25, crate::diagrams::treemap::parse_treemap_json_and_editor_facts)),
1682    typed: Some(ordered(27, render_treemap)),
1683    render_kind: Some("treemap"),
1684    metadata: Some(metadata("treemap", Some(30))),
1685    headers: TREEMAP_HEADERS,
1686    config_alias_order: None,
1687}];
1688
1689const RAILROAD_VARIANTS: &[FamilyVariantDefinition] = &[
1690    variant! {
1691        feature: "diagram-railroad",
1692        id: "railroad",
1693        catalog_order: 34,
1694        detector: Some(ordered(34, crate::detect::detector_railroad)),
1695        semantic: Some(ordered(11, crate::diagrams::railroad::parse_railroad)),
1696        combined: Some(ordered(6, crate::diagrams::railroad::parse_railroad_json_and_editor_facts)),
1697        typed: Some(ordered(12, render_railroad)),
1698        render_kind: Some("railroad"),
1699        metadata: Some(metadata("railroad", Some(19))),
1700        headers: RAILROAD_HEADERS,
1701        config_alias_order: None,
1702    },
1703    variant! {
1704        feature: "diagram-railroad",
1705        id: "railroadEbnf",
1706        catalog_order: 35,
1707        detector: Some(ordered(35, crate::detect::detector_railroad_ebnf)),
1708        semantic: Some(ordered(12, crate::diagrams::railroad::parse_railroad_ebnf)),
1709        combined: Some(ordered(7, crate::diagrams::railroad::parse_railroad_ebnf_json_and_editor_facts)),
1710        typed: Some(ordered(13, render_railroad_ebnf)),
1711        render_kind: Some("railroad"),
1712        metadata: Some(metadata("railroadEbnf", Some(21))),
1713        headers: RAILROAD_EBNF_HEADERS,
1714        config_alias_order: None,
1715    },
1716    variant! {
1717        feature: "diagram-railroad",
1718        id: "railroadAbnf",
1719        catalog_order: 36,
1720        detector: Some(ordered(36, crate::detect::detector_railroad_abnf)),
1721        semantic: Some(ordered(13, crate::diagrams::railroad::parse_railroad_abnf)),
1722        combined: Some(ordered(8, crate::diagrams::railroad::parse_railroad_abnf_json_and_editor_facts)),
1723        typed: Some(ordered(14, render_railroad_abnf)),
1724        render_kind: Some("railroad"),
1725        metadata: Some(metadata("railroadAbnf", Some(20))),
1726        headers: RAILROAD_ABNF_HEADERS,
1727        config_alias_order: None,
1728    },
1729    variant! {
1730        feature: "diagram-railroad",
1731        id: "railroadPeg",
1732        catalog_order: 37,
1733        detector: Some(ordered(37, crate::detect::detector_railroad_peg)),
1734        semantic: Some(ordered(14, crate::diagrams::railroad::parse_railroad_peg)),
1735        combined: Some(ordered(9, crate::diagrams::railroad::parse_railroad_peg_json_and_editor_facts)),
1736        typed: Some(ordered(15, render_railroad_peg)),
1737        render_kind: Some("railroad"),
1738        metadata: Some(metadata("railroadPeg", Some(22))),
1739        headers: RAILROAD_PEG_HEADERS,
1740        config_alias_order: None,
1741    },
1742];
1743
1744const VENN_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1745    feature: "diagram-venn",
1746    id: "venn",
1747    catalog_order: 38,
1748    detector: Some(ordered(38, crate::detect::detector_venn)),
1749    semantic: Some(ordered(37, crate::diagrams::venn::parse_venn)),
1750    combined: Some(ordered(35, crate::diagrams::venn::parse_venn_json_and_editor_facts)),
1751    typed: Some(ordered(37, render_venn)),
1752    render_kind: Some("venn"),
1753    metadata: Some(metadata("venn", Some(31))),
1754    headers: VENN_HEADERS,
1755    config_alias_order: None,
1756}];
1757
1758const WARDLEY_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1759    feature: "diagram-wardley",
1760    id: "wardley",
1761    catalog_order: 39,
1762    detector: Some(ordered(39, crate::detect::detector_wardley)),
1763    semantic: Some(ordered(40, crate::diagrams::wardley::parse_wardley)),
1764    combined: Some(ordered(39, crate::diagrams::wardley::parse_wardley_json_and_editor_facts)),
1765    typed: Some(ordered(40, render_wardley)),
1766    render_kind: Some("wardley"),
1767    metadata: Some(metadata("wardley", Some(32))),
1768    headers: WARDLEY_HEADERS,
1769    config_alias_order: None,
1770}];
1771
1772const CYNEFIN_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1773    feature: "diagram-cynefin",
1774    id: "cynefin",
1775    catalog_order: 40,
1776    detector: Some(ordered(40, crate::detect::detector_cynefin)),
1777    semantic: Some(ordered(10, crate::diagrams::cynefin::parse_cynefin)),
1778    combined: Some(ordered(13, crate::diagrams::cynefin::parse_cynefin_json_and_editor_facts)),
1779    typed: Some(ordered(11, render_cynefin)),
1780    render_kind: Some("cynefin"),
1781    metadata: Some(metadata("cynefin", Some(4))),
1782    headers: CYNEFIN_HEADERS,
1783    config_alias_order: None,
1784}];
1785
1786const USECASE_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1787    feature: "diagram-usecase",
1788    id: "usecase",
1789    catalog_order: 42,
1790    detector: Some(ordered(42, crate::detect::detector_usecase)),
1791    semantic: Some(ordered(42, crate::diagrams::usecase::parse_usecase)),
1792    combined: Some(ordered(42, crate::diagrams::usecase::parse_usecase_json_and_editor_facts)),
1793    typed: Some(ordered(42, render_usecase)),
1794    render_kind: Some("usecase"),
1795    metadata: Some(metadata("usecase", Some(36))),
1796    headers: USECASE_HEADERS,
1797    config_alias_order: None,
1798}];
1799
1800const AGENTFLOW_VARIANTS: &[FamilyVariantDefinition] = &[variant! {
1801    feature: "diagram-agentflow",
1802    id: "agentflow",
1803    catalog_order: 43,
1804    detector: Some(ordered(43, crate::detect::detector_agentflow)),
1805    semantic: Some(ordered(43, crate::diagrams::agentflow::parse_agentflow)),
1806    warning_semantic: crate::diagrams::agentflow::parse_agentflow_with_warning_facts,
1807    combined: Some(ordered(43, crate::diagrams::agentflow::parse_agentflow_json_and_editor_facts)),
1808    typed: Some(ordered(43, render_agentflow)),
1809    render_kind: Some("agentflow"),
1810    metadata: Some(metadata("agentflow", Some(35))),
1811    headers: AGENTFLOW_HEADERS,
1812    config_alias_order: Some(36),
1813}];
1814
1815const FAMILY_CATALOG: &[DiagramFamilyDefinition] = &[
1816    DiagramFamilyDefinition {
1817        logical_kind: "error",
1818        selector: None,
1819        editor_semantics: GENERIC_EDITOR_SEMANTICS,
1820        config: None,
1821        variants: ERROR_VARIANTS,
1822    },
1823    DiagramFamilyDefinition {
1824        logical_kind: "flowchart",
1825        selector: Some("diagram-flowchart"),
1826        editor_semantics: FLOWCHART_EDITOR_SEMANTICS,
1827        config: Some(FamilyConfigDefinition {
1828            namespace: "flowchart",
1829            frontmatter_order: 7,
1830        }),
1831        variants: FLOWCHART_VARIANTS,
1832    },
1833    DiagramFamilyDefinition {
1834        logical_kind: "swimlane",
1835        selector: Some("diagram-swimlane"),
1836        editor_semantics: SWIMLANE_EDITOR_SEMANTICS,
1837        config: Some(FamilyConfigDefinition {
1838            namespace: "swimlane",
1839            frontmatter_order: 23,
1840        }),
1841        variants: SWIMLANE_VARIANTS,
1842    },
1843    DiagramFamilyDefinition {
1844        logical_kind: "mindmap",
1845        selector: Some("diagram-mindmap"),
1846        editor_semantics: MINDMAP_EDITOR_SEMANTICS,
1847        config: Some(FamilyConfigDefinition {
1848            namespace: "mindmap",
1849            frontmatter_order: 13,
1850        }),
1851        variants: MINDMAP_VARIANTS,
1852    },
1853    DiagramFamilyDefinition {
1854        logical_kind: "architecture",
1855        selector: Some("diagram-architecture"),
1856        editor_semantics: GENERIC_EDITOR_SEMANTICS,
1857        config: Some(FamilyConfigDefinition {
1858            namespace: "architecture",
1859            frontmatter_order: 0,
1860        }),
1861        variants: ARCHITECTURE_VARIANTS,
1862    },
1863    DiagramFamilyDefinition {
1864        logical_kind: "zenuml",
1865        selector: Some("diagram-zenuml"),
1866        editor_semantics: GENERIC_EDITOR_SEMANTICS,
1867        config: Some(FamilyConfigDefinition {
1868            namespace: "zenuml",
1869            frontmatter_order: 30,
1870        }),
1871        variants: ZENUML_VARIANTS,
1872    },
1873    DiagramFamilyDefinition {
1874        logical_kind: "sequence",
1875        selector: Some("diagram-sequence"),
1876        editor_semantics: SEQUENCE_EDITOR_SEMANTICS,
1877        config: Some(FamilyConfigDefinition {
1878            namespace: "sequence",
1879            frontmatter_order: 21,
1880        }),
1881        variants: SEQUENCE_VARIANTS,
1882    },
1883    DiagramFamilyDefinition {
1884        logical_kind: "c4",
1885        selector: Some("diagram-c4"),
1886        editor_semantics: CARDINAL_EDITOR_SEMANTICS,
1887        config: Some(FamilyConfigDefinition {
1888            namespace: "c4",
1889            frontmatter_order: 2,
1890        }),
1891        variants: C4_VARIANTS,
1892    },
1893    DiagramFamilyDefinition {
1894        logical_kind: "kanban",
1895        selector: Some("diagram-kanban"),
1896        editor_semantics: GENERIC_EDITOR_SEMANTICS,
1897        config: Some(FamilyConfigDefinition {
1898            namespace: "kanban",
1899            frontmatter_order: 12,
1900        }),
1901        variants: KANBAN_VARIANTS,
1902    },
1903    DiagramFamilyDefinition {
1904        logical_kind: "class",
1905        selector: Some("diagram-class"),
1906        editor_semantics: CLASS_EDITOR_SEMANTICS,
1907        config: Some(FamilyConfigDefinition {
1908            namespace: "class",
1909            frontmatter_order: 3,
1910        }),
1911        variants: CLASS_VARIANTS,
1912    },
1913    DiagramFamilyDefinition {
1914        logical_kind: "er",
1915        selector: Some("diagram-er"),
1916        editor_semantics: ER_EDITOR_SEMANTICS,
1917        config: Some(FamilyConfigDefinition {
1918            namespace: "er",
1919            frontmatter_order: 5,
1920        }),
1921        variants: ER_VARIANTS,
1922    },
1923    DiagramFamilyDefinition {
1924        logical_kind: "gantt",
1925        selector: Some("diagram-gantt"),
1926        editor_semantics: GENERIC_EDITOR_SEMANTICS,
1927        config: Some(FamilyConfigDefinition {
1928            namespace: "gantt",
1929            frontmatter_order: 8,
1930        }),
1931        variants: GANTT_VARIANTS,
1932    },
1933    DiagramFamilyDefinition {
1934        logical_kind: "info",
1935        selector: Some("diagram-info"),
1936        editor_semantics: GENERIC_EDITOR_SEMANTICS,
1937        config: None,
1938        variants: INFO_VARIANTS,
1939    },
1940    DiagramFamilyDefinition {
1941        logical_kind: "pie",
1942        selector: Some("diagram-pie"),
1943        editor_semantics: GENERIC_EDITOR_SEMANTICS,
1944        config: Some(FamilyConfigDefinition {
1945            namespace: "pie",
1946            frontmatter_order: 15,
1947        }),
1948        variants: PIE_VARIANTS,
1949    },
1950    DiagramFamilyDefinition {
1951        logical_kind: "requirement",
1952        selector: Some("diagram-requirement"),
1953        editor_semantics: CARDINAL_EDITOR_SEMANTICS,
1954        config: Some(FamilyConfigDefinition {
1955            namespace: "requirement",
1956            frontmatter_order: 19,
1957        }),
1958        variants: REQUIREMENT_VARIANTS,
1959    },
1960    DiagramFamilyDefinition {
1961        logical_kind: "timeline",
1962        selector: Some("diagram-timeline"),
1963        editor_semantics: GENERIC_EDITOR_SEMANTICS,
1964        config: Some(FamilyConfigDefinition {
1965            namespace: "timeline",
1966            frontmatter_order: 24,
1967        }),
1968        variants: TIMELINE_VARIANTS,
1969    },
1970    DiagramFamilyDefinition {
1971        logical_kind: "gitGraph",
1972        selector: Some("diagram-git-graph"),
1973        editor_semantics: GENERIC_EDITOR_SEMANTICS,
1974        config: Some(FamilyConfigDefinition {
1975            namespace: "gitGraph",
1976            frontmatter_order: 9,
1977        }),
1978        variants: GIT_GRAPH_VARIANTS,
1979    },
1980    DiagramFamilyDefinition {
1981        logical_kind: "state",
1982        selector: Some("diagram-state"),
1983        editor_semantics: STATE_EDITOR_SEMANTICS,
1984        config: Some(FamilyConfigDefinition {
1985            namespace: "state",
1986            frontmatter_order: 22,
1987        }),
1988        variants: STATE_VARIANTS,
1989    },
1990    DiagramFamilyDefinition {
1991        logical_kind: "journey",
1992        selector: Some("diagram-journey"),
1993        editor_semantics: GENERIC_EDITOR_SEMANTICS,
1994        config: Some(FamilyConfigDefinition {
1995            namespace: "journey",
1996            frontmatter_order: 11,
1997        }),
1998        variants: JOURNEY_VARIANTS,
1999    },
2000    DiagramFamilyDefinition {
2001        logical_kind: "quadrantChart",
2002        selector: Some("diagram-quadrant-chart"),
2003        editor_semantics: GENERIC_EDITOR_SEMANTICS,
2004        config: Some(FamilyConfigDefinition {
2005            namespace: "quadrantChart",
2006            frontmatter_order: 16,
2007        }),
2008        variants: QUADRANT_VARIANTS,
2009    },
2010    DiagramFamilyDefinition {
2011        logical_kind: "sankey",
2012        selector: Some("diagram-sankey"),
2013        editor_semantics: GENERIC_EDITOR_SEMANTICS,
2014        config: Some(FamilyConfigDefinition {
2015            namespace: "sankey",
2016            frontmatter_order: 20,
2017        }),
2018        variants: SANKEY_VARIANTS,
2019    },
2020    DiagramFamilyDefinition {
2021        logical_kind: "packet",
2022        selector: Some("diagram-packet"),
2023        editor_semantics: GENERIC_EDITOR_SEMANTICS,
2024        config: Some(FamilyConfigDefinition {
2025            namespace: "packet",
2026            frontmatter_order: 14,
2027        }),
2028        variants: PACKET_VARIANTS,
2029    },
2030    DiagramFamilyDefinition {
2031        logical_kind: "xychart",
2032        selector: Some("diagram-xychart"),
2033        editor_semantics: GENERIC_EDITOR_SEMANTICS,
2034        config: Some(FamilyConfigDefinition {
2035            namespace: "xyChart",
2036            frontmatter_order: 29,
2037        }),
2038        variants: XYCHART_VARIANTS,
2039    },
2040    DiagramFamilyDefinition {
2041        logical_kind: "block",
2042        selector: Some("diagram-block"),
2043        editor_semantics: BLOCK_EDITOR_SEMANTICS,
2044        config: Some(FamilyConfigDefinition {
2045            namespace: "block",
2046            frontmatter_order: 1,
2047        }),
2048        variants: BLOCK_VARIANTS,
2049    },
2050    DiagramFamilyDefinition {
2051        logical_kind: "eventmodeling",
2052        selector: Some("diagram-event-modeling"),
2053        editor_semantics: GENERIC_EDITOR_SEMANTICS,
2054        config: Some(FamilyConfigDefinition {
2055            namespace: "eventmodeling",
2056            frontmatter_order: 6,
2057        }),
2058        variants: EVENTMODELING_VARIANTS,
2059    },
2060    DiagramFamilyDefinition {
2061        logical_kind: "treeView",
2062        selector: Some("diagram-tree-view"),
2063        editor_semantics: GENERIC_EDITOR_SEMANTICS,
2064        config: Some(FamilyConfigDefinition {
2065            namespace: "treeView",
2066            frontmatter_order: 25,
2067        }),
2068        variants: TREE_VIEW_VARIANTS,
2069    },
2070    DiagramFamilyDefinition {
2071        logical_kind: "radar",
2072        selector: Some("diagram-radar"),
2073        editor_semantics: GENERIC_EDITOR_SEMANTICS,
2074        config: Some(FamilyConfigDefinition {
2075            namespace: "radar",
2076            frontmatter_order: 17,
2077        }),
2078        variants: RADAR_VARIANTS,
2079    },
2080    DiagramFamilyDefinition {
2081        logical_kind: "ishikawa",
2082        selector: Some("diagram-ishikawa"),
2083        editor_semantics: GENERIC_EDITOR_SEMANTICS,
2084        config: Some(FamilyConfigDefinition {
2085            namespace: "ishikawa",
2086            frontmatter_order: 10,
2087        }),
2088        variants: ISHIKAWA_VARIANTS,
2089    },
2090    DiagramFamilyDefinition {
2091        logical_kind: "treemap",
2092        selector: Some("diagram-treemap"),
2093        editor_semantics: GENERIC_EDITOR_SEMANTICS,
2094        config: Some(FamilyConfigDefinition {
2095            namespace: "treemap",
2096            frontmatter_order: 26,
2097        }),
2098        variants: TREEMAP_VARIANTS,
2099    },
2100    DiagramFamilyDefinition {
2101        logical_kind: "railroad",
2102        selector: Some("diagram-railroad"),
2103        editor_semantics: GENERIC_EDITOR_SEMANTICS,
2104        config: Some(FamilyConfigDefinition {
2105            namespace: "railroad",
2106            frontmatter_order: 18,
2107        }),
2108        variants: RAILROAD_VARIANTS,
2109    },
2110    DiagramFamilyDefinition {
2111        logical_kind: "venn",
2112        selector: Some("diagram-venn"),
2113        editor_semantics: GENERIC_EDITOR_SEMANTICS,
2114        config: Some(FamilyConfigDefinition {
2115            namespace: "venn",
2116            frontmatter_order: 27,
2117        }),
2118        variants: VENN_VARIANTS,
2119    },
2120    DiagramFamilyDefinition {
2121        logical_kind: "wardley",
2122        selector: Some("diagram-wardley"),
2123        editor_semantics: GENERIC_EDITOR_SEMANTICS,
2124        config: Some(FamilyConfigDefinition {
2125            namespace: "wardley-beta",
2126            frontmatter_order: 28,
2127        }),
2128        variants: WARDLEY_VARIANTS,
2129    },
2130    DiagramFamilyDefinition {
2131        logical_kind: "cynefin",
2132        selector: Some("diagram-cynefin"),
2133        editor_semantics: GENERIC_EDITOR_SEMANTICS,
2134        config: Some(FamilyConfigDefinition {
2135            namespace: "cynefin",
2136            frontmatter_order: 4,
2137        }),
2138        variants: CYNEFIN_VARIANTS,
2139    },
2140    DiagramFamilyDefinition {
2141        logical_kind: "usecase",
2142        selector: Some("diagram-usecase"),
2143        editor_semantics: GENERIC_EDITOR_SEMANTICS,
2144        config: Some(FamilyConfigDefinition {
2145            namespace: "usecase",
2146            frontmatter_order: 35,
2147        }),
2148        variants: USECASE_VARIANTS,
2149    },
2150    DiagramFamilyDefinition {
2151        logical_kind: "agentflow",
2152        selector: Some("diagram-agentflow"),
2153        editor_semantics: GENERIC_EDITOR_SEMANTICS,
2154        config: Some(FamilyConfigDefinition {
2155            namespace: "agentflow",
2156            frontmatter_order: 36,
2157        }),
2158        variants: AGENTFLOW_VARIANTS,
2159    },
2160];
2161
2162#[cfg(test)]
2163mod catalog_tests {
2164    use super::*;
2165    use std::collections::BTreeSet;
2166
2167    #[test]
2168    fn selectors_cover_logical_families_once_without_infrastructure_or_aliases() {
2169        let selectors = diagram_family_selectors();
2170        assert_eq!(selectors.len(), 34);
2171        let mut features = BTreeSet::new();
2172        let mut families = BTreeSet::new();
2173        for selector in selectors {
2174            assert!(features.insert(selector.feature));
2175            assert!(families.insert(selector.logical_family_kind));
2176            assert!(selector.feature.starts_with("diagram-"));
2177            assert!(
2178                selector
2179                    .feature
2180                    .bytes()
2181                    .all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || byte == b'-')
2182            );
2183        }
2184        for family in FAMILY_CATALOG {
2185            assert_eq!(family.selector.is_some(), family.logical_kind != "error");
2186            assert_eq!(
2187                families.contains(family.logical_kind),
2188                family.logical_kind != "error"
2189            );
2190        }
2191        for (id, expected) in [
2192            ("flowchart", "diagram-flowchart"),
2193            ("flowchart-v2", "diagram-flowchart"),
2194            ("flowchart-elk", "diagram-flowchart"),
2195            ("swimlane", "diagram-swimlane"),
2196            ("gitGraph", "diagram-git-graph"),
2197            ("quadrantChart", "diagram-quadrant-chart"),
2198            ("treeView", "diagram-tree-view"),
2199            ("eventmodeling", "diagram-event-modeling"),
2200        ] {
2201            let (family, _) = find_variant(id).expect("known family alias");
2202            assert_eq!(family.selector, Some(expected), "{id}");
2203        }
2204        assert!(!features.contains("diagram-error"));
2205        assert!(!features.contains("diagram-flowchart-elk"));
2206    }
2207
2208    #[test]
2209    fn public_metadata_ids_are_catalog_owned_instead_of_derived_from_family_names() {
2210        assert_eq!(diagram_type_metadata_id("flowchart-v2"), Some("flowchart"));
2211        assert_eq!(diagram_type_metadata_id("gitGraph"), Some("gitgraph"));
2212        assert_eq!(
2213            diagram_type_metadata_id("quadrantChart"),
2214            Some("quadrantchart")
2215        );
2216        assert_eq!(diagram_type_metadata_id("treeView"), Some("treeView"));
2217    }
2218
2219    #[test]
2220    fn editor_semantics_follow_family_identity_across_variant_ids() {
2221        for variant in ["flowchart-v2", "flowchart-elk", "flowchart"] {
2222            assert_eq!(
2223                diagram_type_family_id(variant),
2224                Some(DiagramFamilyId::FLOWCHART),
2225                "{variant} must retain typed Flowchart family identity"
2226            );
2227        }
2228
2229        let flowchart = diagram_type_editor_semantics("flowchart-v2")
2230            .expect("flowchart-v2 has editor family semantics");
2231        for variant in ["flowchart-elk", "flowchart"] {
2232            assert_eq!(
2233                diagram_type_editor_semantics(variant),
2234                Some(flowchart),
2235                "{variant} must inherit flowchart family semantics"
2236            );
2237        }
2238
2239        let class = diagram_type_editor_semantics("classDiagram")
2240            .expect("classDiagram has editor family semantics");
2241        assert_eq!(diagram_type_editor_semantics("class"), Some(class));
2242        assert_ne!(flowchart, class);
2243    }
2244
2245    #[test]
2246    fn absent_implementations_preserve_the_complete_identity_projection() {
2247        let unavailable = variants()
2248            .map(|(family, variant)| {
2249                (
2250                    family,
2251                    FamilyVariantDefinition {
2252                        identity: variant.identity,
2253                        implementation: FamilyImplementation::default(),
2254                    },
2255                )
2256            })
2257            .collect::<Vec<_>>();
2258        let projected = FamilyCatalogProjection::build(
2259            unavailable
2260                .iter()
2261                .map(|(family, variant)| (*family, variant)),
2262        );
2263        let complete = family_catalog_projection();
2264
2265        assert!(projected.semantic_parser_facts.is_empty());
2266        assert!(projected.combined_parser_facts.is_empty());
2267        assert!(projected.render_parser_facts.is_empty());
2268        assert!(projected.supported_diagram_metadata_ids.is_empty());
2269        assert!(projected.built_in_typed_render_families.is_empty());
2270        assert_eq!(
2271            projected.diagram_header_facts,
2272            complete.diagram_header_facts
2273        );
2274        assert_eq!(
2275            projected
2276                .detector_facts
2277                .iter()
2278                .map(|fact| fact.id)
2279                .collect::<Vec<_>>(),
2280            complete
2281                .detector_facts
2282                .iter()
2283                .map(|fact| fact.id)
2284                .collect::<Vec<_>>()
2285        );
2286        let expected = complete
2287            .diagram_family_capabilities
2288            .iter()
2289            .map(|fact| DiagramFamilyCapability {
2290                has_semantic_parser: false,
2291                has_editor_parser: false,
2292                has_combined_parser: false,
2293                has_render_parser: false,
2294                ..*fact
2295            })
2296            .collect::<Vec<_>>();
2297        assert_eq!(projected.diagram_family_capabilities, expected);
2298    }
2299
2300    #[test]
2301    #[cfg(feature = "diagram-flowchart")]
2302    fn parser_and_typed_model_enumerations_follow_their_own_callbacks() {
2303        let (family, variant) = find_variant("flowchart-v2").unwrap();
2304        let semantic_only = FamilyVariantDefinition {
2305            identity: variant.identity,
2306            implementation: FamilyImplementation {
2307                semantic: variant.implementation.semantic,
2308                ..FamilyImplementation::default()
2309            },
2310        };
2311        let projected = FamilyCatalogProjection::build([(family, &semantic_only)]);
2312        assert_eq!(projected.supported_diagram_metadata_ids, ["flowchart"]);
2313        assert!(projected.built_in_typed_render_families.is_empty());
2314        assert!(projected.diagram_family_capabilities[0].has_semantic_parser);
2315        assert!(!projected.diagram_family_capabilities[0].has_editor_parser);
2316        assert!(!projected.diagram_family_capabilities[0].has_render_parser);
2317
2318        let typed_only = FamilyVariantDefinition {
2319            identity: variant.identity,
2320            implementation: FamilyImplementation {
2321                typed_render: variant.implementation.typed_render,
2322                ..FamilyImplementation::default()
2323            },
2324        };
2325        let projected = FamilyCatalogProjection::build([(family, &typed_only)]);
2326        assert!(projected.supported_diagram_metadata_ids.is_empty());
2327        assert_eq!(
2328            projected.built_in_typed_render_families,
2329            [BuiltInTypedRenderFamily {
2330                diagram_type: "flowchart",
2331                render_model_kind: "flowchart",
2332            }]
2333        );
2334        assert!(!projected.diagram_family_capabilities[0].has_semantic_parser);
2335        assert!(projected.diagram_family_capabilities[0].has_render_parser);
2336    }
2337
2338    #[test]
2339    #[cfg(feature = "diagram-swimlane")]
2340    fn shared_typed_models_do_not_admit_an_unavailable_logical_family() {
2341        let projected_variants = variants()
2342            .map(|(family, variant)| {
2343                let mut variant = *variant;
2344                if family.logical_kind != "swimlane" {
2345                    variant.implementation = FamilyImplementation::default();
2346                }
2347                (family, variant)
2348            })
2349            .collect::<Vec<_>>();
2350        let projected = FamilyCatalogProjection::build(
2351            projected_variants
2352                .iter()
2353                .map(|(family, variant)| (*family, variant)),
2354        );
2355        assert_eq!(projected.supported_diagram_metadata_ids, ["swimlane"]);
2356        assert_eq!(
2357            projected.built_in_typed_render_families,
2358            [BuiltInTypedRenderFamily {
2359                diagram_type: "swimlane",
2360                render_model_kind: "flowchart",
2361            }]
2362        );
2363        assert!(
2364            projected.diagram_family_capabilities.iter().all(|fact| {
2365                fact.has_semantic_parser == (fact.logical_family_kind == "swimlane")
2366            })
2367        );
2368    }
2369
2370    #[test]
2371    fn catalog_ids_orders_and_family_policy_are_internally_consistent() {
2372        let mut ids = BTreeSet::new();
2373        let mut catalog_orders = BTreeSet::new();
2374        let mut detector_orders = BTreeSet::new();
2375        let mut semantic_orders = BTreeSet::new();
2376        let mut combined_orders = BTreeSet::new();
2377        let mut render_orders = BTreeSet::new();
2378        let mut metadata_orders = BTreeSet::new();
2379        let mut header_orders = BTreeSet::new();
2380
2381        for family in FAMILY_CATALOG {
2382            for variant in family.variants {
2383                assert_ne!(
2384                    variant.identity.id, "---",
2385                    "frontmatter guard is not a family"
2386                );
2387                assert!(
2388                    ids.insert(variant.identity.id),
2389                    "duplicate catalog id {}",
2390                    variant.identity.id
2391                );
2392                assert!(
2393                    catalog_orders.insert(variant.identity.catalog_order),
2394                    "duplicate catalog order {}",
2395                    variant.identity.catalog_order
2396                );
2397                assert!(
2398                    variant.implementation.typed_render.is_none()
2399                        || variant.identity.render_model_kind.is_some(),
2400                    "{} typed parser requires a known render kind",
2401                    variant.identity.id
2402                );
2403                assert!(
2404                    variant.identity.metadata.is_none()
2405                        || variant.identity.render_model_kind.is_some(),
2406                    "{} metadata requires a known render kind",
2407                    variant.identity.id
2408                );
2409                assert!(
2410                    variant.implementation.combined.is_none()
2411                        || variant.implementation.semantic.is_some(),
2412                    "{} combined parsing requires a semantic adapter",
2413                    variant.identity.id
2414                );
2415
2416                if let Some(fact) = variant.identity.detector {
2417                    assert!(detector_orders.insert(fact.order));
2418                }
2419                if let Some(fact) = variant.implementation.semantic {
2420                    assert!(semantic_orders.insert(fact.order));
2421                }
2422                if let Some(fact) = variant.implementation.combined {
2423                    assert!(combined_orders.insert(fact.order));
2424                }
2425                if let Some(fact) = variant.implementation.typed_render {
2426                    assert!(render_orders.insert(fact.order));
2427                }
2428                if let Some(order) = variant
2429                    .identity
2430                    .metadata
2431                    .and_then(|metadata| metadata.order)
2432                {
2433                    assert!(metadata_orders.insert(order));
2434                }
2435                for fact in variant.identity.headers {
2436                    assert!(header_orders.insert(fact.order));
2437                }
2438            }
2439        }
2440
2441        let config_orders = FAMILY_CATALOG
2442            .iter()
2443            .filter_map(|family| family.config.map(|config| config.frontmatter_order))
2444            .collect::<BTreeSet<_>>();
2445        let config_count = FAMILY_CATALOG
2446            .iter()
2447            .filter(|family| family.config.is_some())
2448            .count();
2449        assert_eq!(config_orders.len(), config_count);
2450        assert_eq!(ids.len(), diagram_family_capabilities().len());
2451    }
2452}