Skip to main content

k2db_api_contract/
error.rs

1// SPDX-FileCopyrightText: 2026 Alexander R. Croft
2// SPDX-License-Identifier: GPL-3.0-only
3
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8pub struct ErrorChainItem {
9    pub error: String,
10    #[serde(rename = "error_description")]
11    pub error_description: String,
12    pub stage: Option<String>,
13    pub at: u64,
14}
15
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
17pub struct ProblemDetailsPayload {
18    #[serde(rename = "type")]
19    pub type_uri: Option<String>,
20    pub title: Option<String>,
21    pub status: Option<u16>,
22    pub detail: Option<String>,
23    pub trace: Option<String>,
24    pub chain: Option<Vec<ErrorChainItem>>,
25    #[serde(flatten)]
26    pub extra: serde_json::Map<String, Value>,
27}
28
29#[cfg(test)]
30mod tests {
31    use serde_json::json;
32
33    use super::*;
34
35    #[test]
36    fn problem_details_preserves_standard_fields() {
37        let payload = ProblemDetailsPayload {
38            type_uri: Some("urn:service-error:not_found".to_owned()),
39            title: Some("not_found".to_owned()),
40            status: Some(404),
41            detail: Some("Document not found".to_owned()),
42            trace: Some("sys_mdb_get_not_found".to_owned()),
43            chain: Some(vec![ErrorChainItem {
44                error: "not_found".to_owned(),
45                error_description: "Document not found".to_owned(),
46                stage: Some("api.get-by-id".to_owned()),
47                at: 1,
48            }]),
49            extra: serde_json::Map::new(),
50        };
51
52        let value = serde_json::to_value(payload).expect("serialize problem details");
53        assert_eq!(
54            value,
55            json!({
56                "type": "urn:service-error:not_found",
57                "title": "not_found",
58                "status": 404,
59                "detail": "Document not found",
60                "trace": "sys_mdb_get_not_found",
61                "chain": [
62                    {
63                        "error": "not_found",
64                        "error_description": "Document not found",
65                        "stage": "api.get-by-id",
66                        "at": 1,
67                    }
68                ],
69            })
70        );
71    }
72}