pub mod cache;
pub mod cli;
pub mod config;
pub mod errors;
pub mod fs;
pub mod git;
use anyhow::Result;
use clap::Parser;
pub fn run() -> Result<()> {
let cli = cli::Cli::parse();
run_with_cli(cli)
}
fn run_with_cli(cli: cli::Cli) -> Result<()> {
init_tracing(cli.verbose);
let command = config::AppCommand::from_cli(cli)?;
match command {
config::AppCommand::Mount(config) => fs::mount(config),
config::AppCommand::Unmount(config) => fs::unmount(config),
}
}
fn init_tracing(verbosity: u8) {
let level = match verbosity {
0 => tracing::level_filters::LevelFilter::WARN,
1 => tracing::level_filters::LevelFilter::INFO,
_ => tracing::level_filters::LevelFilter::DEBUG,
};
let _ = tracing_subscriber::fmt()
.with_max_level(level)
.with_writer(std::io::stderr)
.without_time()
.try_init();
}
#[cfg(test)]
mod tests {
use super::run_with_cli;
use crate::cli::Cli;
use std::fs;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
#[test]
fn mount_rejects_missing_repository_path() {
let mountpoint = create_temp_dir("missing-repo-mountpoint");
let repo = unique_path("missing-repo");
let cli = parse_cli([
"timefs",
"mount",
repo.to_string_lossy().as_ref(),
mountpoint.to_string_lossy().as_ref(),
]);
let error = run_with_cli(cli).expect_err("mount should fail for a missing repo path");
assert_eq!(
error.to_string(),
format!("repository path does not exist: {}", repo.display())
);
fs::remove_dir_all(mountpoint).expect("temporary mountpoint cleanup should succeed");
}
#[test]
fn mount_rejects_missing_mountpoint_path() {
let repo = create_temp_dir("missing-mountpoint-repo");
let mountpoint = unique_path("missing-mountpoint");
let cli = parse_cli([
"timefs",
"mount",
repo.to_string_lossy().as_ref(),
mountpoint.to_string_lossy().as_ref(),
]);
let error = run_with_cli(cli).expect_err("mount should fail for a missing mountpoint path");
assert_eq!(
error.to_string(),
format!("mountpoint path does not exist: {}", mountpoint.display())
);
fs::remove_dir_all(repo).expect("temporary repo cleanup should succeed");
}
fn parse_cli<I, T>(args: I) -> Cli
where
I: IntoIterator<Item = T>,
T: Into<std::ffi::OsString> + Clone,
{
<Cli as clap::Parser>::parse_from(args)
}
fn create_temp_dir(prefix: &str) -> PathBuf {
let path = unique_path(prefix);
fs::create_dir_all(&path).expect("temporary directory creation should succeed");
path
}
fn unique_path(prefix: &str) -> PathBuf {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system time should be after the Unix epoch")
.as_nanos();
std::env::temp_dir().join(format!("timefs-{prefix}-{nanos}"))
}
#[allow(dead_code)]
fn _assert_exists(path: &Path) {
assert!(path.exists(), "expected path to exist: {}", path.display());
}
}