gcode_nom/params/
mp.rs

1use core::hash::Hash;
2
3use nom::bytes::complete::tag;
4use nom::bytes::complete::take_until;
5use nom::character::complete::char;
6use nom::character::complete::space0;
7use nom::combinator::complete;
8use nom::combinator::map;
9use nom::sequence::delimited;
10use nom::sequence::preceded;
11use nom::IResult;
12use nom::Parser;
13
14/// Parameters used in M486 Commands
15#[derive(Clone, Debug, Eq, Hash, PartialEq)]
16pub enum MultiPartVal {
17    /// A Integrity check
18    /// used to assert this name is equal to be equivalent embedded stl file name.
19    A(String),
20    /// C   ; Cancel the current object (use with care!)
21    C,
22    /// P10 ; Cancel object with index 10 (the 11th object)
23    P(i128),
24    /// M486 S3                ; Indicate that the 4th object is starting now
25    /// M486 S3 A"cube copy 3" ; Indicate that the 4th object is starting now and name it
26    /// S-1 ; Indicate a non-object, purge tower, or other global feature
27    S(i128, Option<String>),
28    /// T12 ; Total of 12 objects (otherwise the firmware must count)
29    T(i128),
30    /// U2  ; Un-cancel object with index 2 (the 3rd object)
31    U(i128),
32}
33
34macro_rules! parse_mp_val {
35    ($name:ident, $tag:literal, $variant:ident) => {
36        #[doc = "Extracts multipart"]
37        #[doc = stringify!($tag)]
38        #[doc = " parameter"]
39        #[doc = ""]
40        #[doc = "# Errors"]
41        #[doc = "  When match fails."]
42        pub fn $name(i: &str) -> IResult<&str, MultiPartVal> {
43            map(
44                preceded((space0, tag($tag)), nom::character::complete::i128),
45                MultiPartVal::$variant,
46            )
47            .parse(i)
48        }
49    };
50}
51
52/// Extract multipart A parameter
53///
54/// TODO: Should I used `line_ending` instead of newline?
55///
56/// # Errors
57///   when match fails.
58pub fn parse_mp_a(i: &str) -> IResult<&str, MultiPartVal> {
59    map(
60        complete(delimited(tag("A"), take_until("\n"), char('\n'))),
61        |s: &str| MultiPartVal::A(s.to_string()),
62    )
63    .parse(i)
64}
65
66/// Extract multipart C parameter
67///
68/// # Errors
69///   when match fails.
70pub fn parse_mp_c(i: &str) -> IResult<&str, MultiPartVal> {
71    map((space0, tag("C")), |_| MultiPartVal::C).parse(i)
72}
73
74/// Extract multipart S parameter
75///
76/// #Errors// /// # Errors
77///   when match fails.
78pub fn parse_mp_s(i: &str) -> IResult<&str, MultiPartVal> {
79    map(
80        preceded(
81            (space0::<&str, _>, tag("S")),
82            (
83                // This is broken when a name is supplied, it is not recognized!!!
84                nom::character::complete::i128,
85                nom::combinator::opt(preceded(
86                    space0,
87                    delimited(char('"'), take_until("\""), char('"')),
88                )),
89            ),
90        ),
91        |(s, name)| MultiPartVal::S(s, name.map(std::string::ToString::to_string)),
92    )
93    .parse(i)
94}
95
96parse_mp_val!(parse_mp_p, "P", P);
97// TODO must implement [ S<i> A"<name>" ]
98// M486 S3 A"cube copy 3" ; Indicate that the 4th object is starting now and name it
99
100parse_mp_val!(parse_mp_t, "T", T);
101parse_mp_val!(parse_mp_u, "U", U);
102
103#[cfg(test)]
104mod test {
105    use super::*;
106
107    #[test]
108    fn multipart_value_equality() {
109        // Pass: - parameter wrapper and inner value match.
110        assert_eq!(
111            parse_mp_a("Aa.stl\n"),
112            Ok(("", MultiPartVal::A(String::from("a.stl"))))
113        );
114
115        // Pass: - parameter wrapper and inner value match.
116        assert_eq!(parse_mp_c("C"), Ok(("", MultiPartVal::C)));
117
118        // Pass: - parameter wrapper and inner value match.
119        assert_eq!(parse_mp_s("S-1"), Ok(("", MultiPartVal::S(-1, None))));
120    }
121}