mod empty;
mod end;
mod include;
mod start;
pub use empty::EmptyLine;
pub use end::EndLine;
pub use include::IncludeLine;
pub use start::StartLine;
use crate::{wr, ParseContainer};
use nom::branch::alt;
use nom::bytes::complete::take_till;
use nom::character::complete::line_ending;
use nom::combinator::eof;
use nom::sequence::tuple;
use nom::IResult;
#[derive(Clone, Debug)]
pub struct PlantUmlLine {
content: PlantUmlLineKind,
raw: ParseContainer,
}
#[derive(Clone, Debug)]
pub enum PlantUmlLineKind {
Start(StartLine),
End(EndLine),
Include(IncludeLine),
Empty,
Others,
}
impl PlantUmlLine {
pub fn parse(input: ParseContainer) -> IResult<ParseContainer, Self> {
let (raw, content) = PlantUmlLineKind::parse(input.clone())?;
let (rest, raw) = input.split_at(raw.len());
let ret = (rest, Self { content, raw });
Ok(ret)
}
pub fn raw_str(&self) -> &str {
self.raw.as_str()
}
pub fn start(&self) -> Option<&StartLine> {
if let PlantUmlLineKind::Start(x) = &self.content {
Some(x)
} else {
None
}
}
pub fn end(&self) -> Option<&EndLine> {
if let PlantUmlLineKind::End(x) = &self.content {
Some(x)
} else {
None
}
}
pub fn include(&self) -> Option<&IncludeLine> {
if let PlantUmlLineKind::Include(x) = &self.content {
Some(x)
} else {
None
}
}
pub fn diagram_kind(&self) -> Option<&str> {
self.start().map(|x| x.diagram_kind())
}
pub fn empty(&self) -> Option<()> {
if let PlantUmlLineKind::Empty = &self.content {
Some(())
} else {
None
}
}
}
impl PlantUmlLineKind {
fn parse(input: ParseContainer) -> IResult<ParseContainer, Self> {
if let Ok((_, (raws, content))) = StartLine::parse(input.clone()) {
let content = PlantUmlLineKind::Start(content);
return Ok((raws.into(), content));
}
if let Ok((_, (raws, content))) = EndLine::parse(input.clone()) {
let content = PlantUmlLineKind::End(content);
return Ok((raws.into(), content));
}
if let Ok((_, (raws, content))) = IncludeLine::parse(input.clone()) {
let content = PlantUmlLineKind::Include(content);
return Ok((raws.into(), content));
}
if let Ok((_, (raws, _))) = EmptyLine::parse(input.clone()) {
let content = PlantUmlLineKind::Empty;
return Ok((raws.into(), content));
}
let (_, raws) = Self::parse_others(input.clone())?;
let content = PlantUmlLineKind::Others;
Ok((raws.into(), content))
}
fn parse_others(input: ParseContainer) -> IResult<ParseContainer, Vec<ParseContainer>> {
let (rest, (not_end, end)) = tuple((
wr!(take_till(|c| c == '\n' || c == '\r')),
alt((wr!(eof), wr!(line_ending))),
))(input)?;
Ok((rest, vec![not_end, end]))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_start_line() -> anyhow::Result<()> {
let testdata = "@startuml\n";
let (rest, parsed) = PlantUmlLine::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(testdata, parsed.raw);
assert!(parsed.start().is_some());
let testdata = " \t@startuml\n";
let (rest, parsed) = PlantUmlLine::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(testdata, parsed.raw);
assert!(parsed.start().is_some());
let testdata = "@startuml \t\n";
let (rest, parsed) = PlantUmlLine::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(testdata, parsed.raw);
assert!(parsed.start().is_some());
let testdata = " \t@startuml \t\n";
let (rest, parsed) = PlantUmlLine::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(testdata, parsed.raw);
assert!(parsed.start().is_some());
let testdata = " \t@startuml id_foo\t\n";
let (rest, parsed) = PlantUmlLine::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(testdata, parsed.raw);
assert!(parsed.start().is_some());
let testdata = " \t@startuml(id=id_bar)\t\n";
let (rest, parsed) = PlantUmlLine::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(testdata, parsed.raw);
assert!(parsed.start().is_some());
Ok(())
}
#[test]
fn test_parse_end_line() -> anyhow::Result<()> {
let testdata = "@enduml\n";
let (rest, parsed) = PlantUmlLine::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(testdata, parsed.raw);
assert!(parsed.end().is_some());
let testdata = " \t@enduml\n";
let (rest, parsed) = PlantUmlLine::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(testdata, parsed.raw);
assert!(parsed.end().is_some());
let testdata = "@enduml \t\n";
let (rest, parsed) = PlantUmlLine::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(testdata, parsed.raw);
assert!(parsed.end().is_some());
let testdata = " \t@enduml \t\n";
let (rest, parsed) = PlantUmlLine::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(testdata, parsed.raw);
assert!(parsed.end().is_some());
Ok(())
}
#[test]
fn test_parse_include_line() -> anyhow::Result<()> {
let testdata = "!include foo.puml\n";
let (rest, parsed) = PlantUmlLine::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(testdata, parsed.raw);
assert!(parsed.include().is_some());
let testdata = " !include foo.puml!1\n";
let (rest, parsed) = PlantUmlLine::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(testdata, parsed.raw);
assert!(parsed.include().is_some());
let testdata = "\t!include foo.puml!bar \n";
let (rest, parsed) = PlantUmlLine::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(testdata, parsed.raw);
assert!(parsed.include().is_some());
Ok(())
}
}