Skip to content

Instantly share code, notes, and snippets.

@Marmiz

Marmiz/main.rs

Last active Jan 7, 2021
Embed
What would you like to do?
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

This comment has been minimized.

Copy link

@TriplEight TriplEight commented Jan 6, 2021

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

This comment has been minimized.

Copy link
Owner Author

@Marmiz 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 👍

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