g_code/
lib.rs

1/// g-code emitter with a few basic commands and argument-checking
2pub mod emit;
3/// g-code parser written with [peg]
4pub mod parse;
5
6#[cfg(test)]
7mod test {
8    use crate::{emit::FormatOptions, parse::into_diagnostic};
9    use pretty_assertions::assert_eq;
10
11    #[test]
12    #[cfg(feature = "codespan_helpers")]
13    fn parsing_gcode_then_emitting_then_parsing_again_returns_functionally_identical_gcode() {
14        use codespan_reporting::diagnostic::{Diagnostic, Label};
15        use codespan_reporting::term::{
16            emit,
17            termcolor::{ColorChoice, StandardStream},
18        };
19
20        let mut writer = StandardStream::stderr(ColorChoice::Auto);
21        let config = codespan_reporting::term::Config::default();
22
23        for str in [
24            include_str!("../tests/square.gcode"),
25            include_str!("../tests/edge_cases.gcode"),
26            include_str!("../tests/vandy_commodores_logo.gcode"),
27            include_str!("../tests/ncviewer_sample.gcode"),
28        ] {
29            let parsed_file = crate::parse::file_parser(str).unwrap();
30            let emission_tokens = parsed_file.iter_emit_tokens().collect::<Vec<_>>();
31
32            for format_options in [true, false]
33                .iter()
34                .copied()
35                .map(|a| [[a, true], [a, false]])
36                .flatten()
37                .map(|[a, b]| [[a, b, true], [a, b, false]])
38                .flatten()
39                .map(|[a, b, c]| [[a, b, c, true], [a, b, c, false]])
40                .flatten()
41                .map(
42                    |[checksums, line_numbers, delimit_with_percent, newline_before_comment]| {
43                        FormatOptions {
44                            checksums,
45                            line_numbers,
46                            delimit_with_percent,
47                            newline_before_comment,
48                        }
49                    },
50                )
51            {
52                let mut emitted_gcode = String::new();
53                crate::emit::format_gcode_fmt(
54                    emission_tokens.iter(),
55                    format_options,
56                    &mut emitted_gcode,
57                )
58                .unwrap();
59
60                let reparsed_file = match super::parse::file_parser(&emitted_gcode) {
61                    Ok(reparsed) => reparsed,
62                    Err(err) => {
63                        emit(
64                            &mut writer,
65                            &config,
66                            &codespan_reporting::files::SimpleFile::new(
67                                "test_input.gcode",
68                                emitted_gcode,
69                            ),
70                            &into_diagnostic(&err),
71                        )
72                        .unwrap();
73                        panic!("{}", err);
74                    }
75                };
76
77                for line in reparsed_file.iter() {
78                    let validated_checksum = line.validate_checksum();
79                    assert!(
80                        validated_checksum.clone().transpose().is_ok(),
81                        "got {:?} for {:#?}",
82                        validated_checksum,
83                        line
84                    );
85                }
86
87                parsed_file
88                    .iter_fields()
89                    .filter(|field| field.letters != "N")
90                    .zip(
91                        reparsed_file
92                            .iter_fields()
93                            .filter(|field| field.letters != "N"),
94                    )
95                    .for_each(|(expected, actual)| {
96                        if expected.value != actual.value || expected.letters != actual.letters {
97                            emit(
98                                &mut writer,
99                                &config,
100                                &codespan_reporting::files::SimpleFile::new(
101                                    "test_input.gcode",
102                                    str,
103                                ),
104                                &Diagnostic::error()
105                                    .with_message("fields do not match")
106                                    .with_labels(vec![Label::primary((), expected.span)
107                                        .with_message("this one here officer")]),
108                            )
109                            .unwrap();
110                        }
111                        assert_eq!(
112                            expected.letters, actual.letters,
113                            "{:?} vs {:?}",
114                            expected, actual
115                        );
116                        assert_eq!(
117                            expected.value, actual.value,
118                            "{:?} vs {:?}",
119                            expected, actual
120                        );
121                    })
122            }
123        }
124    }
125}