tmc_langs_util/
parse_util.rs

1//! Contains parse functions that may be convenient for implementing language plugins.
2
3use nom::{IResult, Parser, branch, bytes, character, combinator, multi, sequence};
4use nom_language::error::VerboseError;
5
6/// Parses a string delimited by double quotes. Trims.
7pub fn string(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
8    combinator::map(
9        sequence::delimited(
10            character::complete::char('"'),
11            bytes::complete::is_not("\""),
12            character::complete::char('"'),
13        ),
14        str::trim,
15    )
16    .parse(i)
17}
18
19/// Parses a string delimited by single quotes. Trims.
20pub fn string_single(i: &str) -> IResult<&str, &str, VerboseError<&str>> {
21    combinator::map(
22        sequence::delimited(
23            character::complete::char('\''),
24            bytes::complete::is_not("'"),
25            character::complete::char('\''),
26        ),
27        str::trim,
28    )
29    .parse(i)
30}
31
32/// Parses a comma-separated list of double quote strings like "a", "b", "c".
33pub fn comma_separated_strings(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
34    comma_separated_things(string, i)
35}
36
37/// Parses a comma-separated list of single quote strings like 'a', 'b', 'c'.
38pub fn comma_separated_strings_single(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
39    comma_separated_things(string_single, i)
40}
41
42/// Parses a comma-separated list of mixed quote strings like 'a', "b", 'c'.
43pub fn comma_separated_strings_either(i: &str) -> IResult<&str, Vec<&str>, VerboseError<&str>> {
44    comma_separated_things(branch::alt((string, string_single)), i)
45}
46
47/// Parses a comma-separated list of things, thing being defined by the parser given to the function.
48fn comma_separated_things<'a>(
49    thing_parser: impl Parser<&'a str, Output = &'a str, Error = VerboseError<&'a str>>,
50    i: &'a str,
51) -> IResult<&'a str, Vec<&'a str>, VerboseError<&'a str>> {
52    multi::separated_list1(
53        sequence::delimited(
54            character::complete::multispace0,
55            character::complete::char(','),
56            character::complete::multispace0,
57        ),
58        thing_parser,
59    )
60    .parse(i)
61}
62
63#[cfg(test)]
64#[allow(clippy::unwrap_used)]
65mod test {
66    use super::*;
67
68    #[test]
69    fn parses_string() {
70        let (left, res) = string("\"abcd\"").unwrap();
71        assert!(left.is_empty());
72        assert_eq!(res, "abcd");
73    }
74
75    #[test]
76    fn parses_string_single() {
77        let (left, res) = string_single("'abcd'").unwrap();
78        assert!(left.is_empty());
79        assert_eq!(res, "abcd");
80    }
81
82    #[test]
83    fn parses_comma_separated_strings() {
84        let (left, res) = comma_separated_strings("\"abcd\", \"efgh\", \"hijk\"").unwrap();
85        assert!(left.is_empty());
86        assert_eq!(res, &["abcd", "efgh", "hijk"]);
87    }
88
89    #[test]
90    fn parses_comma_separated_strings_single() {
91        let (left, res) = comma_separated_strings_single("'abcd', 'efgh', 'hijk'").unwrap();
92        assert!(left.is_empty());
93        assert_eq!(res, &["abcd", "efgh", "hijk"]);
94    }
95
96    #[test]
97    fn parses_comma_separated_strings_either() {
98        let (left, res) = comma_separated_strings_either("'abcd', \"efgh\", 'hijk'").unwrap();
99        assert!(left.is_empty());
100        assert_eq!(res, &["abcd", "efgh", "hijk"]);
101    }
102}