1use nom::{
2 IResult,
3 character::streaming::*,
4 bytes::streaming::*,
5};
6use nom::branch::*;
7use nom::combinator::*;
8use nom::sequence::*;
9use nom::multi::*;
10
11use super::{
12 Response,
13 f32_str,
14};
15
16#[derive(Clone, Debug, PartialEq)]
17pub enum Busy {
18 Processing,
19 PausedForUser,
20 Other(String),
21}
22
23#[derive(Clone, Debug, PartialEq)]
24pub enum Feedback {
25 SDCard(SDCard),
26 ActualTemperatures(Vec<(String, f32)>),
27 Positions(Positions),
28 StartSDWrite(StartSDWrite),
29 SDWriteComplete,
30 SDPrintComplete,
31 Busy(Busy),
32 PausedForUser,
33}
34
35#[derive(Clone, Debug, PartialEq)]
36pub struct Positions {
37 pub target_positions: Option<Vec<(String, f32)>>,
38 pub actual_positions: Vec<(String, f32)>,
39}
40
41#[derive(Clone, Debug, PartialEq)]
42pub struct StartSDWrite {
43 pub filename: String,
44}
45
46#[derive(Clone, Debug, PartialEq)]
47pub struct SDCard {
48 pub enabled: bool,
49 pub size: Option<u32>,
50}
51
52pub fn feedback_resp<'r>(input: &'r str) -> IResult<&'r str, Response> {
53 map(
54 feedback,
55 |feedback| Response::Feedback(feedback),
56 )(input)
57}
58
59pub fn feedback<'r>(input: &'r str) -> IResult<&'r str, Feedback> {
60 preceded(
61 space0,
62 alt((
63 temperature_feedback,
64 position_feedback,
65 )),
66 )(input)
67}
68
69pub fn key_value<'r>(input: &'r str) -> IResult<&'r str, (String, Option<f32>)> {
70 separated_pair(
74 map(
75 recognize(many1(
76 verify(anychar, |c| c.is_ascii_alphanumeric() || c == &'@'),
77 )),
78 |address: &str| address.to_ascii_lowercase()
79 ),
80 pair(
81 char(':'),
82 space0,
83 ),
84 alt((
85 value(None, char('?')),
86 map(f32_str(), |f| Some(f)),
87 ))
88 )(input)
89}
90
91pub fn temperature_feedback<'r>(input: &'r str) -> IResult<&'r str, Feedback> {
92 map(
95 preceded(
96 peek(tag("T:")),
97 separated_list1(
98 pair(
99 space1,
100 opt(tuple((
101 char('/'),
102 f32_str(),
103 space1,
104 ))),
105 ),
106 key_value,
107 ),
108 ),
109 |temperatures| {
110 let temperatures = temperatures
111 .into_iter()
112 .filter_map(|(address, v)| {
113 if address.contains('@') || address == "e" || address == "w" {
116 return None
117 };
118
119 let address = if &address[..] == "t" {
120 "e0".to_string()
121 } else if address.starts_with('t') {
122 address.replace("t", "e")
123 }else {
124 address
125 };
126
127 v.map(|v| (address, v))
128 })
129 .collect();
130 Feedback::ActualTemperatures(temperatures)
131 }
132 )(input)
133}
134
135pub fn position_feedback<'r>(input: &'r str) -> IResult<&'r str, Feedback> {
136 map(
141 preceded(
142 peek(tag("X:")),
143 pair(
144 many1(preceded(
145 space0,
146 key_value,
147 )),
148 opt(preceded(
149 tuple((
150 space1,
151 tag_no_case("Count"),
152 space1,
153 )),
154 many0(terminated(
155 key_value,
156 space0,
157 )),
158 )),
159 ),
160 ),
161 |(p1, p2)| {
162 if let Some(p2) = p2 {
163 Feedback::Positions(Positions {
164 target_positions: Some(normalize_positions(p1)),
165 actual_positions: normalize_positions(p2),
166 })
167 } else {
168 Feedback::Positions(Positions {
169 target_positions: None,
170 actual_positions: normalize_positions(p1),
171 })
172 }
173 }
174 )(input)
175}
176
177fn normalize_positions(positions: Vec<(String, Option<f32>)>) -> Vec<(String, f32)> {
178 positions
179 .into_iter()
180 .filter_map(|(address, v)| {
181 let address = if &address[..] == "e" {
182 "e0".to_string()
183 } else if address.starts_with('t') {
184 address.replace("t", "e")
185 }else {
186 address
187 };
188
189 v.map(|v| (address, v))
190 })
191 .collect()
192}