Skip to main content

gcode/
line.rs

1use crate::{
2    buffers::{self, Buffer, Buffers, CapacityError, DefaultBuffers},
3    Comment, GCode, Span, Word,
4};
5use core::fmt::{self, Debug, Formatter};
6
7/// A single line, possibly containing some [`Comment`]s or [`GCode`]s.
8#[derive(Clone, PartialEq)]
9#[cfg_attr(
10    feature = "serde-1",
11    derive(serde_derive::Serialize, serde_derive::Deserialize)
12)]
13pub struct Line<'input, B: Buffers<'input> = DefaultBuffers> {
14    gcodes: B::Commands,
15    comments: B::Comments,
16    line_number: Option<Word>,
17    span: Span,
18}
19
20impl<'input, B> Debug for Line<'input, B>
21where
22    B: Buffers<'input>,
23{
24    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
25        // explicitly implement Debug because the normal derive is too strict
26        let Line {
27            gcodes,
28            comments,
29            line_number,
30            span,
31        } = self;
32
33        f.debug_struct("Line")
34            .field("gcodes", &buffers::debug(gcodes))
35            .field("comments", &buffers::debug(comments))
36            .field("line_number", line_number)
37            .field("span", span)
38            .finish()
39    }
40}
41
42impl<'input, B> Default for Line<'input, B>
43where
44    B: Buffers<'input>,
45    B::Commands: Default,
46    B::Comments: Default,
47{
48    fn default() -> Line<'input, B> {
49        Line {
50            gcodes: B::Commands::default(),
51            comments: B::Comments::default(),
52            line_number: None,
53            span: Span::default(),
54        }
55    }
56}
57
58impl<'input, B: Buffers<'input>> Line<'input, B> {
59    /// All [`GCode`]s in this line.
60    pub fn gcodes(&self) -> &[GCode<B::Arguments>] { self.gcodes.as_slice() }
61
62    /// All [`Comment`]s in this line.
63    pub fn comments(&self) -> &[Comment<'input>] { self.comments.as_slice() }
64
65    /// Try to add another [`GCode`] to the line.
66    pub fn push_gcode(
67        &mut self,
68        gcode: GCode<B::Arguments>,
69    ) -> Result<(), CapacityError<GCode<B::Arguments>>> {
70        // Note: We need to make sure a failed push doesn't change our span
71        let span = self.span.merge(gcode.span());
72        self.gcodes.try_push(gcode)?;
73        self.span = span;
74
75        Ok(())
76    }
77
78    /// Try to add a [`Comment`] to the line.
79    pub fn push_comment(
80        &mut self,
81        comment: Comment<'input>,
82    ) -> Result<(), CapacityError<Comment<'input>>> {
83        let span = self.span.merge(comment.span);
84        self.comments.try_push(comment)?;
85        self.span = span;
86        Ok(())
87    }
88
89    /// Does the [`Line`] contain anything at all?
90    pub fn is_empty(&self) -> bool {
91        self.gcodes.as_slice().is_empty()
92            && self.comments.as_slice().is_empty()
93            && self.line_number().is_none()
94    }
95
96    /// Try to get the line number, if there was one.
97    pub fn line_number(&self) -> Option<Word> { self.line_number }
98
99    /// Set the [`Line::line_number()`].
100    pub fn set_line_number<W: Into<Option<Word>>>(&mut self, line_number: W) {
101        match line_number.into() {
102            Some(n) => {
103                self.span = self.span.merge(n.span);
104                self.line_number = Some(n);
105            },
106            None => self.line_number = None,
107        }
108    }
109
110    /// Get the [`Line`]'s position in its source text.
111    pub fn span(&self) -> Span { self.span }
112
113    pub(crate) fn into_gcodes(self) -> B::Commands { self.gcodes }
114}