1use crate::{
2 buffers::{Buffer, CapacityError, DefaultArguments},
3 Span, Word,
4};
5use core::fmt::{self, Debug, Display, Formatter};
6
7#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
9#[cfg_attr(
10 feature = "serde-1",
11 derive(serde_derive::Serialize, serde_derive::Deserialize)
12)]
13#[repr(C)]
14pub enum Mnemonic {
15 General,
18 Miscellaneous,
20 ProgramNumber,
22 ToolChange,
24}
25
26impl Mnemonic {
27 pub fn for_letter(letter: char) -> Option<Mnemonic> {
37 match letter.to_ascii_lowercase() {
38 'g' => Some(Mnemonic::General),
39 'm' => Some(Mnemonic::Miscellaneous),
40 'o' => Some(Mnemonic::ProgramNumber),
41 't' => Some(Mnemonic::ToolChange),
42 _ => None,
43 }
44 }
45}
46
47impl Display for Mnemonic {
48 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
49 match self {
50 Mnemonic::General => write!(f, "G"),
51 Mnemonic::Miscellaneous => write!(f, "M"),
52 Mnemonic::ProgramNumber => write!(f, "O"),
53 Mnemonic::ToolChange => write!(f, "T"),
54 }
55 }
56}
57
58#[derive(Clone)]
61#[cfg_attr(
62 feature = "serde-1",
63 derive(serde_derive::Serialize, serde_derive::Deserialize)
64)]
65pub struct GCode<A = DefaultArguments> {
66 mnemonic: Mnemonic,
67 number: f32,
68 arguments: A,
69 span: Span,
70}
71
72impl GCode {
73 pub fn new(mnemonic: Mnemonic, number: f32, span: Span) -> Self {
75 GCode {
76 mnemonic,
77 number,
78 span,
79 arguments: DefaultArguments::default(),
80 }
81 }
82}
83
84impl<A: Buffer<Word>> GCode<A> {
85 pub fn new_with_argument_buffer(
87 mnemonic: Mnemonic,
88 number: f32,
89 span: Span,
90 arguments: A,
91 ) -> Self {
92 GCode {
93 mnemonic,
94 number,
95 span,
96 arguments,
97 }
98 }
99
100 pub fn mnemonic(&self) -> Mnemonic { self.mnemonic }
102
103 pub fn major_number(&self) -> u32 {
105 debug_assert!(self.number >= 0.0);
106
107 libm::floorf(self.number) as u32
108 }
109
110 pub fn minor_number(&self) -> u32 {
112 let fract = self.number - libm::floorf(self.number);
113 let digit = libm::roundf(fract * 10.0);
114 digit as u32
115 }
116
117 pub fn arguments(&self) -> &[Word] { self.arguments.as_slice() }
119
120 pub fn span(&self) -> Span { self.span }
122
123 pub fn push_argument(
125 &mut self,
126 arg: Word,
127 ) -> Result<(), CapacityError<Word>> {
128 self.span = self.span.merge(arg.span);
129 self.arguments.try_push(arg)
130 }
131
132 pub fn with_argument(mut self, arg: Word) -> Self {
139 if let Err(e) = self.push_argument(arg) {
140 panic!("Unable to add the argument {:?}: {}", arg, e);
141 }
142 self
143 }
144
145 pub fn value_for(&self, letter: char) -> Option<f32> {
158 let letter = letter.to_ascii_lowercase();
159
160 for arg in self.arguments() {
161 if arg.letter.to_ascii_lowercase() == letter {
162 return Some(arg.value);
163 }
164 }
165
166 None
167 }
168}
169
170impl<A: Buffer<Word>> Extend<Word> for GCode<A> {
171 fn extend<I: IntoIterator<Item = Word>>(&mut self, words: I) {
172 for word in words {
173 if self.push_argument(word).is_err() {
174 return;
176 }
177 }
178 }
179}
180
181impl<A: Buffer<Word>> Debug for GCode<A> {
182 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
183 let GCode {
187 mnemonic,
188 number,
189 arguments,
190 span,
191 } = self;
192
193 f.debug_struct("GCode")
194 .field("mnemonic", mnemonic)
195 .field("number", number)
196 .field("arguments", &crate::buffers::debug(arguments))
197 .field("span", span)
198 .finish()
199 }
200}
201
202impl<A: Buffer<Word>> Display for GCode<A> {
203 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
204 write!(f, "{}{}", self.mnemonic(), self.major_number())?;
205
206 if self.minor_number() != 0 {
207 write!(f, ".{}", self.minor_number())?;
208 }
209
210 for arg in self.arguments() {
211 write!(f, " {}", arg)?;
212 }
213
214 Ok(())
215 }
216}
217
218impl<A, B> PartialEq<GCode<B>> for GCode<A>
219where
220 A: Buffer<Word>,
221 B: Buffer<Word>,
222{
223 fn eq(&self, other: &GCode<B>) -> bool {
224 let GCode {
225 mnemonic,
226 number,
227 arguments,
228 span,
229 } = self;
230
231 *span == other.span()
232 && *mnemonic == other.mnemonic
233 && *number == other.number
234 && arguments.as_slice() == other.arguments.as_slice()
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241 use arrayvec::ArrayVec;
242 use std::prelude::v1::*;
243
244 type BigBuffer = ArrayVec<[Word; 32]>;
245
246 #[test]
247 fn correct_major_number() {
248 let code = GCode {
249 mnemonic: Mnemonic::General,
250 number: 90.5,
251 arguments: BigBuffer::default(),
252 span: Span::default(),
253 };
254
255 assert_eq!(code.major_number(), 90);
256 }
257
258 #[test]
259 fn correct_minor_number() {
260 for i in 0..=9 {
261 let code = GCode {
262 mnemonic: Mnemonic::General,
263 number: 10.0 + (i as f32) / 10.0,
264 arguments: BigBuffer::default(),
265 span: Span::default(),
266 };
267
268 assert_eq!(code.minor_number(), i);
269 }
270 }
271
272 #[test]
273 fn get_argument_values() {
274 let mut code = GCode::new_with_argument_buffer(
275 Mnemonic::General,
276 90.0,
277 Span::default(),
278 BigBuffer::default(),
279 );
280 code.push_argument(Word {
281 letter: 'X',
282 value: 10.0,
283 span: Span::default(),
284 })
285 .unwrap();
286 code.push_argument(Word {
287 letter: 'y',
288 value: -3.5,
289 span: Span::default(),
290 })
291 .unwrap();
292
293 assert_eq!(code.value_for('X'), Some(10.0));
294 assert_eq!(code.value_for('x'), Some(10.0));
295 assert_eq!(code.value_for('Y'), Some(-3.5));
296 assert_eq!(code.value_for('Z'), None);
297 }
298}