diff options
Diffstat (limited to 'src/actions/index/index.rs')
-rw-r--r-- | src/actions/index/index.rs | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/src/actions/index/index.rs b/src/actions/index/index.rs new file mode 100644 index 0000000..96acf8a --- /dev/null +++ b/src/actions/index/index.rs @@ -0,0 +1,152 @@ +use chrono::prelude::*; +use icalwrap::*; +use std::collections::HashMap; +use std::fs; +use std::path::{Path,PathBuf}; +use std::time::SystemTime; +use walkdir::DirEntry; + +use defaults::*; +use super::indextime; +use utils::fileutil; +use utils::lock; +use utils::misc; + + +fn add_buckets_for_calendar(buckets: &mut HashMap<String, Vec<String>>, cal: &IcalVCalendar) { + use super::bucketable::Bucketable; + use super::bucketable::Merge; + + match cal.get_buckets() { + Ok(cal_buckets) => buckets.merge(cal_buckets), + Err(error) => { + warn!("{}", error) + } + } +} + +pub fn index_dir(dir: &Path, reindex: bool) { + use std::time::Instant; + + let lock = lock::lock_file_exclusive(&get_indexlockfile()); + if lock.is_err() { + error!("Failed to obtain index lock!"); + return; + } + + info!("Recursively indexing '.ics' files in directory: {}", dir.to_string_lossy()); + if !dir.exists() { + error!("Directory doesn't exist: {}", dir.to_string_lossy()); + return; + } + + let now = Instant::now(); + let start_time = Utc::now(); + + let last_index_time = if reindex { + debug!("Forced reindex, indexing all files"); + None + } else { + let last_index_time = indextime::get_index_time(); + match last_index_time { + Some(time) => debug!("Previously indexed {}, indexing newer files only", time.with_timezone(&Local)), + None => debug!("No previous index time, indexing all files"), + } + last_index_time + }; + + let modified_since = last_index_time.map(|time| time.timestamp()).unwrap_or(0); + let ics_files = get_ics_files(dir, modified_since); + + let buckets = read_buckets(ics_files); + + let indexdir = get_indexdir(); + let clear_index_dir = last_index_time.is_none(); + if let Err(error) = prepare_index_dir(&indexdir, clear_index_dir) { + error!("{}", error); + return; + } + + write_index(&indexdir, &buckets); + info!("Index written in {}ms", misc::format_duration(&now.elapsed())); + + indextime::write_index_time(&start_time); +} + +pub fn get_ics_files(dir: &Path, modified_since: i64) -> impl Iterator<Item = PathBuf> { + use walkdir::WalkDir; + + WalkDir::new(dir).into_iter() + .filter_entry(move |entry| accept_entry(entry, modified_since)) + .filter_map(|e| e.ok()) + .filter(|e| e.file_type().is_file()) + .filter(|e| e.path().extension().map_or(false, |extension| extension == "ics")) + .map(|entry| entry.into_path()) +} + +fn accept_entry(dir_entry: &DirEntry, modified_since: i64) -> bool { + if dir_entry.path().is_dir() { + return true; + } + dir_entry.metadata() + .map_err(|err| err.into()) // transform to io::Error + .and_then(|metadata| metadata.modified()) + .map(|modified| modified.duration_since(SystemTime::UNIX_EPOCH).unwrap()) + .map(|modified| modified.as_secs() as i64) + .map(|modified| modified > modified_since) + .unwrap_or(false) +} + +fn read_buckets(ics_files: impl Iterator<Item = PathBuf>) -> HashMap<String, Vec<String>> { + let mut buckets: HashMap<String, Vec<String>> = HashMap::new(); + + let mut total_files = 0; + for file in ics_files { + debug!("Indexing file: {:?}", file); + match fileutil::read_file_to_string(&file) { + Ok(content) => { + total_files += 1; + match IcalVCalendar::from_str(&content, Some(&file)) { + Ok(mut cal) => add_buckets_for_calendar(&mut buckets, &cal), + Err(error) => error!("{:?}: {}", file, error) + } + } + Err(error) => error!("{}", error), + } + } + + info!("Loaded {} files into {} buckets", total_files, buckets.len()); + buckets +} + +fn write_index(index_dir: &Path, buckets: &HashMap<String, Vec<String>>) { + for (key, val) in buckets.iter() { + let bucketfile = bucket_file(index_dir, key); + trace!("Writing bucket: {}", key); + let content = &[&val.join("\n"), "\n"].concat(); + if let Err(error) = fileutil::append_file(&bucketfile, content) { + error!("{}", error); + return; + } + } +} + +fn bucket_file(index_dir: &Path, key: &str) -> PathBuf { + let mut result = PathBuf::from(index_dir); + result.push(key); + result +} + +fn prepare_index_dir(indexdir: &Path, clear_index_dir: bool) -> Result<(), std::io::Error> { + if indexdir.exists() && clear_index_dir { + info!("Clearing index directory: {}", indexdir.to_string_lossy()); + fs::remove_dir_all(&indexdir)? + } + + if !indexdir.exists() { + info!("Creating index directory: {}", indexdir.to_string_lossy()); + fs::create_dir(&indexdir)?; + } + + Ok(()) +} |