1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
use crate::DiagramIdToken;
use crate::{wr, wr2, ParseContainer, ParseResult};
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::bytes::complete::take_while1;
use nom::sequence::tuple;
use nom::IResult;
/// A token sequence that is the include specifier ([`DiagramIdToken`]) around the include keyword. (like `"foo.puml"` or `"bar.iuml!buz"`.)
///
/// * `[relative filepath]`
/// * `[relative filepath]![index]`
/// * `[relative filepath]![id]`
///
/// # Examples
///
/// ```
/// use plantuml_parser::{IncludeSpecifierToken, ParseContainer};
///
/// # fn main() -> anyhow::Result<()> {
/// let input = "filepath_0 rest";
/// let (rest, (raws, token)) = IncludeSpecifierToken::parse(input.into())?;
/// let combined_raw: ParseContainer = raws.into();
/// assert_eq!(rest, " rest");
/// assert_eq!(combined_raw, "filepath_0");
/// assert_eq!(token.filepath(), "filepath_0");
/// assert_eq!(token.index(), None);
/// assert_eq!(token.id(), None);
///
/// let input = "file_path_1!diagram_2 rest";
/// let (rest, (raws, token)) = IncludeSpecifierToken::parse(input.into())?;
/// let combined_raw: ParseContainer = raws.into();
/// assert_eq!(rest, " rest");
/// assert_eq!(combined_raw, "file_path_1!diagram_2");
/// assert_eq!(token.filepath(), "file_path_1");
/// assert_eq!(token.index(), None);
/// assert_eq!(token.id(), Some("diagram_2"));
///
/// let input = "file_path_2!4 rest";
/// let (rest, (raws, token)) = IncludeSpecifierToken::parse(input.into())?;
/// let combined_raw: ParseContainer = raws.into();
/// assert_eq!(rest, " rest");
/// assert_eq!(combined_raw, "file_path_2!4");
/// assert_eq!(token.filepath(), "file_path_2");
/// assert_eq!(token.index(), Some(4));
/// assert_eq!(token.id(), Some("4"));
/// # Ok(())
/// # }
/// ```
#[derive(Clone, Debug)]
pub struct IncludeSpecifierToken {
filepath: ParseContainer,
id: Option<DiagramIdToken>,
}
impl IncludeSpecifierToken {
/// Tries to parse [`IncludeSpecifierToken`]. (e.g. `"foo.puml"`, `"bar.iuml!buz"`.)
pub fn parse(input: ParseContainer) -> ParseResult<Self> {
let ret = alt((wr2!(parse_file_with_id), wr2!(parse_only_file)))(input)?;
Ok(ret)
}
/// Returns the filepath specified by include keyword.
pub fn filepath(&self) -> &str {
self.filepath.as_str()
}
/// Returns the index specified by include keyword if existed.
pub fn index(&self) -> Option<usize> {
self.id.as_ref().and_then(|x| x.id().parse().ok())
}
/// Returns the ID specified by include keyword if existed.
pub fn id(&self) -> Option<&str> {
self.id.as_ref().map(|x| x.id())
}
}
/// parse like `"foo.puml"`
///
/// TODO: accept escaped space charactor: like `"foo\ bar.puml"` ("\ "?)
fn parse_only_file(input: ParseContainer) -> ParseResult<IncludeSpecifierToken> {
let result: IResult<_, _> = wr!(take_while1(|c: char| {
!['!', ' ', '\t', '\n', '\r'].contains(&c)
}))(input);
let (rest, filepath) = result?;
let parsed_raw = filepath.clone();
let parsed = IncludeSpecifierToken { filepath, id: None };
Ok((rest, (parsed_raw, parsed)))
}
/// parse like `"bar.iuml!buz"`
fn parse_file_with_id(input: ParseContainer) -> ParseResult<IncludeSpecifierToken> {
let (input, ((file_raw, file), tag, (id_raw, id))) = tuple((
wr2!(parse_only_file),
wr!(tag("!")),
wr2!(DiagramIdToken::parse),
))(input)?;
let parsed_raw = ParseContainer::from(vec![file_raw, tag, id_raw]);
let parsed = IncludeSpecifierToken {
filepath: file.filepath,
id: Some(id),
};
Ok((input, (parsed_raw, parsed)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse() -> anyhow::Result<()> {
let testdata = "foo.puml";
let (rest, (parsed, include)) = IncludeSpecifierToken::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(parsed, testdata);
assert_eq!(include.filepath(), "foo.puml");
assert!(include.id().is_none());
let testdata = "foo.puml!bar";
let (rest, (parsed, include)) = IncludeSpecifierToken::parse(testdata.into())?;
assert_eq!(rest, "");
assert_eq!(parsed, testdata);
assert_eq!(include.filepath(), "foo.puml");
assert_eq!(include.id(), Some("bar"));
Ok(())
}
}