object-space 0.1.1

An object store library for highly concurrent program written in Rust.
Documentation
extern crate chrono;
extern crate object_space;
extern crate serde;
#[macro_use]
extern crate serde_derive;

use std::fmt;
use std::io::{stdin, stdout, Write};
use std::process::exit;
use std::sync::atomic::{AtomicIsize, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;

use chrono::{DateTime, Duration as ChronoDuration, NaiveDateTime, Utc};
use object_space::{ObjectSpace, RangeLookupObjectSpace, TreeObjectSpace, ValueLookupObjectSpace};

fn main() {
    let store = ReminderStore::new();
    run(store);
}

fn run(store: ReminderStore) {
    let arc = Arc::new(store);
    let main_clone = arc.clone();
    let thread1 = thread::spawn(move || main_clone.main_loop());
    let thread2 = thread::spawn(move || arc.clone().check_reminder());
    thread1.join().unwrap();
    thread2.join().unwrap();
}

#[derive(Serialize, Deserialize, Debug)]
struct Reminder {
    id: isize,
    time: i64,
    content: String,
}

struct ReminderStore {
    space: TreeObjectSpace,
    counter: AtomicIsize,
}

impl ReminderStore {
    fn check_reminder(&self) {
        loop {
            thread::sleep(Duration::new(60, 0));
            let now = Utc::now();
            for r in self.get_reminder_between_time(now, now + ChronoDuration::minutes(1)) {
                println!();
                println!("{}", r);
                print!(">>> ");
                stdout().flush().unwrap();
            }
        }
    }

    fn main_loop(&self) {
        loop {
            print!(">>> ");
            let _ = stdout().flush();
            let mut input = String::new();
            match stdin().read_line(&mut input) {
                Ok(_) => {
                    let mut input_split = input.split_whitespace();
                    match input_split.next() {
                        Some("quit") => {
                            println!("Exiting");
                            exit(0)
                        }
                        Some("add") => self.request_reminder_info(),
                        Some("complete") => {
                            let id = input_split.next().and_then(|s| s.parse::<isize>().ok());
                            match id {
                                Some(n) => {
                                    self.complete_reminder(n);
                                    ()
                                }
                                None => println!("Please provide reminder id"),
                            }
                        }
                        Some("all") => for r in self.get_all_todo_reminders() {
                            println!("{}", r);
                        },
                        Some("next") => match self.get_next_reminder() {
                            Some(r) => println!("{}", r),
                            None => println!("There is no reminder here"),
                        },
                        Some("outdated") => for r in self.get_all_outdated_reminders() {
                            println!("{}", r);
                        },
                        _ => println!("Unrecognizable command: {}", &input),
                    }
                }
                Err(error) => println!("error: {}", error),
            }
        }
    }

    fn request_reminder_info(&self) {
        print!("Reminder content: ");
        let _ = stdout().flush();
        let mut content = String::new();
        match stdin().read_line(&mut content) {
            Err(_) => {
                println!("Cannot read input");
                return;
            }
            Ok(_) => (),
        }

        print!("Minutes to remind: ");
        let _ = stdout().flush();
        let mut time_str = String::new();
        match stdin().read_line(&mut time_str) {
            Err(_) => {
                println!("Cannot read input");
                return;
            }
            Ok(_) => (),
        }
        let id = time_str.trim().parse::<i64>();
        match id {
            Ok(n) => {
                self.add_reminder(
                    Utc::now() + ChronoDuration::minutes(n),
                    content.trim().to_owned(),
                );
            }
            Err(_) => println!("Please provide numeric minutes to remind"),
        }
    }

    fn new() -> ReminderStore {
        ReminderStore {
            space: TreeObjectSpace::new(),
            counter: AtomicIsize::new(0),
        }
    }

    fn add_reminder(&self, time: DateTime<Utc>, content: String) {
        let id = self.counter.fetch_add(1, Ordering::Relaxed);
        self.space.write(Reminder {
            id: id,
            time: time.timestamp(),
            content: content,
        });
    }

    fn get_reminder_until_time<'a>(
        &'a self,
        time: DateTime<Utc>,
    ) -> Box<Iterator<Item = Reminder> + 'a> {
        self.space
            .read_all_by_range::<Reminder, _>("time", Utc::now().timestamp()..time.timestamp())
    }

    fn get_reminder_between_time<'a>(
        &'a self,
        start_time: DateTime<Utc>,
        end_time: DateTime<Utc>,
    ) -> Box<Iterator<Item = Reminder> + 'a> {
        self.space
            .read_all_by_range::<Reminder, _>("time", start_time.timestamp()..end_time.timestamp())
    }

    fn get_all_todo_reminders<'a>(&'a self) -> Box<Iterator<Item = Reminder> + 'a> {
        self.space
            .read_all_by_range::<Reminder, _>("time", Utc::now().timestamp()..)
    }

    fn get_all_outdated_reminders<'a>(&'a self) -> Box<Iterator<Item = Reminder> + 'a> {
        self.space
            .read_all_by_range::<Reminder, _>("time", ..Utc::now().timestamp())
    }

    fn complete_reminder(&self, id: isize) -> Option<Reminder> {
        self.space.try_take_by_value::<Reminder>("id", &(id as i64))
    }

    fn edit_reminder_content(&self, id: isize, content: &str) {
        match self.space.try_take_by_value::<Reminder>("id", &(id as i64)) {
            Some(Reminder {
                id: _,
                time: rtime,
                content: _,
            }) => self.space.write(Reminder {
                id: id,
                time: rtime,
                content: content.to_owned(),
            }),
            _ => {}
        }
    }

    fn edit_reminder_time(&self, id: isize, time: DateTime<Utc>) {
        match self.space.try_take_by_value::<Reminder>("id", &(id as i64)) {
            Some(Reminder {
                id: _,
                time: _,
                content: rcontent,
            }) => self.space.write(Reminder {
                id: id,
                time: time.timestamp(),
                content: rcontent,
            }),
            _ => {}
        }
    }

    fn get_next_reminder(&self) -> Option<Reminder> {
        self.space
            .read_all_by_range::<Reminder, _>("time", Utc::now().timestamp()..)
            .min_by_key(|r| r.time)
    }
}

impl fmt::Display for Reminder {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Reminder id: {}, content: {}, remind time: {}",
            self.id,
            self.content,
            DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(self.time, 0), Utc)
        )
    }
}