chj_rustbin/parse/
parse_error.rs1use std::rc::Rc;
2use std::{fmt::Write, sync::Arc};
3
4pub trait IntoOwningBacking<S: AsRef<str>> {
13 type Owning;
14 fn into_owning_backing(self) -> Self::Owning;
15}
16
17pub trait Backing: AsRef<str> + ToOwned + PartialEq {}
20
21impl Backing for str {}
22impl<'t> Backing for &'t str {}
23impl Backing for String {}
24impl<'t> Backing for &'t String {}
25impl Backing for Arc<str> {}
26impl Backing for Rc<str> {}
27
28pub trait ParseContext: PartialEq {
35 fn show_context(&self, out: &mut String);
39}
40
41#[derive(Debug, PartialEq, Clone, Copy)]
42pub struct NoContext;
43
44impl ParseContext for NoContext {
45 fn show_context(&self, out: &mut String) {
46 out.push_str("(no context)")
48 }
49}
50
51#[derive(Debug)]
55pub struct StringParseContext<B: Backing> {
56 pub position: usize,
57 pub backing: B,
58}
59
60impl<'t, B: Backing> IntoOwningBacking<B::Owned> for StringParseContext<&'t B>
67where
68 B::Owned: Backing,
69 &'t B: Backing,
70{
71 type Owning = StringParseContext<B::Owned>;
72
73 fn into_owning_backing(self) -> Self::Owning {
74 let Self { position, backing } = self;
75 StringParseContext {
76 position,
77 backing: backing.to_owned(),
78 }
79 }
80}
81
82impl<B: Backing + Clone> Clone for StringParseContext<B> {
85 fn clone(&self) -> Self {
86 Self {
87 position: self.position,
88 backing: self.backing.clone(),
89 }
90 }
91}
92
93impl<B: Backing> PartialEq for StringParseContext<B> {
94 fn eq(&self, other: &Self) -> bool {
95 self.position == other.position
96 && self.backing.as_ref() == other.backing.as_ref()
97 }
98}
99
100impl<B: Backing> ParseContext for StringParseContext<B> {
101 fn show_context(&self, out: &mut String) {
102 let remainder = &self.backing.as_ref()[self.position..];
103 if remainder.is_empty() {
104 out.push_str(" at end of input")
105 } else {
106 write!(out, " at {:?}", remainder).expect("no err on string")
107 }
108 }
109}
110
111#[derive(Debug)]
112pub struct ParseError<C: ParseContext> {
113 pub message: String,
114 pub context: C,
115 pub backtrace: Vec<FileLocation>,
116}
117
118impl<'t, B: Backing> IntoOwningBacking<B::Owned>
119 for ParseError<StringParseContext<&'t B>>
120where
121 B::Owned: Backing,
122 &'t B: Backing,
123{
124 type Owning = ParseError<StringParseContext<B::Owned>>;
125
126 fn into_owning_backing(self) -> Self::Owning {
127 let ParseError {
128 message,
129 context,
130 backtrace,
131 } = self;
132 ParseError {
133 message,
134 context: context.into_owning_backing(),
135 backtrace,
136 }
137 }
138}
139
140impl<C: ParseContext> ParseError<C> {
141 pub fn to_string_showing_location(&self, show_backtrace: bool) -> String {
142 let ParseError {
143 message, context, ..
144 } = self;
145 let mut message = message.clone();
146 context.show_context(&mut message);
147 if show_backtrace {
148 self.show_backtrace(&mut message);
149 }
150 message
151 }
152
153 pub fn show_backtrace(&self, out: &mut String) {
156 for l in &self.backtrace {
157 out.push_str("\n\t");
158 out.push_str(&l.to_string());
159 }
160 }
161
162 pub fn message_append(mut self, s: &str) -> Self {
164 self.message.push_str(s);
165 self
166 }
167}
168
169impl<C: ParseContext> PartialEq for ParseError<C> {
173 fn eq(&self, other: &Self) -> bool {
174 self.message == other.message && self.context == other.context
175 }
176}
177
178#[derive(Debug)]
179pub struct FileLocation {
180 pub file: &'static str,
181 pub line: u32,
182 pub column: u32,
183}
184
185impl FileLocation {
186 pub fn to_string(&self) -> String {
187 format!("{}:{}:{}", self.file, self.line, self.column)
188 }
189}
190
191#[macro_export]
192macro_rules! file_location {
193 {} => {
194 $crate::parse::parse_error::FileLocation {
195 file: file!(),
196 line: line!(),
197 column: column!(),
198 }
199 }
200}
201
202#[macro_export]
203macro_rules! parse_error {
204 { $($body:tt)* } => {
205 ParseError {
206 backtrace: vec![$crate::file_location!()],
207 $($body)*
208 }
209 }
210}
211
212#[macro_export]
215macro_rules! T {
216 { $e:expr } => {
217 $e.map_err(|e| {
218 let mut e: $crate::parse::parse_error::ParseError<_> = e.into();
219 e.backtrace.push($crate::file_location!());
220 e
221 })
222 }
223}