1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use std::ops::Deref;
use std::str::FromStr;

use sqlite as sql;
use sqlite::Value;

use crate::identity::RepoId;
use crate::node;
use crate::node::{Address, UserAgent};

/// Run an SQL query inside a transaction.
/// Commits the transaction on success, and rolls back on error.
pub fn transaction<T, E: From<sql::Error>>(
    db: &sql::Connection,
    query: impl FnOnce(&sql::Connection) -> Result<T, E>,
) -> Result<T, E> {
    db.execute("BEGIN")?;

    match query(db) {
        Ok(result) => {
            db.execute("COMMIT")?;
            Ok(result)
        }
        Err(err) => {
            db.execute("ROLLBACK")?;
            Err(err)
        }
    }
}

impl TryFrom<&Value> for RepoId {
    type Error = sql::Error;

    fn try_from(value: &Value) -> Result<Self, Self::Error> {
        match value {
            Value::String(id) => RepoId::from_urn(id).map_err(|e| sql::Error {
                code: None,
                message: Some(e.to_string()),
            }),
            _ => Err(sql::Error {
                code: None,
                message: Some(format!("sql: invalid type `{:?}` for id", value.kind())),
            }),
        }
    }
}

impl sqlite::BindableWithIndex for &RepoId {
    fn bind<I: sql::ParameterIndex>(self, stmt: &mut sql::Statement<'_>, i: I) -> sql::Result<()> {
        self.urn().as_str().bind(stmt, i)
    }
}

impl sql::BindableWithIndex for node::Features {
    fn bind<I: sql::ParameterIndex>(self, stmt: &mut sql::Statement<'_>, i: I) -> sql::Result<()> {
        (*self.deref() as i64).bind(stmt, i)
    }
}

impl TryFrom<&Value> for node::Features {
    type Error = sql::Error;

    fn try_from(value: &Value) -> Result<Self, Self::Error> {
        match value {
            Value::Integer(bits) => Ok(node::Features::from(*bits as u64)),
            _ => Err(sql::Error {
                code: None,
                message: Some(format!(
                    "sql: invalid type `{:?}` for node features",
                    value.kind()
                )),
            }),
        }
    }
}

impl TryFrom<&sql::Value> for Address {
    type Error = sql::Error;

    fn try_from(value: &sql::Value) -> Result<Self, Self::Error> {
        match value {
            sql::Value::String(s) => Address::from_str(s.as_str()).map_err(|e| sql::Error {
                code: None,
                message: Some(e.to_string()),
            }),
            _ => Err(sql::Error {
                code: None,
                message: Some(format!(
                    "sql: invalid type `{:?}` for address",
                    value.kind()
                )),
            }),
        }
    }
}

impl sql::BindableWithIndex for &Address {
    fn bind<I: sql::ParameterIndex>(self, stmt: &mut sql::Statement<'_>, i: I) -> sql::Result<()> {
        self.to_string().bind(stmt, i)
    }
}

impl TryFrom<&Value> for UserAgent {
    type Error = sql::Error;

    fn try_from(value: &Value) -> Result<Self, Self::Error> {
        match value {
            Value::String(ua) => UserAgent::from_str(ua).map_err(|e| sql::Error {
                code: None,
                message: Some(e.to_string()),
            }),
            _ => Err(sql::Error {
                code: None,
                message: Some(format!(
                    "sql: invalid type `{:?}` for user-agent",
                    value.kind()
                )),
            }),
        }
    }
}