1mod block_comment_close;
2mod block_comment_open;
3mod empty;
4mod end;
5mod footer;
6mod header;
7mod include;
8mod shared;
9mod start;
10mod title;
11
12pub use block_comment_close::BlockCommentCloseLine;
13pub use block_comment_open::BlockCommentOpenLine;
14pub use empty::EmptyLine;
15pub use end::EndLine;
16pub use footer::FooterLine;
17pub use header::HeaderLine;
18pub use include::IncludeLine;
19pub use start::StartLine;
20pub use title::TitleLine;
21
22use crate::{ParseContainer, ParseResult, wr};
23use nom::branch::alt;
24use nom::bytes::complete::take_till;
25use nom::character::complete::line_ending;
26use nom::combinator::eof;
27use nom::{IResult, Parser};
28use shared::LineWithComment;
29
30#[derive(Clone, Debug)]
91pub struct PlantUmlLine {
92 content: PlantUmlLineKind,
93 raw: ParseContainer,
94}
95
96#[derive(Clone, Debug)]
98pub enum PlantUmlLineKind {
99 Start(StartLine),
101
102 End(EndLine),
104
105 BlockCommentOpen(BlockCommentOpenLine),
107
108 BlockCommentClose(BlockCommentCloseLine),
110
111 Include(IncludeLine),
113
114 Title(TitleLine),
116
117 Header(HeaderLine),
119
120 Footer(FooterLine),
122
123 Empty,
125
126 InComment(Box<PlantUmlLineKind>),
128
129 Others,
131}
132
133impl PlantUmlLine {
134 pub fn parse(input: ParseContainer) -> ParseResult<Self> {
136 let (rest, (raw, content)) = PlantUmlLineKind::parse(input.clone())?;
137 let ret = (raw.clone(), Self { content, raw });
138 Ok((rest, ret))
139 }
140
141 pub fn raw_str(&self) -> &str {
143 self.raw.as_str()
144 }
145
146 pub fn kind(&self) -> &PlantUmlLineKind {
148 &self.content
149 }
150
151 pub fn start(&self) -> Option<&StartLine> {
153 if let PlantUmlLineKind::Start(x) = &self.content {
154 Some(x)
155 } else {
156 None
157 }
158 }
159
160 pub fn end(&self) -> Option<&EndLine> {
162 if let PlantUmlLineKind::End(x) = &self.content {
163 Some(x)
164 } else {
165 None
166 }
167 }
168
169 pub fn block_comment_open(&self) -> Option<&BlockCommentOpenLine> {
171 if let PlantUmlLineKind::BlockCommentOpen(x) = &self.content {
172 Some(x)
173 } else {
174 None
175 }
176 }
177
178 pub fn block_comment_close(&self) -> Option<&BlockCommentCloseLine> {
180 if let PlantUmlLineKind::BlockCommentClose(x) = &self.content {
181 Some(x)
182 } else {
183 None
184 }
185 }
186
187 pub fn include(&self) -> Option<&IncludeLine> {
189 if let PlantUmlLineKind::Include(x) = &self.content {
190 Some(x)
191 } else {
192 None
193 }
194 }
195
196 pub fn title(&self) -> Option<&TitleLine> {
198 if let PlantUmlLineKind::Title(x) = &self.content {
199 Some(x)
200 } else {
201 None
202 }
203 }
204
205 pub fn header(&self) -> Option<&HeaderLine> {
207 if let PlantUmlLineKind::Header(x) = &self.content {
208 Some(x)
209 } else {
210 None
211 }
212 }
213
214 pub fn footer(&self) -> Option<&FooterLine> {
216 if let PlantUmlLineKind::Footer(x) = &self.content {
217 Some(x)
218 } else {
219 None
220 }
221 }
222
223 pub fn diagram_kind(&self) -> Option<&str> {
225 self.start().map(|x| x.diagram_kind())
226 }
227
228 pub fn empty(&self) -> Option<()> {
230 if let PlantUmlLineKind::Empty = &self.content {
231 Some(())
232 } else {
233 None
234 }
235 }
236
237 pub fn into_in_comment(self) -> Self {
239 Self {
240 content: PlantUmlLineKind::InComment(Box::new(self.content)),
241 raw: self.raw,
242 }
243 }
244}
245
246impl PlantUmlLineKind {
247 fn parse(input: ParseContainer) -> ParseResult<Self> {
248 if let Ok((rest, (raw, content))) = StartLine::parse(input.clone()) {
249 let content = PlantUmlLineKind::Start(content);
250 return Ok((rest, (raw, content)));
251 }
252
253 if let Ok((rest, (raw, content))) = EndLine::parse(input.clone()) {
254 let content = PlantUmlLineKind::End(content);
255 return Ok((rest, (raw, content)));
256 }
257
258 if let Ok((rest, (raw, content))) = BlockCommentOpenLine::parse(input.clone()) {
259 let content = PlantUmlLineKind::BlockCommentOpen(content);
260 return Ok((rest, (raw, content)));
261 }
262
263 if let Ok((rest, (raw, content))) = BlockCommentCloseLine::parse(input.clone()) {
264 let content = PlantUmlLineKind::BlockCommentClose(content);
265 return Ok((rest, (raw, content)));
266 }
267
268 if let Ok((rest, (raw, content))) = IncludeLine::parse(input.clone()) {
269 let content = PlantUmlLineKind::Include(content);
270 return Ok((rest, (raw, content)));
271 }
272
273 if let Ok((rest, (raw, content))) = TitleLine::parse(input.clone()) {
274 let content = PlantUmlLineKind::Title(content);
275 return Ok((rest, (raw, content)));
276 }
277
278 if let Ok((rest, (raw, content))) = HeaderLine::parse(input.clone()) {
279 let content = PlantUmlLineKind::Header(content);
280 return Ok((rest, (raw, content)));
281 }
282
283 if let Ok((rest, (raw, content))) = FooterLine::parse(input.clone()) {
284 let content = PlantUmlLineKind::Footer(content);
285 return Ok((rest, (raw, content)));
286 }
287
288 if let Ok((rest, (raw, _))) = EmptyLine::parse(input.clone()) {
289 let content = PlantUmlLineKind::Empty;
290 return Ok((rest, (raw, content)));
291 }
292
293 let (rest, raws) = Self::parse_others(input.clone())?;
294 let content = PlantUmlLineKind::Others;
295 Ok((rest, (raws.into(), content)))
296 }
297
298 fn parse_others(input: ParseContainer) -> IResult<ParseContainer, Vec<ParseContainer>> {
299 let (rest, (not_end, end)) = (
300 wr!(take_till(|c| c == '\n' || c == '\r')),
301 alt((wr!(eof), wr!(line_ending))),
302 )
303 .parse(input)?;
304 Ok((rest, vec![not_end, end]))
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use super::*;
311
312 #[test]
313 fn test_parse_start_line() -> anyhow::Result<()> {
314 let testdata = "@startuml\n";
315 let (rest, (_, parsed)) = PlantUmlLine::parse(testdata.into())?;
316 assert_eq!(rest, "");
317 assert_eq!(testdata, parsed.raw);
318 assert!(parsed.start().is_some());
319
320 let testdata = " \t@startuml\n";
321 let (rest, (_, parsed)) = PlantUmlLine::parse(testdata.into())?;
322 assert_eq!(rest, "");
323 assert_eq!(testdata, parsed.raw);
324 assert!(parsed.start().is_some());
325
326 let testdata = "@startuml \t\n";
327 let (rest, (_, parsed)) = PlantUmlLine::parse(testdata.into())?;
328 assert_eq!(rest, "");
329 assert_eq!(testdata, parsed.raw);
330 assert!(parsed.start().is_some());
331
332 let testdata = " \t@startuml \t\n";
333 let (rest, (_, parsed)) = PlantUmlLine::parse(testdata.into())?;
334 assert_eq!(rest, "");
335 assert_eq!(testdata, parsed.raw);
336 assert!(parsed.start().is_some());
337
338 let testdata = " \t@startuml id_foo\t\n";
339 let (rest, (_, parsed)) = PlantUmlLine::parse(testdata.into())?;
340 assert_eq!(rest, "");
341 assert_eq!(testdata, parsed.raw);
342 assert!(parsed.start().is_some());
343
344 let testdata = " \t@startuml(id=id_bar)\t\n";
345 let (rest, (_, parsed)) = PlantUmlLine::parse(testdata.into())?;
346 assert_eq!(rest, "");
347 assert_eq!(testdata, parsed.raw);
348 assert!(parsed.start().is_some());
349
350 Ok(())
351 }
352
353 #[test]
354 fn test_parse_end_line() -> anyhow::Result<()> {
355 let testdata = "@enduml\n";
356 let (rest, (_, parsed)) = PlantUmlLine::parse(testdata.into())?;
357 assert_eq!(rest, "");
358 assert_eq!(testdata, parsed.raw);
359 assert!(parsed.end().is_some());
360
361 let testdata = " \t@enduml\n";
362 let (rest, (_, parsed)) = PlantUmlLine::parse(testdata.into())?;
363 assert_eq!(rest, "");
364 assert_eq!(testdata, parsed.raw);
365 assert!(parsed.end().is_some());
366
367 let testdata = "@enduml \t\n";
368 let (rest, (_, parsed)) = PlantUmlLine::parse(testdata.into())?;
369 assert_eq!(rest, "");
370 assert_eq!(testdata, parsed.raw);
371 assert!(parsed.end().is_some());
372
373 let testdata = " \t@enduml \t\n";
374 let (rest, (_, parsed)) = PlantUmlLine::parse(testdata.into())?;
375 assert_eq!(rest, "");
376 assert_eq!(testdata, parsed.raw);
377 assert!(parsed.end().is_some());
378
379 Ok(())
380 }
381
382 #[test]
383 fn test_parse_include_line() -> anyhow::Result<()> {
384 let testdata = "!include foo.puml\n";
385 let (rest, (_, parsed)) = PlantUmlLine::parse(testdata.into())?;
386 assert_eq!(rest, "");
387 assert_eq!(testdata, parsed.raw);
388 assert!(parsed.include().is_some());
389
390 let testdata = " !include foo.puml!1\n";
391 let (rest, (_, parsed)) = PlantUmlLine::parse(testdata.into())?;
392 assert_eq!(rest, "");
393 assert_eq!(testdata, parsed.raw);
394 assert!(parsed.include().is_some());
395
396 let testdata = "\t!include foo.puml!bar \n";
397 let (rest, (_, parsed)) = PlantUmlLine::parse(testdata.into())?;
398 assert_eq!(rest, "");
399 assert_eq!(testdata, parsed.raw);
400 assert!(parsed.include().is_some());
401
402 Ok(())
403 }
404}