Skip to content

Commit 15dad99

Browse files
committed
feat(day34): add file I/O + JSON todo lesson
1 parent 9464807 commit 15dad99

4 files changed

Lines changed: 102 additions & 2 deletions

File tree

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ edition = "2021"
99
rayon = "1.5"
1010
tokio = { version = "1", features = ["full"] }
1111
serde = { version = "1.0", features = ["derive"] }
12+
serde_json = "1.0"
1213
actix-web = "4"
1314
axum = "0.7.7"
1415
# # for actix crates
@@ -18,4 +19,4 @@ axum = "0.7.7"
1819
utoipa = { version = "4.2.3", features = ["axum_extras"] }
1920
utoipa-swagger-ui = { version = "7.1.0", features = ["axum"] }
2021
tokio-postgres = "0.7"
21-
deadpool-postgres = "0.10"
22+
deadpool-postgres = "0.10"

src/day34_to_day36/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
pub mod todo_json;
2+
3+
pub fn run() {
4+
println!("Days 34-36: File I/O and JSON");
5+
if let Err(err) = todo_json::run() {
6+
eprintln!("Todo JSON lesson failed: {err}");
7+
}
8+
}

src/day34_to_day36/todo_json.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use serde::{Deserialize, Serialize};
2+
use std::error::Error;
3+
use std::fs;
4+
use std::path::Path;
5+
6+
const TODO_FILE: &str = "data/day34_todo.json";
7+
8+
#[derive(Debug, Serialize, Deserialize, Clone)]
9+
struct TodoItem {
10+
id: u32,
11+
title: String,
12+
done: bool,
13+
}
14+
15+
#[derive(Debug, Serialize, Deserialize, Default)]
16+
struct TodoStore {
17+
items: Vec<TodoItem>,
18+
}
19+
20+
impl TodoStore {
21+
fn add_task(&mut self, title: impl Into<String>) {
22+
let next_id = self.items.last().map_or(1, |item| item.id + 1);
23+
self.items.push(TodoItem {
24+
id: next_id,
25+
title: title.into(),
26+
done: false,
27+
});
28+
}
29+
30+
fn mark_done(&mut self, id: u32) -> bool {
31+
if let Some(item) = self.items.iter_mut().find(|item| item.id == id) {
32+
item.done = true;
33+
true
34+
} else {
35+
false
36+
}
37+
}
38+
39+
fn load(path: &Path) -> Result<Self, Box<dyn Error>> {
40+
if !path.exists() {
41+
return Ok(Self::default());
42+
}
43+
44+
let content = fs::read_to_string(path)?;
45+
let store = serde_json::from_str(&content)?;
46+
Ok(store)
47+
}
48+
49+
fn save(&self, path: &Path) -> Result<(), Box<dyn Error>> {
50+
if let Some(parent) = path.parent() {
51+
fs::create_dir_all(parent)?;
52+
}
53+
54+
let json = serde_json::to_string_pretty(self)?;
55+
fs::write(path, json)?;
56+
Ok(())
57+
}
58+
}
59+
60+
pub fn run() -> Result<(), Box<dyn Error>> {
61+
println!("\nLesson: Build a tiny Todo with File I/O + JSON");
62+
println!("Data file: {TODO_FILE}");
63+
64+
let path = Path::new(TODO_FILE);
65+
let mut store = TodoStore::load(path)?;
66+
67+
if store.items.is_empty() {
68+
store.add_task("Read ownership chapter again");
69+
store.add_task("Practice borrowing with 3 small exercises");
70+
}
71+
72+
if let Some(first_open_id) = store.items.iter().find(|item| !item.done).map(|item| item.id) {
73+
store.mark_done(first_open_id);
74+
}
75+
76+
store.save(path)?;
77+
78+
println!("Current tasks:");
79+
for item in &store.items {
80+
let status = if item.done { "done" } else { "todo" };
81+
println!("- [{}] #{} {}", status, item.id, item.title);
82+
}
83+
84+
println!("Saved to {}", path.display());
85+
Ok(())
86+
}
87+

src/main.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ pub mod day28_to_day30;
3131
//Day 31 to day 33
3232
pub mod day31_to_day33;
3333

34+
//Day 34 to day 36
35+
pub mod day34_to_day36;
36+
3437
fn main() {
3538
// day01_to_day03::run();
3639
// day04_to_day06::run();
@@ -42,6 +45,7 @@ fn main() {
4245
// day22_to_day24::run();
4346
// day25_to_day27::run();
4447
// day28_to_day30::run();
45-
day31_to_day33::run();
48+
// day31_to_day33::run();
49+
day34_to_day36::run();
4650

4751
}

0 commit comments

Comments
 (0)