fts_sqlite/types/
ids.rs

1//! Strongly-typed identifier types for flow trading entities.
2//!
3//! This module provides newtype wrappers around UUIDs for different entity types
4//! in the system. Using distinct types for each kind of ID prevents mixing up
5//! identifiers at compile time and improves code clarity.
6//!
7//! All ID types implement:
8//! - Serialization/deserialization as transparent UUIDs
9//! - SQLite storage as strings
10//! - Display formatting
11//! - Conversion to/from standard UUIDs
12
13macro_rules! new_id {
14    ($struct:ident) => {
15        new_id!($struct, "A newtype wrapper around a uuid");
16    };
17    ($struct:ident, $doc:literal) => {
18        #[doc = $doc]
19        #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
20        #[derive(
21            Debug,
22            Clone,
23            Copy,
24            PartialEq,
25            Eq,
26            Hash,
27            PartialOrd,
28            Ord,
29            serde::Serialize,
30            serde::Deserialize,
31        )]
32        #[serde(transparent)]
33        pub struct $struct(pub uuid::Uuid);
34
35        impl Into<uuid::Uuid> for $struct {
36            fn into(self) -> uuid::Uuid {
37                self.0
38            }
39        }
40
41        impl From<uuid::Uuid> for $struct {
42            fn from(value: uuid::Uuid) -> Self {
43                Self(value)
44            }
45        }
46
47        impl std::fmt::Display for $struct {
48            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49                self.0.fmt(f)
50            }
51        }
52
53        impl std::str::FromStr for $struct {
54            type Err = <uuid::Uuid as std::str::FromStr>::Err;
55
56            fn from_str(s: &str) -> Result<Self, Self::Err> {
57                Ok(Self(s.parse()?))
58            }
59        }
60
61        impl sqlx::Type<sqlx::Sqlite> for $struct {
62            fn type_info() -> sqlx::sqlite::SqliteTypeInfo {
63                <String as sqlx::Type<sqlx::Sqlite>>::type_info()
64            }
65        }
66
67        impl<'q> sqlx::Encode<'q, sqlx::Sqlite> for $struct {
68            fn encode_by_ref(
69                &self,
70                args: &mut Vec<sqlx::sqlite::SqliteArgumentValue<'q>>,
71            ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
72                sqlx::Encode::<'q, sqlx::Sqlite>::encode_by_ref(&self.0.to_string(), args)
73            }
74        }
75
76        impl<'r> sqlx::Decode<'r, sqlx::Sqlite> for $struct {
77            fn decode(
78                value: sqlx::sqlite::SqliteValueRef<'r>,
79            ) -> Result<Self, sqlx::error::BoxDynError> {
80                let string = <&str as sqlx::Decode<'r, sqlx::Sqlite>>::decode(value)?;
81                let value = string.parse()?;
82                Ok(value)
83            }
84        }
85    };
86}
87
88new_id!(
89    BidderId,
90    "Unique identifier for a bidder in the flow trading system"
91);
92new_id!(DemandId, "Unique identifier for a demand curve submission");
93new_id!(
94    PortfolioId,
95    "Unique identifier for a portfolio that groups demands and products"
96);
97new_id!(ProductId, "Unique identifier for a tradeable product");