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