/ radicle / src / sql.rs
sql.rs
 1  use std::ops::Deref;
 2  use std::str::FromStr;
 3  
 4  use sqlite as sql;
 5  use sqlite::Value;
 6  
 7  use crate::identity::Id;
 8  use crate::node;
 9  use crate::node::Address;
10  
11  /// Run an SQL query inside a transaction.
12  /// Commits the transaction on success, and rolls back on error.
13  pub fn transaction<T>(
14      db: &sql::Connection,
15      query: impl FnOnce(&sql::Connection) -> Result<T, sql::Error>,
16  ) -> Result<T, sql::Error> {
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  
31  impl TryFrom<&Value> for Id {
32      type Error = sql::Error;
33  
34      fn try_from(value: &Value) -> Result<Self, Self::Error> {
35          match value {
36              Value::String(id) => Id::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("sql: invalid type for id".to_owned()),
43              }),
44          }
45      }
46  }
47  
48  impl sqlite::BindableWithIndex for &Id {
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  
54  impl 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  
60  impl 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("sql: invalid type for node features".to_owned()),
69              }),
70          }
71      }
72  }
73  
74  impl TryFrom<&sql::Value> for Address {
75      type Error = sql::Error;
76  
77      fn try_from(value: &sql::Value) -> Result<Self, Self::Error> {
78          match value {
79              sql::Value::String(s) => Address::from_str(s.as_str()).map_err(|e| sql::Error {
80                  code: None,
81                  message: Some(e.to_string()),
82              }),
83              _ => Err(sql::Error {
84                  code: None,
85                  message: Some("sql: invalid type for address".to_owned()),
86              }),
87          }
88      }
89  }
90  
91  impl sql::BindableWithIndex for &Address {
92      fn bind<I: sql::ParameterIndex>(self, stmt: &mut sql::Statement<'_>, i: I) -> sql::Result<()> {
93          self.to_string().bind(stmt, i)
94      }
95  }