tag2upload_service_manager/
bsql_rows.rs
use crate::prelude::*;
use crate::bsql_queries::*;
#[derive(Clone, Copy)]
pub struct SkipRowId;
pub struct AsBSqlColumnData<'r> {
pub name: &'r str,
pub value: DynToSql<'r>,
pub isnt_row_id: Result<(), SkipRowId>,
}
pub trait AsBSqlRow: Sync {
fn as_sql_row_columns(&self) -> impl Iterator<Item=AsBSqlColumnData<'_>>;
}
#[derive(Clone, Copy)]
pub struct AsBSqlRowParams<'r, R: AsBSqlRow> {
pub row: &'r R,
pub include_row_id: Result<(), bsql_rows::SkipRowId>,
}
impl<R: AsBSqlRow> AsBSqlRowParams<'_, R> {
fn cols(&self) -> impl Iterator<Item = AsBSqlColumnData> {
self.row.as_sql_row_columns()
.filter(|c| {
c.isnt_row_id
.or_else(|_: SkipRowId| self.include_row_id)
.is_ok()
})
}
}
impl<R: AsBSqlRow> IsFragment for AsBSqlRowParams<'_, R> {
fn bsql_extend_text(&self, s: &mut String) {
*s += "(";
extend_texts_sep_commas(s, self.cols().map(|c| c.name));
*s += ") VALUES (";
extend_texts_sep_commas(s, self.cols().map(|_c| "?"));
*s += ")";
}
fn bsql_extend_params<'v, 'p: 'v>(&'p self, p: &mut Vec<DynToSql<'v>>) {
for c in self.cols() {
p.push(c.value)
}
}
fn bsql_note_locs(&self, _la: &mut CodeLocationAccumulator) {}
}
define_derive_deftly! {
export AsBSqlRow for struct, expect items:
impl $crate::bsql_rows::AsBSqlRow for $ttype {
fn as_sql_row_columns(&self) -> impl Iterator<
Item=$crate::bsql_rows::AsBSqlColumnData<'_>
> {
use $crate::prelude::*;
use bsql_rows::*;
chain!(
$(
${if fmeta(bsql(flatten)) {
AsBSqlRow::as_sql_row_columns(&self.$fname)
} else {
[AsBSqlColumnData {
name: stringify!($fname),
value: &self.$fname,
isnt_row_id: ${if fmeta(bsql(rowid)) {
Err(SkipRowId)
} else {
Ok(())
}},
}]
}}
,
)
)
}
}
}
pub struct BSqlUpdateColumnData<'u> {
pub name: &'u str,
pub value: DynToSql<'u>,
}
pub trait HasUpdateSqlRow {
type UpdateSqlRow<'u>: UpdateSqlRow + Default;
}
pub trait UpdateSqlRow: Sized + Sync {
fn bsql(&self) -> Option<impl AsFragment + '_> {
let _: BSqlUpdateColumnData = self.bsql_update_row_columns().next()?;
Some(Update(self))
}
fn bsql_update_row_columns(
&self
) -> impl Iterator<Item=BSqlUpdateColumnData<'_>>;
}
struct Update<'u, U: UpdateSqlRow>(&'u U);
#[macro_export]
macro_rules! bsql_update {
{
let $bind:ident = $tname:ident { $($body:tt)+ } $(;)?
} => {
let $bind = $crate::bsql_update! { $tname { $($body)+ } };
let $bind = $bind.bsql().expect("nonempty by construction");
};
{
$tname:ident { $(
$fname:ident $( : $value:expr )?
),* $(,)?
}
} => { paste!{
[< $tname Update >] {
$(
$fname: $crate::bsql_update!(@ $fname $( : $value )? ),
)*
..Default::default()
}
} };
{ @ $fname:ident } => { $crate::bsql_update!(@ $fname: $fname ) };
{ @ $fname:ident: $value:expr } => {
std::option::Option::Some(
std::convert::From::from(
$value
)
)
}
}
impl<'u, U: UpdateSqlRow> AsFragment for Update<'u, U> {
type F<'uu> = Self where Self: 'uu;
fn as_fragment(&self) -> Self::F<'_> { Update(self.0) }
}
impl<'u, U: UpdateSqlRow> IsFragment for Update<'u, U> {
fn bsql_extend_text(&self, s: &mut String) {
for (sep, c) in izip!(
chain!([""], iter::repeat(", ")),
self.0.bsql_update_row_columns(),
) {
write!(s, "{sep}{} = ?", c.name).expect("write to string failed");
}
}
fn bsql_extend_params<'v, 'p: 'v>(&'p self, p: &mut Vec<DynToSql<'v>>) {
for c in self.0.bsql_update_row_columns() {
p.push(c.value)
}
}
fn bsql_note_locs(&self, _la: &mut CodeLocationAccumulator) {}
}
define_derive_deftly! {
export UpdateSqlRow for struct, expect items:
#[derive(Clone, Default)]
$tvis struct $<$tname Update><'update_sql_row, $tdefgens> { $(
${when not(fmeta(bsql(rowid)))}
$fvis $fname: std::option::Option<
${if fmeta(bsql(flatten)) {
<$ftype as $crate::bsql_rows::HasUpdateSqlRow>
::UpdateSqlRow<'update_sql_row>
} else {
$crate::prelude::MaybeOwned<'update_sql_row, $ftype>
}}
>,
) }
impl<$tgens> $crate::bsql_rows::HasUpdateSqlRow for $ttype {
type UpdateSqlRow<'update_sql_row> =
$<$tname Update><'update_sql_row, $tgens>;
}
impl<$tgens> $crate::bsql_rows::UpdateSqlRow
for $<$tname Update><'_, $tgens>
{
fn bsql_update_row_columns(
&self
) -> impl Iterator<Item=$crate::bsql_rows::BSqlUpdateColumnData<'_>> {
#[allow(unused_imports)] use $crate::bsql_rows::*;
chain!( $(
${when not(fmeta(bsql(rowid)))}
self.$fname.as_ref().map(|v| {
${if fmeta(bsql(flatten)) {
UpdateSqlRow::bsql_update_row_columns(v)
} else {
[BSqlUpdateColumnData {
name: stringify!($fname),
value: &**v as _,
}]
}}
}).into_iter().flatten(),
) )
}
}
impl<'update_sql_row, $tgens> std::ops::BitOr
for $<$tname Update><'update_sql_row, $tgens>
{
type Output = $<$tname Update><'update_sql_row, $tgens>;
fn bitor(self, rhs: Self) -> Self::Output {
$<$tname Update> { $(
${when not(fmeta(bsql(rowid)))}
${if fmeta(bsql(flatten)) {
$fname: Some(
self.$fname.unwrap_or_default() |
rhs.$fname.unwrap_or_default()
)
} else {
$fname: self.$fname.or(rhs.$fname)
}}
,
) }
}
}
}
pub trait FromSqlRow: Sized {
fn from_sql_row(row: &rusqlite::Row) -> Result<Self, InternalError>;
}
define_derive_deftly! {
export FromSqlRow for struct, expect items:
impl FromSqlRow for $ttype {
fn from_sql_row(row: &rusqlite::Row) -> Result<Self, InternalError> {
Ok($ttype {
$(
$fname:
${if fmeta(bsql(flatten)) {
FromSqlRow::from_sql_row(row)?
} else {
row.get(stringify!($fname))
.into_internal(concat!(
"failed to convert ",
stringify!($fname),
))?
}},
)
})
}
}
}