//! Integrates `ArrayString` with other crates' traits use crate::prelude::*; #[cfg(all(feature = "diesel-traits", feature = "std"))] use std::io::Write; #[cfg(feature = "diesel-traits")] use diesel::{expression::*, prelude::*, query_builder::*, row::Row, sql_types::*}; #[cfg(feature = "diesel-traits")] use diesel::backend::Backend; #[cfg(feature = "diesel-traits")] use diesel::deserialize::{self, FromSql, FromSqlRow, Queryable}; #[cfg(all(feature = "diesel-traits", feature = "std"))] use diesel::serialize::{self, Output, ToSql}; #[cfg(feature = "serde-traits")] use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize}; #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "serde-traits")))] #[cfg(feature = "serde-traits")] impl Serialize for ArrayString where SIZE: Capacity, { #[inline] fn serialize(&self, ser: S) -> Result { Serialize::serialize(self.as_str(), ser) } } #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "serde-traits")))] #[cfg(feature = "serde-traits")] impl<'a, SIZE> Deserialize<'a> for ArrayString where SIZE: Capacity, { #[inline] fn deserialize>(des: D) -> Result { <&str>::deserialize(des).map(Self::from_str_truncate) } } #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl Expression for ArrayString { type SqlType = VarChar; } #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl SelectableExpression for ArrayString {} #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl AppearsOnTable for ArrayString {} #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl NonAggregate for ArrayString {} #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl QueryFragment for ArrayString where SIZE: Capacity, DB: Backend + HasSqlType, { #[inline] fn walk_ast(&self, mut pass: AstPass) -> QueryResult<()> { pass.push_bind_param::(&self.as_str())?; Ok(()) } } #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl FromSql for ArrayString where SIZE: Capacity, DB: Backend, *const str: FromSql, { #[inline] fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result { let ptr: *const str = FromSql::::from_sql(bytes)?; // We know that the pointer impl will never return null Ok(Self::from_str_truncate(unsafe { &*ptr })) } } #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl FromSqlRow for ArrayString where SIZE: Capacity, DB: Backend, *const str: FromSql, { const FIELDS_NEEDED: usize = 1; #[inline] fn build_from_row>(row: &mut T) -> deserialize::Result { FromSql::::from_sql(row.take()) } } #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl Queryable for ArrayString where SIZE: Capacity, DB: Backend, *const str: FromSql, { type Row = Self; #[inline] fn build(row: Self::Row) -> Self { row } } #[cfg_attr( docs_rs_workaround, doc(cfg(all(feature = "diesel-traits", feature = "std"))) )] #[cfg(all(feature = "diesel-traits", feature = "std"))] impl ToSql for ArrayString where SIZE: Capacity, DB: Backend, { #[inline] fn to_sql(&self, out: &mut Output) -> serialize::Result { ToSql::::to_sql(self.as_str(), out) } } #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl Expression for CacheString { type SqlType = VarChar; } #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl SelectableExpression for CacheString {} #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl AppearsOnTable for CacheString {} #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl NonAggregate for CacheString {} #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl QueryFragment for CacheString where DB: Backend + HasSqlType, { #[inline] fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { self.0.walk_ast(pass) } } #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl FromSql for CacheString where DB: Backend, *const str: FromSql, { #[inline] fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result { Ok(CacheString(FromSql::from_sql(bytes)?)) } } #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl FromSqlRow for CacheString where DB: Backend, *const str: FromSql, { const FIELDS_NEEDED: usize = 1; #[inline] fn build_from_row>(row: &mut T) -> deserialize::Result { Ok(CacheString(FromSqlRow::build_from_row(row)?)) } } #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "diesel-traits")))] #[cfg(feature = "diesel-traits")] impl Queryable for CacheString where DB: Backend, *const str: FromSql, { type Row = Self; #[inline] fn build(row: Self::Row) -> Self { row } } #[cfg_attr( docs_rs_workaround, doc(cfg(all(feature = "diesel-traits", feature = "std"))) )] #[cfg(all(feature = "diesel-traits", feature = "std"))] impl ToSql for CacheString where DB: Backend, { #[inline] fn to_sql(&self, out: &mut Output) -> serialize::Result { ToSql::to_sql(&self.0, out) } } #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "serde-traits")))] #[cfg(feature = "serde-traits")] impl Serialize for CacheString { #[inline] fn serialize(&self, ser: S) -> Result { self.0.serialize(ser) } } #[cfg_attr(docs_rs_workaround, doc(cfg(feature = "serde-traits")))] #[cfg(feature = "serde-traits")] impl<'a> Deserialize<'a> for CacheString { #[inline] fn deserialize>(des: D) -> Result { Ok(CacheString(Deserialize::deserialize(des)?)) } } #[cfg(test)] mod tests { #![allow(proc_macro_derive_resolution_fallback)] #![allow(unused_import_braces)] use super::*; use crate::ArrayString; #[cfg(feature = "serde-traits")] #[derive(Serialize, Deserialize, PartialEq, Debug)] struct DeriveSerde(pub ArrayString); #[cfg(feature = "serde-traits")] #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Derive2Serde(pub CacheString); #[test] #[cfg(feature = "serde-traits")] fn serde_derive_json() { let string = serde_json::to_string(&DeriveSerde(ArrayString::try_from_str("abcdefg").unwrap())) .unwrap(); let s: DeriveSerde = serde_json::from_str(&string).unwrap(); assert_eq!( s, DeriveSerde(ArrayString::try_from_str("abcdefg").unwrap()) ); } #[test] #[cfg(feature = "serde-traits")] fn serde_derive2_json() { let string = serde_json::to_string(&Derive2Serde(CacheString( ArrayString::try_from_str("abcdefg").unwrap(), ))) .unwrap(); let s: DeriveSerde = serde_json::from_str(&string).unwrap(); assert_eq!( s, DeriveSerde(ArrayString::try_from_str("abcdefg").unwrap()) ); } #[test] #[cfg(feature = "serde-traits")] fn serde_json() { let string = serde_json::to_string(&ArrayString::::try_from_str("abcdefg").unwrap()) .unwrap(); let s: ArrayString = serde_json::from_str(&string).unwrap(); assert_eq!( s, ArrayString::::try_from_str("abcdefg").unwrap() ); } #[cfg(all(feature = "diesel-traits", feature = "std"))] use diesel::{debug_query, insert_into, mysql, pg, sqlite, update}; #[cfg(all(feature = "diesel-traits", feature = "std"))] #[macro_use] table! { derives (name) { name -> VarChar, } } #[cfg(all(feature = "diesel-traits", feature = "std"))] #[derive(Queryable, Insertable, Clone, Debug)] #[table_name = "derives"] struct DeriveDiesel { pub name: ArrayString, } #[cfg(all(feature = "diesel-traits", feature = "std"))] #[derive(Queryable, Insertable, Clone, Debug)] #[table_name = "derives"] struct Derive2Diesel { pub name: CacheString, } #[cfg(all(feature = "diesel-traits", feature = "std"))] #[derive(Queryable, Insertable, Clone, Debug)] #[table_name = "derives"] struct Derive3Diesel<'a> { pub name: &'a str, } #[cfg(all(feature = "diesel-traits", feature = "std"))] #[test] fn diesel_derive_query_compare_insert() { let array = DeriveDiesel { name: ArrayString::try_from_str("Name1").unwrap(), }; let cache = Derive2Diesel { name: CacheString(ArrayString::try_from_str("Name1").unwrap()), }; let string = Derive3Diesel { name: "Name1" }; let insert_array = insert_into(derives::table).values(&array); let insert_cache = insert_into(derives::table).values(&cache); let insert_string = insert_into(derives::table).values(&string); assert_eq!( debug_query::(&insert_array).to_string(), debug_query::(&insert_string).to_string() ); assert_eq!( debug_query::(&insert_cache).to_string(), debug_query::(&insert_string).to_string() ); assert_eq!( debug_query::(&insert_array).to_string(), debug_query::(&insert_string).to_string() ); assert_eq!( debug_query::(&insert_cache).to_string(), debug_query::(&insert_string).to_string() ); assert_eq!( debug_query::(&insert_array).to_string(), debug_query::(&insert_string).to_string() ); assert_eq!( debug_query::(&insert_cache).to_string(), debug_query::(&insert_string).to_string() ); } #[test] fn diesel_derive_query_compare_update() { let array = DeriveDiesel { name: ArrayString::try_from_str("Name1").unwrap(), }; let cache = Derive2Diesel { name: CacheString(ArrayString::try_from_str("Name1").unwrap()), }; let string = Derive3Diesel { name: "Name1" }; let update_array = update(derives::table).set(derives::name.eq(&array.name)); let update_cache = update(derives::table).set(derives::name.eq(&cache.name)); let update_string = update(derives::table).set(derives::name.eq(&string.name)); assert_eq!( debug_query::(&update_array).to_string(), debug_query::(&update_string).to_string() ); assert_eq!( debug_query::(&update_cache).to_string(), debug_query::(&update_string).to_string() ); assert_eq!( debug_query::(&update_array).to_string(), debug_query::(&update_string).to_string() ); assert_eq!( debug_query::(&update_cache).to_string(), debug_query::(&update_string).to_string() ); assert_eq!( debug_query::(&update_array).to_string(), debug_query::(&update_string).to_string() ); assert_eq!( debug_query::(&update_cache).to_string(), debug_query::(&update_string).to_string() ); } #[test] #[ignore] #[cfg(feature = "std")] fn diesel_select_query_compiles() { let conn = pg::PgConnection::establish("").unwrap(); let select_array: Vec = derives::table .select(derives::all_columns) .load(&conn) .unwrap(); let select_cache: Vec = derives::table .select(derives::all_columns) .load(&conn) .unwrap(); assert_eq!( select_cache .into_iter() .map(|d| d.name.to_string()) .collect::>(), select_array .into_iter() .map(|d| d.name.to_string()) .collect::>() ); let _: std::time::SystemTime = derives::table.select(dsl::now).first(&conn).unwrap(); let _: std::time::SystemTime = derives::table.select(dsl::now).first(&conn).unwrap(); let conn = mysql::MysqlConnection::establish("").unwrap(); let select_array: Vec = derives::table .select(derives::all_columns) .load(&conn) .unwrap(); let select_cache: Vec = derives::table .select(derives::all_columns) .load(&conn) .unwrap(); assert_eq!( select_array .into_iter() .map(|d| d.name.to_string()) .collect::>(), select_cache .into_iter() .map(|d| d.name.to_string()) .collect::>() ); } #[cfg(all(feature = "diesel-traits", feature = "std"))] #[test] fn diesel_derive_query_sqlite() { let conn = diesel::sqlite::SqliteConnection::establish(":memory:").unwrap(); let _ = diesel::sql_query("CREATE TABLE derives (name VARCHAR(32));") .execute(&conn) .unwrap(); let string = DeriveDiesel { name: ArrayString::try_from_str("Name1").unwrap(), }; let _ = insert_into(derives::table) .values(&string) .execute(&conn) .unwrap(); let queried: DeriveDiesel = derives::table.first(&conn).unwrap(); assert_eq!(queried.name.as_str(), "Name1"); } #[cfg(all(feature = "diesel-traits", feature = "std"))] #[test] fn diesel_derive2_query_sqlite() { let conn = diesel::sqlite::SqliteConnection::establish(":memory:").unwrap(); let _ = diesel::sql_query("CREATE TABLE derives (name VARCHAR(32));") .execute(&conn) .unwrap(); let string = Derive2Diesel { name: CacheString(ArrayString::try_from_str("Name1").unwrap()), }; let _ = insert_into(derives::table) .values(&string) .execute(&conn) .unwrap(); let queried: Derive2Diesel = derives::table.first(&conn).unwrap(); assert_eq!(queried.name.as_str(), "Name1"); } }