diff --git a/src/files.rs b/src/files.rs index 0fbb151..addce9d 100644 --- a/src/files.rs +++ b/src/files.rs @@ -11,9 +11,21 @@ pub mod commands; mod format; mod parse; +#[derive(Debug)] +struct LoadedFile { + file: File, + dirty: bool, +} + +impl LoadedFile { + pub fn new(file: File) -> Self { + Self { file, dirty: false } + } +} + #[derive(Debug)] pub struct Files { - files: HashMap, + files: HashMap, timezone: Tz, } @@ -42,7 +54,7 @@ impl Files { Ok(Self { files, timezone }) } - fn load_file(files: &mut HashMap, path: &Path) -> Result<()> { + fn load_file(files: &mut HashMap, path: &Path) -> Result<()> { let canon_path = path.canonicalize()?; if files.contains_key(&canon_path) { // We've already loaded this exact file. @@ -53,31 +65,34 @@ impl Files { let file = parse::parse(path, &content)?; let includes = file.includes.clone(); - files.insert(canon_path, file); + files.insert(canon_path, LoadedFile::new(file)); for include in includes { - Self::load_file(files, &include)?; + // Since we've successfully opened the file, its name can't be the + // root directory or empty string and must thus have a parent. + let include_path = path.parent().unwrap().join(include); + Self::load_file(files, &include_path)?; } Ok(()) } - fn determine_timezone(files: &HashMap) -> Result { + fn determine_timezone(files: &HashMap) -> Result { let mut found: Option<(PathBuf, String)> = None; for file in files.values() { - if let Some(file_tz) = &file.timezone { + if let Some(file_tz) = &file.file.timezone { if let Some((found_name, found_tz)) = &found { if found_tz != file_tz { return Err(Error::TzConflict { file1: found_name.clone(), tz1: found_tz.clone(), - file2: file.name.clone(), + file2: file.file.name.clone(), tz2: file_tz.clone(), }); } } else { - found = Some((file.name.clone(), file_tz.clone())); + found = Some((file.file.name.clone(), file_tz.clone())); } } } @@ -89,6 +104,26 @@ impl Files { }) } + pub fn save(&self) -> Result<()> { + for (path, file) in &self.files { + if file.dirty { + Self::save_file(path, &file.file)?; + } + } + Ok(()) + } + + fn save_file(path: &Path, file: &File) -> Result<()> { + fs::write(path, &format!("{}", file))?; + Ok(()) + } + + pub fn mark_all_dirty(&mut self) { + for (_, file) in self.files.iter_mut() { + file.dirty = true; + } + } + pub fn now(&self) -> DateTime<&Tz> { Utc::now().with_timezone(&&self.timezone) } diff --git a/src/files/commands.rs b/src/files/commands.rs index 58dbd92..1c4283a 100644 --- a/src/files/commands.rs +++ b/src/files/commands.rs @@ -329,7 +329,7 @@ pub enum Command { #[derive(Debug)] pub struct File { pub name: PathBuf, - pub includes: Vec, + pub includes: Vec, pub timezone: Option, pub commands: Vec, } diff --git a/src/files/format.rs b/src/files/format.rs index 0e311e2..3cf5e01 100644 --- a/src/files/format.rs +++ b/src/files/format.rs @@ -273,14 +273,28 @@ impl fmt::Display for Command { impl fmt::Display for File { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut commands = self.commands.iter(); - if let Some(command) = commands.next() { - write!(f, "{}", command)?; - for command in commands { - writeln!(f)?; - write!(f, "{}", command)?; - } + let mut empty = true; + for include in &self.includes { + writeln!(f, "INCLUDE {}", include)?; + empty = false; } + + if let Some(tz) = &self.timezone { + if !empty { + writeln!(f)?; + } + writeln!(f, "TIMEZONE {}", tz)?; + empty = false; + } + + for command in &self.commands { + if !empty { + writeln!(f)?; + } + write!(f, "{}", command)?; + empty = false; + } + Ok(()) } } diff --git a/src/files/parse.rs b/src/files/parse.rs index 129ceda..7e4433a 100644 --- a/src/files/parse.rs +++ b/src/files/parse.rs @@ -706,12 +706,7 @@ fn parse_command(p: Pair, file: &mut File) -> Result<()> { let p = p.into_inner().next().unwrap(); match p.as_rule() { - Rule::include => { - // Since we've successfully opened the file, its name can't be the - // root directory or empty string and must thus have a parent. - let parent = file.name.parent().unwrap(); - file.includes.push(parent.join(parse_include(p))); - } + Rule::include => file.includes.push(parse_include(p)), Rule::timezone => match file.timezone { None => file.timezone = Some(parse_timezone(p)), Some(_) => fail(p.as_span(), "cannot set timezone multiple times")?, diff --git a/src/main.rs b/src/main.rs index 4db3574..19a33a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,12 @@ pub struct Opt { fn main() -> anyhow::Result<()> { let opt = Opt::from_args(); - let files = Files::load(&opt.file)?; + + let mut files = Files::load(&opt.file)?; println!("{}", files.now().format("%F %T %Z")); + + files.mark_all_dirty(); + files.save()?; + Ok(()) }