Skip to main content

systemprompt_models/api/errors/
wire.rs

1//! Redacting wire serialisation of [`ApiError`].
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use chrono::{DateTime, Utc};
7use serde::{Serialize, Serializer};
8use systemprompt_identifiers::TraceId;
9
10use super::{ApiError, ErrorCode, ValidationError};
11
12#[derive(Serialize)]
13struct WireApiError<'a> {
14    code: ErrorCode,
15    message: &'a str,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    details: Option<&'a str>,
18    #[serde(skip_serializing_if = "Option::is_none")]
19    error_key: Option<&'a str>,
20    #[serde(skip_serializing_if = "Option::is_none")]
21    path: Option<&'a str>,
22    #[serde(skip_serializing_if = "no_validation_errors")]
23    validation_errors: &'a [ValidationError],
24    timestamp: DateTime<Utc>,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    trace_id: Option<&'a TraceId>,
27}
28
29const fn no_validation_errors(errors: &&[ValidationError]) -> bool {
30    errors.is_empty()
31}
32
33impl Serialize for ApiError {
34    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
35        let server_error = self.code.is_server_error();
36        let message: &str = if server_error {
37            self.code.public_server_message()
38        } else {
39            self.message.as_str()
40        };
41        let details = if server_error {
42            None
43        } else {
44            self.details.as_deref()
45        };
46        let validation_errors: &[ValidationError] = if server_error {
47            <&[ValidationError]>::default()
48        } else {
49            self.validation_errors.as_slice()
50        };
51        WireApiError {
52            code: self.code,
53            message,
54            details,
55            error_key: self.error_key.as_deref(),
56            path: self.path.as_deref(),
57            validation_errors,
58            timestamp: self.timestamp,
59            trace_id: self.trace_id.as_ref(),
60        }
61        .serialize(serializer)
62    }
63}