diff --git a/identity-api-rs/Cargo.toml b/identity-api-rs/Cargo.toml index 4bee58b..793ad75 100644 --- a/identity-api-rs/Cargo.toml +++ b/identity-api-rs/Cargo.toml @@ -8,4 +8,6 @@ axum = { version = "0.7" } chrono = "0.4" diesel = { version = "2.2", features = ["sqlite", "returning_clauses_for_sqlite_3_35", "chrono"] } dotenvy = "0.15" +serde = { version = "1", features = ["derive"] } +serde_json = "1" tokio = { version = "1", features = ["full"] } \ No newline at end of file diff --git a/identity-api-rs/src/database/list.rs b/identity-api-rs/src/database/list.rs index a1e450a..c6d48af 100644 --- a/identity-api-rs/src/database/list.rs +++ b/identity-api-rs/src/database/list.rs @@ -1,69 +1,40 @@ -use std::fmt::{Display, Write}; +use std::fmt::Display; use diesel::{ backend::Backend, deserialize::{FromSql, FromSqlRow}, serialize::ToSql, sql_types::Text, sqlite::Sqlite }; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; -#[derive(FromSqlRow, Debug, Clone)] +#[derive(FromSqlRow, Deserialize, Serialize, Debug, Clone)] +#[serde(transparent)] #[repr(transparent)] -pub struct List(pub Vec); +pub struct List(pub Vec); -impl Display for List { +impl List { + fn from_vec(vec: Vec) -> Self { + Self(vec) + } + + fn into_vec(self) -> Vec { + self.0 + } +} + +impl Display for List { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_char('[')?; - f.write_str( - &self - .0 - .iter() - .map(|v| "\"".to_owned() + &v.replace("\"", "\\\"") + "\"") - .collect::>() - .join(",") - )?; - f.write_char(']') + f.write_str(&serde_json::to_string(&self).unwrap()) } } -impl From for List { - fn from(mut value: String) -> Self { - if value.starts_with('[') { - value.remove(0); - } - - if value.ends_with(']') { - value.pop(); - } - - let char_len = value.chars().count(); - let mut values = Vec::new(); - let mut current = String::new(); - let mut writing_str = false; - let mut escape_next = false; - - for (i, ch) in value.chars().enumerate() { - if i == char_len - 1 { - values.push(current); - current = String::new(); - } else if escape_next { - current.push(ch); - escape_next = false; - } else if ch == '\\' { - escape_next = true; - } else if ch == '\"' { - writing_str = !writing_str; - } else if ch == ',' && !writing_str { - values.push(current); - current = String::new(); - } else if writing_str { - current.push(ch); - } - } - - List(values) - } -} - -impl FromSql for List +impl From for List where - String: FromSql, + A: DeserializeOwned + std::fmt::Debug + std::clone::Clone, +{ + fn from(value: String) -> Self { + serde_json::from_str(&value).expect("failed to deserialize list") + } +} + +impl FromSql for List { fn from_sql(bytes: ::RawValue<'_>) -> diesel::deserialize::Result { let str = >::from_sql(bytes)?; @@ -71,9 +42,7 @@ where } } -impl ToSql for List -where - String: ToSql, +impl ToSql for List { fn to_sql<'b>( &'b self, diff --git a/identity-api-rs/src/database/models.rs b/identity-api-rs/src/database/models.rs index 6166ef8..f506f04 100644 --- a/identity-api-rs/src/database/models.rs +++ b/identity-api-rs/src/database/models.rs @@ -16,10 +16,23 @@ use chrono::NaiveDateTime; use diesel::prelude::*; +use serde::{Deserialize, Serialize}; use crate::database::schema; use crate::database::list::List; +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UniversalId { + provider: String, + id: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LocationCoordinates { + latitude: f64, + longitude: f64, +} + #[derive(Queryable, Selectable)] #[diesel(table_name = schema::date_entries)] #[diesel(check_for_backend(diesel::sqlite::Sqlite))] @@ -44,3 +57,72 @@ pub struct Entry { location_entry: Option, date_entry: Option, } + +#[derive(Queryable, Selectable)] +#[diesel(table_name = schema::heirs)] +#[diesel(check_for_backend(diesel::sqlite::Sqlite))] +pub struct Heir { + id: String, + user_id: String, + created_at: NaiveDateTime, + name: String, + email: Option, +} + +#[derive(Queryable, Selectable)] +#[diesel(table_name = schema::limits)] +#[diesel(check_for_backend(diesel::sqlite::Sqlite))] +pub struct Limit { + id: String, + current_asset_count: i32, + max_asset_count: i32, +} + +#[derive(Queryable, Selectable)] +#[diesel(table_name = schema::location_entries)] +#[diesel(check_for_backend(diesel::sqlite::Sqlite))] +pub struct LocationEntry { + id: String, + location_text: Option, + /// JSON value: { latitude: number, longitude: number } + location_coordinates: Option, +} + +impl LocationEntry { + pub fn location_coordinates(&self) -> Option { + self.location_coordinates.as_ref().map(|v| serde_json::from_str(&v).unwrap()) + } +} + +#[derive(Queryable, Selectable)] +#[diesel(table_name = schema::music_entries)] +#[diesel(check_for_backend(diesel::sqlite::Sqlite))] +pub struct MusicEntry { + id: String, + artist: String, + title: String, + links: List, + universal_ids: List, +} + +#[derive(Queryable, Selectable)] +#[diesel(table_name = schema::session_keys)] +#[diesel(check_for_backend(diesel::sqlite::Sqlite))] +pub struct SessionKey { + key: String, + user_id: String, +} + +#[derive(Queryable, Selectable)] +#[diesel(table_name = schema::users)] +#[diesel(check_for_backend(diesel::sqlite::Sqlite))] +pub struct User { + id: String, + created_at: NaiveDateTime, + last_connected_at: NaiveDateTime, + email: String, + password: String, + name: String, + limits: String, + assets: String, +}