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}