1extern crate nom;
2
3use std::fmt;
4use thiserror::Error;
5
6mod mnemonic;
7pub use mnemonic::*;
8
9mod parse_command;
10pub use parse_command::parse_command;
11
12mod parse_args;
13pub use parse_args::parse_args;
14pub use parse_args::parse_kv_arg;
15
16mod parse_comments;
17pub use parse_comments::*;
18
19mod parse_gcode;
20pub use parse_gcode::parse_gcode;
21
22#[derive(Error, Debug)]
23pub enum GCodeParseError {
24 #[error("Invalid GCode. GCodes must start with a letter, a number and a space. Got: {0}")]
25 InvalidGCode(String),
26 #[error("Badly formatted GCode arguments. Got: {0}")]
27 InvalidArguments(String),
28 #[error("Badly formatted GCode comment. Got: {0}")]
29 InvalidComment(String),
30}
31
32#[derive(Debug, PartialEq, Clone)]
33pub struct Comment<'r>(
34 pub &'r str
35);
36
37#[derive(Debug, PartialEq, Clone)]
38pub enum DocComment<'r> {
39 GCodeFlavor(&'r str),
40 PrintTime(std::time::Duration),
41 FilamentUsed { meters: f64 },
42 LayerHeight { millis: f64 },
43}
44
45#[derive(Debug, PartialEq, Clone)]
46pub enum GCodeLine<'r> {
47 FileDemarcator,
53 GCode(GCode<'r>),
54 Comment(Comment<'r>),
55 DocComment(DocComment<'r>),
56}
57
58impl<'r> From<Comment<'r>> for GCodeLine<'r> {
59 fn from(comment: Comment<'r>) -> Self {
60 GCodeLine::Comment(comment)
61 }
62}
63
64#[derive(Debug, PartialEq, Clone)]
65pub struct GCode<'r> {
66 pub line_number: Option<u32>,
67 pub mnemonic: Mnemonic,
68 pub major: u32,
69 pub minor: u32,
70 args_or_comments: Option<Vec<ArgOrComment<'r>>>,
71}
72
73#[derive(Debug, PartialEq, Clone)]
74pub enum ArgOrComment<'r> {
75 KeyValue(KeyValue),
76 TextArg(&'r str),
77 Comment(Comment<'r>),
78}
79
80pub type KeyValue = (char, Option<f32>);
81
82impl<'r> fmt::Display for GCode<'r> {
83 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84 let gcode = format!("{}{}.{}", self.mnemonic, self.major, self.minor);
85
86 let mut words = vec![gcode];
87
88 let arg_words = self.arguments()
89 .map(|(k, v)| {
90 format!("{}{}", k, v.map(|f| f.to_string()).unwrap_or("".to_string()))
91 });
92
93 words.extend(arg_words);
94
95 if let Some(text) = self.text() {
96 words.push(text.to_string());
97 };
98
99 write!(f, "{}", words.join(" "))
100 }
101}
102
103impl<'r> GCode<'r> {
104 fn args_or_comments_iter(&self) -> impl Iterator<Item = &ArgOrComment<'r>> {
106 use std::convert::identity;
107
108 self.args_or_comments
109 .iter()
110 .flat_map(identity)
111 }
112
113 pub fn text(&self) -> Option<&'r str> {
115 self.args_or_comments_iter()
116 .find_map(|ac| {
117 if let ArgOrComment::TextArg(text) = ac {
118 Some(*text)
119 } else {
120 None
121 }
122 })
123 }
124
125 pub fn arguments(&self) -> impl Iterator<Item = &KeyValue> {
127 self.args_or_comments_iter()
128 .filter_map(|ac| {
129 if let ArgOrComment::KeyValue(arg) = ac {
130 Some(arg)
131 } else {
132 None
133 }
134 })
135 }
136}