feat: add event database entities
							parent
							
								
									1bc9ae01d8
								
							
						
					
					
						commit
						d7c5c85460
					
				|  | @ -253,6 +253,7 @@ dependencies = [ | |||
|  "chrono", | ||||
|  "r2d2", | ||||
|  "r2d2_sqlite", | ||||
|  "rusqlite", | ||||
|  "serde", | ||||
|  "tera", | ||||
|  "tokio", | ||||
|  | @ -286,6 +287,7 @@ dependencies = [ | |||
|  "iana-time-zone", | ||||
|  "js-sys", | ||||
|  "num-traits", | ||||
|  "serde", | ||||
|  "wasm-bindgen", | ||||
|  "windows-targets", | ||||
| ] | ||||
|  | @ -1062,6 +1064,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" | ||||
| dependencies = [ | ||||
|  "bitflags", | ||||
|  "chrono", | ||||
|  "fallible-iterator", | ||||
|  "fallible-streaming-iterator", | ||||
|  "hashlink", | ||||
|  |  | |||
|  | @ -5,9 +5,12 @@ edition = "2021" | |||
| 
 | ||||
| [dependencies] | ||||
| axum = { version = "0.7.9", features = ["macros"] } | ||||
| chrono = "0.4.39" | ||||
| chrono = { version = "0.4.39", features = ["serde"] } | ||||
| r2d2 = "0.8.10" | ||||
| r2d2_sqlite = "0.25.0" | ||||
| # this dependency is needed soly because the r2d2_sqlite crate doesn't export | ||||
| # the 'chrono' feature flag | ||||
| rusqlite = { version = "0.32.1", features = ["chrono"] } | ||||
| serde = { version = "1.0.217", features = ["derive"] } | ||||
| tera = "1.20.0" | ||||
| tokio = { version = "1.42.0", features = ["full"] } | ||||
|  |  | |||
|  | @ -0,0 +1,88 @@ | |||
| use std::{fmt::Display, str::FromStr}; | ||||
| 
 | ||||
| use chrono::Utc; | ||||
| use r2d2_sqlite::rusqlite::{ | ||||
|     self, | ||||
|     types::{FromSql, FromSqlError}, | ||||
|     Row, ToSql, | ||||
| }; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize)] | ||||
| pub enum EventType { | ||||
|     Watering, | ||||
| } | ||||
| 
 | ||||
| impl ToString for EventType { | ||||
|     fn to_string(&self) -> String { | ||||
|         String::from(match self { | ||||
|             Self::Watering => "watering", | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct ParseEventTypeErr; | ||||
| 
 | ||||
| impl Display for ParseEventTypeErr { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         write!(f, "invalid name") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl std::error::Error for ParseEventTypeErr {} | ||||
| 
 | ||||
| impl FromStr for EventType { | ||||
|     type Err = ParseEventTypeErr; | ||||
| 
 | ||||
|     fn from_str(s: &str) -> Result<Self, Self::Err> { | ||||
|         match s { | ||||
|             "watering" => Ok(Self::Watering), | ||||
|             _ => Err(ParseEventTypeErr), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ToSql for EventType { | ||||
|     fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> { | ||||
|         Ok(self.to_string().into()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl FromSql for EventType { | ||||
|     fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> { | ||||
|         value | ||||
|             .as_str()? | ||||
|             .parse() | ||||
|             .map_err(|e| FromSqlError::Other(Box::new(e))) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Serialize)] | ||||
| pub struct Event { | ||||
|     id: i32, | ||||
|     plant_id: i32, | ||||
|     event_type: EventType, | ||||
|     time: chrono::DateTime<Utc>, | ||||
|     description: String, | ||||
| } | ||||
| 
 | ||||
| impl Event { | ||||
|     pub fn from_row(row: Row<'_>) -> rusqlite::Result<Self> { | ||||
|         Ok(Self { | ||||
|             id: row.get("id")?, | ||||
|             plant_id: row.get("plant_id")?, | ||||
|             event_type: row.get("event_type")?, | ||||
|             time: row.get("time")?, | ||||
|             description: row.get("description")?, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Deserialize)] | ||||
| pub struct NewEvent { | ||||
|     plant_id: i32, | ||||
|     event_type: EventType, | ||||
|     time: chrono::DateTime<Utc>, | ||||
|     description: String, | ||||
| } | ||||
|  | @ -1,4 +1,5 @@ | |||
| mod comment; | ||||
| mod event; | ||||
| mod plant; | ||||
| 
 | ||||
| use r2d2_sqlite::{rusqlite, SqliteConnectionManager}; | ||||
|  |  | |||
|  | @ -7,10 +7,11 @@ use r2d2_sqlite::SqliteConnectionManager; | |||
| use tera::Tera; | ||||
| use tower_http::compression::CompressionLayer; | ||||
| 
 | ||||
| const MIGRATIONS: [&str; 3] = [ | ||||
| const MIGRATIONS: [&str; 4] = [ | ||||
|     include_str!("migrations/000_initial.sql"), | ||||
|     include_str!("migrations/001_plants.sql"), | ||||
|     include_str!("migrations/002_comments.sql"), | ||||
|     include_str!("migrations/003_events.sql"), | ||||
| ]; | ||||
| const STATIC_FILES: [(&str, &'static str); 1] = [( | ||||
|     "htmx_2.0.4.min.js", | ||||
|  |  | |||
|  | @ -0,0 +1,9 @@ | |||
| create table events ( | ||||
|     id integer primary key, | ||||
|     plant_id integer not null | ||||
|         references plants (id) | ||||
|         on delete cascade, | ||||
|     event_type text not null, | ||||
|     time text not null, | ||||
|     description text not null | ||||
| ); | ||||
		Loading…
	
		Reference in New Issue