From 42a75ba80073f1ccca793e2845e6f7642766e2c3 Mon Sep 17 00:00:00 2001 From: Sandro Eiler Date: Thu, 5 Oct 2023 14:33:12 +0200 Subject: [PATCH] feat: add model --- src/error.rs | 2 ++ src/main.rs | 9 +++++- src/model.rs | 61 ++++++++++++++++++++++++++++++++++++ src/web/mod.rs | 2 ++ src/web/routes_properties.rs | 34 ++++++++++++++++++++ tests/quick_dev.rs | 10 ++++++ 6 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 src/model.rs create mode 100644 src/web/routes_properties.rs diff --git a/src/error.rs b/src/error.rs index cb1ed89..c0ed6e2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,8 +6,10 @@ pub type Result = core::result::Result; #[derive(Debug)] pub enum Error { LoginFail, + PropertyDeleteFailIdNotFound { id: u64 }, } +/// FIXME: return different status codes for different errors impl IntoResponse for Error { fn into_response(self) -> Response { println!("->> {:<12} - {self:?}", "INTO_RESPONSE"); diff --git a/src/main.rs b/src/main.rs index 9cbd153..6bb81b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ use tower_cookies::CookieManagerLayer; mod error; mod web; +mod model; #[derive(Debug, Deserialize)] struct HelloParams { @@ -29,7 +30,8 @@ async fn main() { .merge(routes_hello()) .merge(web::routes_login::routes()) .layer(middleware::map_response(main_response_mapper)) - .layer(CookieManagerLayer::new()) + .layer(CookieManagerLayer::new()) // must be above? the auth routes + // TODO: continue video at 22:15 .fallback_service(routes_static()); let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); @@ -40,6 +42,10 @@ async fn main() { .unwrap(); } + +/// Map the response to add headers, etc. +/// +/// * `res`: the response to map async fn main_response_mapper(res: Response) -> Response { println!("->> {:<12} - main_response_mapper", "HANDLER"); @@ -47,6 +53,7 @@ async fn main_response_mapper(res: Response) -> Response { res } +/// Serve static files fn routes_static() -> Router { Router::new().nest_service("/", get_service(ServeDir::new("./"))) } diff --git a/src/model.rs b/src/model.rs new file mode 100644 index 0000000..ce7bbdd --- /dev/null +++ b/src/model.rs @@ -0,0 +1,61 @@ +//! Simplistic model layer +//! (with mock-store layer) + +use crate::{Error, Result}; +use serde::{Deserialize, Serialize}; +use std::sync::{Arc, Mutex}; + +// region: --- Property Types +#[derive(Clone, Debug, Serialize)] +pub struct Property { + pub id: u64, + pub address: String, + pub contact: String, +} + +#[derive(Deserialize)] +pub struct PropertyForCreate { + pub address: String, + pub contact: String, +} + +// endregion: --- Property Types + +// region: --- Model Controller + +#[derive(Clone)] +pub struct ModelController { + property_store: Arc>>>, +} + +// Constructor +impl ModelController { + pub async fn new() -> Result { + Ok(Self { property_store: Arc::default() }) + } +} + +// CRUD implementation +impl ModelController { + pub async fn create_property(&self, property: PropertyForCreate) -> Result { + let mut store = self.property_store.lock().unwrap(); + let id = store.len() as u64; + let property = Property { id, address: property.address, contact: property.contact }; + store.push(Some(property.clone())); + Ok(property) + } + + pub async fn list_properties(&self) -> Result> { + let store = self.property_store.lock().unwrap(); + let properties = store.iter().filter_map(|p| p.clone()).collect(); + Ok(properties) + } + + pub async fn delete_property(&self, id: u64) -> Result { + let mut store = self.property_store.lock().unwrap(); + let property = store.get_mut(id as usize).and_then(|p| p.take()); + property.ok_or(Error::PropertyDeleteFailIdNotFound { id }) + } +} + +// endregion: --- Model Controller diff --git a/src/web/mod.rs b/src/web/mod.rs index 8c933c3..950e7e6 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -1,3 +1,5 @@ pub mod routes_login; +pub mod routes_properties; +/// The cookie name for the auth token pub const AUTH_TOKEN: &str = "auth-token"; diff --git a/src/web/routes_properties.rs b/src/web/routes_properties.rs new file mode 100644 index 0000000..3f0b116 --- /dev/null +++ b/src/web/routes_properties.rs @@ -0,0 +1,34 @@ +use crate::model::{ModelController, Property, PropertyForCreate}; +use crate::Result; +use axum::extract::{Path, State}; +use axum::routing::{delete, post}; +use axum::{Json, Router}; + +pub fn routes(mc: ModelController) -> Router { + Router::new() + .route("/properties", post(create_property).get(list_properties)) + .route("/properties/:id", delete(delete_property)) + .with_state(mc) +} + +// region: --- REST Handlers + +async fn create_property(State(mc): State, Json(property_fc): Json) -> Result> { + println!("->> {:<12} - create_property", "HANDLER"); + let property = mc.create_property(property_fc).await?; + Ok(Json(property)) +} + +async fn list_properties(State(mc): State) -> Result>> { + println!("->> {:<12} - list_properties", "HANDLER"); + let properties = mc.list_properties().await?; + Ok(Json(properties)) +} + +async fn delete_property(State(mc): State, Path(id): Path) -> Result> { + println!("->> {:<12} - delete_property", "HANDLER"); + let property = mc.delete_property(id).await?; + Ok(Json(property)) +} + +// endregion: --- REST Handlers diff --git a/tests/quick_dev.rs b/tests/quick_dev.rs index 6e4b042..54f7ec6 100644 --- a/tests/quick_dev.rs +++ b/tests/quick_dev.rs @@ -26,6 +26,16 @@ async fn test_quick_dev() -> Result<()> { ) ); req_login.await?.print().await?; + let req_login = hc.do_post( + "/api/login", + json!( + { + "username": "demo1", + "password": "demowrong" + } + ) + ); + req_login.await?.print().await?; hc.do_get("/hello2/mike").await?.print().await?;