#![allow(unused)] use query::{quick_query::QuickQuery, sql_part::{ToSqlPart, WhereItemToSqlPart}, Accept, Query, WhereItem}; use sqlx::MySql; use std::{marker::PhantomData, mem::take}; pub trait DebugAny where Self: std::fmt::Debug, { fn ty(&self) -> &'static str; fn debug(&self) -> String { format!("{}: {:?}", self.ty(), self) } } #[derive(Debug, Default)] pub struct MockMySql; impl ToSqlPart for WhereItemToSqlPart where T: WhereItem + 'static, { fn to_sql_part( self, ctx: &mut >::Context1, ) -> >::SqlPart { let ctx2 = unsafe { &mut *(ctx as *mut _) }; let item = self.0.where_item(ctx2); Box::new(move |ctx2| item(ctx2)) } } impl Query for MockMySql { type SqlPart = Box String>; type Context1 = Vec Box>>>; type Context2 = ( Vec Box>>>, Vec>, ); fn build_sql_part_back( ctx: &mut Self::Context2, from: Self::SqlPart, ) -> String { from(ctx) } type Output = (); fn build_query( _: Self::Context1, _: impl FnOnce(&mut Self::Context2) -> String, ) -> (String, Self::Output) { panic!("just an example") } } macro_rules! debug_any { ($($ident:ident), *) => { $(impl DebugAny for $ident where $ident: std::fmt::Debug { fn ty(&self) -> &'static str { stringify!($ident) } })* }; } debug_any!(String, i8, i16, i32, u8, u16, u32, u64, bool); impl Accept for MockMySql where A: FnOnce() -> T + 'static, T: DebugAny + 'static, { fn accept( this: A, ctx1: &mut Self::Context1, ) -> impl FnOnce(&mut Self::Context2) -> String + 'static { ctx1.push(Some(Box::new(|| Box::new(this())))); let len = ctx1.len(); move |ctx2| { let found = take(ctx2.0.get_mut(len - 1).expect("overflow")) .unwrap(); let found = found(); ctx2.1.push(found); "?".to_string() } } } struct Condition(A1, A2); impl WhereItem for Condition where MockMySql: Accept, MockMySql: Accept, { fn where_item( self, ctx: &mut >::Context1, ) -> impl FnOnce( &mut >::Context2, ) -> String { let ctx1 = unsafe { &mut *(ctx as *mut _) }; let s1 = >::accept( self.0, ctx1, ); let ctx2 = unsafe { &mut *(ctx as *mut _) }; let s2 = >::accept( self.1, ctx2, ); |ctx2| { if B { format!("{} AND {}", s1(ctx2), s2(ctx2)) } else { format!("{} AND {}", s2(ctx2), s1(ctx2)) } } } } #[test] fn test() { let mut ctx = Default::default(); let ctx_mut = unsafe { &mut *((&mut ctx) as *mut _) }; let part1 = Condition::(|| 3, || "hello".to_string()) .where_item(ctx_mut); let ctx_mut2 = unsafe { &mut *((&mut ctx) as *mut _) }; let part2 = Condition::(|| 3, || "hello".to_string()) .where_item(ctx_mut2); let mut ctx2: >::Context2 = (ctx, Default::default()); let res_str1 = part1(&mut ctx2); let _ = part2(&mut ctx2); let res_val = ctx2 .1 .into_iter() .map(|e| e.debug()) .collect::>(); assert_eq!(res_str1, "? AND ?"); assert_eq!( res_val, vec![ "i32: 3".to_string(), "String: \"hello\"".to_string(), "String: \"hello\"".to_string(), "i32: 3".to_string(), ] ); } struct WhereClause> { columns: Vec, args: Q::Context1, _pd: PhantomData, } impl> Default for WhereClause { fn default() -> Self { Self { columns: Default::default(), args: Default::default(), _pd: PhantomData, } } } impl WhereClause where S: 'static, MockMySql: Query< S, Context2: 'static, Context1: 'static, SqlPart = Box< dyn FnOnce( &mut >::Context2, ) -> String, >, >, { fn item( &mut self, item: impl WhereItem + 'static, ) { let mut_arg = unsafe { &mut *(&mut self.args as *mut _) }; let part = MockMySql::handle_where_item(item, mut_arg); self.columns.push(part); } } struct WhereEx(T); impl WhereItem for WhereEx where Q: Query, Q: Accept, { fn where_item( self, ctx: &mut Q::Context1, ) -> impl FnOnce(&mut Q::Context2) -> String { let ctx1 = unsafe { &mut *(ctx as *mut _) }; let s1 = >::accept(self.0, ctx1); move |ctx2| s1(ctx2) } } #[test] fn test_where_clause() { let mut where_clause = WhereClause::default(); where_clause.item(WhereEx(|| 3)); where_clause.item(WhereEx(|| "hello".to_string())); let mut ctx2: >::Context2 = (where_clause.args, Default::default()); let res = where_clause .columns .into_iter() .map(|e| MockMySql::build_sql_part_back(&mut ctx2, e)); let res = res.collect::>(); assert_eq!(res, vec!["?", "?"]); }