Skip to main content

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