refactor db complete
This commit is contained in:
@@ -17,7 +17,7 @@ pub use error::{AuthError, CommandError, Error, RequestError, StartError};
|
|||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub database_pool: sqlite::Pool<sqlite::Sqlite>,
|
pub database_pool: sqlite::Pool,
|
||||||
pub client_state: ClientState,
|
pub client_state: ClientState,
|
||||||
pub auth_config: auth::Config,
|
pub auth_config: auth::Config,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ pub struct Inventory {
|
|||||||
|
|
||||||
impl Inventory {
|
impl Inventory {
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn load(ctx: &Context, pool: &sqlx::Pool<sqlx::Sqlite>) -> Result<Self, Error> {
|
pub async fn load(ctx: &Context, pool: &sqlite::Pool) -> Result<Self, Error> {
|
||||||
let user_id = ctx.user.id.to_string();
|
let user_id = ctx.user.id.to_string();
|
||||||
|
|
||||||
let mut categories = crate::query_all!(
|
let mut categories = crate::query_all!(
|
||||||
@@ -70,7 +70,7 @@ impl Category {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn _find(
|
pub async fn _find(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
) -> Result<Option<Category>, Error> {
|
) -> Result<Option<Category>, Error> {
|
||||||
let id_param = id.to_string();
|
let id_param = id.to_string();
|
||||||
@@ -98,11 +98,7 @@ impl Category {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn save(
|
pub async fn save(ctx: &Context, pool: &sqlite::Pool, name: &str) -> Result<Uuid, Error> {
|
||||||
ctx: &Context,
|
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
|
||||||
name: &str,
|
|
||||||
) -> Result<Uuid, Error> {
|
|
||||||
let id = Uuid::new_v4();
|
let id = Uuid::new_v4();
|
||||||
let id_param = id.to_string();
|
let id_param = id.to_string();
|
||||||
let user_id = ctx.user.id.to_string();
|
let user_id = ctx.user.id.to_string();
|
||||||
@@ -141,7 +137,7 @@ impl Category {
|
|||||||
pub async fn populate_items(
|
pub async fn populate_items(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let id = self.id.to_string();
|
let id = self.id.to_string();
|
||||||
let user_id = ctx.user.id.to_string();
|
let user_id = ctx.user.id.to_string();
|
||||||
@@ -237,11 +233,7 @@ impl TryFrom<DbInventoryItemRow> for InventoryItem {
|
|||||||
|
|
||||||
impl InventoryItem {
|
impl InventoryItem {
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn find(
|
pub async fn find(ctx: &Context, pool: &sqlite::Pool, id: Uuid) -> Result<Option<Self>, Error> {
|
||||||
ctx: &Context,
|
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
|
||||||
id: Uuid,
|
|
||||||
) -> Result<Option<Self>, Error> {
|
|
||||||
let id_param = id.to_string();
|
let id_param = id.to_string();
|
||||||
let user_id = ctx.user.id.to_string();
|
let user_id = ctx.user.id.to_string();
|
||||||
|
|
||||||
@@ -282,7 +274,7 @@ impl InventoryItem {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn name_exists(
|
pub async fn name_exists(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> Result<bool, Error> {
|
) -> Result<bool, Error> {
|
||||||
let user_id = ctx.user.id.to_string();
|
let user_id = ctx.user.id.to_string();
|
||||||
@@ -304,11 +296,7 @@ impl InventoryItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn delete(
|
pub async fn delete(ctx: &Context, pool: &sqlite::Pool, id: Uuid) -> Result<bool, Error> {
|
||||||
ctx: &Context,
|
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
|
||||||
id: Uuid,
|
|
||||||
) -> Result<bool, Error> {
|
|
||||||
let id_param = id.to_string();
|
let id_param = id.to_string();
|
||||||
let user_id = ctx.user.id.to_string();
|
let user_id = ctx.user.id.to_string();
|
||||||
let results = crate::execute!(
|
let results = crate::execute!(
|
||||||
@@ -332,7 +320,7 @@ impl InventoryItem {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn update(
|
pub async fn update(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
name: &str,
|
name: &str,
|
||||||
weight: u32,
|
weight: u32,
|
||||||
@@ -367,7 +355,7 @@ impl InventoryItem {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn save(
|
pub async fn save(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
name: &str,
|
name: &str,
|
||||||
category_id: Uuid,
|
category_id: Uuid,
|
||||||
weight: u32,
|
weight: u32,
|
||||||
@@ -402,7 +390,7 @@ impl InventoryItem {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn get_category_max_weight(
|
pub async fn get_category_max_weight(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
category_id: Uuid,
|
category_id: Uuid,
|
||||||
) -> Result<i64, Error> {
|
) -> Result<i64, Error> {
|
||||||
let user_id = ctx.user.id.to_string();
|
let user_id = ctx.user.id.to_string();
|
||||||
@@ -468,7 +456,7 @@ impl Item {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn _get_category_total_picked_weight(
|
pub async fn _get_category_total_picked_weight(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
category_id: Uuid,
|
category_id: Uuid,
|
||||||
) -> Result<i64, Error> {
|
) -> Result<i64, Error> {
|
||||||
let user_id = ctx.user.id.to_string();
|
let user_id = ctx.user.id.to_string();
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use time;
|
use time;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(sqlx::Type, PartialEq, PartialOrd, Deserialize, Debug)]
|
#[derive(sqlite::Type, PartialEq, PartialOrd, Deserialize, Debug)]
|
||||||
pub enum TripState {
|
pub enum TripState {
|
||||||
Init,
|
Init,
|
||||||
Planning,
|
Planning,
|
||||||
@@ -131,18 +131,80 @@ impl TripCategory {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn find(
|
pub async fn find(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
trip_id: Uuid,
|
trip_id: Uuid,
|
||||||
category_id: Uuid,
|
category_id: Uuid,
|
||||||
) -> Result<Option<TripCategory>, Error> {
|
) -> Result<Option<TripCategory>, Error> {
|
||||||
let mut category: Option<TripCategory> = None;
|
|
||||||
|
|
||||||
let user_id = ctx.user.id.to_string();
|
let user_id = ctx.user.id.to_string();
|
||||||
let trip_id_param = trip_id.to_string();
|
let trip_id_param = trip_id.to_string();
|
||||||
let category_id_param = category_id.to_string();
|
let category_id_param = category_id.to_string();
|
||||||
|
|
||||||
sqlx::query!(
|
struct Row {
|
||||||
"
|
category_id: String,
|
||||||
|
category_name: String,
|
||||||
|
category_description: Option<String>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
trip_id: Option<String>,
|
||||||
|
item_id: Option<String>,
|
||||||
|
item_name: Option<String>,
|
||||||
|
item_description: Option<String>,
|
||||||
|
item_weight: Option<i64>,
|
||||||
|
item_is_picked: Option<bool>,
|
||||||
|
item_is_packed: Option<bool>,
|
||||||
|
item_is_ready: Option<bool>,
|
||||||
|
item_is_new: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RowParsed {
|
||||||
|
category: TripCategory,
|
||||||
|
item: Option<TripItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Row> for RowParsed {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(row: Row) -> Result<Self, Self::Error> {
|
||||||
|
let category = inventory::Category {
|
||||||
|
id: Uuid::try_parse(&row.category_id)?,
|
||||||
|
name: row.category_name,
|
||||||
|
description: row.category_description,
|
||||||
|
items: None,
|
||||||
|
};
|
||||||
|
Ok(Self {
|
||||||
|
category: TripCategory {
|
||||||
|
category,
|
||||||
|
items: None,
|
||||||
|
},
|
||||||
|
|
||||||
|
item: match row.item_id {
|
||||||
|
Some(item_id) => Some(TripItem {
|
||||||
|
item: inventory::Item {
|
||||||
|
id: Uuid::try_parse(&item_id)?,
|
||||||
|
name: row.item_name.unwrap(),
|
||||||
|
description: row.item_description,
|
||||||
|
weight: row.item_weight.unwrap(),
|
||||||
|
category_id: Uuid::try_parse(&row.category_id)?,
|
||||||
|
},
|
||||||
|
picked: row.item_is_picked.unwrap(),
|
||||||
|
packed: row.item_is_packed.unwrap(),
|
||||||
|
ready: row.item_is_ready.unwrap(),
|
||||||
|
new: row.item_is_new.unwrap(),
|
||||||
|
}),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut rows = crate::query_all!(
|
||||||
|
sqlite::QueryClassification {
|
||||||
|
query_type: sqlite::QueryType::Select,
|
||||||
|
component: sqlite::Component::Trips,
|
||||||
|
},
|
||||||
|
pool,
|
||||||
|
Row,
|
||||||
|
RowParsed,
|
||||||
|
r#"
|
||||||
SELECT
|
SELECT
|
||||||
category.id as category_id,
|
category.id as category_id,
|
||||||
category.name as category_name,
|
category.name as category_name,
|
||||||
@@ -182,67 +244,34 @@ impl TripCategory {
|
|||||||
) AS inner
|
) AS inner
|
||||||
ON inner.category_id = category.id
|
ON inner.category_id = category.id
|
||||||
WHERE category.id = ?
|
WHERE category.id = ?
|
||||||
",
|
"#,
|
||||||
trip_id_param,
|
trip_id_param,
|
||||||
user_id,
|
user_id,
|
||||||
category_id_param
|
category_id_param
|
||||||
)
|
)
|
||||||
.fetch(pool)
|
.await?;
|
||||||
.map_ok(|row| -> Result<(), Error> {
|
|
||||||
match &category {
|
let mut category = match rows.pop() {
|
||||||
Some(_) => (),
|
None => return Ok(None),
|
||||||
None => {
|
Some(initial) => TripCategory {
|
||||||
category = Some(TripCategory {
|
category: initial.category.category,
|
||||||
category: inventory::Category {
|
items: initial.item.map(|item| vec![item]).or_else(|| Some(vec![])),
|
||||||
id: Uuid::try_parse(&row.category_id)?,
|
|
||||||
name: row.category_name,
|
|
||||||
description: row.category_description,
|
|
||||||
items: None,
|
|
||||||
},
|
},
|
||||||
items: None,
|
};
|
||||||
|
|
||||||
|
for row in rows {
|
||||||
|
let item = row.item;
|
||||||
|
category.items = category.items.or_else(|| Some(vec![]));
|
||||||
|
|
||||||
|
if let Some(item) = item {
|
||||||
|
category.items = category.items.map(|mut c| {
|
||||||
|
c.push(item);
|
||||||
|
c
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match row.item_id {
|
|
||||||
None => {
|
|
||||||
// we have an empty (unused) category which has NULL values
|
|
||||||
// for the item_id column
|
|
||||||
category.as_mut().unwrap().items = Some(vec![]);
|
|
||||||
category.as_mut().unwrap().category.items = Some(vec![]);
|
|
||||||
}
|
|
||||||
Some(item_id) => {
|
|
||||||
let item = TripItem {
|
|
||||||
item: inventory::Item {
|
|
||||||
id: Uuid::try_parse(&item_id)?,
|
|
||||||
name: row.item_name.unwrap(),
|
|
||||||
description: row.item_description,
|
|
||||||
weight: row.item_weight.unwrap(),
|
|
||||||
category_id: category.as_ref().unwrap().category.id,
|
|
||||||
},
|
|
||||||
picked: row.item_is_picked.unwrap(),
|
|
||||||
packed: row.item_is_packed.unwrap(),
|
|
||||||
ready: row.item_is_ready.unwrap(),
|
|
||||||
new: row.item_is_new.unwrap(),
|
|
||||||
};
|
|
||||||
|
|
||||||
match &mut category.as_mut().unwrap().items {
|
|
||||||
None => category.as_mut().unwrap().items = Some(vec![item]),
|
|
||||||
Some(ref mut items) => items.push(item),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(Some(category))
|
||||||
})
|
|
||||||
.try_collect::<Vec<Result<(), Error>>>()
|
|
||||||
.await?
|
|
||||||
.into_iter()
|
|
||||||
.collect::<Result<(), Error>>()?;
|
|
||||||
|
|
||||||
// this may be None if there are no results (which
|
|
||||||
// means that the category was not found)
|
|
||||||
Ok(category)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,7 +321,7 @@ impl TripItem {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn find(
|
pub async fn find(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
trip_id: Uuid,
|
trip_id: Uuid,
|
||||||
item_id: Uuid,
|
item_id: Uuid,
|
||||||
) -> Result<Option<Self>, Error> {
|
) -> Result<Option<Self>, Error> {
|
||||||
@@ -335,7 +364,7 @@ impl TripItem {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn set_state(
|
pub async fn set_state(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
trip_id: Uuid,
|
trip_id: Uuid,
|
||||||
item_id: Uuid,
|
item_id: Uuid,
|
||||||
key: TripItemStateKey,
|
key: TripItemStateKey,
|
||||||
@@ -477,7 +506,7 @@ pub enum TripAttribute {
|
|||||||
|
|
||||||
impl Trip {
|
impl Trip {
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn all(ctx: &Context, pool: &sqlx::Pool<sqlx::Sqlite>) -> Result<Vec<Trip>, Error> {
|
pub async fn all(ctx: &Context, pool: &sqlite::Pool) -> Result<Vec<Trip>, Error> {
|
||||||
let user_id = ctx.user.id.to_string();
|
let user_id = ctx.user.id.to_string();
|
||||||
crate::query_all!(
|
crate::query_all!(
|
||||||
sqlite::QueryClassification {
|
sqlite::QueryClassification {
|
||||||
@@ -507,7 +536,7 @@ impl Trip {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn find(
|
pub async fn find(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
trip_id: Uuid,
|
trip_id: Uuid,
|
||||||
) -> Result<Option<Self>, Error> {
|
) -> Result<Option<Self>, Error> {
|
||||||
let trip_id_param = trip_id.to_string();
|
let trip_id_param = trip_id.to_string();
|
||||||
@@ -541,7 +570,7 @@ impl Trip {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn trip_type_remove(
|
pub async fn trip_type_remove(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
type_id: Uuid,
|
type_id: Uuid,
|
||||||
) -> Result<bool, Error> {
|
) -> Result<bool, Error> {
|
||||||
@@ -576,7 +605,7 @@ impl Trip {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn trip_type_add(
|
pub async fn trip_type_add(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
type_id: Uuid,
|
type_id: Uuid,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
@@ -614,7 +643,7 @@ impl Trip {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn set_state(
|
pub async fn set_state(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
new_state: &TripState,
|
new_state: &TripState,
|
||||||
) -> Result<bool, Error> {
|
) -> Result<bool, Error> {
|
||||||
@@ -641,7 +670,7 @@ impl Trip {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn set_comment(
|
pub async fn set_comment(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
new_comment: &str,
|
new_comment: &str,
|
||||||
) -> Result<bool, Error> {
|
) -> Result<bool, Error> {
|
||||||
@@ -668,7 +697,7 @@ impl Trip {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn set_attribute(
|
pub async fn set_attribute(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
trip_id: Uuid,
|
trip_id: Uuid,
|
||||||
attribute: TripAttribute,
|
attribute: TripAttribute,
|
||||||
value: &str,
|
value: &str,
|
||||||
@@ -785,7 +814,7 @@ impl Trip {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn save(
|
pub async fn save(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
name: &str,
|
name: &str,
|
||||||
date_start: time::Date,
|
date_start: time::Date,
|
||||||
date_end: time::Date,
|
date_end: time::Date,
|
||||||
@@ -823,7 +852,7 @@ impl Trip {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn find_total_picked_weight(
|
pub async fn find_total_picked_weight(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
trip_id: Uuid,
|
trip_id: Uuid,
|
||||||
) -> Result<i64, Error> {
|
) -> Result<i64, Error> {
|
||||||
let user_id = ctx.user.id.to_string();
|
let user_id = ctx.user.id.to_string();
|
||||||
@@ -890,7 +919,7 @@ impl Trip {
|
|||||||
pub async fn load_trips_types(
|
pub async fn load_trips_types(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
struct Row {
|
struct Row {
|
||||||
id: String,
|
id: String,
|
||||||
@@ -956,7 +985,7 @@ impl Trip {
|
|||||||
pub async fn sync_trip_items_with_inventory(
|
pub async fn sync_trip_items_with_inventory(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// we need to get all items that are part of the inventory but not
|
// we need to get all items that are part of the inventory but not
|
||||||
// part of the trip items
|
// part of the trip items
|
||||||
@@ -1009,9 +1038,6 @@ impl Trip {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// looks like there is currently no nice way to do multiple inserts
|
|
||||||
// with sqlx. whatever, this won't matter
|
|
||||||
|
|
||||||
// only mark as new when the trip is already underway
|
// only mark as new when the trip is already underway
|
||||||
let mark_as_new = self.state != TripState::new();
|
let mark_as_new = self.state != TripState::new();
|
||||||
|
|
||||||
@@ -1056,15 +1082,80 @@ impl Trip {
|
|||||||
pub async fn load_categories(
|
pub async fn load_categories(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let mut categories: Vec<TripCategory> = vec![];
|
let mut categories: Vec<TripCategory> = vec![];
|
||||||
// we can ignore the return type as we collect into `categories`
|
// we can ignore the return type as we collect into `categories`
|
||||||
// in the `map_ok()` closure
|
// in the `map_ok()` closure
|
||||||
let id = self.id.to_string();
|
let id = self.id.to_string();
|
||||||
let user_id = ctx.user.id.to_string();
|
let user_id = ctx.user.id.to_string();
|
||||||
sqlx::query!(
|
|
||||||
"
|
struct Row {
|
||||||
|
category_id: String,
|
||||||
|
category_name: String,
|
||||||
|
category_description: Option<String>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
trip_id: Option<String>,
|
||||||
|
item_id: Option<String>,
|
||||||
|
item_name: Option<String>,
|
||||||
|
item_description: Option<String>,
|
||||||
|
item_weight: Option<i64>,
|
||||||
|
item_is_picked: Option<bool>,
|
||||||
|
item_is_packed: Option<bool>,
|
||||||
|
item_is_ready: Option<bool>,
|
||||||
|
item_is_new: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RowParsed {
|
||||||
|
category: TripCategory,
|
||||||
|
item: Option<TripItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Row> for RowParsed {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(row: Row) -> Result<Self, Self::Error> {
|
||||||
|
let category = inventory::Category {
|
||||||
|
id: Uuid::try_parse(&row.category_id)?,
|
||||||
|
name: row.category_name,
|
||||||
|
description: row.category_description,
|
||||||
|
items: None,
|
||||||
|
};
|
||||||
|
Ok(Self {
|
||||||
|
category: TripCategory {
|
||||||
|
category,
|
||||||
|
items: None,
|
||||||
|
},
|
||||||
|
|
||||||
|
item: match row.item_id {
|
||||||
|
Some(item_id) => Some(TripItem {
|
||||||
|
item: inventory::Item {
|
||||||
|
id: Uuid::try_parse(&item_id)?,
|
||||||
|
name: row.item_name.unwrap(),
|
||||||
|
description: row.item_description,
|
||||||
|
weight: row.item_weight.unwrap(),
|
||||||
|
category_id: Uuid::try_parse(&row.category_id)?,
|
||||||
|
},
|
||||||
|
picked: row.item_is_picked.unwrap(),
|
||||||
|
packed: row.item_is_packed.unwrap(),
|
||||||
|
ready: row.item_is_ready.unwrap(),
|
||||||
|
new: row.item_is_new.unwrap(),
|
||||||
|
}),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rows = crate::query_all!(
|
||||||
|
sqlite::QueryClassification {
|
||||||
|
query_type: sqlite::QueryType::Select,
|
||||||
|
component: sqlite::Component::Trips,
|
||||||
|
},
|
||||||
|
pool,
|
||||||
|
Row,
|
||||||
|
RowParsed,
|
||||||
|
r#"
|
||||||
SELECT
|
SELECT
|
||||||
category.id as category_id,
|
category.id as category_id,
|
||||||
category.name as category_name,
|
category.name as category_name,
|
||||||
@@ -1103,70 +1194,93 @@ impl Trip {
|
|||||||
) AS inner
|
) AS inner
|
||||||
ON inner.category_id = category.id
|
ON inner.category_id = category.id
|
||||||
WHERE category.user_id = ?
|
WHERE category.user_id = ?
|
||||||
",
|
"#,
|
||||||
id,
|
id,
|
||||||
user_id,
|
user_id,
|
||||||
user_id,
|
user_id,
|
||||||
)
|
)
|
||||||
.fetch(pool)
|
.await?;
|
||||||
.map_ok(|row| -> Result<(), Error> {
|
|
||||||
let mut category = TripCategory {
|
|
||||||
category: inventory::Category {
|
|
||||||
id: Uuid::try_parse(&row.category_id)?,
|
|
||||||
name: row.category_name,
|
|
||||||
description: row.category_description,
|
|
||||||
|
|
||||||
items: None,
|
for row in rows {
|
||||||
},
|
match categories
|
||||||
items: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
match row.item_id {
|
|
||||||
None => {
|
|
||||||
// we have an empty (unused) category which has NULL values
|
|
||||||
// for the item_id column
|
|
||||||
category.items = Some(vec![]);
|
|
||||||
categories.push(category);
|
|
||||||
}
|
|
||||||
Some(item_id) => {
|
|
||||||
let item = TripItem {
|
|
||||||
item: inventory::Item {
|
|
||||||
id: Uuid::try_parse(&item_id)?,
|
|
||||||
name: row.item_name.unwrap(),
|
|
||||||
description: row.item_description,
|
|
||||||
weight: row.item_weight.unwrap(),
|
|
||||||
category_id: category.category.id,
|
|
||||||
},
|
|
||||||
picked: row.item_is_picked.unwrap(),
|
|
||||||
packed: row.item_is_packed.unwrap(),
|
|
||||||
ready: row.item_is_ready.unwrap(),
|
|
||||||
new: row.item_is_new.unwrap(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(&mut ref mut c) = categories
|
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|c| c.category.id == category.category.id)
|
.find(|cat| cat.category.id == row.category.category.id)
|
||||||
{
|
{
|
||||||
// we always populate c.items when we add a new category, so
|
Some(ref mut existing_category) => {
|
||||||
// it's safe to unwrap here
|
// taking and then readding later
|
||||||
c.items.as_mut().unwrap().push(item);
|
let mut items = existing_category.items.take().unwrap_or(vec![]);
|
||||||
} else {
|
|
||||||
category.items = Some(vec![item]);
|
if let Some(item) = row.item {
|
||||||
categories.push(category);
|
items.push(item);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
existing_category.items = Some(items);
|
||||||
})
|
}
|
||||||
.try_collect::<Vec<Result<(), Error>>>()
|
None => categories.push(TripCategory {
|
||||||
.await?
|
category: row.category.category,
|
||||||
.into_iter()
|
items: row.item.map(|item| vec![item]).or_else(|| Some(vec![])),
|
||||||
.collect::<Result<(), Error>>()?;
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.categories = Some(categories);
|
self.categories = Some(categories);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
// .fetch(pool)
|
||||||
|
// .map_ok(|row| -> Result<(), Error> {
|
||||||
|
// let mut category = TripCategory {
|
||||||
|
// category: inventory::Category {
|
||||||
|
// id: Uuid::try_parse(&row.category_id)?,
|
||||||
|
// name: row.category_name,
|
||||||
|
// description: row.category_description,
|
||||||
|
|
||||||
|
// items: None,
|
||||||
|
// },
|
||||||
|
// items: None,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// match row.item_id {
|
||||||
|
// None => {
|
||||||
|
// // we have an empty (unused) category which has NULL values
|
||||||
|
// // for the item_id column
|
||||||
|
// category.items = Some(vec![]);
|
||||||
|
// categories.push(category);
|
||||||
|
// }
|
||||||
|
// Some(item_id) => {
|
||||||
|
// let item = TripItem {
|
||||||
|
// item: inventory::Item {
|
||||||
|
// id: Uuid::try_parse(&item_id)?,
|
||||||
|
// name: row.item_name.unwrap(),
|
||||||
|
// description: row.item_description,
|
||||||
|
// weight: row.item_weight.unwrap(),
|
||||||
|
// category_id: category.category.id,
|
||||||
|
// },
|
||||||
|
// picked: row.item_is_picked.unwrap(),
|
||||||
|
// packed: row.item_is_packed.unwrap(),
|
||||||
|
// ready: row.item_is_ready.unwrap(),
|
||||||
|
// new: row.item_is_new.unwrap(),
|
||||||
|
// };
|
||||||
|
|
||||||
|
// if let Some(&mut ref mut c) = categories
|
||||||
|
// .iter_mut()
|
||||||
|
// .find(|c| c.category.id == category.category.id)
|
||||||
|
// {
|
||||||
|
// // we always populate c.items when we add a new category, so
|
||||||
|
// // it's safe to unwrap here
|
||||||
|
// c.items.as_mut().unwrap().push(item);
|
||||||
|
// } else {
|
||||||
|
// category.items = Some(vec![item]);
|
||||||
|
// categories.push(category);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Ok(())
|
||||||
|
// })
|
||||||
|
// .try_collect::<Vec<Result<(), Error>>>()
|
||||||
|
// .await?
|
||||||
|
// .into_iter()
|
||||||
|
// .collect::<Result<(), Error>>()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1179,7 +1293,7 @@ pub struct TripType {
|
|||||||
|
|
||||||
impl TripsType {
|
impl TripsType {
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn all(ctx: &Context, pool: &sqlx::Pool<sqlx::Sqlite>) -> Result<Vec<Self>, Error> {
|
pub async fn all(ctx: &Context, pool: &sqlite::Pool) -> Result<Vec<Self>, Error> {
|
||||||
let user_id = ctx.user.id.to_string();
|
let user_id = ctx.user.id.to_string();
|
||||||
crate::query_all!(
|
crate::query_all!(
|
||||||
sqlite::QueryClassification {
|
sqlite::QueryClassification {
|
||||||
@@ -1200,11 +1314,7 @@ impl TripsType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn save(
|
pub async fn save(ctx: &Context, pool: &sqlite::Pool, name: &str) -> Result<Uuid, Error> {
|
||||||
ctx: &Context,
|
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
|
||||||
name: &str,
|
|
||||||
) -> Result<Uuid, Error> {
|
|
||||||
let user_id = ctx.user.id.to_string();
|
let user_id = ctx.user.id.to_string();
|
||||||
let id = Uuid::new_v4();
|
let id = Uuid::new_v4();
|
||||||
let id_param = id.to_string();
|
let id_param = id.to_string();
|
||||||
@@ -1230,7 +1340,7 @@ impl TripsType {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn set_name(
|
pub async fn set_name(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
pool: &sqlx::Pool<sqlx::Sqlite>,
|
pool: &sqlite::Pool,
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
new_name: &str,
|
new_name: &str,
|
||||||
) -> Result<bool, Error> {
|
) -> Result<bool, Error> {
|
||||||
|
|||||||
@@ -57,11 +57,16 @@ impl User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn create(pool: &sqlx::Pool<sqlx::Sqlite>, user: NewUser<'_>) -> Result<Uuid, Error> {
|
pub async fn create(pool: &sqlite::Pool, user: NewUser<'_>) -> Result<Uuid, Error> {
|
||||||
let id = Uuid::new_v4();
|
let id = Uuid::new_v4();
|
||||||
let id_param = id.to_string();
|
let id_param = id.to_string();
|
||||||
|
|
||||||
sqlx::query!(
|
crate::execute!(
|
||||||
|
sqlite::QueryClassification {
|
||||||
|
query_type: sqlite::QueryType::Insert,
|
||||||
|
component: sqlite::Component::User,
|
||||||
|
},
|
||||||
|
pool,
|
||||||
"INSERT INTO users
|
"INSERT INTO users
|
||||||
(id, username, fullname)
|
(id, username, fullname)
|
||||||
VALUES
|
VALUES
|
||||||
@@ -70,7 +75,6 @@ pub async fn create(pool: &sqlx::Pool<sqlx::Sqlite>, user: NewUser<'_>) -> Resul
|
|||||||
user.username,
|
user.username,
|
||||||
user.fullname
|
user.fullname
|
||||||
)
|
)
|
||||||
.execute(pool)
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(id)
|
Ok(id)
|
||||||
|
|||||||
@@ -7,14 +7,26 @@ use tracing::Instrument;
|
|||||||
|
|
||||||
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
|
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
|
||||||
use sqlx::ConnectOptions;
|
use sqlx::ConnectOptions;
|
||||||
pub use sqlx::{Pool, Sqlite};
|
pub use sqlx::{Pool as SqlitePool, Sqlite};
|
||||||
|
|
||||||
use std::str::FromStr as _;
|
use std::str::FromStr as _;
|
||||||
|
|
||||||
|
pub use sqlx::Type;
|
||||||
|
|
||||||
use crate::StartError;
|
use crate::StartError;
|
||||||
|
|
||||||
|
pub type Pool = sqlx::Pool<sqlx::Sqlite>;
|
||||||
|
|
||||||
|
pub fn int_to_bool(value: i32) -> bool {
|
||||||
|
match value {
|
||||||
|
0 => false,
|
||||||
|
1 => true,
|
||||||
|
_ => panic!("got invalid boolean from sqlite"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn init_database_pool(url: &str) -> Result<Pool<Sqlite>, StartError> {
|
pub async fn init_database_pool(url: &str) -> Result<Pool, StartError> {
|
||||||
async {
|
async {
|
||||||
SqlitePoolOptions::new()
|
SqlitePoolOptions::new()
|
||||||
.max_connections(5)
|
.max_connections(5)
|
||||||
|
|||||||
Reference in New Issue
Block a user