use std::{fs::File, io::{BufReader, BufWriter}}; use serde::{Serialize, Deserialize}; use clap::{Parser, Subcommand}; use chrono::NaiveDateTime; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] struct Schedule { id: u64, subject: String, start: NaiveDateTime, end: NaiveDateTime, } impl Schedule { fn intersects(&self, other: &Schedule) -> bool { self.start < other.end && other.start < self.end } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] struct Calendar { schedules: Vec<Schedule>, } const SCHEDULE_FILE : &str = "schedule.json"; #[derive(Parser)] struct Cli { #[command(subcommand)] command: Commands, } #[derive(Subcommand)] enum Commands { List, Add { subject: String, start: NaiveDateTime, end: NaiveDateTime, } } fn main() { let options = Cli::parse(); match options.command { Commands::List => show_list(), Commands::Add { subject, start, end } => add_schedule(subject, start, end), } } fn show_list() { let calendar : Calendar = { let file = File::open(SCHEDULE_FILE).unwrap(); let reader = BufReader::new(file); serde_json::from_reader(reader).unwrap() }; println!("ID\tSTART\tEND\tSUBJECT"); for schedule in calendar.schedules { println!( "{}\t{}\t{}\t{}", schedule.id, schedule.start, schedule.end, schedule.subject ); } } fn add_schedule( subject: String, start: NaiveDateTime, end: NaiveDateTime, ) { let mut calendar : Calendar = { let file = File::open(SCHEDULE_FILE).unwrap(); let reader = BufReader::new(file); serde_json::from_reader(reader).unwrap() }; let id = calendar.schedules.len() as u64; let new_schedule = Schedule { id, subject, start, end, }; for schedule in &calendar.schedules { if schedule.intersects(&new_schedule) { println!("エラー: 予定が重複しています"); return; } } calendar.schedules.push(new_schedule); { let file = File::create(SCHEDULE_FILE).unwrap(); let writer = BufWriter::new(file); serde_json::to_writer(writer, &calendar).unwrap(); } println!("予定を追加しました。"); } #[cfg(test)] mod tests { use super::*; fn naive_date_time( year: i32, month: u32, day: u32, hour: u32, minute: u32, second: u32, ) -> NaiveDateTime { chrono::NaiveDate::from_ymd_opt(year, month, day) .unwrap() .and_hms_opt(hour, minute, second) .unwrap() } fn test_schedule_intersects( h0: u32, m0: u32, h1: u32, m1: u32, should_intersects: bool ) { let schedule = Schedule { id: 0, subject: "既存予定1".to_string(), start: naive_date_time(2024, 1, 1, h0, m0, 0), end: naive_date_time(2024, 1, 1, h1, m1, 0), }; let new_schedule = Schedule { id: 999, subject: "新規予定".to_string(), start: naive_date_time(2024, 1, 1, 19, 0, 0), end: naive_date_time(2024, 1, 1, 20, 0, 0), }; assert_eq!(should_intersects, schedule.intersects(&new_schedule)); } #[test] fn test_schedule_intersects_1(){ test_schedule_intersects(18, 15, 19, 15, true) } #[test] fn test_schedule_intersects_2(){ test_schedule_intersects(19, 45, 20, 45, true) } }
$ cargo test
Compiling calendar v0.1.0 (/home/vagrant/dev/work/rusty/calendar)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.40s
Running unittests src/main.rs (target/debug/deps/calendar-867e0c10670d3913)
running 2 tests
test tests::test_schedule_intersects_2 … ok
test tests::test_schedule_intersects_1 … ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
なるほど、これはかなり参考になる。