Skip to main content

api_bones_test/builders/
problem.rs

1use api_bones::error::{ApiError, ErrorCode, ValidationError};
2
3/// Builder for a fake [`ApiError`].
4///
5/// # Quick start
6///
7/// ```rust
8/// use api_bones::error::ErrorCode;
9/// use api_bones_test::builders::FakeProblem;
10///
11/// let err = FakeProblem::new(ErrorCode::ResourceNotFound)
12///     .detail("Widget 42 not found")
13///     .build();
14/// assert_eq!(err.code, ErrorCode::ResourceNotFound);
15/// assert_eq!(err.status, 404);
16/// ```
17pub struct FakeProblem {
18    code: ErrorCode,
19    detail: Option<String>,
20    title: Option<String>,
21    status: Option<u16>,
22    request_id: Option<String>,
23    fields: Vec<(String, String)>,
24}
25
26impl FakeProblem {
27    #[must_use]
28    pub fn new(code: ErrorCode) -> Self {
29        Self {
30            code,
31            detail: None,
32            title: None,
33            status: None,
34            request_id: None,
35            fields: Vec::new(),
36        }
37    }
38
39    #[must_use]
40    pub fn detail(mut self, detail: impl Into<String>) -> Self {
41        self.detail = Some(detail.into());
42        self
43    }
44
45    #[must_use]
46    pub fn title(mut self, title: impl Into<String>) -> Self {
47        self.title = Some(title.into());
48        self
49    }
50
51    #[must_use]
52    pub fn field(mut self, name: impl Into<String>, message: impl Into<String>) -> Self {
53        self.fields.push((name.into(), message.into()));
54        self
55    }
56
57    #[must_use]
58    pub fn status(mut self, status: u16) -> Self {
59        self.status = Some(status);
60        self
61    }
62
63    #[must_use]
64    pub fn request_id(mut self, id: impl Into<String>) -> Self {
65        self.request_id = Some(id.into());
66        self
67    }
68
69    #[must_use]
70    pub fn build(self) -> ApiError {
71        let detail = self.detail.unwrap_or_else(|| self.code.title().to_string());
72        let mut err = ApiError::new(self.code, detail);
73        if let Some(title) = self.title {
74            err.title = title;
75        }
76        if let Some(status) = self.status {
77            err.status = status;
78        }
79        if let Some(id_str) = self.request_id {
80            if let Ok(uuid) = id_str.parse::<uuid::Uuid>() {
81                err = err.with_request_id(uuid);
82            }
83        }
84        if !self.fields.is_empty() {
85            let errors: Vec<ValidationError> = self
86                .fields
87                .into_iter()
88                .map(|(field, message)| ValidationError {
89                    field,
90                    message,
91                    rule: None,
92                })
93                .collect();
94            err = err.with_errors(errors);
95        }
96        err
97    }
98}