pub(crate) use crate::import::atuin::Atuin; pub(crate) use crate::import::autojump::Autojump; pub(crate) use crate::import::fasd::Fasd; pub(crate) use crate::import::z::Z; pub(crate) use crate::import::z_lua::ZLua; pub(crate) use crate::import::zsh_z::ZshZ; mod atuin; mod autojump; mod fasd; mod z; mod z_lua; mod zsh_z; use std::io::{self, Write}; use std::path::PathBuf; use anyhow::Result; use crate::config; use crate::db::{Database, Dir}; pub(crate) trait Importer { /// Yields directory entries to be imported. /// /// The outer `Result` reports failure to fetch the input (e.g. missing /// file, subprocess errored). The per-item `Result` reports a malformed /// row, which doesn't necessarily abort the whole import. fn dirs(&self) -> Result, ImportError>>>; } /// A single record that failed to import. #[derive(Debug)] pub(crate) struct ImportError { /// Path of the source file containing the offending record. `None` if the /// importer is not file-based (e.g. atuin streams from a subprocess). pub path: Option, /// 1-indexed line number of the offending input. pub line_num: usize, /// Underlying reason the record could not be imported. pub source: anyhow::Error, } /// Drives a single importer end-to-end: writes each `Ok` dir into the /// database and prints each `Err` to stderr in `:: ` /// format. Doesn't abort on per-record errors — bad rows are skipped, the /// rest of the import continues. After the iteration completes successfully, /// the database is deduplicated and aged. pub(crate) fn run(importer: &impl Importer, db: &mut Database) -> Result<()> { let exclude_dirs = config::exclude_dirs()?; let stderr = io::stderr(); let mut stderr = stderr.lock(); for entry in importer.dirs()? { match entry { Ok(dir) => { if exclude_dirs.iter().any(|glob| glob.matches(&dir.path)) { continue; } db.add_unchecked(dir.path, dir.rank, dir.last_accessed); } Err(e) => { let location = match &e.path { Some(path) => format!("{}:{}", path.display(), e.line_num), None => format!("line {}", e.line_num), }; _ = writeln!(stderr, "{location}: {:#}", e.source); } } } if db.dirty() { db.dedup(); let max_age = config::maxage()?; db.age(max_age); } Ok(()) }