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