Skip to main content

systemprompt_identifiers/
error.rs

1//! Error types raised by identifier validation and database value conversion.
2//!
3//! The crate exposes two error enums:
4//!
5//! - `IdValidationError` — produced when a `try_new` constructor on a typed
6//!   identifier rejects its input (empty string, malformed shape, etc.).
7//! - `DbValueError` — produced when `FromDbValue` cannot convert a `DbValue`
8//!   variant into the requested target type (NULL where a value is required,
9//!   type mismatch, parse failure, numeric overflow).
10//!
11//! Both implement `std::error::Error` so callers can compose them into
12//! larger `thiserror`-derived enums via `#[from]`.
13//!
14//! Copyright (c) systemprompt.io — Business Source License 1.1.
15//! See <https://systemprompt.io> for licensing details.
16
17use std::sync::Arc;
18
19use thiserror::Error;
20
21#[derive(Debug, Clone, Error)]
22pub enum IdValidationError {
23    #[error("{id_type} cannot be empty")]
24    Empty { id_type: &'static str },
25    #[error("{id_type}: {message}")]
26    Invalid {
27        id_type: &'static str,
28        message: String,
29    },
30    #[error("{id_type}: {source}")]
31    Uuid {
32        id_type: &'static str,
33        #[source]
34        source: uuid::Error,
35    },
36    #[error("{id_type}: {source}")]
37    Json {
38        id_type: &'static str,
39        #[source]
40        source: Arc<serde_json::Error>,
41    },
42}
43
44impl PartialEq for IdValidationError {
45    fn eq(&self, other: &Self) -> bool {
46        self.comparable() == other.comparable()
47    }
48}
49
50impl Eq for IdValidationError {}
51
52impl IdValidationError {
53    #[must_use]
54    pub const fn empty(id_type: &'static str) -> Self {
55        Self::Empty { id_type }
56    }
57
58    #[must_use]
59    pub fn invalid(id_type: &'static str, message: impl Into<String>) -> Self {
60        Self::Invalid {
61            id_type,
62            message: message.into(),
63        }
64    }
65
66    #[must_use]
67    pub const fn uuid(id_type: &'static str, source: uuid::Error) -> Self {
68        Self::Uuid { id_type, source }
69    }
70
71    #[must_use]
72    pub fn json(id_type: &'static str, source: serde_json::Error) -> Self {
73        Self::Json {
74            id_type,
75            source: Arc::new(source),
76        }
77    }
78
79    fn comparable(&self) -> (u8, &'static str, String) {
80        match self {
81            Self::Empty { id_type } => (0, *id_type, String::new()),
82            Self::Invalid { id_type, message } => (1, *id_type, message.clone()),
83            Self::Uuid { id_type, source } => (2, *id_type, source.to_string()),
84            Self::Json { id_type, source } => (3, *id_type, source.to_string()),
85        }
86    }
87}
88
89#[derive(Debug, Clone, Error)]
90pub enum DbValueError {
91    #[error("cannot convert NULL to {target}")]
92    Null { target: &'static str },
93    #[error("cannot convert {from} to {target}")]
94    Incompatible {
95        from: &'static str,
96        target: &'static str,
97    },
98    #[error("cannot parse {value:?} as {target}")]
99    Parse { value: String, target: &'static str },
100    #[error("value out of range for {target}")]
101    OutOfRange { target: &'static str },
102}
103
104impl DbValueError {
105    #[must_use]
106    pub const fn null_for(target: &'static str) -> Self {
107        Self::Null { target }
108    }
109
110    #[must_use]
111    pub const fn incompatible(from: &'static str, target: &'static str) -> Self {
112        Self::Incompatible { from, target }
113    }
114
115    #[must_use]
116    pub fn parse(value: impl Into<String>, target: &'static str) -> Self {
117        Self::Parse {
118            value: value.into(),
119            target,
120        }
121    }
122
123    #[must_use]
124    pub const fn out_of_range(target: &'static str) -> Self {
125        Self::OutOfRange { target }
126    }
127}