use crate::DiagramIdToken;
use crate::{wr, ParseContainer};
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::{alpha1, space1};
use nom::sequence::tuple;
use nom::IResult;
#[derive(Clone, Debug)]
pub struct StartDiagramToken {
diagram_kind: ParseContainer,
id: Option<DiagramIdToken>,
}
impl StartDiagramToken {
pub fn parse(input: ParseContainer) -> IResult<ParseContainer, (Vec<ParseContainer>, Self)> {
let (rest, (start, diagram_kind, (id_raw, id))) =
tuple((wr!(tag("@start")), wr!(alpha1), parse_id_part))(input)?;
let ret0 = [start, diagram_kind.clone()]
.into_iter()
.chain(id_raw)
.collect();
let ret1 = Self { diagram_kind, id };
Ok((rest, (ret0, ret1)))
}
pub fn diagram_kind(&self) -> &str {
self.diagram_kind.as_str()
}
pub fn id(&self) -> Option<&str> {
self.id.as_ref().map(|x| x.id())
}
}
fn parse_id_part(
input: ParseContainer,
) -> IResult<ParseContainer, (Vec<ParseContainer>, Option<DiagramIdToken>)> {
alt((parse_spaced_id, parse_parened_id, parse_none))(input)
}
fn parse_spaced_id(
input: ParseContainer,
) -> IResult<ParseContainer, (Vec<ParseContainer>, Option<DiagramIdToken>)> {
let (input, (space, (id_raw, id))) = tuple((wr!(space1), DiagramIdToken::parse))(input)?;
let parsed = Vec::from([space, id_raw]);
Ok((input, (parsed, Some(id))))
}
fn parse_parened_id(
input: ParseContainer,
) -> IResult<ParseContainer, (Vec<ParseContainer>, Option<DiagramIdToken>)> {
let (input, (open, (id_raw, id), close)) =
tuple((wr!(tag("(id=")), DiagramIdToken::parse, wr!(tag(")"))))(input)?;
let parsed = Vec::from([open, id_raw, close]);
Ok((input, (parsed, Some(id))))
}
fn parse_none(
input: ParseContainer,
) -> IResult<ParseContainer, (Vec<ParseContainer>, Option<DiagramIdToken>)> {
Ok((input, (vec![], None)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse() -> anyhow::Result<()> {
let testdata = "@startuml";
let (rest, (raws, StartDiagramToken { diagram_kind, id })) =
StartDiagramToken::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(raws.join(""), testdata);
assert_eq!(diagram_kind, "uml");
assert!(id.is_none());
let testdata = "@startjson \t\n";
let (rest, (raws, StartDiagramToken { diagram_kind, id })) =
StartDiagramToken::parse(testdata.into())?;
assert_eq!(rest, " \t\n");
assert_eq!(raws.join(""), "@startjson");
assert_eq!(format!("{}{rest}", raws.join("")), testdata);
assert_eq!(diagram_kind, "json");
assert!(id.is_none());
let testdata = "@startuml \t id aaa\n";
let (rest, (raws, StartDiagramToken { diagram_kind, id })) =
StartDiagramToken::parse(testdata.into())?;
assert_eq!(rest, " aaa\n");
assert_eq!(raws.join(""), "@startuml \t id");
assert_eq!(format!("{}{rest}", raws.join("")), testdata);
assert_eq!(diagram_kind, "uml");
assert_eq!(id.as_ref().map(|x| x.id()), Some("id"));
let testdata = "@startuml(id=foo_BAR) bbb\n";
let (rest, (raws, StartDiagramToken { diagram_kind, id })) =
StartDiagramToken::parse(testdata.into())?;
assert_eq!(rest, " bbb\n");
assert_eq!(raws.join(""), "@startuml(id=foo_BAR)");
assert_eq!(format!("{}{rest}", raws.join("")), testdata);
assert_eq!(diagram_kind, "uml");
assert_eq!(id.as_ref().map(|x| x.id()), Some("foo_BAR"));
Ok(())
}
#[test]
fn test_parse_id_part() -> anyhow::Result<()> {
let testdata = "";
let (rest, (parsed, id)) = parse_id_part(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(parsed.join(""), testdata);
assert!(id.is_none());
let testdata = " aaa ";
let (rest, (parsed, id)) = parse_id_part(testdata.into())?;
assert_eq!(rest, " ");
assert_eq!(parsed.join(""), " aaa");
assert_eq!(format!("{}{rest}", parsed.join("")), testdata);
assert_eq!(id.as_ref().map(|x| x.id()), Some("aaa"));
let testdata = "(id=FOO_bar) \txxx";
let (rest, (parsed, id)) = parse_id_part(testdata.into())?;
assert_eq!(rest, " \txxx");
assert_eq!(parsed.join(""), "(id=FOO_bar)");
assert_eq!(format!("{}{rest}", parsed.join("")), testdata);
assert_eq!(id.as_ref().map(|x| x.id()), Some("FOO_bar"));
Ok(())
}
#[test]
fn test_parse_spaced_id() -> anyhow::Result<()> {
let testdata = " aaa ";
let (rest, (parsed, id)) = parse_spaced_id(testdata.into())?;
assert_eq!(rest, " ");
assert_eq!(parsed.join(""), " aaa");
assert_eq!(format!("{}{rest}", parsed.join("")), testdata);
assert_eq!(id.as_ref().map(|x| x.id()), Some("aaa"));
Ok(())
}
#[test]
fn test_parse_parened_id() -> anyhow::Result<()> {
let testdata = "(id=FOO_bar) \txxx";
let (rest, (parsed, id)) = parse_parened_id(testdata.into())?;
assert_eq!(rest, " \txxx");
assert_eq!(parsed.join(""), "(id=FOO_bar)");
assert_eq!(format!("{}{rest}", parsed.join("")), testdata);
assert_eq!(id.as_ref().map(|x| x.id()), Some("FOO_bar"));
Ok(())
}
#[test]
fn test_parse_none() -> anyhow::Result<()> {
let testdata = " ";
let (rest, (parsed, id)) = parse_none(testdata.into())?;
assert_eq!(rest, " ");
assert_eq!(parsed.join(""), "");
assert_eq!(format!("{}{rest}", parsed.join("")), testdata);
assert!(id.is_none());
Ok(())
}
}