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 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
//! # Sql Helper types.
//! For some dependencies we don't want to include the rusqlite dependency so
//! we need a way to define the [`rusqlite::ToSql`] trait for types defined
//! in upstream crates.
use holochain_zome_types::prelude::*;
use rusqlite::types::ToSqlOutput;
#[cfg(test)]
mod test;
/// A helper trait for types we can't implement [`rusqlite::ToSql`]
/// for due to the orphan rule.
pub trait AsSql<'a> {
/// Convert this type to sql which might fail.
fn as_sql(&'a self) -> SqlOutput<'a>;
}
/// A trait to convert a reference of a
/// type to a sql statement for use in
/// [`rusqlite::Connection`] prepare.
pub trait ToSqlStatement {
/// Convert the reference to a statement.
fn to_sql_statement(&self) -> String;
}
#[derive(Clone, Debug, PartialEq)]
/// A wrapper around [`rusqlite::types::ToSqlOutput`].
/// This allows implementing `From<Foo> for SqlOutput`
/// for types defined outside this crate.
pub struct SqlOutput<'a>(pub ToSqlOutput<'a>);
impl<'a> rusqlite::ToSql for SqlOutput<'a> {
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
rusqlite::ToSql::to_sql(&self.0)
}
}
impl<'a, T> AsSql<'a> for T
where
SqlOutput<'a>: From<&'a T>,
T: 'a,
{
fn as_sql(&'a self) -> SqlOutput {
self.into()
}
}
impl<'a, T> AsSql<'a> for Option<T>
where
SqlOutput<'a>: From<&'a T>,
T: 'a,
{
fn as_sql(&'a self) -> SqlOutput {
match self {
Some(d) => d.into(),
None => SqlOutput(ToSqlOutput::Owned(rusqlite::types::Value::Null)),
}
}
}
fn via_display(data: &impl std::fmt::Display) -> SqlOutput {
SqlOutput(ToSqlOutput::Owned(data.to_string().into()))
}
impl<'a> From<&'a ActionType> for SqlOutput<'a> {
fn from(d: &'a ActionType) -> Self {
via_display(d)
}
}
impl<'a> From<&'a EntryType> for SqlOutput<'a> {
fn from(d: &'a EntryType) -> Self {
via_display(d)
}
}
impl<'a> From<&'a LinkTag> for SqlOutput<'a> {
fn from(d: &'a LinkTag) -> Self {
SqlOutput(ToSqlOutput::Borrowed((&d.0[..]).into()))
}
}
impl<'a, 'b> From<&'b ZomeIndex> for SqlOutput<'a> {
fn from(d: &'b ZomeIndex) -> Self {
Self(d.0.into())
}
}
impl<'a> From<&CapAccess> for SqlOutput<'a> {
fn from(value: &CapAccess) -> Self {
SqlOutput(ToSqlOutput::Owned(
value.as_variant_string().to_owned().into(),
))
}
}
impl ToSqlStatement for LinkTypeFilter {
fn to_sql_statement(&self) -> String {
match self {
LinkTypeFilter::Types(types) => {
match types
.first()
.filter(|(_, t)| types.len() == 1 && t.len() == 1)
.and_then(|(z, t)| t.first().map(|t| (z, t)))
{
Some((zome_index, link_type)) => {
format!(
" AND zome_index = {} AND link_type = {} ",
zome_index.0, link_type.0
)
}
_ => {
let mut out = types
.iter()
.flat_map(|(zome_index, types)| {
let mut types: Vec<String> = types
.iter()
.flat_map(|t| {
[format!(" link_type = {} ", t.0), "OR".to_string()]
})
.collect();
// Pop last " OR "
types.pop();
[
format!(
" ( zome_index = {} AND ({}) ) ",
zome_index.0,
types.into_iter().collect::<String>()
),
"OR".to_string(),
]
})
.collect::<Vec<_>>();
// Pop last " OR "
out.pop();
if out.is_empty() {
String::new()
} else {
format!(" AND ({}) ", out.into_iter().collect::<String>())
}
}
}
}
LinkTypeFilter::Dependencies(dependencies) => {
let mut out = dependencies
.iter()
.flat_map(|z| [format!(" zome_index = {} ", z.0), "OR".to_string()])
.collect::<Vec<_>>();
// Pop last " OR "
out.pop();
if out.is_empty() {
String::new()
} else {
format!(" AND ({}) ", out.into_iter().collect::<String>())
}
}
}
}
}