#![warn(rust_2018_idioms)] #![forbid(elided_lifetimes_in_paths, unsafe_code)] mod time; use crate::time::{parse_date, parse_time, Date, Time}; use camino::Utf8PathBuf as PathBuf; use clap::Parser; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; use std::{ fmt::Display, fs, io::{self, BufRead as _, Write} }; #[derive(Debug, Parser)] struct Args { #[clap(short = 'C', long, default_value = ".")] directory: PathBuf, #[clap(short = 'c', long, default_value = "23ws-malo")] course: String } #[derive(Deserialize, Serialize)] struct Project { lecture: ProjectLecture, source: ProjectSource } #[serde_as] #[derive(Deserialize, Serialize)] struct ProjectLecture { course: String, #[serde_as(as = "DisplayFromStr")] date: Date } #[serde_as] #[derive(Deserialize, Serialize)] struct ProjectSource { files: Vec, #[serde_as(as = "DisplayFromStr")] first_file_start: Time, #[serde_as(as = "DisplayFromStr")] last_file_end: Time } fn ask_time(question: impl Display) -> Time { let mut stdout = io::stdout().lock(); let mut stdin = io::stdin().lock(); writeln!(stdout, "{question}").unwrap(); let mut line = String::new(); loop { line.clear(); write!(stdout, "> ").unwrap(); stdout.flush().unwrap(); stdin.read_line(&mut line).unwrap(); let line = line.trim(); match parse_time(line) { Ok(time) => return time, Err(err) => writeln!(stdout, "Invalid Input {line:?}: {err}").unwrap() } } } fn main() { let args = Args::parse(); // process arguments let directory = args.directory.canonicalize_utf8().unwrap(); let course = args.course; // let's see if we need to initialise the project let project_path = directory.join("project.toml"); let project = if project_path.exists() { toml::from_slice(&fs::read(&project_path).unwrap()).unwrap() } else { let dirname = directory.file_name().unwrap(); let date = parse_date(dirname).expect("Directory name is not in the expected format"); let mut files = Vec::new(); for entry in directory.read_dir_utf8().unwrap() { let entry = entry.unwrap(); let name = entry.file_name(); let lower = name.to_ascii_lowercase(); if (lower.ends_with(".mp4") || lower.ends_with(".mts")) && entry.file_type().unwrap().is_file() { files.push(String::from(name)); } } files.sort_unstable(); assert!(!files.is_empty()); println!("I found the following source files: {files:?}"); let first_file_start = ask_time(format_args!( "Please take a look at the file {} and tell me the first second you want included", files.first().unwrap() )); let last_file_end = ask_time(format_args!( "Please take a look at the file {} and tell me the last second you want included", files.last().unwrap() )); let project = Project { lecture: ProjectLecture { course, date }, source: ProjectSource { files, first_file_start, last_file_end } }; fs::write(&project_path, toml::to_string(&project).unwrap().as_bytes()).unwrap(); project }; println!("{}", toml::to_string(&project).unwrap()); }