plantuml_parser/dsl/token/
include.rs

1use crate::IncludeSpecifierToken;
2use crate::{ParseContainer, ParseResult, wr, wr2};
3use nom::Parser;
4use nom::branch::alt;
5use nom::bytes::complete::tag;
6use nom::character::complete::space1;
7
8/// A token sequence with [`IncludeSpecifierToken`] that is around the include keyword. (like `"!include foo.puml"` or `"!include bar.iuml!buz"` , `"!include_many foo.puml"` or `"!include_many bar.iuml!buz"`.)
9///
10/// * `!include [include specifier]`
11/// * `!include_many [include specifier]`
12/// * `!include_once [include specifier]`
13///
14/// The above `include specifier` is parsed by [`IncludeSpecifierToken`].
15///
16/// # Examples
17///
18/// ```
19/// use plantuml_parser::{IncludeKind, IncludeToken, ParseContainer};
20///
21/// # fn main() -> anyhow::Result<()> {
22/// let input = "!include foo.puml rest";
23/// let (rest, (raws, token)) = IncludeToken::parse(input.into())?;
24/// let combined_raw: ParseContainer = raws.into();
25/// assert_eq!(rest, " rest");
26/// assert_eq!(combined_raw, "!include foo.puml");
27/// assert_eq!(token.filepath(), "foo.puml");
28/// assert_eq!(token.index(), None);
29/// assert_eq!(token.id(), None);
30/// assert_eq!(token.kind(), &IncludeKind::Include);
31///
32/// let input = "!include_many bar.iuml!1 rest";
33/// let (rest, (raws, token)) = IncludeToken::parse(input.into())?;
34/// let combined_raw: ParseContainer = raws.into();
35/// assert_eq!(rest, " rest");
36/// assert_eq!(combined_raw, "!include_many bar.iuml!1");
37/// assert_eq!(token.filepath(), "bar.iuml");
38/// assert_eq!(token.index(), Some(1));
39/// assert_eq!(token.id(), Some("1"));
40/// assert_eq!(token.kind(), &IncludeKind::IncludeMany);
41///
42/// let input = "!include_once baz.txt!qux rest";
43/// let (rest, (raws, token)) = IncludeToken::parse(input.into())?;
44/// let combined_raw: ParseContainer = raws.into();
45/// assert_eq!(rest, " rest");
46/// assert_eq!(combined_raw, "!include_once baz.txt!qux");
47/// assert_eq!(token.filepath(), "baz.txt");
48/// assert_eq!(token.index(), None);
49/// assert_eq!(token.id(), Some("qux"));
50/// assert_eq!(token.kind(), &IncludeKind::IncludeOnce);
51/// # Ok(())
52/// # }
53/// ```
54#[derive(Clone, Debug)]
55pub struct IncludeToken {
56    specifier: IncludeSpecifierToken,
57    kind: IncludeKind,
58}
59
60/// A kind of include keywords. `!include` | `!include_many` | `!include_once`
61#[derive(Clone, Debug, PartialEq, Eq)]
62pub enum IncludeKind {
63    Include,
64    IncludeMany,
65    IncludeOnce,
66}
67
68impl IncludeToken {
69    /// Tries to parse [`IncludeToken`]. (e.g. `"!include foo.puml"`, `"!include bar.iuml!buz"`.)
70    pub fn parse(input: ParseContainer) -> ParseResult<Self> {
71        let (rest, (include, spaces, (specifier_raws, specifier))) = (
72            alt((
73                wr!(tag("!include_many")),
74                wr!(tag("!include_once")),
75                wr!(tag("!include")),
76            )),
77            wr!(space1),
78            wr2!(IncludeSpecifierToken::parse),
79        )
80            .parse(input)?;
81
82        let kind = match include.as_str() {
83            "!include" => IncludeKind::Include,
84            "!include_many" => IncludeKind::IncludeMany,
85            "!include_once" => {
86                tracing::warn!("Multiple include errors with `include_once` are not supported.");
87                IncludeKind::IncludeOnce
88            }
89
90            _ => unreachable!("IncludeToken"),
91        };
92
93        let ret0 = ParseContainer::from(vec![include, spaces, specifier_raws]);
94        let ret1 = Self { specifier, kind };
95
96        Ok((rest, (ret0, ret1)))
97    }
98
99    /// Returns the [`IncludeKind`] of include keywords.
100    pub fn kind(&self) -> &IncludeKind {
101        &self.kind
102    }
103}
104
105impl std::ops::Deref for IncludeToken {
106    type Target = IncludeSpecifierToken;
107    fn deref(&self) -> &Self::Target {
108        &self.specifier
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    #[test]
117    fn test_parse() -> anyhow::Result<()> {
118        let testdata = "!include foo.puml  ";
119        let (rest, (parsed, include)) = IncludeToken::parse(testdata.into())?;
120        assert_eq!(rest, "  ");
121        assert_eq!(parsed, "!include foo.puml");
122        assert_eq!(include.filepath(), "foo.puml");
123        assert!(include.id().is_none());
124        assert_eq!(include.kind(), &IncludeKind::Include);
125
126        let testdata = "!include foo.puml!bar   ";
127        let (rest, (parsed, include)) = IncludeToken::parse(testdata.into())?;
128        assert_eq!(rest, "   ");
129        assert_eq!(parsed, "!include foo.puml!bar");
130        assert_eq!(include.filepath(), "foo.puml");
131        assert_eq!(include.id(), Some("bar"));
132        assert_eq!(include.kind(), &IncludeKind::Include);
133
134        let testdata = "!include_many foo.puml!bar  ";
135        let (rest, (parsed, include)) = IncludeToken::parse(testdata.into())?;
136        assert_eq!(rest, "  ");
137        assert_eq!(parsed, "!include_many foo.puml!bar");
138        assert_eq!(include.filepath(), "foo.puml");
139        assert_eq!(include.id(), Some("bar"));
140        assert_eq!(include.kind(), &IncludeKind::IncludeMany);
141
142        let testdata = "!include_once foo.puml!bar  ";
143        let (rest, (parsed, include)) = IncludeToken::parse(testdata.into())?;
144        assert_eq!(rest, "  ");
145        assert_eq!(parsed, "!include_once foo.puml!bar");
146        assert_eq!(include.filepath(), "foo.puml");
147        assert_eq!(include.id(), Some("bar"));
148        assert_eq!(include.kind(), &IncludeKind::IncludeOnce);
149
150        Ok(())
151    }
152}