From 817732abf6634010c0a3032197c3fbf3c13d2ddb Mon Sep 17 00:00:00 2001 From: Joscha Date: Tue, 23 Nov 2021 23:59:21 +0100 Subject: [PATCH] Make Source less of a hassle to work with --- src/files.rs | 76 +++++++++++++++++++++++++++---------------- src/files/commands.rs | 3 -- src/files/parse.rs | 7 ++-- 3 files changed, 51 insertions(+), 35 deletions(-) diff --git a/src/files.rs b/src/files.rs index 400a64d..7b7e97f 100644 --- a/src/files.rs +++ b/src/files.rs @@ -13,31 +13,41 @@ mod parse; #[derive(Debug)] struct LoadedFile { + /// Canonical path for this file + path: PathBuf, + // User-readable path for this file + name: PathBuf, file: File, + /// Whether this file has been changed dirty: bool, } impl LoadedFile { - pub fn new(file: File) -> Self { - Self { file, dirty: false } + pub fn new(path: PathBuf, name: PathBuf, file: File) -> Self { + Self { + path, + name, + file, + dirty: false, + } } } -#[derive(Debug)] -pub struct Source<'a> { - file: &'a Path, - index: usize, +#[derive(Debug, Clone, Copy)] +pub struct Source { + file: usize, + command: usize, } #[derive(Debug)] pub struct SourcedCommand<'a> { - pub source: Source<'a>, + pub source: Source, pub command: &'a Command, } #[derive(Debug)] pub struct Files { - files: HashMap, + files: Vec, timezone: Tz, } @@ -60,51 +70,58 @@ pub type Result = result::Result; impl Files { pub fn load(path: &Path) -> Result { - let mut files = HashMap::new(); - Self::load_file(&mut files, path)?; + let mut paths = HashMap::new(); + let mut files = vec![]; + Self::load_file(&mut paths, &mut files, path)?; let timezone = Self::determine_timezone(&files)?; Ok(Self { files, timezone }) } - fn load_file(files: &mut HashMap, path: &Path) -> Result<()> { - let canon_path = path.canonicalize()?; - if files.contains_key(&canon_path) { + fn load_file( + paths: &mut HashMap, + files: &mut Vec, + name: &Path, + ) -> Result<()> { + let path = name.canonicalize()?; + if paths.contains_key(&path) { // We've already loaded this exact file. return Ok(()); } - let content = fs::read_to_string(path)?; - let file = parse::parse(path, &content)?; + let content = fs::read_to_string(name)?; + // Using `name` instead of `path` for the unwrap below. + let file = parse::parse(name, &content)?; let includes = file.includes.clone(); - files.insert(canon_path, LoadedFile::new(file)); + paths.insert(path.clone(), files.len()); + files.push(LoadedFile::new(path, name.to_owned(), file)); for include in includes { // 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)?; + let include_path = name.parent().unwrap().join(include); + Self::load_file(paths, files, &include_path)?; } Ok(()) } - fn determine_timezone(files: &HashMap) -> Result { + fn determine_timezone(files: &[LoadedFile]) -> Result { let mut found: Option<(PathBuf, String)> = None; - for file in files.values() { + for file in files { 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.file.name.clone(), + file2: file.name.clone(), tz2: file_tz.clone(), }); } } else { - found = Some((file.file.name.clone(), file_tz.clone())); + found = Some((file.name.clone(), file_tz.clone())); } } } @@ -117,9 +134,9 @@ impl Files { } pub fn save(&self) -> Result<()> { - for (path, file) in &self.files { + for file in &self.files { if file.dirty { - Self::save_file(path, &file.file)?; + Self::save_file(&file.path, &file.file)?; } } Ok(()) @@ -131,16 +148,19 @@ impl Files { } pub fn mark_all_dirty(&mut self) { - for (_, file) in self.files.iter_mut() { + for file in self.files.iter_mut() { file.dirty = true; } } pub fn commands(&self) -> Vec> { let mut result = vec![]; - for (path, file) in &self.files { - for (index, command) in file.file.commands.iter().enumerate() { - let source = Source { file: path, index }; + for (file_index, file) in self.files.iter().enumerate() { + for (command_index, command) in file.file.commands.iter().enumerate() { + let source = Source { + file: file_index, + command: command_index, + }; result.push(SourcedCommand { source, command }); } } diff --git a/src/files/commands.rs b/src/files/commands.rs index 1c4283a..947e7e3 100644 --- a/src/files/commands.rs +++ b/src/files/commands.rs @@ -1,5 +1,3 @@ -use std::path::PathBuf; - use chrono::NaiveDate; #[derive(Debug)] @@ -328,7 +326,6 @@ pub enum Command { #[derive(Debug)] pub struct File { - pub name: PathBuf, pub includes: Vec, pub timezone: Option, pub commands: Vec, diff --git a/src/files/parse.rs b/src/files/parse.rs index fa62b28..27099fa 100644 --- a/src/files/parse.rs +++ b/src/files/parse.rs @@ -1,4 +1,4 @@ -use std::path::{Path, PathBuf}; +use std::path::Path; use std::result; use chrono::NaiveDate; @@ -720,11 +720,10 @@ fn parse_command(p: Pair<'_, Rule>, file: &mut File) -> Result<()> { Ok(()) } -pub fn parse_file(p: Pair<'_, Rule>, name: PathBuf) -> Result { +pub fn parse_file(p: Pair<'_, Rule>) -> Result { assert_eq!(p.as_rule(), Rule::file); let mut file = File { - name, includes: vec![], timezone: None, commands: vec![], @@ -749,5 +748,5 @@ pub fn parse(path: &Path, input: &str) -> Result { let file_pair = pairs.next().unwrap(); assert_eq!(pairs.next(), None); - parse_file(file_pair, path.to_owned()).map_err(|e| e.with_path(&pathstr)) + parse_file(file_pair).map_err(|e| e.with_path(&pathstr)) }