1use crate::winnow::error::{ContextError, ParseError, StrContext};
2use orion_error::{OrionError, StructError, UvsReason};
3use serde::Serialize;
4use winnow::error::{ErrMode, Needed};
5use wp_error::util::split_string;
6use wp_model_core::model::MetaErr;
7fn translate_position(input: &[u8], index: usize) -> (usize, usize) {
10 if input.is_empty() {
11 return (0, index);
12 }
13
14 let safe_index = index.min(input.len() - 1);
15 let column_offset = index - safe_index;
16 let index = safe_index;
17
18 let nl = input[0..index]
19 .iter()
20 .rev()
21 .enumerate()
22 .find(|(_, b)| **b == b'\n')
23 .map(|(nl, _)| index - nl - 1);
24 let line_start = match nl {
25 Some(nl) => nl + 1,
26 None => 0,
27 };
28 let line = input[0..line_start].iter().filter(|b| **b == b'\n').count();
29
30 let column = std::str::from_utf8(&input[line_start..=index])
31 .map(|s| s.chars().count() - 1)
32 .unwrap_or_else(|_| index - line_start);
33 let column = column + column_offset;
34
35 (line, column)
36}
37
38pub fn error_detail(error: ParseError<&str, ContextError<StrContext>>) -> String {
39 let offset = error.offset();
40 let original = *error.input();
41 let span = if offset == original.len() {
42 offset..offset
43 } else {
44 offset..(offset + 1)
45 };
46
47 let mut msg = String::new();
48 let (line, column) = translate_position(original.as_bytes(), span.start);
49 let line_num = line + 1;
50 let col_num = column + 1;
51 let gutter = line_num.to_string().len();
52 let content = original.split('\n').nth(line).expect("valid line number");
53
54 msg.push_str(&format!(
55 "parse error at line {}, column {}\n",
56 line_num, col_num
57 ));
58 for _ in 0..=gutter {
60 msg.push(' ');
61 }
62 msg.push_str("|\n");
63
64 msg.push_str(&format!("{} | ", line_num));
66 msg.push_str(&format!("{}\n", content));
67 for _ in 0..=gutter {
68 msg.push(' ');
69 }
70 msg.push('|');
71 for _ in 0..=column {
72 msg.push(' ');
73 }
74 msg.push('^');
77 for _ in (span.start + 1)..(span.end.min(span.start + content.len())) {
78 msg.push('^');
79 }
80 msg.push('\n');
81 msg.push('\n');
82 msg.push_str(&error.inner().to_string());
83 msg
84}
85
86#[derive(Debug, Clone, PartialEq, Serialize, OrionError)]
87pub enum WplCodeReason {
88 #[orion_error(identity = "biz.plugin")]
89 Plugin,
90 #[orion_error(identity = "biz.syntax")]
91 Syntax,
92 #[orion_error(identity = "biz.wpl_empty")]
93 Empty,
94 #[orion_error(identity = "biz.unsupport")]
95 UnSupport,
96 #[orion_error(transparent)]
97 Uvs(UvsReason),
98}
99
100impl From<UvsReason> for WplCodeReason {
101 fn from(r: UvsReason) -> Self {
102 WplCodeReason::Uvs(r)
103 }
104}
105
106pub type WplCodeError = StructError<WplCodeReason>;
107
108pub type WplCodeResult<T> = Result<T, WplCodeError>;
109
110pub(crate) trait IntoWplCodeError {
113 fn into_wpl_err(self) -> WplCodeError;
114}
115
116impl IntoWplCodeError for WplCodeError {
117 fn into_wpl_err(self) -> WplCodeError {
118 self
119 }
120}
121
122impl IntoWplCodeError for MetaErr {
123 fn into_wpl_err(self) -> WplCodeError {
124 let msg = self.to_string();
125 StructError::builder(WplCodeReason::UnSupport)
126 .detail(msg)
127 .finish()
128 }
129}
130
131impl IntoWplCodeError for wp_parse_api::WparseError {
132 fn into_wpl_err(self) -> WplCodeError {
133 let msg = self.to_string();
134 StructError::builder(WplCodeReason::Plugin)
135 .detail(msg)
136 .finish()
137 }
138}
139
140impl IntoWplCodeError for crate::idcard::Error {
141 fn into_wpl_err(self) -> WplCodeError {
142 let msg = self.to_string();
143 StructError::builder(WplCodeReason::Plugin)
144 .detail(msg)
145 .finish()
146 }
147}
148
149impl IntoWplCodeError for std::string::FromUtf8Error {
150 fn into_wpl_err(self) -> WplCodeError {
151 let msg = self.to_string();
152 StructError::builder(WplCodeReason::Plugin)
153 .detail(msg)
154 .finish()
155 }
156}
157
158impl IntoWplCodeError for std::net::AddrParseError {
159 fn into_wpl_err(self) -> WplCodeError {
160 let msg = self.to_string();
161 StructError::builder(WplCodeReason::Plugin)
162 .detail(msg)
163 .finish()
164 }
165}
166
167pub trait WPLCodeErrorTrait {
168 fn from_syntax(e: ErrMode<ContextError>, code: &str, path: &str) -> Self;
169 fn from_parse_err(e: ParseError<&str, ContextError>, code: &str, path: &str) -> Self;
170}
171impl WPLCodeErrorTrait for StructError<WplCodeReason> {
172 fn from_syntax(e: ErrMode<ContextError>, code: &str, path: &str) -> Self {
173 match e {
174 ErrMode::Incomplete(Needed::Size(u)) => {
175 let msg = format!("parsing require {u}");
176 StructError::builder(WplCodeReason::Syntax)
177 .detail(msg)
178 .finish()
179 }
180 ErrMode::Incomplete(Needed::Unknown) => {
181 let msg = "parsing require more data".to_string();
182 StructError::builder(WplCodeReason::Syntax)
183 .detail(msg)
184 .finish()
185 }
186 ErrMode::Backtrack(e) => {
187 let where_in = split_string(code);
188 let msg = format!(
189 ":wpl code parse fail!\n[path ]: '{}'\n[where]: '{}'\n[error]: {}",
190 path, where_in, e
191 );
192 StructError::builder(WplCodeReason::Syntax)
193 .detail(msg)
194 .finish()
195 }
196 ErrMode::Cut(e) => {
197 let where_in = split_string(code);
198 let msg = format!(
199 ":code parse fail\n[path ]: '{}'\n[where]: '{}'\n[error]: {}",
200 path, where_in, e
201 );
202 StructError::builder(WplCodeReason::Syntax)
203 .detail(msg)
204 .finish()
205 }
206 }
207 }
208
209 fn from_parse_err(e: ParseError<&str, ContextError>, code: &str, path: &str) -> Self {
210 let where_in = split_string(code);
211 let msg = format!(
212 "parse error\n[path ]: '{}'\n[where]: '{}'\n[error]: {}",
213 path, where_in, e
214 );
215 StructError::builder(WplCodeReason::Syntax)
216 .detail(msg)
217 .finish()
218 }
219}
220
221