Skip to main content

yaml_edit/
parse.rs

1//! Parser types and utilities.
2
3use rowan::GreenNode;
4
5/// The result of a parse operation.
6///
7/// Returns the syntax tree even if there are parse errors. Use [`errors()`](Self::errors)
8/// or [`positioned_errors()`](Self::positioned_errors) to check for errors if needed.
9/// This allows for error-resilient tooling that can work with partial or invalid input.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct Parse<T> {
12    green_node: GreenNode,
13    positioned_errors: Vec<crate::PositionedParseError>,
14    _ty: std::marker::PhantomData<fn() -> T>,
15}
16
17impl<T> Parse<T> {
18    pub(crate) fn new(
19        green_node: GreenNode,
20        positioned_errors: Vec<crate::PositionedParseError>,
21    ) -> Self {
22        Parse {
23            green_node,
24            positioned_errors,
25            _ty: std::marker::PhantomData,
26        }
27    }
28
29    /// The parse tree. If there were no parse errors, this is a valid tree.
30    /// If there were parse errors, this tree might be only partially valid.
31    pub fn tree(&self) -> T
32    where
33        T: rowan::ast::AstNode<Language = crate::Lang>,
34    {
35        let syntax_node = rowan::SyntaxNode::new_root_mut(self.green_node.clone());
36        T::cast(syntax_node)
37            .expect("Parse<T> always holds a green node whose root kind matches T::can_cast")
38    }
39
40    /// Positioned parse errors with location information.
41    pub fn positioned_errors(&self) -> &[crate::PositionedParseError] {
42        &self.positioned_errors
43    }
44
45    /// Parse error messages, if any.
46    pub fn errors(&self) -> Vec<String> {
47        self.positioned_errors
48            .iter()
49            .map(|e| e.message.clone())
50            .collect()
51    }
52
53    /// Convert parse result to a `Result`, failing with the first error if any.
54    ///
55    /// The returned `YamlError::Parse` contains the error message but **no
56    /// line/column information** because `Parse` does not retain the source
57    /// text. Use [`YamlFile::from_str`](crate::YamlFile) (which calls
58    /// `byte_offset_to_line_column` internally) if you need precise positions
59    /// in error messages.
60    pub fn to_result(self) -> Result<T, crate::YamlError>
61    where
62        T: rowan::ast::AstNode<Language = crate::Lang>,
63    {
64        if let Some(first) = self.positioned_errors.first() {
65            Err(crate::YamlError::Parse {
66                message: first.message.clone(),
67                line: None,
68                column: None,
69            })
70        } else {
71            Ok(self.tree())
72        }
73    }
74
75    /// Whether the parse had any errors.
76    pub fn has_errors(&self) -> bool {
77        !self.positioned_errors.is_empty()
78    }
79
80    /// Whether the parse succeeded without any errors.
81    pub fn ok(&self) -> bool {
82        self.positioned_errors.is_empty()
83    }
84}
85
86impl Parse<crate::YamlFile> {
87    /// Parse YAML text, returning a Parse result
88    pub fn parse_yaml(text: &str) -> Self {
89        let parsed = crate::yaml::parse(text);
90        Parse::new(parsed.green_node, parsed.positioned_errors)
91    }
92}