This commit is contained in:
2023-05-18 00:11:52 +02:00
parent f03814e622
commit 0ddeac69e6
7 changed files with 692 additions and 81 deletions

View File

@@ -1,7 +1,13 @@
use sqlx::{sqlite::SqliteRow, Row};
use sqlx::{
database::Database,
database::HasValueRef,
sqlite::{Sqlite, SqliteRow},
Decode, Row,
};
use std::convert;
use std::error;
use std::fmt;
use std::str::FromStr;
use uuid::Uuid;
use sqlx::sqlite::SqlitePoolOptions;
@@ -13,6 +19,7 @@ pub enum Error {
SqlError { description: String },
UuidError { description: String },
NotFoundError { description: String },
InvalidEnumError { description: String },
}
impl fmt::Display for Error {
@@ -27,6 +34,9 @@ impl fmt::Display for Error {
Self::NotFoundError { description } => {
write!(f, "Not found: {description}")
}
Self::InvalidEnumError { description } => {
write!(f, "Enum error: {description}")
}
}
}
}
@@ -56,11 +66,41 @@ impl convert::From<sqlx::Error> for Error {
impl error::Error for Error {}
#[derive(sqlx::Type)]
pub enum TripState {
Planning,
Planned,
Active,
Review,
Done,
}
impl fmt::Display for TripState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::Planning => "Planning",
Self::Planned => "Planned",
Self::Active => "Active",
Self::Review => "Review",
Self::Done => "Done",
},
)
}
}
pub struct Trip {
pub id: Uuid,
pub name: String,
pub start_date: time::Date,
pub end_date: time::Date,
pub state: TripState,
pub location: String,
pub temp_min: i32,
pub temp_max: i32,
types: Option<Vec<TripType>>,
}
impl TryFrom<SqliteRow> for Trip {
@@ -70,18 +110,91 @@ impl TryFrom<SqliteRow> for Trip {
let name: &str = row.try_get("name")?;
let id: &str = row.try_get("id")?;
let start_date: time::Date = row.try_get("start_date")?;
let end_date: time::Date = row.try_get("start_date")?;
let end_date: time::Date = row.try_get("end_date")?;
let state: TripState = row.try_get("state")?;
let location = row.try_get("location")?;
let temp_min = row.try_get("temp_min")?;
let temp_max = row.try_get("temp_max")?;
let id: Uuid = Uuid::try_parse(id)?;
Ok(Trip {
name: name.to_string(),
id,
name: name.to_string(),
start_date,
end_date,
state,
location,
temp_min,
temp_max,
types: None,
})
}
}
impl<'a> Trip {
pub fn types(&'a self) -> &Vec<TripType> {
self.types
.as_ref()
.expect("you need to call load_triptypes()")
}
}
impl<'a> Trip {
pub async fn load_triptypes(
&'a mut self,
pool: &sqlx::Pool<sqlx::Sqlite>,
) -> Result<(), Error> {
let types = sqlx::query(
"
SELECT
type.id as id,
type.name as name,
CASE WHEN inner.id IS NOT NULL THEN true ELSE false END AS active
FROM triptypes AS type
LEFT JOIN (
SELECT type.id as id, type.name as name
FROM trips as trip
INNER JOIN trips_to_triptypes as ttt
ON ttt.trip_id = trip.id
INNER JOIN triptypes AS type
ON type.id == ttt.trip_type_id
WHERE trip.id = ?
) AS inner
ON inner.id = type.id
",
)
.bind(self.id.to_string())
.fetch(pool)
.map_ok(std::convert::TryInto::try_into)
.try_collect::<Vec<Result<TripType, Error>>>()
.await?
.into_iter()
.collect::<Result<Vec<TripType>, Error>>()?;
self.types = Some(types);
Ok(())
}
}
pub struct TripType {
pub id: Uuid,
pub name: String,
pub active: bool,
}
impl TryFrom<SqliteRow> for TripType {
type Error = Error;
fn try_from(row: SqliteRow) -> Result<Self, Self::Error> {
let id: Uuid = Uuid::try_parse(row.try_get("id")?)?;
let name: String = row.try_get::<&str, _>("name")?.to_string();
let active: bool = row.try_get("active")?;
Ok(Self { id, name, active })
}
}
#[derive(Debug)]
pub struct Category {
pub id: Uuid,