Improve List implementation and finish model creation

This commit is contained in:
Sofía Aritz 2024-10-14 18:13:13 +02:00
parent b7b938c564
commit d6a1cc2feb
Signed by: sofia
GPG key ID: 90B5116E3542B28F
3 changed files with 111 additions and 58 deletions

View file

@ -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"] }

View file

@ -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<V: std::fmt::Debug>(pub Vec<V>);
pub struct List<V: std::fmt::Debug + std::clone::Clone>(pub Vec<V>);
impl Display for List<String> {
impl<V: std::fmt::Debug + std::clone::Clone> List<V> {
fn from_vec(vec: Vec<V>) -> Self {
Self(vec)
}
fn into_vec(self) -> Vec<V> {
self.0
}
}
impl<A: Serialize + std::fmt::Debug + std::clone::Clone> Display for List<A> {
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::<Vec<String>>()
.join(",")
)?;
f.write_char(']')
f.write_str(&serde_json::to_string(&self).unwrap())
}
}
impl From<String> for List<String> {
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<Text, Sqlite> for List<String>
impl<A> From<String> for List<A>
where
String: FromSql<Text, Sqlite>,
A: DeserializeOwned + std::fmt::Debug + std::clone::Clone,
{
fn from(value: String) -> Self {
serde_json::from_str(&value).expect("failed to deserialize list")
}
}
impl<V: std::fmt::Debug + std::clone::Clone + DeserializeOwned> FromSql<Text, Sqlite> for List<V>
{
fn from_sql(bytes: <Sqlite as Backend>::RawValue<'_>) -> diesel::deserialize::Result<Self> {
let str = <String as FromSql<Text, Sqlite>>::from_sql(bytes)?;
@ -71,9 +42,7 @@ where
}
}
impl ToSql<Text, Sqlite> for List<String>
where
String: ToSql<Text, Sqlite>,
impl<V: std::fmt::Debug + std::clone::Clone + Serialize> ToSql<Text, Sqlite> for List<V>
{
fn to_sql<'b>(
&'b self,

View file

@ -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<String>,
date_entry: Option<String>,
}
#[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<String>,
}
#[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<String>,
/// JSON value: { latitude: number, longitude: number }
location_coordinates: Option<String>,
}
impl LocationEntry {
pub fn location_coordinates(&self) -> Option<LocationCoordinates> {
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<String>,
universal_ids: List<UniversalId>,
}
#[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,
}