Skip to main content

syntree/
error.rs

1use core::convert::Infallible;
2use core::fmt;
3
4/// Errors raised while building a tree.
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6#[non_exhaustive]
7pub enum Error<E = Infallible> {
8    /// Error raised by [Builder::close][crate::Builder::close] if there
9    /// currently is no node being built.
10    ///
11    /// # Examples
12    ///
13    /// ```
14    /// use syntree::{Builder, Error};
15    ///
16    /// let mut tree = Builder::new();
17    ///
18    /// tree.open("root")?;
19    /// tree.close()?;
20    ///
21    /// // Syntax::Root and Syntax::Child is left open.
22    /// assert_eq!(tree.close(), Err(Error::CloseError));
23    /// # Ok::<_, Box<dyn core::error::Error>>(())
24    /// ```
25    CloseError,
26    /// Error raised by [Builder::build][crate::Builder::build] if the
27    /// tree isn't correctly balanced.
28    ///
29    /// # Examples
30    ///
31    /// ```
32    /// use syntree::{Builder, Error};
33    ///
34    /// let mut tree = Builder::new();
35    ///
36    /// tree.open("number")?;
37    /// tree.token("lit", 3)?;
38    /// tree.close()?;
39    ///
40    /// tree.open("number")?;
41    ///
42    /// // Syntax::Number is left open.
43    /// assert_eq!(tree.build(), Err(Error::BuildError));
44    /// # Ok::<_, Box<dyn core::error::Error>>(())
45    /// ```
46    BuildError,
47    /// Error raised by [Builder::close_at][crate::Builder::close_at] if
48    /// we're not trying to close at a sibling node.
49    ///
50    /// # Examples
51    ///
52    /// ```
53    /// use syntree::{Builder, Error};
54    ///
55    /// let mut tree = Builder::new();
56    ///
57    /// let c = tree.checkpoint()?;
58    ///
59    /// tree.open("child")?;
60    /// tree.token("token", 3)?;
61    ///
62    /// assert_eq!(tree.close_at(&c, "operation"), Err(Error::CloseAtError));
63    /// # Ok::<_, Box<dyn core::error::Error>>(())
64    /// ```
65    CloseAtError,
66    /// Numerical overflow.
67    ///
68    /// This happens when the tree has more nodes than the flavor's
69    /// [`Width`][crate::Flavor::Width] can address, or when a span would exceed
70    /// its [`Index`][crate::Flavor::Index]. A narrow width such as `u8` makes
71    /// this easy to reach.
72    ///
73    /// # Examples
74    ///
75    /// This is an example where we're trying to build a really small tree using
76    /// u8 pointers:
77    ///
78    /// ```
79    /// use syntree::{Builder, Error};
80    ///
81    /// syntree::flavor! {
82    ///     struct CustomFlavor {
83    ///         type Index = u32;
84    ///         type Width = u8;
85    ///     }
86    /// }
87    ///
88    /// let mut tree: Builder<_, CustomFlavor> = Builder::new_with();
89    ///
90    /// for d in 0..u8::MAX {
91    ///     tree.token(d, 1)?;
92    /// }
93    ///
94    /// assert_eq!(tree.token(255, 1), Err(Error::Overflow));
95    /// # Ok::<_, Box<dyn core::error::Error>>(())
96    /// ```
97    Overflow,
98    /// The node of the given id is missing.
99    ///
100    /// # Examples
101    ///
102    /// The following showcases what could happen if you mix checkpoints from
103    /// two compatible trees:
104    ///
105    /// ```
106    /// use syntree::{Builder, Error};
107    ///
108    /// let mut a = Builder::new();
109    /// let mut b = Builder::new();
110    ///
111    /// b.open("child")?;
112    /// b.close()?;
113    ///
114    /// let c = b.checkpoint()?;
115    ///
116    /// assert_eq!(a.close_at(&c, "operation"), Err(Error::MissingNode(1)));
117    /// # Ok::<_, Box<dyn core::error::Error>>(())
118    /// ```
119    MissingNode(usize),
120    /// An error raised by the particular [Flavor] in use.
121    ///
122    /// [Flavor]: crate::Flavor
123    Flavor(E),
124}
125
126impl<E> From<E> for Error<E> {
127    #[inline]
128    fn from(error: E) -> Self {
129        Error::Flavor(error)
130    }
131}
132
133impl<E> core::error::Error for Error<E>
134where
135    E: 'static + core::error::Error,
136{
137    #[inline]
138    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
139        match self {
140            Error::Flavor(error) => Some(error),
141            _ => None,
142        }
143    }
144}
145
146impl<E> fmt::Display for Error<E>
147where
148    E: fmt::Display,
149{
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        match self {
152            Error::CloseError => {
153                write!(f, "no node being built")
154            }
155            Error::BuildError => {
156                write!(f, "tree is currently being built")
157            }
158            Error::CloseAtError => {
159                write!(
160                    f,
161                    "trying to close a node which is not a sibling of the checkpoint being closed"
162                )
163            }
164            Error::Overflow => {
165                write!(f, "numerical overflow")
166            }
167            Error::MissingNode(p) => {
168                write!(f, "missing node with id `{p}`")
169            }
170            Error::Flavor(error) => error.fmt(f),
171        }
172    }
173}