Skip to main content

wdl_core/
parse.rs

1//! Generalized parse results.
2
3use crate::concern::Concerns;
4
5/// An error related to a parse [`Result`].
6#[derive(Debug)]
7pub enum Error {
8    /// A contradiction was encountered.
9    Contradiction(String),
10}
11
12impl std::fmt::Display for Error {
13    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14        match self {
15            Error::Contradiction(reason) => {
16                write!(f, "contradiction encountered: {reason}")
17            }
18        }
19    }
20}
21
22impl std::error::Error for Error {}
23
24/// A parse result.
25///
26/// This struct contains the results of parsing either (a) a WDL parse tree or
27/// (b) a WDL abstract syntax tree. It contains two distinct entities:
28///
29/// * An optional list of [`Concerns`], if any were emitted during parsing.
30/// * An optional tree based on type `E`, if one was able to be constructed.
31///
32/// Notably, you cannot create a [`Result`] that has no concerns and no parse
33/// tree, as that scenario is non-sensical.
34#[derive(Debug)]
35pub struct Result<E> {
36    /// Concerns emitted during parsing (if there were any).
37    concerns: Option<Concerns>,
38
39    /// The inner parse tree (if one was able to be created).
40    tree: Option<E>,
41}
42
43impl<E> Result<E> {
44    /// Attempts to create a new [`Result`].
45    ///
46    /// # Examples
47    ///
48    /// ```
49    /// use wdl_core::concern::concerns::Builder;
50    /// use wdl_core::concern::parse;
51    /// use wdl_core::file::Location;
52    /// use wdl_core::parse::Result;
53    /// use wdl_core::Concern;
54    ///
55    /// // Substitute `42` for your parse tree or abstract syntax tree.
56    /// let result = Result::try_new(Some(42), None).unwrap();
57    /// assert!(result.tree().is_some());
58    /// assert!(result.concerns().is_none());
59    ///
60    /// let error = parse::Error::new("Hello, world!", Location::Unplaced);
61    /// let concern = Concern::ParseError(error);
62    /// let concerns = Builder::default().push(concern).build();
63    ///
64    /// let result = Result::<usize>::try_new(None, concerns).unwrap();
65    /// assert!(result.tree().is_none());
66    /// assert!(result.concerns().is_some());
67    ///
68    /// let err = Result::<usize>::try_new(None, None).unwrap_err();
69    /// assert_eq!(
70    ///     err.to_string(),
71    ///     String::from(
72    ///         "contradiction encountered: cannot create a parse Result with no concerns and no \
73    ///          parse tree"
74    ///     )
75    /// );
76    /// ```
77    pub fn try_new(
78        tree: Option<E>,
79        concerns: Option<Concerns>,
80    ) -> std::result::Result<Self, Error> {
81        if concerns.is_none() && tree.is_none() {
82            return Err(Error::Contradiction(String::from(
83                "cannot create a parse Result with no concerns and no parse tree",
84            )));
85        }
86
87        Ok(Self { concerns, tree })
88    }
89
90    /// Gets the concerns from the [`Result`] by reference.
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// use wdl_core::concern::concerns::Builder;
96    /// use wdl_core::concern::parse;
97    /// use wdl_core::file::Location;
98    /// use wdl_core::parse::Result;
99    /// use wdl_core::Concern;
100    ///
101    /// let error = parse::Error::new("Hello, world!", Location::Unplaced);
102    /// let concern = Concern::ParseError(error);
103    /// let concerns = Builder::default().push(concern).build();
104    ///
105    /// let result = Result::<usize>::try_new(None, concerns).unwrap();
106    ///
107    /// let first = result.concerns().unwrap().inner().iter().next().unwrap();
108    /// let error = first.as_parse_error().unwrap();
109    /// assert_eq!(error.message(), "Hello, world!");
110    /// ```
111    pub fn concerns(&self) -> Option<&Concerns> {
112        self.concerns.as_ref()
113    }
114
115    /// Consumes `self` and returns the concerns from the [`Result`].
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// use wdl_core::concern::concerns::Builder;
121    /// use wdl_core::concern::parse;
122    /// use wdl_core::file::Location;
123    /// use wdl_core::parse::Result;
124    /// use wdl_core::Concern;
125    ///
126    /// let error = parse::Error::new("Hello, world!", Location::Unplaced);
127    /// let concern = Concern::ParseError(error);
128    /// let concerns = Builder::default().push(concern).build();
129    ///
130    /// let result = Result::<usize>::try_new(None, concerns).unwrap();
131    ///
132    /// let first = result
133    ///     .into_concerns()
134    ///     .unwrap()
135    ///     .into_inner()
136    ///     .into_iter()
137    ///     .next()
138    ///     .unwrap();
139    /// let error = first.into_parse_error().unwrap();
140    /// assert_eq!(error.message(), String::from("Hello, world!"));
141    /// ```
142    pub fn into_concerns(self) -> Option<Concerns> {
143        self.concerns
144    }
145
146    /// Gets the tree from the [`Result`] by reference.
147    ///
148    /// # Examples
149    ///
150    /// ```
151    /// use wdl_core::parse::Result;
152    ///
153    /// // Substitute `42` for your parse tree or abstract syntax tree.
154    /// let result = Result::<usize>::try_new(Some(42), None).unwrap();
155    ///
156    /// let first = result.tree().unwrap();
157    /// assert_eq!(first, &42);
158    /// ```
159    pub fn tree(&self) -> Option<&E> {
160        self.tree.as_ref()
161    }
162
163    /// Consumes `self` and returns the tree from the [`Result`].
164    ///
165    /// # Examples
166    ///
167    /// ```
168    /// use wdl_core::parse::Result;
169    ///
170    /// // Substitute `42` for your parse tree or abstract syntax tree.
171    /// let result = Result::<usize>::try_new(Some(42), None).unwrap();
172    ///
173    /// let first = result.into_tree().unwrap();
174    /// assert_eq!(first, 42);
175    /// ```
176    pub fn into_tree(self) -> Option<E> {
177        self.tree
178    }
179
180    /// Breaks a [`Result`] down into its parts.
181    ///
182    /// # Examples
183    ///
184    /// ```
185    /// use wdl_core::concern::concerns::Builder;
186    /// use wdl_core::concern::parse;
187    /// use wdl_core::file::Location;
188    /// use wdl_core::parse::Result;
189    /// use wdl_core::Concern;
190    ///
191    /// let error = parse::Error::new("Hello, world!", Location::Unplaced);
192    /// let concern = Concern::ParseError(error);
193    /// let concerns = Builder::default().push(concern).build();
194    ///
195    /// // Substitute `42` for your parse tree or abstract syntax tree.
196    /// let result = Result::<usize>::try_new(Some(42), concerns).unwrap();
197    /// assert_eq!(result.tree(), Some(&42));
198    /// assert_eq!(
199    ///     result
200    ///         .concerns()
201    ///         .unwrap()
202    ///         .inner()
203    ///         .iter()
204    ///         .next()
205    ///         .unwrap()
206    ///         .as_parse_error()
207    ///         .unwrap()
208    ///         .message(),
209    ///     "Hello, world!"
210    /// );
211    /// ```
212    pub fn into_parts(self) -> (Option<E>, Option<Concerns>) {
213        (self.tree, self.concerns)
214    }
215}