1use yog_data::Schema;
6
7#[derive(Debug, Clone, Default, PartialEq, Schema)]
9pub struct ServiceError {
10 pub message: String,
11 #[data(default)]
13 pub problems: Vec<Problem>,
14 pub file: Option<String>,
16}
17
18#[derive(Debug, Clone, Default, PartialEq, Schema)]
20pub struct Problem {
21 pub code: String,
23 pub message: String,
24 #[data(default)]
26 pub path: String,
27 pub line: Option<u32>,
29 pub col: Option<u32>,
30}
31
32impl ServiceError {
33 pub fn of(code: &str, message: impl Into<String>) -> Self {
35 let message = message.into();
36 ServiceError {
37 problems: vec![Problem {
38 code: code.to_string(),
39 message: message.clone(),
40 ..Problem::default()
41 }],
42 message,
43 file: None,
44 }
45 }
46
47 pub fn at(code: &str, message: impl Into<String>, line: u32, col: Option<u32>) -> Self {
49 let mut e = Self::of(code, message);
50 e.problems[0].line = Some(line);
51 e.problems[0].col = col;
52 e
53 }
54
55 pub fn syntax(e: &yog_data::SyntaxError) -> Self {
57 Self::at(
58 yog_ids::problem_code::SYNTAX,
59 e.message.clone(),
60 e.at.line,
61 Some(e.at.col),
62 )
63 }
64
65 pub fn not_found(message: impl Into<String>) -> Self {
67 Self::of(yog_ids::problem_code::NOT_FOUND, message)
68 }
69
70 pub fn param(name: &str, problems: Vec<yog_data::schema::Problem>) -> Self {
72 let problems: Vec<Problem> = problems
73 .into_iter()
74 .map(|p| Problem {
75 code: p.code.to_string(),
76 path: if p.path.is_root() {
77 name.to_string()
78 } else {
79 format!("{name}.{}", p.path)
80 },
81 message: p.message,
82 ..Problem::default()
83 })
84 .collect();
85 let message = match problems.as_slice() {
86 [one] => format!("`{}`: {}", one.path, one.message),
87 _ => format!("`{name}` has {} problems", problems.len()),
88 };
89 ServiceError {
90 message,
91 problems,
92 file: None,
93 }
94 }
95}
96
97impl From<String> for ServiceError {
98 fn from(message: String) -> Self {
99 Self::of(yog_ids::problem_code::FAILED, message)
100 }
101}
102
103impl From<&str> for ServiceError {
104 fn from(message: &str) -> Self {
105 message.to_string().into()
106 }
107}