1use rusqlite::{Connection, Params, Row};
2
3use crate::error::Result;
4
5pub type ID = u32;
6
7#[macro_export]
8#[doc(hidden)]
9macro_rules! _get_column {
10 ($table_name:literal, $column_name:ident, $ColTy:ty) => {
11 pub fn $column_name(self, gd: &$crate::GD) -> $crate::error::Result<$ColTy> {
12 #[expect(clippy::allow_attributes)]
13 #[allow(unused_imports)]
14 use $crate::node::Node;
15 gd.0.query_one(
16 concat!(
17 "SELECT \"",
18 stringify!($column_name),
19 "\" FROM ",
20 $table_name,
21 " WHERE i = ?"
22 ),
23 (self.i(),),
24 |a| a.get(0),
25 )
26 .map_err(|e| {
27 if e == rusqlite::Error::QueryReturnedNoRows {
28 $crate::error::Error::NoNodeOfType(self.i(), self.ty())
29 } else {
30 e.into()
31 }
32 })
33 }
34 };
35 ($table_name:literal, $fn_name:ident, $column_name:literal, $ColTy:ty) => {
36 pub fn $fn_name(self, gd: &$crate::GD) -> $crate::error::Result<$ColTy> {
37 #[expect(clippy::allow_attributes)]
38 #[allow(unused_imports)]
39 use $crate::node::Node;
40 gd.0.query_one(
41 concat!(
42 "SELECT \"",
43 $column_name,
44 "\" FROM ",
45 $table_name,
46 " WHERE i = ?"
47 ),
48 (self.i(),),
49 |a| a.get(0),
50 )
51 .map_err(|e| {
52 if e == rusqlite::Error::QueryReturnedNoRows {
53 $crate::error::Error::NoNodeOfType(self.i(), self.ty())
54 } else {
55 e.into()
56 }
57 })
58 }
59 };
60}
61#[macro_export]
62#[doc(hidden)]
63macro_rules! _get_set {
64 ($table_name:literal, $fn_name:ident, $column_name:literal, $ColTy:ty) => {
65 pub fn $fn_name(self, gd: &$crate::GD) -> $crate::error::Result<Vec<$ColTy>> {
66 #[expect(clippy::allow_attributes)]
67 #[allow(unused_imports)]
68 use $crate::node::Node;
69 match gd
70 .0
71 .prepare_cached(concat!(
72 "SELECT DISTINCT \"",
73 $column_name,
74 "\" FROM ",
75 $table_name,
76 " WHERE i = ?"
77 ))?
78 .query_and_then((self.i(),), |a| a.get(0).map_err(Into::into))
79 {
80 Ok(a) => Ok(a.collect::<$crate::error::Result<Vec<_>>>()?),
81 Err(e) => Err(e.into()),
82 }
83 }
84 };
85}
86
87#[macro_export]
88#[doc(hidden)]
89macro_rules! _get_derived_vec {
90 ($fn_name:ident, $RetTy:ty, $key:literal) => {
91 pub fn $fn_name(self, gd: &$crate::GD) -> $crate::error::Result<Vec<$RetTy>> {
92 use $crate::util::ConnectionExt;
93 gd.0.query_and_then_get_vec(include_str!($key), (self.0,), |row| Ok(row.get(0)?))
94 }
95 };
96}
97
98#[macro_export]
99#[doc(hidden)]
100macro_rules! _from_sql_for_enum {
101 ($Ty:ty) => {
102 impl rusqlite::types::FromSql for $Ty {
103 fn column_result(
104 value: rusqlite::types::ValueRef<'_>,
105 ) -> rusqlite::types::FromSqlResult<Self> {
106 use std::str::FromStr;
107 Self::from_str(value.as_str()?)
108 .map_err(|e| rusqlite::types::FromSqlError::Other(e.into()))
109 }
110 }
111 };
112}
113
114pub trait ConnectionExt {
115 fn query_and_then_get_vec<T, P, F>(&self, sql: &str, params: P, f: F) -> Result<Vec<T>>
116 where
117 P: Params,
118 F: FnMut(&Row<'_>) -> Result<T>;
119}
120impl ConnectionExt for Connection {
121 fn query_and_then_get_vec<T, P, F>(&self, sql: &str, params: P, f: F) -> Result<Vec<T>>
122 where
123 P: Params,
124 F: FnMut(&Row<'_>) -> Result<T>,
125 {
126 match self.prepare(sql)?.query_and_then(params, f) {
127 Ok(a) => Ok(a.collect::<Result<Vec<_>>>()?),
128 Err(e) => Err(e.into()),
129 }
130 }
131}