Skip to main content

yog_cli/serve/
error.rs

1//! What a request got wrong, typed: what to say, and each problem with
2//! where it is — a parameter's path, a file's line and column — so a tool
3//! shows it without reading it out of a string.
4
5use yog_data::{Diagnostic, Path, Schema};
6
7/// The error a request is answered with.
8#[derive(Debug, Clone, Default, PartialEq, Schema)]
9pub(crate) struct ServiceError {
10    pub(crate) message: String,
11    /// Every problem, each with its code; at least one.
12    #[data(default)]
13    pub(crate) problems: Vec<Diagnostic>,
14    /// The file the request is about, when it's about one.
15    pub(crate) file: Option<String>,
16}
17
18impl ServiceError {
19    /// One problem of kind `code`.
20    pub(crate) fn of(code: &str, message: impl Into<String>) -> Self {
21        let message = message.into();
22        ServiceError {
23            problems: vec![Diagnostic::error(code, &message)],
24            message,
25            file: None,
26        }
27    }
28
29    /// `message` of kind `code`, about the text at `line`:`col`.
30    pub(crate) fn at(code: &str, message: impl Into<String>, line: u32, col: Option<u32>) -> Self {
31        let mut e = Self::of(code, message);
32        e.problems[0].line = Some(line);
33        e.problems[0].col = col;
34        e
35    }
36
37    /// Text that doesn't parse.
38    pub(crate) fn syntax(e: &yog_data::SyntaxError) -> Self {
39        Self::at(
40            yog_ids::data_problem::SYNTAX,
41            e.message.clone(),
42            e.at.line,
43            Some(e.at.col),
44        )
45    }
46
47    /// What a request names isn't there.
48    pub(crate) fn not_found(message: impl Into<String>) -> Self {
49        Self::of(yog_ids::request_problem::NOT_FOUND, message)
50    }
51
52    /// Parameter `name` that doesn't read: each of its problems, at its path.
53    pub(crate) fn param(name: &str, problems: Vec<Diagnostic>) -> Self {
54        let param = Path::root().key(name);
55        let problems: Vec<Diagnostic> = problems
56            .into_iter()
57            .map(|p| {
58                let path = param.then(&p.path);
59                p.at_path(path)
60            })
61            .collect();
62        let message = match problems.as_slice() {
63            [one] => format!("`{}`: {}", one.path, one.message),
64            _ => format!("`{name}` has {} problems", problems.len()),
65        };
66        ServiceError {
67            message,
68            problems,
69            file: None,
70        }
71    }
72}
73
74impl From<String> for ServiceError {
75    fn from(message: String) -> Self {
76        Self::of(yog_ids::request_problem::FAILED, message)
77    }
78}
79
80impl From<&str> for ServiceError {
81    fn from(message: &str) -> Self {
82        message.to_string().into()
83    }
84}
85
86/// Yog Data that didn't read: each problem with its code, field and place.
87impl From<yog_data::Errors> for ServiceError {
88    fn from(found: yog_data::Errors) -> Self {
89        ServiceError {
90            message: found.to_string(),
91            problems: found.0,
92            file: None,
93        }
94    }
95}