architect_api/utils/
uuid_val.rs1#[macro_export]
3macro_rules! uuid_val {
4 ($name:ident, $ns:ident) => {
5 uuid_val!($name, $ns, {});
6 };
7 ($name:ident, $ns:ident, { $($key:expr_2021 => $value:expr_2021),* $(,)? }) => {
8 #[derive(
12 Debug,
13 Clone,
14 Copy,
15 Hash,
16 PartialEq,
17 Eq,
18 PartialOrd,
19 Ord,
20 serde::Serialize,
21 serde_with::DeserializeFromStr,
22 )]
23 #[cfg_attr(feature = "juniper", derive(juniper::GraphQLScalar))]
24 pub struct $name(pub uuid::Uuid);
25
26 impl std::fmt::Display for $name {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 write!(f, "{}", self.0)
29 }
30 }
31
32 impl std::str::FromStr for $name {
33 type Err = std::convert::Infallible;
34
35 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
36 $(
37 if s == $key {
38 return Ok($value);
39 }
40 )*
41 match s.parse::<uuid::Uuid>() {
42 Ok(uuid) => Ok(Self(uuid)),
43 Err(_) => Ok(Self::from(s)),
44 }
45 }
46 }
47
48 impl<S: AsRef<str>> From<S> for $name {
50 fn from(s: S) -> Self {
51 Self(Uuid::new_v5(&$ns, s.as_ref().as_bytes()))
52 }
53 }
54
55 impl std::ops::Deref for $name {
56 type Target = uuid::Uuid;
57
58 fn deref(&self) -> &Self::Target {
59 &self.0
60 }
61 }
62
63 impl std::borrow::Borrow<uuid::Uuid> for $name {
64 fn borrow(&self) -> &uuid::Uuid {
65 &self.0
66 }
67 }
68
69 #[cfg(feature = "sqlx")]
70 impl<'q> sqlx::Encode<'q, sqlx::Postgres> for $name {
71 fn encode_by_ref(
72 &self,
73 buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>,
74 ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
75 self.0.encode(buf)
76 }
77 }
78
79 #[cfg(feature = "sqlx")]
80 impl<'r> sqlx::Decode<'r, sqlx::Postgres> for $name {
81 fn decode(
82 value: <sqlx::Postgres as sqlx::Database>::ValueRef<'r>,
83 ) -> Result<Self, sqlx::error::BoxDynError> {
84 let value: String = sqlx::Decode::<'r, sqlx::Postgres>::decode(value)?;
85 Ok($name::from(&value))
86 }
87 }
88
89 #[cfg(feature = "sqlx")]
90 impl sqlx::Type<sqlx::Postgres> for $name {
91 fn type_info() -> <sqlx::Postgres as sqlx::Database>::TypeInfo {
92 Uuid::type_info()
93 }
94 }
95
96 #[cfg(feature = "juniper")]
97 impl $name {
98 #[allow(clippy::wrong_self_convention)]
99 fn to_output<S: juniper::ScalarValue>(&self) -> juniper::Value<S> {
100 juniper::Value::scalar(self.0.to_string())
101 }
102
103 fn from_input<S>(v: &juniper::InputValue<S>) -> Result<Self, String>
104 where
105 S: juniper::ScalarValue,
106 {
107 v.as_string_value()
108 .map(|s| <Self as std::str::FromStr>::from_str(s))
109 .ok_or_else(|| format!("Expected `String`, found: {v}"))?
110 .map(|uuid| Self(*uuid))
111 .map_err(|e| e.to_string())
112 }
113
114 fn parse_token<S>(
115 value: juniper::ScalarToken<'_>,
116 ) -> juniper::ParseScalarResult<S>
117 where
118 S: juniper::ScalarValue,
119 {
120 <String as juniper::ParseScalarValue<S>>::from_str(value)
121 }
122 }
123
124 impl schemars::JsonSchema for $name {
125 fn schema_name() -> String {
126 format!("{}", stringify!($name)).to_string()
127 }
128
129 fn json_schema(
130 r#gen: &mut schemars::r#gen::SchemaGenerator,
131 ) -> schemars::schema::Schema {
132 uuid::Uuid::json_schema(r#gen)
133 }
134 }
135
136 #[cfg(feature = "rusqlite")]
137 impl rusqlite::ToSql for $name {
138 fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
139 use rusqlite::types::{ToSqlOutput, Value};
140 Ok(ToSqlOutput::Owned(Value::Text(self.to_string())))
141 }
142 }
143
144 #[cfg(feature = "tokio-postgres")]
145 impl tokio_postgres::types::ToSql for $name {
146 tokio_postgres::types::to_sql_checked!();
147
148 fn to_sql(
149 &self,
150 ty: &tokio_postgres::types::Type,
151 out: &mut bytes::BytesMut,
152 ) -> Result<
153 tokio_postgres::types::IsNull,
154 Box<dyn std::error::Error + Sync + Send>,
155 > {
156 self.0.to_sql(ty, out)
157 }
158
159 fn accepts(ty: &tokio_postgres::types::Type) -> bool {
160 Uuid::accepts(ty)
161 }
162 }
163
164 #[cfg(feature = "tokio-postgres")]
165 impl<'a> tokio_postgres::types::FromSql<'a> for $name {
166 fn from_sql(
167 ty: &tokio_postgres::types::Type,
168 raw: &'a [u8],
169 ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
170 Uuid::from_sql(ty, raw).map($name)
171 }
172
173 fn accepts(ty: &tokio_postgres::types::Type) -> bool {
174 <Uuid as postgres_types::FromSql>::accepts(ty)
175 }
176 }
177 };
178}