merman_render/svg/pipeline/
context.rs1use super::builtin::util::extract_root_svg_id;
2use super::preset::SvgPipelinePreset;
3
4#[derive(Debug, Clone, Default, PartialEq, Eq)]
5pub struct SvgPostprocessMetadata {
6 diagram_type: Option<String>,
7 diagram_title: Option<String>,
8 svg_id: Option<String>,
9}
10
11impl SvgPostprocessMetadata {
12 pub fn new() -> Self {
13 Self::default()
14 }
15
16 pub fn from_svg(svg: &str) -> Self {
17 Self {
18 svg_id: extract_root_svg_id(svg),
19 ..Self::default()
20 }
21 }
22
23 pub fn with_diagram_type(mut self, diagram_type: impl Into<String>) -> Self {
24 self.diagram_type = Some(diagram_type.into());
25 self
26 }
27
28 pub fn with_optional_diagram_type(mut self, diagram_type: Option<impl Into<String>>) -> Self {
29 self.diagram_type = diagram_type.map(Into::into);
30 self
31 }
32
33 pub fn with_diagram_title(mut self, diagram_title: impl Into<String>) -> Self {
34 self.diagram_title = Some(diagram_title.into());
35 self
36 }
37
38 pub fn with_optional_diagram_title(mut self, diagram_title: Option<impl Into<String>>) -> Self {
39 self.diagram_title = diagram_title.map(Into::into);
40 self
41 }
42
43 pub fn with_svg_id(mut self, svg_id: impl Into<String>) -> Self {
44 self.svg_id = Some(svg_id.into());
45 self
46 }
47
48 pub fn with_optional_svg_id(mut self, svg_id: Option<impl Into<String>>) -> Self {
49 if let Some(svg_id) = svg_id {
50 self.svg_id = Some(svg_id.into());
51 }
52 self
53 }
54
55 pub fn diagram_type(&self) -> Option<&str> {
56 self.diagram_type.as_deref()
57 }
58
59 pub fn diagram_title(&self) -> Option<&str> {
60 self.diagram_title.as_deref()
61 }
62
63 pub fn svg_id(&self) -> Option<&str> {
64 self.svg_id.as_deref()
65 }
66}
67
68#[derive(Debug, Clone, Copy)]
69pub struct SvgPostprocessContext<'a> {
70 preset: SvgPipelinePreset,
71 pass_index: usize,
72 pass_name: &'a str,
73 metadata: &'a SvgPostprocessMetadata,
74}
75
76impl<'a> SvgPostprocessContext<'a> {
77 pub(crate) fn new(
78 preset: SvgPipelinePreset,
79 pass_index: usize,
80 pass_name: &'a str,
81 metadata: &'a SvgPostprocessMetadata,
82 ) -> Self {
83 Self {
84 preset,
85 pass_index,
86 pass_name,
87 metadata,
88 }
89 }
90
91 pub fn preset(&self) -> SvgPipelinePreset {
92 self.preset
93 }
94
95 pub fn pass_index(&self) -> usize {
96 self.pass_index
97 }
98
99 pub fn pass_name(&self) -> &'a str {
100 self.pass_name
101 }
102
103 pub fn diagram_type(&self) -> Option<&'a str> {
104 self.metadata.diagram_type()
105 }
106
107 pub fn diagram_title(&self) -> Option<&'a str> {
108 self.metadata.diagram_title()
109 }
110
111 pub fn svg_id(&self) -> Option<&'a str> {
112 self.metadata.svg_id()
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn metadata_extracts_root_svg_id() {
122 let metadata = SvgPostprocessMetadata::from_svg(
123 r#"<svg xmlns="http://www.w3.org/2000/svg" id="diagram-1"><g/></svg>"#,
124 );
125
126 assert_eq!(metadata.svg_id(), Some("diagram-1"));
127 }
128
129 #[test]
130 fn metadata_ignores_non_root_ids() {
131 let metadata = SvgPostprocessMetadata::from_svg(r#"<g id="nested"></g>"#);
132
133 assert_eq!(metadata.svg_id(), None);
134 }
135}