Skip to content

Instantly share code, notes, and snippets.

@Marmiz
Last active March 4, 2024 19:12
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Marmiz/541c3ccea832a27bfb60d4882450a4a8 to your computer and use it in GitHub Desktop.
Save Marmiz/541c3ccea832a27bfb60d4882450a4a8 to your computer and use it in GitHub Desktop.
Final section of todo-cli
use std::collections::HashMap;
fn main() {
let action = std::env::args().nth(1).expect("Please provide an action");
let item = std::env::args().nth(2).expect("Please provide an item");
let mut todo = Todo::new().expect("Initialisation of db failed");
if action == "add" {
todo.insert(item);
match todo.save() {
Ok(_) => println!("todo saved"),
Err(why) => println!("An error occurred: {}", why),
}
} else if action == "complete" {
match todo.complete(&item) {
None => println!("'{}' is not present in the list", item),
Some(_) => match todo.save() {
Ok(_) => println!("todo saved"),
Err(why) => println!("An error occurred: {}", why),
},
}
}
}
struct Todo {
// use rust built in HashMap to store key - val pairs
map: HashMap<String, bool>,
}
impl Todo {
fn new() -> Result<Todo, std::io::Error> {
let f = std::fs::OpenOptions::new()
.write(true)
.create(true)
.read(true)
.open("db.json")?;
match serde_json::from_reader(f) {
Ok(map) => Ok(Todo { map }),
Err(e) if e.is_eof() => Ok(Todo {
map: HashMap::new(),
}),
Err(e) => panic!("An error occurred: {}", e),
}
}
fn insert(&mut self, key: String) {
// insert a new item into our map.
// active state is set to true by default.
self.map.insert(key, true);
}
fn save(self) -> Result<(), Box<dyn std::error::Error>> {
let f = std::fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open("db.json")?;
serde_json::to_writer_pretty(f, &self.map)?;
Ok(())
}
fn complete(&mut self, key: &String) -> Option<()> {
match self.map.get_mut(key) {
Some(v) => Some(*v = false),
None => None,
}
}
}
@TriplEight
Copy link

Hey, thanks for a nice tutorial, I've got a bug.
Repro:

cargo run -- add "make coffee" 
cargo run -- complete "make coffee" 
cargo run -- add "make coffee" 

cat db.json

and the json will be broken:

{
  "make coffee": true
}}⏎    

@Marmiz
Copy link
Author

Marmiz commented Jan 7, 2021

Thanks for raising this.

This is a case where writing some test would have helped me catching this in time :)
Anyway the fix is quite easy: it's enough to set truncate OpenOption to true when opening the file:

    fn save(self) -> Result<(), Box<dyn std::error::Error>> {
        let f = std::fs::OpenOptions::new()
            .write(true)
            .create(true)
            .truncate(true) // add this flag
            .open("db.json")?;
        serde_json::to_writer_pretty(f, &self.map)?;

        Ok(())
    }

Docs about truncate.
Happy coding 👍

@bscott
Copy link

bscott commented Dec 22, 2021

Not seeing any data written to db.json, it was working before adding serde_json

@Marmiz
Copy link
Author

Marmiz commented Dec 22, 2021

Hey @bscott, just tried and seems to be working on my end.
Can you provide any additional info that can help me understand better the issue? Like versions...etc

Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment