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
なるほど、これはかなり参考になる。