• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cbruiz / printhor / 13643357678

04 Mar 2025 12:25AM UTC coverage: 90.599% (+2.5%) from 88.116%
13643357678

push

github

web-flow
Refactor to clean-up dead code (#34)

* Refactor to clean-up dead code and setup a scenario for a more clear state review

* code cleanup

* code cleanup

* Increase code coverage

* Increase code coverage

* Increase code coverage
s-plot recovery with unit tests

* Test fixes

879 of 887 new or added lines in 9 files covered. (99.1%)

2 existing lines in 2 files now uncovered.

14524 of 16031 relevant lines covered (90.6%)

753057.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.18
/printhor/src/bin/control/processing/gcode_parser.rs
1
#[allow(unused)]
2
use crate::control::{EFSXYZ, EXYZ, FXYZ, GCodeCmd, GCodeValue, N, S};
3
use crate::helpers;
4
use crate::hwa;
5
use hwa::CommChannel;
6

7
#[derive(Debug)]
8
pub enum GCodeLineParserError {
9
    /// There was an error parsing
10
    ParseError(u32),
11
    /// The Gcode was not implemented
12
    GCodeNotImplemented(u32, alloc::string::String),
13
    /// EOF Reading from parser
14
    EOF,
15
    /// Unexpected fatal error
16
    #[allow(unused)]
17
    FatalError,
18
}
19

20
#[cfg(feature = "debug-gcode")]
21
struct FormatableGCode<'a>(&'a async_gcode::GCode);
22

23
#[cfg(feature = "debug-gcode")]
24
impl core::fmt::Debug for FormatableGCode<'_> {
25
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
26
        match self.0 {
27
            async_gcode::GCode::StatusCommand => {
28
                f.write_str("StatusCommand")?;
29
            }
30
            async_gcode::GCode::BlockDelete => {
31
                f.write_str("BlockDelete")?;
32
            }
33
            async_gcode::GCode::LineNumber(_ln) => {
34
                core::write!(f, "LineNumber<{:?}>", _ln)?;
35
            }
36
            async_gcode::GCode::Word(_w, _v) => {
37
                core::write!(f, "Word[{}", _w.to_uppercase())?;
38
                match _v {
39
                    async_gcode::RealValue::Literal(_l) => {
40
                        match _l {
41
                            async_gcode::Literal::RealNumber(_r) => {
42
                                let div = 10_i32.pow(_r.scale().into());
43
                                let w0 = _r.integer_part() / div;
44
                                let w1 = _r.integer_part() % div;
45
                                core::write!(
46
                                    f,
47
                                    "{}.{:0width$}",
48
                                    w0,
49
                                    w1,
50
                                    width = _r.scale() as usize
51
                                )?;
52
                            }
53
                            async_gcode::Literal::String(_s) => {
54
                                core::write!(f, " {}", _s)?;
55
                            }
56
                        }
57
                        core::write!(f, "]")?;
58
                    }
59
                    _ => {}
60
                }
61
            }
62
            async_gcode::GCode::Text(_t) => match _t {
63
                async_gcode::RealValue::Literal(async_gcode::Literal::String(_s)) => {
64
                    core::write!(f, "String[{}]", _s)?;
65
                }
66
                _ => {}
67
            },
68
            async_gcode::GCode::Execute => {
69
                core::write!(f, "Execute")?;
70
            }
71
            #[allow(unreachable_patterns)]
72
            _ => {}
73
        }
74
        Ok(())
75
    }
76
}
77
#[cfg(all(feature = "with-defmt", feature = "debug-gcode"))]
78
impl defmt::Format for FormatableGCode<'_> {
79
    fn format(&self, f: defmt::Formatter) {
80
        match self.0 {
81
            async_gcode::GCode::StatusCommand => {
82
                defmt::write!(f, "StatusCommand");
83
            }
84
            async_gcode::GCode::BlockDelete => {
85
                defmt::write!(f, "BlockDelete");
86
            }
87
            async_gcode::GCode::LineNumber(_ln) => {
88
                defmt::write!(f, "LineNumber<{:?}>", _ln);
89
            }
90
            async_gcode::GCode::Word(_w, _v) => {
91
                use alloc::string::ToString;
92
                defmt::write!(f, "Word[{:?}", _w.to_uppercase().to_string().as_str());
93
                match _v {
94
                    async_gcode::RealValue::Literal(_l) => {
95
                        match _l {
96
                            async_gcode::Literal::RealNumber(_r) => {
97
                                defmt::write!(
98
                                    f,
99
                                    " val: {}, scale: {}",
100
                                    _r.integer_part(),
101
                                    _r.scale()
102
                                );
103
                            }
104
                            async_gcode::Literal::String(_s) => {
105
                                defmt::write!(f, " {}", _s.as_str());
106
                            }
107
                        }
108
                        defmt::write!(f, "]");
109
                    }
110
                    _ => {}
111
                }
112
            }
113
            async_gcode::GCode::Text(_t) => match _t {
114
                async_gcode::RealValue::Literal(async_gcode::Literal::String(_s)) => {
115
                    defmt::write!(f, "String[{}]", _s.as_str());
116
                }
117
                _ => {}
118
            },
119
            async_gcode::GCode::Execute => {
120
                defmt::write!(f, "Execute");
121
            }
122
            #[allow(unreachable_patterns)]
123
            _ => {}
124
        }
125
    }
126
}
127

128
#[cfg(feature = "with-defmt")]
129
impl defmt::Format for GCodeLineParserError {
130
    fn format(&self, fmt: defmt::Formatter) {
131
        match self {
132
            GCodeLineParserError::ParseError(ln) => {
133
                defmt::write!(fmt, "ParseError(line={})", ln);
134
            }
135
            GCodeLineParserError::GCodeNotImplemented(ln, string) => {
136
                defmt::write!(
137
                    fmt,
138
                    "GCodeNotImplemented(line={}, gcode={})",
139
                    ln,
140
                    string.as_str()
141
                );
142
            }
143
            GCodeLineParserError::FatalError => {
144
                defmt::write!(fmt, "FatalError");
145
            }
146
            GCodeLineParserError::EOF => {
147
                defmt::write!(fmt, "EOF");
148
            }
149
        }
150
    }
151
}
152

153
/// Represents the raw specification of a G-code command.
154
///
155
/// G-code is a language used to control CNC machines, 3D printers, and other similar equipment.
156
/// A `RawGCodeSpec` captures the initial character of the command (e.g., 'G', 'M') and any numerical
157
/// specifics associated with it, potentially including a sub-value used for more granular control.
158
///
159
/// # Fields
160
/// - `code`: The main character of the G-code command.
161
/// - `spec`: An optional numerical part that follows the main character. For example, 'G1' would have
162
///   '1' as its spec.
163
/// - `sub`: An optional sub-value that provides additional specificity. For example, 'G1.1' would have
164
///   '1' as its spec and '1' as its sub.
165
///
166
/// # Example
167
///
168
/// ```
169
/// let gcode = RawTGCodeSpec::from('G', Some((1, 0)));
170
/// assert_eq!(format!("{:?}", gcode), "G1");
171
/// ```
172
pub struct RawGCodeSpec {
173
    code: char,
174
    spec: Option<i32>,
175
    sub: Option<i32>,
176
}
177

178
impl RawGCodeSpec {
179
    pub fn from(code: char, spec: Option<(i32, u8)>) -> Self {
452,542✔
180
        match spec {
452,542✔
181
            None => Self {
6✔
182
                code,
6✔
183
                spec: None,
6✔
184
                sub: None,
6✔
185
            },
6✔
186
            Some((_num, _scale)) => {
452,536✔
187
                if _scale == 0 {
452,536✔
188
                    Self {
452,506✔
189
                        code,
452,506✔
190
                        spec: Some(_num),
452,506✔
191
                        sub: None,
452,506✔
192
                    }
452,506✔
193
                } else {
194
                    let sc = 10_i32.pow(_scale as u32);
30✔
195
                    let sp = _num / sc;
30✔
196
                    let ss = _num % sc;
30✔
197
                    Self {
30✔
198
                        code,
30✔
199
                        spec: Some(sp),
30✔
200
                        sub: Some(ss),
30✔
201
                    }
30✔
202
                }
203
            }
204
        }
205
    }
452,542✔
206
}
207
impl core::fmt::Debug for RawGCodeSpec {
208
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
345✔
209
        core::write!(f, "{}", self.code.to_uppercase())?;
345✔
210
        match &self.spec {
345✔
211
            Some(_spec) => {
339✔
212
                core::write!(f, "{}", _spec)?;
339✔
213
                match &self.sub {
339✔
214
                    Some(_sub) => {
×
215
                        core::write!(f, ".{}", _sub)
×
216
                    }
217
                    _ => Ok(()),
339✔
218
                }
219
            }
220
            _ => Ok(()),
6✔
221
        }
222
    }
345✔
223
}
224

225
// dyn trait could reduce code size a lot with the penalty of the indirection
226
pub struct GCodeLineParser<STREAM>
227
where
228
    STREAM: async_gcode::ByteStream<Item = Result<u8, async_gcode::Error>>,
229
{
230
    raw_parser: async_gcode::Parser<STREAM, async_gcode::Error>,
231
    gcode_line: Option<u32>,
232
}
233

234
impl<STREAM> GCodeLineParser<STREAM>
235
where
236
    STREAM: async_gcode::ByteStream<Item = Result<u8, async_gcode::Error>>,
237
{
238
    pub fn new(stream: STREAM) -> Self {
220✔
239
        Self {
220✔
240
            raw_parser: async_gcode::Parser::new(stream),
220✔
241
            gcode_line: None,
220✔
242
        }
220✔
243
    }
220✔
244

245
    pub fn gcode_line(&self) -> Option<u32> {
144✔
246
        self.gcode_line
144✔
247
    }
144✔
248

249
    pub async fn next_gcode(
452,599✔
250
        &mut self,
452,599✔
251
        _channel: CommChannel,
452,599✔
252
    ) -> Result<GCodeCmd, GCodeLineParserError> {
452,599✔
253
        // The GcodeCmd being constructed.
452,599✔
254
        // * Initially set to none
452,599✔
255
        // * Reset back to None when built, updated and returned
452,599✔
256
        let mut current_gcode: Option<GCodeCmd> = None;
452,599✔
257
        // Same as previous but unparsed
452,599✔
258
        let mut raw_gcode_spec: Option<RawGCodeSpec> = None;
452,599✔
259

452,599✔
260
        let mut tagged_line_num = None;
452,599✔
261
        let mut skip_gcode = false;
452,599✔
262

263
        loop {
264
            match self.raw_parser.next().await {
2,281,483✔
265
                None => {
266
                    hwa::trace!("EOF reading from stream");
45✔
267
                    self.gcode_line = tagged_line_num;
45✔
268
                    match current_gcode.take() {
45✔
269
                        None => {
270
                            match raw_gcode_spec.take() {
21✔
271
                                None => {
272
                                    return Err(GCodeLineParserError::EOF);
21✔
273
                                } // Empty line. Just ignore
274
                                Some(rgs) => {
×
275
                                    return Err(GCodeLineParserError::GCodeNotImplemented(
×
276
                                        self.raw_parser.get_current_line(),
×
277
                                        alloc::format!("{:?}", rgs),
×
278
                                    ));
×
279
                                }
280
                            }
281
                        }
282
                        Some(mut cgv) => {
24✔
283
                            cgv.order_num = self.raw_parser.get_current_line();
24✔
284
                            return Ok(cgv);
24✔
285
                        }
286
                    }
287
                }
288
                Some(parser_gcode) => {
2,281,402✔
289
                    match parser_gcode {
2,281,402✔
290
                        Ok(g) => {
2,281,393✔
291
                            #[cfg(feature = "debug-gcode")]
2,281,393✔
292
                            hwa::info!(
2,281,393✔
293
                                "[debug-gcode] channel: {:?} gcode: {:?}",
2,281,393✔
294
                                _channel,
2,281,393✔
295
                                FormatableGCode(&g)
2,281,393✔
296
                            );
2,281,393✔
297
                            match g {
2,281,393✔
298
                                // FIXME Undo this hacky temporary solution in async-gcode.
299
                                // A sub-protocol approach is preferred
300
                                #[cfg(feature = "grbl-compat")]
301
                                async_gcode::GCode::StatusCommand => {
302
                                    return Ok(GCodeCmd::new(
303
                                        self.raw_parser.get_current_line(),
304
                                        tagged_line_num,
305
                                        GCodeValue::Status,
306
                                    ));
307
                                }
308
                                async_gcode::GCode::BlockDelete => {
6✔
309
                                    skip_gcode = true;
6✔
310
                                    //crate::debug!("BlockDelete");
6✔
311
                                }
6✔
312
                                async_gcode::GCode::LineNumber(n) => {
18✔
313
                                    tagged_line_num = n;
18✔
314
                                }
18✔
315
                                async_gcode::GCode::Word(ch, fv) => {
1,739,220✔
316
                                    if skip_gcode {
1,739,220✔
317
                                        continue;
333✔
318
                                    }
1,738,887✔
319
                                    let frx = match &fv {
1,738,887✔
320
                                        async_gcode::RealValue::Literal(
321
                                            async_gcode::Literal::RealNumber(f),
1,738,869✔
322
                                        ) => Some((f.integer_part(), f.scale())),
1,738,869✔
323
                                        _ => None,
18✔
324
                                    };
325
                                    if let Some(current_gcode) = &mut current_gcode {
1,738,887✔
326
                                        // We already are building a GCodeCmd, so we update its fields
327
                                        update_current(current_gcode, ch, frx, fv)
1,286,351✔
328
                                    } else {
329
                                        raw_gcode_spec.replace(RawGCodeSpec::from(ch, frx));
452,536✔
330

331
                                        let has_text_argument = match (ch, frx) {
452,536✔
332
                                            ('m', Some((23, 0)))
333
                                            | ('m', Some((117, 0)))
334
                                            | ('m', Some((118, 0))) => true,
30✔
335
                                            _ => false,
452,506✔
336
                                        };
337
                                        // We need to start building the current GCodeCmd that is being parsed
338
                                        match init_current(ch, frx) {
452,536✔
339
                                            None => {
339✔
340
                                                // Unable to init: The GCodeValue is unexpected
339✔
341
                                                // Hence, we will skip every [async_gcode::GCode::Word] until [async_gcode::GCode::Execute]
339✔
342
                                                skip_gcode = true;
339✔
343
                                            }
339✔
344
                                            Some(gcode_value) => {
452,197✔
345
                                                current_gcode.replace(GCodeCmd::new(
452,197✔
346
                                                    0, // Will update later at [async_gcode::GCode::Execute]
452,197✔
347
                                                    tagged_line_num,
452,197✔
348
                                                    gcode_value,
452,197✔
349
                                                ));
452,197✔
350
                                                if has_text_argument {
452,197✔
351
                                                    self.raw_parser.switch_to_text();
30✔
352
                                                }
452,167✔
353
                                            }
354
                                        }
355
                                    }
356
                                }
357
                                async_gcode::GCode::Text(value) => {
30✔
358
                                    if let Some(current_gcode) = &mut current_gcode {
30✔
359
                                        // We already are building a GCodeCmd, so we update its fields
360
                                        update_current(current_gcode, 'T', None, value)
30✔
361
                                    }
×
362
                                }
363
                                async_gcode::GCode::Execute => {
364
                                    // Reset skip_gcode status
365
                                    skip_gcode = false;
542,119✔
366
                                    self.gcode_line = tagged_line_num;
542,119✔
367
                                    match current_gcode.take() {
542,119✔
368
                                        None => {
369
                                            match raw_gcode_spec.take() {
89,949✔
370
                                                None => {
371
                                                    #[cfg(feature = "trace-commands")]
372
                                                    hwa::warn!("Ignoring empty line");
373
                                                    continue;
89,610✔
374
                                                } // Empty line. Just ignore
375
                                                Some(rgs) => {
339✔
376
                                                    return Err(
339✔
377
                                                        GCodeLineParserError::GCodeNotImplemented(
339✔
378
                                                            self.raw_parser.get_current_line(),
339✔
379
                                                            alloc::format!("{:?}", rgs),
339✔
380
                                                        ),
339✔
381
                                                    );
339✔
382
                                                }
383
                                            }
384
                                        }
385
                                        Some(mut cgv) => {
452,170✔
386
                                            cgv.order_num = self.raw_parser.get_current_line();
452,170✔
387
                                            return Ok(cgv);
452,170✔
388
                                        }
389
                                    }
390
                                }
391
                                _ => {
392
                                    return Err(GCodeLineParserError::GCodeNotImplemented(
×
393
                                        self.raw_parser.get_current_line(),
×
394
                                        alloc::string::String::from("N/A"),
×
395
                                    ));
×
396
                                }
397
                            }
398
                        }
399
                        Err(error) => {
9✔
400
                            match error {
9✔
401
                                async_gcode::Error::UnexpectedByte(_b) => {
9✔
402
                                    hwa::warn!("Unexpected byte: {} ({})", _b, char::from(_b));
9✔
403
                                }
404
                                async_gcode::Error::NumberOverflow => {
405
                                    hwa::warn!("Number overflow");
×
406
                                }
407
                                async_gcode::Error::BadNumberFormat => {
408
                                    hwa::warn!("Bad number format");
×
409
                                }
410
                                _e => {
×
411
                                    #[cfg(feature = "native")]
×
412
                                    hwa::error!("Parse error {:?}", _e);
×
413
                                    hwa::error!("Parse error");
×
414
                                }
415
                            }
416
                            return Err(GCodeLineParserError::ParseError(
9✔
417
                                self.raw_parser.get_current_line(),
9✔
418
                            ));
9✔
419
                        }
420
                    }
421
                }
422
            }
423
        }
424
    }
452,563✔
425

426
    pub async fn reset(&mut self) {
30✔
427
        hwa::debug!("AsyncGcodeParser reset");
30✔
428
        self.raw_parser.reset().await;
30✔
429
    }
30✔
430

431
    pub fn reset_current_line(&mut self) {
12✔
432
        hwa::warn!("AsyncGcodeParser reset_current_line");
12✔
433
        self.raw_parser.update_current_line(0);
12✔
434
    }
12✔
435

436
    pub fn get_state(&self) -> async_gcode::AsyncParserState {
36✔
437
        self.raw_parser.get_state()
36✔
438
    }
36✔
439

440
    pub fn get_line(&self) -> u32 {
452,359✔
441
        self.raw_parser.get_current_line()
452,359✔
442
    }
452,359✔
443

444
    pub async fn close(&mut self) {
16✔
445
        self.raw_parser.reset().await;
16✔
446
    }
16✔
447
}
448

449
/// This trait is just a hack to give backward compatibility with unpatched async-gcode 0.3.0
450
/// Normally, won't be used as it is patched in cargo.toml
451
/// Purpose of this trait and impl is just to be able to publish printhor in crates.io
452
#[allow(unused)]
453
trait FixedAdaptor {
454
    fn integer_part(&self) -> i32;
455
    fn scale(&self) -> u8;
456
}
457

458
impl FixedAdaptor for f64 {
459
    fn integer_part(&self) -> i32 {
×
460
        panic!("Please, use patched async-gcode instead")
×
461
    }
462

463
    fn scale(&self) -> u8 {
×
464
        panic!("Please, use patched async-gcode instead")
×
465
    }
466
}
467

468
/// Initialize and EMPTY GCodeValue variant from ch, frx spec coming from parser
469
fn init_current(ch: char, frx: Option<(i32, u8)>) -> Option<GCodeValue> {
452,536✔
470
    match (ch, frx) {
452,536✔
471
        #[cfg(feature = "grbl-compat")]
472
        ('$', None) => Some(GCodeValue::GRBLCmd),
473
        ('g', None) => Some(GCodeValue::G),
×
474
        #[cfg(feature = "with-motion")]
475
        ('g', Some((0, 0))) => Some(GCodeValue::G0(FXYZ::new())),
46✔
476
        #[cfg(feature = "with-motion")]
477
        ('g', Some((1, 0))) => Some(GCodeValue::G1(EFSXYZ::new())),
439,979✔
478
        #[cfg(feature = "with-motion")]
479
        ('g', Some((4, 0))) => Some(GCodeValue::G4(S::new())),
19✔
480
        #[cfg(feature = "with-motion")]
481
        ('g', Some((10, 0))) => Some(GCodeValue::G10),
×
482
        #[cfg(feature = "with-motion")]
483
        ('g', Some((17, 0))) => Some(GCodeValue::G17),
×
484
        #[cfg(feature = "with-motion")]
485
        ('g', Some((21, 0))) => Some(GCodeValue::G21),
10✔
486
        #[cfg(feature = "with-motion")]
487
        ('g', Some((28, 0))) => Some(GCodeValue::G28(EXYZ::new())),
19✔
488
        #[cfg(feature = "with-motion")]
489
        ('g', Some((29, 0))) => Some(GCodeValue::G29),
6✔
490
        #[cfg(feature = "with-motion")]
491
        ('g', Some((291, 1))) => Some(GCodeValue::G29_1),
6✔
492
        #[cfg(feature = "with-motion")]
493
        ('g', Some((292, 1))) => Some(GCodeValue::G29_2),
6✔
494
        #[cfg(feature = "with-probe")]
495
        ('g', Some((31, 0))) => Some(GCodeValue::G31),
6✔
496
        #[cfg(feature = "with-probe")]
497
        ('g', Some((32, 0))) => Some(GCodeValue::G32),
×
498
        ('g', Some((80, 0))) => Some(GCodeValue::G80),
3✔
499
        #[cfg(feature = "with-motion")]
500
        ('g', Some((90, 0))) => Some(GCodeValue::G90),
13✔
501
        #[cfg(feature = "with-motion")]
502
        ('g', Some((91, 0))) => Some(GCodeValue::G91),
×
503
        #[cfg(feature = "with-motion")]
504
        ('g', Some((92, 0))) => Some(GCodeValue::G92(EXYZ::new())),
732✔
505
        #[cfg(feature = "with-motion")]
506
        ('g', Some((94, 0))) => Some(GCodeValue::G94),
×
UNCOV
507
        ('m', None) => Some(GCodeValue::M),
×
508
        #[cfg(all(feature = "with-sd-card", feature = "with-print-job"))]
509
        ('m', Some((2, 0))) => Some(GCodeValue::M2),
7✔
510
        ('m', Some((3, 0))) => Some(GCodeValue::M3),
×
511
        ('m', Some((4, 0))) => Some(GCodeValue::M4),
4✔
512
        ('m', Some((5, 0))) => Some(GCodeValue::M5),
4✔
513
        #[cfg(feature = "with-sd-card")]
514
        ('m', Some((20, 0))) => Some(GCodeValue::M20(None)),
6✔
515
        #[cfg(feature = "with-sd-card")]
516
        ('m', Some((21, 0))) => Some(GCodeValue::M21),
×
517
        #[cfg(all(feature = "with-sd-card", feature = "with-print-job"))]
518
        ('m', Some((23, 0))) => Some(GCodeValue::M23(None)),
18✔
519
        #[cfg(all(feature = "with-sd-card", feature = "with-print-job"))]
520
        ('m', Some((24, 0))) => Some(GCodeValue::M24),
×
521
        #[cfg(all(feature = "with-sd-card", feature = "with-print-job"))]
522
        ('m', Some((25, 0))) => Some(GCodeValue::M25),
×
523
        ('m', Some((37, 0))) => Some(GCodeValue::M37(S::new())),
12✔
524
        ('m', Some((73, 0))) => Some(GCodeValue::M73),
1,176✔
525
        ('m', Some((79, 0))) => Some(GCodeValue::M79),
×
526
        #[cfg(feature = "with-ps-on")]
527
        ('m', Some((80, 0))) => Some(GCodeValue::M80),
3✔
528
        #[cfg(feature = "with-ps-on")]
529
        ('m', Some((81, 0))) => Some(GCodeValue::M81),
3✔
530
        ('m', Some((83, 0))) => Some(GCodeValue::M83),
6✔
531
        #[cfg(feature = "with-motion")]
532
        ('m', Some((84, 0))) => Some(GCodeValue::M84),
3✔
533
        ('m', Some((100, 0))) => Some(GCodeValue::M100),
×
534
        #[cfg(feature = "with-hot-end")]
535
        ('m', Some((104, 0))) => Some(GCodeValue::M104(S::new())),
8✔
536
        ('m', Some((105, 0))) => Some(GCodeValue::M105),
×
537
        #[cfg(feature = "with-fan-layer")]
538
        ('m', Some((106, 0))) => Some(GCodeValue::M106),
640✔
539
        #[cfg(feature = "with-fan-layer")]
540
        ('m', Some((107, 0))) => Some(GCodeValue::M107),
10✔
541
        #[cfg(feature = "with-hot-end")]
542
        ('m', Some((109, 0))) => Some(GCodeValue::M109(S::new())),
4✔
543
        ('m', Some((110, 0))) => Some(GCodeValue::M110(N::new())),
12✔
544
        #[cfg(feature = "with-motion")]
545
        ('m', Some((114, 0))) => Some(GCodeValue::M114),
10✔
546
        ('m', Some((115, 0))) => Some(GCodeValue::M115),
3✔
547
        ('m', Some((117, 0))) => Some(GCodeValue::M117(None)),
6✔
548
        ('m', Some((118, 0))) => Some(GCodeValue::M118(None)),
6✔
549
        ('m', Some((119, 0))) => Some(GCodeValue::M119),
×
550
        #[cfg(feature = "with-hot-bed")]
551
        ('m', Some((140, 0))) => Some(GCodeValue::M140(S::new())),
6✔
552
        #[cfg(feature = "with-hot-bed")]
553
        ('m', Some((190, 0))) => Some(GCodeValue::M190),
2✔
554
        #[cfg(feature = "with-motion")]
555
        ('m', Some((201, 0))) => Some(GCodeValue::M201),
3✔
556
        #[cfg(feature = "with-motion")]
557
        ('m', Some((203, 0))) => Some(GCodeValue::M203),
3✔
558
        ('m', Some((204, 0))) => Some(GCodeValue::M204),
9,315✔
559
        ('m', Some((205, 0))) => Some(GCodeValue::M205),
12✔
560
        #[cfg(feature = "with-motion")]
561
        ('m', Some((206, 0))) => Some(GCodeValue::M206),
10✔
562
        ('m', Some((220, 0))) => Some(GCodeValue::M220(S::new())),
12✔
563
        ('m', Some((221, 0))) => Some(GCodeValue::M221(S::new())),
6✔
564
        ('m', Some((502, 0))) => Some(GCodeValue::M502),
×
565
        ('m', Some((503, 0))) => Some(GCodeValue::M503(S::new())),
×
566
        #[cfg(feature = "with-motion")]
567
        ('m', Some((8621, 1))) => Some(GCodeValue::M862_1),
9✔
568
        #[cfg(feature = "with-motion")]
569
        ('m', Some((8623, 1))) => Some(GCodeValue::M862_3),
9✔
570
        #[cfg(feature = "with-motion")]
571
        ('m', Some((900, 0))) => Some(GCodeValue::M900),
12✔
572
        #[cfg(feature = "with-motion")]
573
        ('m', Some((907, 0))) => Some(GCodeValue::M907),
12✔
574
        _ => None,
339✔
575
    }
576
}
452,536✔
577

578
fn update_current(
1,286,381✔
579
    gcode_cmd: &mut GCodeCmd,
1,286,381✔
580
    ch: char,
1,286,381✔
581
    frx: Option<(i32, u8)>,
1,286,381✔
582
    fv: async_gcode::RealValue,
1,286,381✔
583
) {
1,286,381✔
584
    match &mut gcode_cmd.value {
1,286,381✔
585
        #[cfg(feature = "grbl-compat")]
586
        GCodeValue::Status => match (ch, frx) {
587
            ('I', Some(_val)) => {
588
                hwa::warn!("TODO!!");
589
            }
590
            _ => {}
591
        },
592
        #[cfg(feature = "with-motion")]
593
        GCodeValue::G0(coord) => match (ch, frx) {
170✔
594
            ('f', Some(val)) => {
33✔
595
                coord.f.replace(helpers::to_fixed(val));
33✔
596
            }
33✔
597
            //
598
            #[cfg(feature = "with-x-axis")]
599
            ('x', Some(val)) => {
22✔
600
                coord.x.replace(helpers::to_fixed(val));
22✔
601
            }
22✔
602
            #[cfg(feature = "with-y-axis")]
603
            ('y', Some(val)) => {
22✔
604
                coord.y.replace(helpers::to_fixed(val));
22✔
605
            }
22✔
606
            #[cfg(feature = "with-z-axis")]
607
            ('z', Some(val)) => {
33✔
608
                coord.z.replace(helpers::to_fixed(val));
33✔
609
            }
33✔
610
            //
611
            #[cfg(feature = "with-a-axis")]
612
            ('a', Some(val)) => {
1✔
613
                coord.a.replace(helpers::to_fixed(val));
1✔
614
            }
1✔
615
            #[cfg(feature = "with-b-axis")]
616
            ('b', Some(val)) => {
1✔
617
                coord.b.replace(helpers::to_fixed(val));
1✔
618
            }
1✔
619
            #[cfg(feature = "with-c-axis")]
620
            ('c', Some(val)) => {
1✔
621
                coord.c.replace(helpers::to_fixed(val));
1✔
622
            }
1✔
623
            //
624
            #[cfg(feature = "with-i-axis")]
625
            ('i', Some(val)) => {
1✔
626
                coord.i.replace(helpers::to_fixed(val));
1✔
627
            }
1✔
628
            #[cfg(feature = "with-j-axis")]
629
            ('j', Some(val)) => {
1✔
630
                coord.j.replace(helpers::to_fixed(val));
1✔
631
            }
1✔
632
            #[cfg(feature = "with-k-axis")]
633
            ('k', Some(val)) => {
1✔
634
                coord.k.replace(helpers::to_fixed(val));
1✔
635
            }
1✔
636
            //
637
            #[cfg(feature = "with-u-axis")]
638
            ('u', Some(val)) => {
1✔
639
                coord.u.replace(helpers::to_fixed(val));
1✔
640
            }
1✔
641
            #[cfg(feature = "with-v-axis")]
642
            ('v', Some(val)) => {
1✔
643
                coord.v.replace(helpers::to_fixed(val));
1✔
644
            }
1✔
645
            #[cfg(feature = "with-w-axis")]
646
            ('w', Some(val)) => {
1✔
647
                coord.w.replace(helpers::to_fixed(val));
1✔
648
            }
1✔
649
            _ => {}
51✔
650
        },
651

652
        #[cfg(feature = "with-motion")]
653
        GCodeValue::G1(coord) => match (ch, frx) {
1,272,788✔
654
            #[cfg(feature = "with-e-axis")]
655
            ('e', Some(val)) => {
267,540✔
656
                coord.e.replace(helpers::to_fixed(val));
267,540✔
657
            }
267,540✔
658
            ('f', Some(val)) => {
49,583✔
659
                coord.f.replace(helpers::to_fixed(val));
49,583✔
660
            }
49,583✔
661
            ('s', Some(val)) => {
3,393✔
662
                coord.s.replace(helpers::to_fixed(val));
3,393✔
663
            }
3,393✔
664
            //
665
            #[cfg(feature = "with-x-axis")]
666
            ('x', Some(val)) => {
403,648✔
667
                coord.x.replace(helpers::to_fixed(val));
403,648✔
668
            }
403,648✔
669
            #[cfg(feature = "with-y-axis")]
670
            ('y', Some(val)) => {
403,645✔
671
                coord.y.replace(helpers::to_fixed(val));
403,645✔
672
            }
403,645✔
673
            #[cfg(feature = "with-z-axis")]
674
            ('z', Some(val)) => {
11,152✔
675
                coord.z.replace(helpers::to_fixed(val));
11,152✔
676
            }
11,152✔
677
            //
678
            #[cfg(feature = "with-a-axis")]
679
            ('a', Some(val)) => {
1✔
680
                coord.a.replace(helpers::to_fixed(val));
1✔
681
            }
1✔
682
            #[cfg(feature = "with-b-axis")]
683
            ('b', Some(val)) => {
1✔
684
                coord.b.replace(helpers::to_fixed(val));
1✔
685
            }
1✔
686
            #[cfg(feature = "with-c-axis")]
687
            ('c', Some(val)) => {
1✔
688
                coord.c.replace(helpers::to_fixed(val));
1✔
689
            }
1✔
690
            //
691
            #[cfg(feature = "with-i-axis")]
692
            ('i', Some(val)) => {
1✔
693
                coord.i.replace(helpers::to_fixed(val));
1✔
694
            }
1✔
695
            #[cfg(feature = "with-j-axis")]
696
            ('j', Some(val)) => {
1✔
697
                coord.j.replace(helpers::to_fixed(val));
1✔
698
            }
1✔
699
            #[cfg(feature = "with-k-axis")]
700
            ('k', Some(val)) => {
1✔
701
                coord.k.replace(helpers::to_fixed(val));
1✔
702
            }
1✔
703
            //
704
            #[cfg(feature = "with-u-axis")]
705
            ('u', Some(val)) => {
1✔
706
                coord.u.replace(helpers::to_fixed(val));
1✔
707
            }
1✔
708
            #[cfg(feature = "with-v-axis")]
709
            ('v', Some(val)) => {
1✔
710
                coord.v.replace(helpers::to_fixed(val));
1✔
711
            }
1✔
712
            #[cfg(feature = "with-w-axis")]
713
            ('w', Some(val)) => {
1✔
714
                coord.w.replace(helpers::to_fixed(val));
1✔
715
            }
1✔
716
            _ => {}
133,818✔
717
        },
718
        #[cfg(feature = "with-motion")]
719
        GCodeValue::G4(param) => match (ch, frx) {
6✔
720
            ('s', Some(val)) => {
6✔
721
                param.s.replace(helpers::to_fixed(val).abs());
6✔
722
            }
6✔
723
            _ => {}
×
724
        },
725
        #[cfg(feature = "with-motion")]
726
        GCodeValue::G28(coord) => match (ch, frx) {
39✔
727
            #[cfg(feature = "with-e-axis")]
728
            ('e', Some(val)) => {
2✔
729
                coord.e.replace(helpers::to_fixed(val));
2✔
730
            }
2✔
731
            //
732
            #[cfg(feature = "with-x-axis")]
733
            ('x', Some(val)) => {
6✔
734
                coord.x.replace(helpers::to_fixed(val));
6✔
735
            }
6✔
736
            #[cfg(feature = "with-y-axis")]
737
            ('y', Some(val)) => {
6✔
738
                coord.y.replace(helpers::to_fixed(val));
6✔
739
            }
6✔
740
            #[cfg(feature = "with-z-axis")]
741
            ('z', Some(val)) => {
6✔
742
                coord.z.replace(helpers::to_fixed(val));
6✔
743
            }
6✔
744
            //
745
            #[cfg(feature = "with-a-axis")]
746
            ('a', Some(val)) => {
×
747
                coord.a.replace(helpers::to_fixed(val));
×
748
            }
×
749
            #[cfg(feature = "with-b-axis")]
750
            ('b', Some(val)) => {
×
751
                coord.b.replace(helpers::to_fixed(val));
×
752
            }
×
753
            #[cfg(feature = "with-c-axis")]
754
            ('c', Some(val)) => {
×
755
                coord.c.replace(helpers::to_fixed(val));
×
756
            }
×
757
            #[cfg(feature = "with-i-axis")]
758
            ('i', Some(val)) => {
×
759
                coord.i.replace(helpers::to_fixed(val));
×
760
            }
×
761
            #[cfg(feature = "with-j-axis")]
762
            ('j', Some(val)) => {
×
763
                coord.j.replace(helpers::to_fixed(val));
×
764
            }
×
765
            #[cfg(feature = "with-k-axis")]
766
            ('k', Some(val)) => {
×
767
                coord.k.replace(helpers::to_fixed(val));
×
768
            }
×
769
            #[cfg(feature = "with-u-axis")]
770
            ('u', Some(val)) => {
×
771
                coord.u.replace(helpers::to_fixed(val));
×
772
            }
×
773
            #[cfg(feature = "with-v-axis")]
774
            ('v', Some(val)) => {
×
775
                coord.v.replace(helpers::to_fixed(val));
×
776
            }
×
777
            #[cfg(feature = "with-w-axis")]
778
            ('w', Some(val)) => {
×
779
                coord.w.replace(helpers::to_fixed(val));
×
780
            }
×
781
            _ => {}
19✔
782
        },
783
        #[cfg(feature = "with-motion")]
784
        GCodeValue::G92(coord) => match (ch, frx) {
810✔
785
            #[cfg(feature = "with-e-axis")]
786
            ('e', Some(val)) => {
486✔
787
                coord.e.replace(helpers::to_fixed(val));
486✔
788
            }
486✔
789
            //
790
            #[cfg(feature = "with-x-axis")]
791
            ('x', Some(val)) => {
6✔
792
                coord.x.replace(helpers::to_fixed(val));
6✔
793
            }
6✔
794
            #[cfg(feature = "with-y-axis")]
795
            ('y', Some(val)) => {
6✔
796
                coord.y.replace(helpers::to_fixed(val));
6✔
797
            }
6✔
798
            #[cfg(feature = "with-z-axis")]
799
            ('z', Some(val)) => {
6✔
800
                coord.z.replace(helpers::to_fixed(val));
6✔
801
            }
6✔
802
            //
803
            #[cfg(feature = "with-a-axis")]
804
            ('a', Some(val)) => {
1✔
805
                coord.a.replace(helpers::to_fixed(val));
1✔
806
            }
1✔
807
            #[cfg(feature = "with-b-axis")]
808
            ('b', Some(val)) => {
1✔
809
                coord.b.replace(helpers::to_fixed(val));
1✔
810
            }
1✔
811
            #[cfg(feature = "with-c-axis")]
812
            ('c', Some(val)) => {
1✔
813
                coord.c.replace(helpers::to_fixed(val));
1✔
814
            }
1✔
815
            //
816
            #[cfg(feature = "with-i-axis")]
817
            ('i', Some(val)) => {
1✔
818
                coord.i.replace(helpers::to_fixed(val));
1✔
819
            }
1✔
820
            #[cfg(feature = "with-j-axis")]
821
            ('j', Some(val)) => {
1✔
822
                coord.j.replace(helpers::to_fixed(val));
1✔
823
            }
1✔
824
            #[cfg(feature = "with-k-axis")]
825
            ('k', Some(val)) => {
1✔
826
                coord.k.replace(helpers::to_fixed(val));
1✔
827
            }
1✔
828
            //
829
            #[cfg(feature = "with-u-axis")]
830
            ('u', Some(val)) => {
1✔
831
                coord.u.replace(helpers::to_fixed(val));
1✔
832
            }
1✔
833
            #[cfg(feature = "with-v-axis")]
834
            ('v', Some(val)) => {
1✔
835
                coord.v.replace(helpers::to_fixed(val));
1✔
836
            }
1✔
837
            #[cfg(feature = "with-w-axis")]
838
            ('w', Some(val)) => {
1✔
839
                coord.w.replace(helpers::to_fixed(val));
1✔
840
            }
1✔
841
            _ => {}
297✔
842
        },
843
        #[cfg(feature = "with-sd-card")]
844
        GCodeValue::M20(path) => {
6✔
845
            if ch == 'f' {
6✔
846
                if let async_gcode::RealValue::Literal(async_gcode::Literal::String(mstr)) = fv {
6✔
847
                    path.replace(mstr);
6✔
848
                }
6✔
849
            }
×
850
        }
851
        #[cfg(all(feature = "with-sd-card", feature = "with-print-job"))]
852
        GCodeValue::M23(file) => {
18✔
853
            if let async_gcode::RealValue::Literal(async_gcode::Literal::String(mstr)) = fv {
18✔
854
                file.replace(mstr);
18✔
855
            }
18✔
856
        }
857
        GCodeValue::M37(coord) => match (ch, frx) {
6✔
858
            ('s', Some(val)) => {
6✔
859
                coord.s.replace(helpers::to_fixed(val));
6✔
860
            }
6✔
861
            _ => {}
×
862
        },
863
        #[cfg(feature = "with-hot-end")]
864
        GCodeValue::M104(coord) => match (ch, frx) {
6✔
865
            ('s', Some(val)) => {
6✔
866
                coord.s.replace(helpers::to_fixed(val));
6✔
867
            }
6✔
868
            _ => {}
×
869
        },
870
        #[cfg(feature = "with-hot-end")]
871
        GCodeValue::M109(coord) => match (ch, frx) {
2✔
872
            ('s', Some(val)) => {
2✔
873
                coord.s.replace(helpers::to_fixed(val));
2✔
874
            }
2✔
875
            _ => {}
×
876
        },
877
        GCodeValue::M117(msg) | GCodeValue::M118(msg) => {
6✔
878
            if let async_gcode::RealValue::Literal(async_gcode::Literal::String(text)) = fv {
12✔
879
                msg.replace(text);
12✔
880
            }
12✔
881
        }
882
        #[cfg(feature = "with-hot-bed")]
883
        GCodeValue::M140(coord) => match (ch, frx) {
4✔
884
            ('s', Some(val)) => {
4✔
885
                coord.s.replace(helpers::to_fixed(val));
4✔
886
            }
4✔
887
            _ => {}
×
888
        },
889
        GCodeValue::M220(coord) => match (ch, frx) {
6✔
890
            ('s', Some(val)) => {
6✔
891
                coord.s.replace(helpers::to_fixed(val));
6✔
892
            }
6✔
893
            _ => {}
×
894
        },
895
        GCodeValue::M110(coord) => match (ch, frx) {
6✔
896
            ('n', Some(val)) => {
6✔
897
                coord.n.replace(helpers::to_fixed(val));
6✔
898
            }
6✔
899
            _ => {}
×
900
        },
901
        GCodeValue::M503(param) => match (ch, frx) {
×
902
            ('s', Some(val)) => {
×
903
                param.s.replace(helpers::to_fixed(val));
×
904
            }
×
905
            _ => {}
×
906
        },
907
        _ => {}
12,502✔
908
    }
909
}
1,286,381✔
910

911
#[cfg(feature = "native")]
912
#[cfg(test)]
913
mod tests {
914
    use super::*;
915
    use async_gcode::{AsyncParserState, ByteStream};
916
    use std::collections::VecDeque;
917

918
    struct BufferStream {
919
        buff: VecDeque<u8>,
920
    }
921

922
    impl BufferStream {
923
        const fn new(buff: VecDeque<u8>) -> Self {
204✔
924
            Self { buff }
204✔
925
        }
204✔
926
    }
927

928
    impl async_gcode::ByteStream for BufferStream {
929
        type Item = Result<u8, async_gcode::Error>;
930

931
        async fn next(&mut self) -> Option<Self::Item> {
2,526✔
932
            match self.buff.pop_front() {
2,526✔
933
                None => None,
78✔
934
                Some(_b) => Some(Ok(_b)),
2,448✔
935
            }
936
        }
2,526✔
937

938
        async fn recovery_check(&mut self) {}
18✔
939
    }
940

941
    async fn check_m32_benchy_result(parser: &mut GCodeLineParser<BufferStream>) {
18✔
942
        match parser.next_gcode(CommChannel::Internal).await {
18✔
943
            Ok(_cmd) => match _cmd.value {
18✔
944
                GCodeValue::M23(Some(_path)) => {
18✔
945
                    assert!(_path.eq("BENCHY.G"), "Got the proper string param")
18✔
946
                }
947
                _ => assert!(false, "Unexpected code value"),
×
948
            },
949
            Err(_e) => {
×
950
                assert!(false, "Got an error");
×
951
            }
952
        }
953
    }
18✔
954

955
    async fn check_display_m117_result(parser: &mut GCodeLineParser<BufferStream>) {
6✔
956
        match parser.next_gcode(CommChannel::Internal).await {
6✔
957
            Ok(_cmd) => match _cmd.value {
6✔
958
                GCodeValue::M117(Some(_msg)) => {
6✔
959
                    assert!(_msg.eq("Hello World!"), "Got the proper string param")
6✔
960
                }
961
                _ => assert!(false, "Unexpected code value"),
×
962
            },
963
            Err(_e) => {
×
964
                assert!(false, "Got an error");
×
965
            }
966
        }
967
    }
6✔
968
    async fn check_display_m118_result(parser: &mut GCodeLineParser<BufferStream>) {
6✔
969
        match parser.next_gcode(CommChannel::Internal).await {
6✔
970
            Ok(_cmd) => match _cmd.value {
6✔
971
                GCodeValue::M118(Some(_msg)) => {
6✔
972
                    assert!(_msg.eq("Hello World!"), "Got the proper string param")
6✔
973
                }
974
                _ => assert!(false, "Unexpected code value"),
×
975
            },
976
            Err(_e) => {
×
977
                assert!(false, "Got an error");
×
978
            }
979
        }
980
    }
6✔
981
    #[futures_test::test]
982
    async fn test_m23_with_newline() {
6✔
983
        let stream = BufferStream::new("M23 BENCHY.G\n".as_bytes().to_vec().into());
6✔
984
        let mut parser = GCodeLineParser::new(stream);
6✔
985
        check_m32_benchy_result(&mut parser).await;
6✔
986
    }
12✔
987

988
    #[futures_test::test]
989
    async fn test_m23_with_eof_condition() {
6✔
990
        let stream = BufferStream::new("M23 BENCHY.G".as_bytes().to_vec().into());
6✔
991
        let mut parser = GCodeLineParser::new(stream);
6✔
992
        check_m32_benchy_result(&mut parser).await;
6✔
993
    }
12✔
994

995
    #[futures_test::test]
996
    async fn test_m23_with_trailin_comments() {
6✔
997
        let stream = BufferStream::new("M23 BENCHY.G".as_bytes().to_vec().into());
6✔
998
        let mut parser = GCodeLineParser::new(stream);
6✔
999
        check_m32_benchy_result(&mut parser).await;
6✔
1000
    }
12✔
1001

1002
    #[futures_test::test]
1003
    async fn test_m117() {
6✔
1004
        let stream = BufferStream::new("M117 Hello World!".as_bytes().to_vec().into());
6✔
1005
        let mut parser = GCodeLineParser::new(stream);
6✔
1006
        check_display_m117_result(&mut parser).await;
6✔
1007
    }
12✔
1008

1009
    #[futures_test::test]
1010
    async fn test_m118() {
6✔
1011
        let stream = BufferStream::new("M118 Hello World!".as_bytes().to_vec().into());
6✔
1012
        let mut parser = GCodeLineParser::new(stream);
6✔
1013
        check_display_m118_result(&mut parser).await;
6✔
1014
    }
12✔
1015

1016
    #[futures_test::test]
1017
    async fn test_gs() {
6✔
1018
        // TODO: Check EOF condition
6✔
1019
        let stream = BufferStream::new(
6✔
1020
            "G0 F0 E0 X0 Y0 Z0 A0 B0 C0 I0 J0 K0 U0 V0 W0\n"
6✔
1021
                .as_bytes()
6✔
1022
                .to_vec()
6✔
1023
                .into(),
6✔
1024
        );
6✔
1025
        let mut parser = GCodeLineParser::new(stream);
6✔
1026
        let _ = parser.gcode_line();
6✔
1027
        parser
6✔
1028
            .next_gcode(CommChannel::Internal)
6✔
1029
            .await
6✔
1030
            .expect("G0 OK");
6✔
1031

6✔
1032
        let stream = BufferStream::new(
6✔
1033
            "G1 F0 E0 X0 Y0 Z0 A0 B0 C0 I0 J0 K0 U0 V0 W0\n"
6✔
1034
                .as_bytes()
6✔
1035
                .to_vec()
6✔
1036
                .into(),
6✔
1037
        );
6✔
1038
        let mut parser = GCodeLineParser::new(stream);
6✔
1039
        let _ = parser.gcode_line();
6✔
1040
        parser
6✔
1041
            .next_gcode(CommChannel::Internal)
6✔
1042
            .await
6✔
1043
            .expect("G1 OK");
6✔
1044

6✔
1045
        let stream = BufferStream::new(
6✔
1046
            "G92 F0 E0 X0 Y0 Z0 A0 B0 C0 I0 J0 K0 U0 V0 W0\n"
6✔
1047
                .as_bytes()
6✔
1048
                .to_vec()
6✔
1049
                .into(),
6✔
1050
        );
6✔
1051
        let mut parser = GCodeLineParser::new(stream);
6✔
1052
        let _ = parser.gcode_line();
6✔
1053
        parser
6✔
1054
            .next_gcode(CommChannel::Internal)
6✔
1055
            .await
6✔
1056
            .expect("G1 OK");
6✔
1057

6✔
1058
        let stream = BufferStream::new("G4\n".as_bytes().to_vec().into());
6✔
1059
        let mut parser = GCodeLineParser::new(stream);
6✔
1060
        let _ = parser.gcode_line();
6✔
1061
        parser
6✔
1062
            .next_gcode(CommChannel::Internal)
6✔
1063
            .await
6✔
1064
            .expect("G4 OK");
6✔
1065

6✔
1066
        let stream = BufferStream::new("G4 S10\n".as_bytes().to_vec().into());
6✔
1067
        let mut parser = GCodeLineParser::new(stream);
6✔
1068
        let _ = parser.gcode_line();
6✔
1069
        parser
6✔
1070
            .next_gcode(CommChannel::Internal)
6✔
1071
            .await
6✔
1072
            .expect("G4 S10 OK");
6✔
1073

6✔
1074
        let stream = BufferStream::new("N10 G28 F0 E0 X0 Y0 Z0\n".as_bytes().to_vec().into());
6✔
1075
        let mut parser = GCodeLineParser::new(stream);
6✔
1076
        let _ = parser.gcode_line();
6✔
1077
        parser
6✔
1078
            .next_gcode(CommChannel::Internal)
6✔
1079
            .await
6✔
1080
            .expect("G28 OK");
6✔
1081

6✔
1082
        parser.reset_current_line();
6✔
1083

6✔
1084
        let stream = BufferStream::new("N10 G29 F0 E0 X0 Y0 Z0\n".as_bytes().to_vec().into());
6✔
1085
        let mut parser = GCodeLineParser::new(stream);
6✔
1086
        let _ = parser.gcode_line();
6✔
1087
        parser
6✔
1088
            .next_gcode(CommChannel::Internal)
6✔
1089
            .await
6✔
1090
            .expect("G29 OK");
6✔
1091

6✔
1092
        let stream = BufferStream::new("N11 G29.1 F0 E0 X0 Y0 Z0\n".as_bytes().to_vec().into());
6✔
1093
        let mut parser = GCodeLineParser::new(stream);
6✔
1094
        let _ = parser.gcode_line();
6✔
1095
        parser
6✔
1096
            .next_gcode(CommChannel::Internal)
6✔
1097
            .await
6✔
1098
            .expect("G29.1 OK");
6✔
1099

6✔
1100
        let stream = BufferStream::new("G29.2 F0 E0 X0 Y0 Z0\n".as_bytes().to_vec().into());
6✔
1101
        let mut parser = GCodeLineParser::new(stream);
6✔
1102
        let _ = parser.gcode_line();
6✔
1103
        parser
6✔
1104
            .next_gcode(CommChannel::Internal)
6✔
1105
            .await
6✔
1106
            .expect("G29.2 OK");
6✔
1107

6✔
1108
        let stream = BufferStream::new("G31\n".as_bytes().to_vec().into());
6✔
1109
        let mut parser = GCodeLineParser::new(stream);
6✔
1110
        let _ = parser.gcode_line();
6✔
1111
        parser
6✔
1112
            .next_gcode(CommChannel::Internal)
6✔
1113
            .await
6✔
1114
            .expect("G31 OK");
6✔
1115
    }
6✔
1116

1117
    #[futures_test::test]
1118
    async fn test_machinery() {
6✔
1119
        let mut stream = BufferStream::new("".as_bytes().to_vec().into());
6✔
1120
        assert_eq!(stream.next().await, None);
6✔
1121
        stream.recovery_check().await;
6✔
1122
        assert_eq!(stream.next().await, None);
6✔
1123

1124
        let spec = RawGCodeSpec::from('Z', None);
6✔
1125

6✔
1126
        let _str = format!("{:?}", spec);
6✔
1127

6✔
1128
        let stream = BufferStream::new("".as_bytes().to_vec().into());
6✔
1129
        let mut parser = GCodeLineParser::new(stream);
6✔
1130
        parser
6✔
1131
            .next_gcode(CommChannel::Internal)
6✔
1132
            .await
6✔
1133
            .expect_err("None");
6✔
1134
        assert_eq!(parser.get_state(), AsyncParserState::Start(true));
6✔
1135
        parser
6✔
1136
            .next_gcode(CommChannel::Internal)
6✔
1137
            .await
6✔
1138
            .expect_err("EOF");
6✔
1139
        assert_eq!(parser.get_state(), AsyncParserState::Start(true));
6✔
1140
        let _ = parser.close().await;
6✔
1141

1142
        let stream = BufferStream::new("/X0\n".as_bytes().to_vec().into());
6✔
1143
        let mut parser = GCodeLineParser::new(stream);
6✔
1144
        parser
6✔
1145
            .next_gcode(CommChannel::Internal)
6✔
1146
            .await
6✔
1147
            .expect_err("None");
6✔
1148

6✔
1149
        let stream = BufferStream::new("&0\n".as_bytes().to_vec().into());
6✔
1150
        let mut parser = GCodeLineParser::new(stream);
6✔
1151
        parser
6✔
1152
            .next_gcode(CommChannel::Internal)
6✔
1153
            .await
6✔
1154
            .expect_err("None");
6✔
1155

6✔
1156
        let stream = BufferStream::new("V0\n".as_bytes().to_vec().into());
6✔
1157
        let mut parser = GCodeLineParser::new(stream);
6✔
1158
        parser
6✔
1159
            .next_gcode(CommChannel::Internal)
6✔
1160
            .await
6✔
1161
            .expect_err("None");
6✔
1162
    }
6✔
1163

1164
    #[futures_test::test]
1165
    async fn test_ms() {
6✔
1166
        let stream = BufferStream::new("M20 F\"X.g\"\n".as_bytes().to_vec().into());
6✔
1167
        let mut parser = GCodeLineParser::new(stream);
6✔
1168
        let _ = parser.gcode_line();
6✔
1169
        parser
6✔
1170
            .next_gcode(CommChannel::Internal)
6✔
1171
            .await
6✔
1172
            .expect("M20 OK");
6✔
1173

6✔
1174
        let stream = BufferStream::new("M37\n".as_bytes().to_vec().into());
6✔
1175
        let mut parser = GCodeLineParser::new(stream);
6✔
1176
        let _ = parser.gcode_line();
6✔
1177
        parser
6✔
1178
            .next_gcode(CommChannel::Internal)
6✔
1179
            .await
6✔
1180
            .expect("M37 OK");
6✔
1181

6✔
1182
        let stream = BufferStream::new("M37 S1\n".as_bytes().to_vec().into());
6✔
1183
        let mut parser = GCodeLineParser::new(stream);
6✔
1184
        let _ = parser.gcode_line();
6✔
1185
        parser
6✔
1186
            .next_gcode(CommChannel::Internal)
6✔
1187
            .await
6✔
1188
            .expect("M37 S1 OK");
6✔
1189

6✔
1190
        #[cfg(feature = "with-hot-bed")]
6✔
1191
        {
6✔
1192
            let stream = BufferStream::new("M140\n".as_bytes().to_vec().into());
6✔
1193
            let mut parser = GCodeLineParser::new(stream);
6✔
1194
            let _ = parser.gcode_line();
6✔
1195
            parser
6✔
1196
                .next_gcode(CommChannel::Internal)
6✔
1197
                .await
6✔
1198
                .expect("M140 OK");
6✔
1199
        }
6✔
1200

6✔
1201
        #[cfg(feature = "with-hot-end")]
6✔
1202
        {
6✔
1203
            let stream = BufferStream::new("M104\n".as_bytes().to_vec().into());
6✔
1204
            let mut parser = GCodeLineParser::new(stream);
6✔
1205
            let _ = parser.gcode_line();
6✔
1206
            parser
6✔
1207
                .next_gcode(CommChannel::Internal)
6✔
1208
                .await
6✔
1209
                .expect("M104 OK");
6✔
1210

6✔
1211
            let stream = BufferStream::new("M109\n".as_bytes().to_vec().into());
6✔
1212
            let mut parser = GCodeLineParser::new(stream);
6✔
1213
            let _ = parser.gcode_line();
6✔
1214
            parser
6✔
1215
                .next_gcode(CommChannel::Internal)
6✔
1216
                .await
6✔
1217
                .expect("M109 OK");
6✔
1218
        }
6✔
1219

6✔
1220
        let stream = BufferStream::new("M220\n".as_bytes().to_vec().into());
6✔
1221
        let mut parser = GCodeLineParser::new(stream);
6✔
1222
        let _ = parser.gcode_line();
6✔
1223
        parser
6✔
1224
            .next_gcode(CommChannel::Internal)
6✔
1225
            .await
6✔
1226
            .expect("M220 OK");
6✔
1227

6✔
1228
        let stream = BufferStream::new("M220 S1\n".as_bytes().to_vec().into());
6✔
1229
        let mut parser = GCodeLineParser::new(stream);
6✔
1230
        let _ = parser.gcode_line();
6✔
1231
        parser
6✔
1232
            .next_gcode(CommChannel::Internal)
6✔
1233
            .await
6✔
1234
            .expect("M220 S1 OK");
6✔
1235

6✔
1236
        let stream = BufferStream::new("M110\n".as_bytes().to_vec().into());
6✔
1237
        let mut parser = GCodeLineParser::new(stream);
6✔
1238
        let _ = parser.gcode_line();
6✔
1239
        parser
6✔
1240
            .next_gcode(CommChannel::Internal)
6✔
1241
            .await
6✔
1242
            .expect("M110 OK");
6✔
1243

6✔
1244
        parser.reset_current_line();
6✔
1245
        parser.reset().await;
6✔
1246

1247
        let stream = BufferStream::new("M110 N1\n".as_bytes().to_vec().into());
6✔
1248
        let mut parser = GCodeLineParser::new(stream);
6✔
1249
        let _ = parser.gcode_line();
6✔
1250
        parser
6✔
1251
            .next_gcode(CommChannel::Internal)
6✔
1252
            .await
6✔
1253
            .expect("M110 N1 OK");
6✔
1254

6✔
1255
        let stream = BufferStream::new("M204\n".as_bytes().to_vec().into());
6✔
1256
        let mut parser = GCodeLineParser::new(stream);
6✔
1257
        let _ = parser.gcode_line();
6✔
1258
        parser
6✔
1259
            .next_gcode(CommChannel::Internal)
6✔
1260
            .await
6✔
1261
            .expect("M204 OK");
6✔
1262

6✔
1263
        let stream = BufferStream::new("M205\n".as_bytes().to_vec().into());
6✔
1264
        let mut parser = GCodeLineParser::new(stream);
6✔
1265
        let _ = parser.gcode_line();
6✔
1266
        parser
6✔
1267
            .next_gcode(CommChannel::Internal)
6✔
1268
            .await
6✔
1269
            .expect("M205 OK");
6✔
1270

6✔
1271
        let stream = BufferStream::new("M862.1\n".as_bytes().to_vec().into());
6✔
1272
        let mut parser = GCodeLineParser::new(stream);
6✔
1273
        let _ = parser.gcode_line();
6✔
1274
        parser
6✔
1275
            .next_gcode(CommChannel::Internal)
6✔
1276
            .await
6✔
1277
            .expect("M862.1 OK");
6✔
1278

6✔
1279
        let stream = BufferStream::new("M862.3\n".as_bytes().to_vec().into());
6✔
1280
        let mut parser = GCodeLineParser::new(stream);
6✔
1281
        let _ = parser.gcode_line();
6✔
1282
        parser
6✔
1283
            .next_gcode(CommChannel::Internal)
6✔
1284
            .await
6✔
1285
            .expect("M862.3 OK");
6✔
1286

6✔
1287
        let stream = BufferStream::new("M900\n".as_bytes().to_vec().into());
6✔
1288
        let mut parser = GCodeLineParser::new(stream);
6✔
1289
        let _ = parser.gcode_line();
6✔
1290
        parser
6✔
1291
            .next_gcode(CommChannel::Internal)
6✔
1292
            .await
6✔
1293
            .expect("M900 OK");
6✔
1294

6✔
1295
        let stream = BufferStream::new("M907\n".as_bytes().to_vec().into());
6✔
1296
        let mut parser = GCodeLineParser::new(stream);
6✔
1297
        let _ = parser.gcode_line();
6✔
1298
        parser
6✔
1299
            .next_gcode(CommChannel::Internal)
6✔
1300
            .await
6✔
1301
            .expect("M907 OK");
6✔
1302
    }
6✔
1303
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc