|
@@ -1,15 +1,23 @@
|
|
|
-pub use crate::DB;
|
|
|
+use crate::DB;
|
|
|
|
|
|
-#[derive(Clone, Copy, Debug)]
|
|
|
+pub mod condition;
|
|
|
+
|
|
|
+#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
|
|
|
pub struct ID(i64);
|
|
|
|
|
|
+impl rusqlite::ToSql for ID {
|
|
|
+ fn to_sql(&self) -> Result<rusqlite::types::ToSqlOutput<'_>, rusqlite::Error> {
|
|
|
+ self.0.to_sql()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
#[derive(Debug)]
|
|
|
-pub struct WithID<T: crate::model::Entity + crate::model::Entity> {
|
|
|
+pub struct WithID<T: crate::model::Entity> {
|
|
|
wrap: T,
|
|
|
id: ID,
|
|
|
}
|
|
|
|
|
|
-impl<T: crate::model::Entity + crate::model::Entity> WithID<T> {
|
|
|
+impl<T: crate::model::Entity> WithID<T> {
|
|
|
fn wrap(what: T, raw_id: i64) -> Self {
|
|
|
Self {
|
|
|
wrap: what,
|
|
@@ -18,26 +26,26 @@ impl<T: crate::model::Entity + crate::model::Entity> WithID<T> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl<T: crate::model::Entity + crate::model::Entity> WithID<T> {
|
|
|
+impl<T: crate::model::Entity> WithID<T> {
|
|
|
pub fn id(&self) -> ID {
|
|
|
self.id
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl<T: crate::model::Entity + crate::model::Entity> AsRef<T> for WithID<T> {
|
|
|
+impl<T: crate::model::Entity> AsRef<T> for WithID<T> {
|
|
|
fn as_ref(&self) -> &T {
|
|
|
&self.wrap
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl<T: crate::model::Entity + crate::model::Entity> std::ops::Deref for WithID<T> {
|
|
|
+impl<T: crate::model::Entity> std::ops::Deref for WithID<T> {
|
|
|
type Target = T;
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
&self.wrap
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl<T: crate::model::Entity + crate::model::Entity> std::ops::DerefMut for WithID<T> {
|
|
|
+impl<T: crate::model::Entity> std::ops::DerefMut for WithID<T> {
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
&mut self.wrap
|
|
|
}
|
|
@@ -70,6 +78,58 @@ pub fn get_one_by<T: crate::model::Entity, V: rusqlite::ToSql>(
|
|
|
result.ok()
|
|
|
}
|
|
|
|
|
|
+/// Search for all entities matching a property
|
|
|
+pub fn get_all_by<T: crate::model::Entity, V: rusqlite::ToSql>(
|
|
|
+ db: &DB,
|
|
|
+ c: <T as crate::model::Entity>::Column,
|
|
|
+ val: V) -> Option<Vec<WithID<T>>> {
|
|
|
+
|
|
|
+ let table_name = <T as crate::model::Entity>::table_name();
|
|
|
+ let column_name = <T as crate::model::Entity>::name(c);
|
|
|
+ let mut prepared = db
|
|
|
+ .conn
|
|
|
+ .prepare(&format!(
|
|
|
+ "SELECT rowid, tbl.* FROM {} tbl WHERE {} = ?1",
|
|
|
+ table_name, column_name
|
|
|
+ ))
|
|
|
+ .ok()?;
|
|
|
+
|
|
|
+ let rows = prepared.query_map([&val], |row| {
|
|
|
+ let mut deser = crate::model::load::RowDeserializer::from_row(row);
|
|
|
+ Ok(WithID::wrap(
|
|
|
+ T::deserialize(&mut deser)?,
|
|
|
+ row.get(0).expect("can get rowid"),
|
|
|
+ ))
|
|
|
+ }).ok()?;
|
|
|
+
|
|
|
+ Some(rows.map(|x| x.unwrap()).collect())
|
|
|
+}
|
|
|
+
|
|
|
+/// Search for an entity by ID
|
|
|
+pub fn get_one_by_id<T: crate::model::Entity>(
|
|
|
+ db: &DB,
|
|
|
+ id: ID
|
|
|
+) -> Option<WithID<T>> {
|
|
|
+ let table_name = <T as crate::model::Entity>::table_name();
|
|
|
+ let mut prepared = db
|
|
|
+ .conn
|
|
|
+ .prepare(&format!(
|
|
|
+ "SELECT rowid, tbl.* FROM {} tbl WHERE rowid = ?1",
|
|
|
+ table_name
|
|
|
+ ))
|
|
|
+ .ok()?;
|
|
|
+
|
|
|
+ let result = prepared.query_row([&id.0], |row| {
|
|
|
+ let mut deser = crate::model::load::RowDeserializer::from_row(row);
|
|
|
+ Ok(WithID::wrap(
|
|
|
+ T::deserialize(&mut deser).expect("deserialization works"),
|
|
|
+ row.get(0).expect("can get rowid"),
|
|
|
+ ))
|
|
|
+ });
|
|
|
+
|
|
|
+ result.ok()
|
|
|
+}
|
|
|
+
|
|
|
/// Add an entity to its table
|
|
|
pub fn add<T: crate::model::Entity + serde::Serialize>(db: &DB, m: &T) -> Option<ID> {
|
|
|
let row = crate::model::store::serialize_as_row(m);
|
|
@@ -108,4 +168,14 @@ impl<'a> Context<'a> {
|
|
|
) -> Option<WithID<T>> {
|
|
|
get_one_by(self.db, c, val)
|
|
|
}
|
|
|
+
|
|
|
+ pub fn get_one_by_id<
|
|
|
+ T: crate::model::Entity + for<'de> serde::Deserialize<'de>,
|
|
|
+ V: rusqlite::ToSql,
|
|
|
+ >(
|
|
|
+ &self,
|
|
|
+ id: ID,
|
|
|
+ ) -> Option<WithID<T>> {
|
|
|
+ get_one_by_id(self.db, id)
|
|
|
+ }
|
|
|
}
|