alpm_parsers/error/parse_stack.rs
1//! The [`ParseStack`] error type.
2
3use std::sync::Arc;
4
5use winnow::{
6 error::{AddContext, FromExternalError, ParserError, StrContext},
7 stream::{Location, Stream},
8};
9
10#[cfg(doc)]
11use crate::error::{ContextExt, LayerExt, LayerParser};
12use crate::error::{
13 Input,
14 context::StringContext,
15 layer::{Layer, LayerRef},
16};
17
18/// Get a reference to the full original source from an [`Input`].
19///
20/// Internally, [`Input`] stores the full initial input. By copying its reference and
21/// resetting the pointer to the start, we get the the original string slice that is being parsed.
22///
23/// This is needed, as the offsets of each layer are relative to the start of the full string
24/// slice.
25fn full_source<'i>(input: &Input<'i>) -> &'i str {
26 let mut full = *input;
27 full.reset_to_start();
28 *full
29}
30
31/// A custom nested and span-aware parser error.
32///
33/// This error type is used across the ALPM project and provides well-formated and detailed parsing
34/// errors. It allows addition of context per layer/type. For more information on the formatting see
35/// the `Display` impl of `ParseStack`.
36#[derive(Clone, Debug)]
37pub struct ParseStack<'i> {
38 /// Byte offset of the deepest failure within the input.
39 pub(crate) at: usize,
40 /// An external error, for the case that the final parser failure happened outside of winnow.
41 pub external: Option<Arc<dyn std::error::Error + Send + Sync>>,
42 /// The [`StringContext`] instances that are not yet captured by a layer boundary.
43 pub(crate) pending: Vec<StringContext>,
44 /// All closed nesting layers, innermost first.
45 pub(crate) layers: Vec<Layer>,
46 /// The full original source text. Required for rendering.
47 pub(crate) source: &'i str,
48}
49
50impl<'i> ParseStack<'i> {
51 /// Moves all pending context calls into a new layer named `name`.
52 ///
53 /// This is called by the [`LayerParser`] in case an error unwinds.
54 pub(crate) fn close_layer(mut self, name: String, start: usize) -> Self {
55 let contexts = std::mem::take(&mut self.pending);
56 self.layers.push(Layer {
57 name,
58 start,
59 contexts,
60 });
61 self
62 }
63
64 /// Returns the innermost layer.
65 ///
66 /// Pending context belongs to the outermost "anonymous" layer that's not yet named.
67 /// If no named layer can be found, but there's some pending context, we return the pending
68 /// context as an anonymous layer as a fallback.
69 pub(crate) fn innermost(&self) -> LayerRef<'_> {
70 if let Some(layer) = self.layers.first() {
71 LayerRef::Named(layer)
72 } else {
73 self.anonymous_layer()
74 }
75 }
76
77 /// Returns the stack from outermost to innermost.
78 ///
79 /// Pending context is positioned first as an anonymous outermost layer.
80 pub(crate) fn layer_stack(&self) -> Vec<LayerRef<'_>> {
81 let mut layers = Vec::new();
82 if !self.pending.is_empty() {
83 layers.push(self.anonymous_layer());
84 }
85 layers.extend(self.layers.iter().rev().map(LayerRef::Named));
86 layers
87 }
88
89 /// Returns all "expected" style contexts of the innermost layer that has any.
90 ///
91 /// The layers are searched from the innermost outwards, with pending context being the
92 /// outermost anonymous layer. If there's no hit on the innermost layer, we just take the next
93 /// best layer with such context to at least provide some information to the user.
94 ///
95 /// Returns an empty list if no layer has any "expected" style context.
96 pub(crate) fn innermost_expected(&self) -> Vec<String> {
97 self.layer_stack()
98 .into_iter()
99 .rev()
100 .map(LayerRef::expected_literals)
101 .find(|literals| !literals.is_empty())
102 .unwrap_or_default()
103 }
104
105 /// Returns the "headline" for the error.
106 ///
107 /// Tries to get the best match available in the following order:
108 /// - [`StringContext::Label`] name of the innermost layer
109 /// - Name of first non-anonymous string
110 /// - A simple fallback to the static string `"invalid"`.
111 pub(crate) fn headline(&self) -> String {
112 self.innermost()
113 .label()
114 .map(ToString::to_string)
115 .or_else(|| self.layers.first().map(|layer| layer.name.clone()))
116 .unwrap_or_else(|| "input".to_owned())
117 }
118
119 /// Takes the current pending context and return it as an anonymous layer.
120 fn anonymous_layer(&self) -> LayerRef<'_> {
121 LayerRef::Anonymous {
122 start: self.at,
123 contexts: &self.pending,
124 }
125 }
126}
127
128// The following trait implementations are wiring logic that's necessary to make our error type a
129// valid winnow error.
130
131impl<'i> ParserError<Input<'i>> for ParseStack<'i> {
132 type Inner = Self;
133
134 fn from_input(input: &Input<'i>) -> Self {
135 ParseStack {
136 at: input.current_token_start(),
137 external: None,
138 pending: Vec::new(),
139 layers: Vec::new(),
140 source: full_source(input),
141 }
142 }
143
144 /// When handling multiple parsing branches (`alt`), keep the failure that reached further into
145 /// the input.
146 // Note: This is somewhat of an experiment, but I assume that that branch should usually carry
147 // the more useful message, as it progressed further.
148 fn or(self, other: Self) -> Self {
149 if other.at >= self.at { other } else { self }
150 }
151
152 fn into_inner(self) -> Result<Self::Inner, Self> {
153 Ok(self)
154 }
155}
156
157// TODO: Remove this, once we fully migrated over to the new error type.
158impl<'i> AddContext<Input<'i>, StrContext> for ParseStack<'i> {
159 /// Add a new context entry to the current layer.
160 ///
161 /// This provides backwards compatibility with winnow's [`Parser::context`] API.
162 /// The [`StrContext`] is internally converted to our owned `StringContext` representation.
163 ///
164 /// Entries are staged inside `Self` until the current layer is closed.
165 ///
166 /// [`Parser::context`]: winnow::Parser::context
167 fn add_context(
168 mut self,
169 _input: &Input<'i>,
170 _token_start: &<Input<'i> as Stream>::Checkpoint,
171 context: StrContext,
172 ) -> Self {
173 self.pending.push(context.into());
174 self
175 }
176}
177
178impl<'i, E: std::error::Error + Send + Sync + 'static> FromExternalError<Input<'i>, E>
179 for ParseStack<'i>
180{
181 fn from_external_error(input: &Input<'i>, e: E) -> Self {
182 ParseStack {
183 at: input.current_token_start(),
184 external: Some(Arc::new(e)),
185 pending: Vec::new(),
186 layers: Vec::new(),
187 source: full_source(input),
188 }
189 }
190}
191
192impl std::error::Error for ParseStack<'_> {}