Skip to main content

radicle/
sql.rs

1use std::ops::Deref;
2use std::str::FromStr;
3
4use sqlite as sql;
5use sqlite::Value;
6
7use crate::node;
8use crate::node::{Address, UserAgent};
9
10/// Run an SQL query inside a transaction.
11/// Commits the transaction on success, and rolls back on error.
12pub fn transaction<T, E: From<sql::Error>>(
13    db: &sql::Connection,
14    query: impl FnOnce(&sql::Connection) -> Result<T, E>,
15) -> Result<T, E> {
16    db.execute("BEGIN")?;
17
18    match query(db) {
19        Ok(result) => {
20            db.execute("COMMIT")?;
21            Ok(result)
22        }
23        Err(err) => {
24            db.execute("ROLLBACK")?;
25            Err(err)
26        }
27    }
28}
29
30impl sql::BindableWithIndex for node::Features {
31    fn bind<I: sql::ParameterIndex>(self, stmt: &mut sql::Statement<'_>, i: I) -> sql::Result<()> {
32        (*self.deref() as i64).bind(stmt, i)
33    }
34}
35
36impl TryFrom<&Value> for node::Features {
37    type Error = sql::Error;
38
39    fn try_from(value: &Value) -> Result<Self, Self::Error> {
40        match value {
41            Value::Integer(bits) => Ok(node::Features::from(*bits as u64)),
42            _ => Err(sql::Error {
43                code: None,
44                message: Some(format!(
45                    "sql: invalid type `{:?}` for node features",
46                    value.kind()
47                )),
48            }),
49        }
50    }
51}
52
53impl TryFrom<&sql::Value> for Address {
54    type Error = sql::Error;
55
56    fn try_from(value: &sql::Value) -> Result<Self, Self::Error> {
57        match value {
58            sql::Value::String(s) => Address::from_str(s.as_str()).map_err(|e| sql::Error {
59                code: None,
60                message: Some(e.to_string()),
61            }),
62            _ => Err(sql::Error {
63                code: None,
64                message: Some(format!(
65                    "sql: invalid type `{:?}` for address",
66                    value.kind()
67                )),
68            }),
69        }
70    }
71}
72
73impl sql::BindableWithIndex for &Address {
74    fn bind<I: sql::ParameterIndex>(self, stmt: &mut sql::Statement<'_>, i: I) -> sql::Result<()> {
75        self.to_string().bind(stmt, i)
76    }
77}
78
79impl TryFrom<&Value> for UserAgent {
80    type Error = sql::Error;
81
82    fn try_from(value: &Value) -> Result<Self, Self::Error> {
83        match value {
84            Value::String(ua) => UserAgent::from_str(ua).map_err(|e| sql::Error {
85                code: None,
86                message: Some(e.to_string()),
87            }),
88            _ => Err(sql::Error {
89                code: None,
90                message: Some(format!(
91                    "sql: invalid type `{:?}` for user-agent",
92                    value.kind()
93                )),
94            }),
95        }
96    }
97}