From becf4679e299a1c8edb0b3e2c896e02686e921b4 Mon Sep 17 00:00:00 2001 From: David Westgate Date: Fri, 26 Apr 2024 23:43:26 -0700 Subject: [PATCH] add persistance via file reading/writing --- Cargo.lock | 110 ++++++++++++++++++++++++--------------------- questions.json | 9 ++++ src/api.rs | 7 ++- src/main.rs | 14 +++--- src/questions.json | 9 ---- src/store.rs | 68 +++++++++++++++++++++------- 6 files changed, 133 insertions(+), 84 deletions(-) create mode 100644 questions.json delete mode 100644 src/questions.json diff --git a/Cargo.lock b/Cargo.lock index ca0a6c3..db65316 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -112,9 +112,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bitflags" -version = "1.3.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "block-buffer" @@ -139,9 +139,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cc" -version = "1.0.92" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41" +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" [[package]] name = "cfg-if" @@ -499,9 +499,9 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -606,9 +606,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -616,15 +616,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -673,9 +673,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -721,9 +721,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ "bitflags", ] @@ -734,6 +734,7 @@ version = "0.1.0" dependencies = [ "axum", "serde", + "serde_json", "tokio", "warp", ] @@ -790,9 +791,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -834,9 +835,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -852,9 +853,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" @@ -874,9 +875,9 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "syn" -version = "2.0.58" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -897,18 +898,18 @@ checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", @@ -1177,7 +1178,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -1197,17 +1198,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -1218,9 +1220,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -1230,9 +1232,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -1242,9 +1244,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -1254,9 +1262,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -1266,9 +1274,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -1278,9 +1286,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -1290,6 +1298,6 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/questions.json b/questions.json new file mode 100644 index 0000000..b388411 --- /dev/null +++ b/questions.json @@ -0,0 +1,9 @@ +[ + { + "id": 1, + "title": "Have a question?", + "content": "Just ask me", + "tags": ["intro"] + + } +] \ No newline at end of file diff --git a/src/api.rs b/src/api.rs index eb00597..f935125 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,6 +1,5 @@ -use crate::*; - use self::{question::QuestionDTO, store::Store}; +use crate::*; /** GET /questions (empty body; return JSON) @@ -23,6 +22,7 @@ pub async fn read_question( pub async fn read_questions(State(store): State>>) -> Response { (StatusCode::OK, Json(store.read().await.fetch_all())).into_response() } + pub async fn create_question( State(store): State>>, Json(question_dto): Json, @@ -35,6 +35,7 @@ pub async fn create_question( Err(e) => (StatusCode::CONFLICT, e).into_response(), } } + pub async fn update_question( State(store): State>>, Json(question_dto): Json, @@ -45,6 +46,7 @@ pub async fn update_question( Err(e) => (StatusCode::NOT_FOUND, e).into_response(), } } + pub async fn delete_question( State(store): State>>, Path(id): Path, @@ -54,6 +56,7 @@ pub async fn delete_question( Err(e) => (StatusCode::NOT_FOUND, e).into_response(), } } + pub async fn create_answer(State(_store): State>> /*TODO */) -> Response { todo!() } diff --git a/src/main.rs b/src/main.rs index a57d5c0..b9922aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ mod api; mod question; mod store; - use axum::{ extract::{Path, State}, http::StatusCode, @@ -10,9 +9,13 @@ use axum::{ Json, Router, }; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::net::SocketAddr; -use std::sync::Arc; +use std::{ + collections::HashMap, + fs::File, + io::{ErrorKind, Seek, Write}, + net::SocketAddr, + sync::Arc, +}; use store::Store; use tokio::sync::RwLock; @@ -34,13 +37,12 @@ async fn main() { .route("/question/:id", get(api::read_question)) .route("/questions", get(api::read_questions)) .route("/question", post(api::create_question)) - .route("/question/:id", put(api::update_question)) + .route("/question", put(api::update_question)) .route("/question/:id", delete(api::delete_question)) .route("/answers", post(api::create_answer)) .with_state(store) .fallback(handler_404); let app = Router::new().route("/", get(handle)).merge(apis); - axum::serve(listener, app).await.unwrap(); } diff --git a/src/questions.json b/src/questions.json deleted file mode 100644 index 8b2b25f..0000000 --- a/src/questions.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { - "id": 1, - "title": "How?", - "content": "Please help!", - "tags": ["general"] - - } -] \ No newline at end of file diff --git a/src/store.rs b/src/store.rs index 1b2e20e..1256b42 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1,26 +1,53 @@ +use self::question::{Question, QuestionDTO}; use crate::*; -use self::question::{Question, QuestionDTO}; - -#[derive(Clone, Debug)] -pub(crate) struct Store { +#[derive(Debug)] +pub struct Store { + file: File, questions: HashMap, } impl Store { pub fn new() -> Self { - Store { - questions: Self::init(), - } - } - fn init() -> HashMap { - let file = include_str!("./questions.json"); + let file: File = File::create_new("./questions.json") + .or_else(|e| { + if e.kind() == ErrorKind::AlreadyExists { + File::options() + .read(true) + .write(true) + .open("./questions.json") + } else { + Err(e) + } + }) + .unwrap(); + let json = std::io::read_to_string(&file).expect("could not get json from file"); let questions_vec: Vec = - serde_json::from_str(file).expect("can't read questions.json"); - questions_vec + serde_json::from_str(&json).expect("can't read questions.json"); + let questions: HashMap = questions_vec .into_iter() .map(|question_dto: QuestionDTO| question_dto.to_entity()) - .collect() + .collect(); + Store { questions, file } + } + + fn flush(&mut self) { + let questions: &HashMap = &self.questions; + let questions_vec: Vec = questions + .iter() + .map(|q: (&u8, &Question)| q.1.to_dto(*q.0)) + .collect(); + let json: String = serde_json::to_string(&questions_vec).unwrap(); + let mut f: &File = &self.file; + match f + .rewind() + .and(f.write_all(json.as_bytes())) + .and(f.sync_all()) + .and(f.set_len(f.stream_position().unwrap())) + { + Ok(()) => (), + _ => panic!("Could not flush file"), + } } pub fn add(&mut self, id: u8, question: Question) -> Result { @@ -28,13 +55,19 @@ impl Store { return Err(format!("Question with id {} already exists", id)); } match self.questions.insert(id, question.clone()) { - None => Ok(question), //none since key cannot already exist + None => { + self.flush(); + Ok(question) + } //none since key cannot already exist _ => Err("Server Error".to_string()), } } pub fn remove(&mut self, id: u8) -> Result { match self.questions.remove(&id) { - Some(question) => Ok(question), + Some(question) => { + self.flush(); + Ok(question) + } None => Err(format!("Question with id {} does not exist", id)), } } @@ -52,7 +85,10 @@ impl Store { return Err(format!("Question with id {} does not exists", id)); } match self.questions.insert(id, question) { - Some(question) => Ok(question), + Some(question) => { + self.flush(); + Ok(question) + } None => Err("Server Error".to_string()), } }