1use std::fmt;
4use std::ops::Range;
5
6use strum::{EnumString, IntoStaticStr};
7use svugy_model::model::ByteRange;
8
9#[derive(Debug, Clone, PartialEq, Default)]
11pub struct D2Document {
12 pub statements: Vec<Statement>,
13}
14
15#[derive(Debug, Clone, PartialEq)]
17pub enum Statement {
18 Shape(ShapeDeclaration),
19 Edge(EdgeDeclaration),
20 Field(FieldAssignment),
21}
22
23#[derive(Debug, Clone, PartialEq)]
25pub struct ShapeDeclaration {
26 pub path: KeyPath,
27 pub label: Option<Scalar>,
28 pub body: Vec<Statement>,
29 pub span: Span,
30}
31
32#[derive(Debug, Clone, PartialEq)]
34pub struct EdgeDeclaration {
35 pub source: KeyPath,
36 pub links: Vec<EdgeLink>,
37 pub label: Option<Scalar>,
38 pub fields: Vec<Field>,
39 pub span: Span,
40}
41
42#[derive(Debug, Clone, PartialEq)]
44pub struct EdgeLink {
45 pub operator: EdgeOperator,
46 pub operator_span: Span,
47 pub target: KeyPath,
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum EdgeOperator {
53 Forward,
54 Backward,
55 Bidirectional,
56 Undirected,
57}
58
59#[derive(Debug, Clone, PartialEq)]
61pub struct FieldAssignment {
62 pub target: KeyPath,
63 pub field: Field,
64 pub span: Span,
65}
66
67#[derive(Debug, Clone, PartialEq)]
69pub enum Field {
70 Shape(Scalar),
71 Label(Scalar),
72 Direction(Scalar),
73 Link(Scalar),
74 Tooltip(Scalar),
75 Style(Vec<StyleProperty>),
76 Svugy(Vec<SvugyProperty>),
77}
78
79#[derive(Debug, Clone, PartialEq)]
81pub struct StyleProperty {
82 pub name: KeySegment,
83 pub value: Scalar,
84 pub span: Span,
85}
86
87#[derive(Debug, Clone, PartialEq)]
89pub struct SvugyProperty {
90 pub attribute_path: Vec<String>,
91 pub value: SvugyValue,
92 pub span: Span,
93}
94
95#[derive(Debug, Clone, PartialEq)]
97pub enum SvugyValue {
98 Scalar(Scalar),
99 List { items: Vec<Scalar>, span: Span },
100}
101
102#[derive(Debug, Clone, PartialEq, Default)]
104pub struct KeyPath {
105 pub parent_references: usize,
106 pub segments: Vec<KeySegment>,
107 pub span: Span,
108}
109
110#[derive(Debug, Clone, PartialEq)]
112pub struct KeySegment {
113 pub name: String,
114 pub span: Span,
115}
116
117#[derive(Debug, Clone, PartialEq)]
119pub struct Scalar {
120 pub text: String,
121 pub span: Span,
122}
123
124#[derive(Clone, Copy, PartialEq, Eq, Default, Hash)]
126pub struct Span {
127 pub start: usize,
128 pub end: usize,
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumString, IntoStaticStr)]
133#[strum(serialize_all = "kebab-case", ascii_case_insensitive)]
134pub(crate) enum Keyword {
135 Shape,
136 Label,
137 Direction,
138 Link,
139 Style,
140 Vars,
141 Class,
142 Classes,
143 Layers,
144 Scenarios,
145 Steps,
146 Icon,
147 Constraint,
148 Tooltip,
149 Near,
150 Width,
151 Height,
152 Top,
153 Left,
154 GridRows,
155 GridColumns,
156 GridGap,
157 VerticalGap,
158 HorizontalGap,
159 SourceArrowhead,
160 TargetArrowhead,
161}
162
163impl Field {
164 pub(crate) fn keyword(&self) -> Keyword {
166 match self {
167 Self::Shape(_shape) => Keyword::Shape,
168 Self::Label(_label) => Keyword::Label,
169 Self::Direction(_direction) => Keyword::Direction,
170 Self::Link(_link) => Keyword::Link,
171 Self::Tooltip(_tooltip) => Keyword::Tooltip,
172 Self::Style(_properties) => Keyword::Style,
173 Self::Svugy(_properties) => Keyword::Vars,
174 }
175 }
176}
177
178impl SvugyProperty {
179 pub const VARIABLE: &str = "svugy";
181}
182
183impl EdgeOperator {
184 pub(crate) fn from_arrow(arrow: &str) -> Self {
186 let has_source_arrowhead = arrow.starts_with('<');
187 let has_target_arrowhead = arrow.ends_with('>');
188 match (has_source_arrowhead, has_target_arrowhead) {
189 (true, true) => Self::Bidirectional,
190 (true, false) => Self::Backward,
191 (false, true) => Self::Forward,
192 (false, false) => Self::Undirected,
193 }
194 }
195
196 pub fn arrow(self) -> &'static str {
198 match self {
199 Self::Forward => "->",
200 Self::Backward => "<-",
201 Self::Bidirectional => "<->",
202 Self::Undirected => "--",
203 }
204 }
205}
206
207impl Span {
208 pub fn new(start: usize, end: usize) -> Self {
210 Self { start, end }
211 }
212
213 pub fn range(self) -> Range<usize> {
215 self.start..self.end
216 }
217}
218
219impl fmt::Debug for Span {
220 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
221 write!(formatter, "{}..{}", self.start, self.end)
222 }
223}
224
225impl From<Range<usize>> for Span {
226 fn from(range: Range<usize>) -> Self {
227 Self::new(range.start, range.end)
228 }
229}
230
231impl From<Span> for Range<usize> {
232 fn from(span: Span) -> Self {
233 span.range()
234 }
235}
236
237impl From<Span> for ByteRange {
238 fn from(span: Span) -> Self {
239 ByteRange::new(span.start, span.end)
240 }
241}
242
243impl From<Span> for miette::SourceSpan {
244 fn from(span: Span) -> Self {
245 span.range().into()
246 }
247}
248
249impl chumsky::span::Span for Span {
250 type Context = ();
251 type Offset = usize;
252
253 fn new(_context: (), range: Range<usize>) -> Self {
254 range.into()
255 }
256
257 fn context(&self) {}
258
259 fn start(&self) -> usize {
260 self.start
261 }
262
263 fn end(&self) -> usize {
264 self.end
265 }
266}
267
268impl Statement {
269 pub fn span(&self) -> Span {
271 match self {
272 Self::Shape(shape) => shape.span,
273 Self::Edge(edge) => edge.span,
274 Self::Field(assignment) => assignment.span,
275 }
276 }
277}