use crate::manifest::Shell;
use crate::operations::{DestinationDirectory, SourceDirectory};
use crate::Manifest;
use anyhow::{Context, Result};
use directories::{BaseDirs, ProjectDirs};
use std::borrow::Cow;
use std::path::{Path, PathBuf};
use tempfile::{tempdir, TempDir};
fn project_dirs() -> Result<ProjectDirs> {
ProjectDirs::from("de", "swsnr", "homebins")
.with_context(|| "Failed to get home directory".to_string())
}
#[derive(Debug)]
pub struct HomebinProjectDirs {
repos_dir: PathBuf,
download_dir: PathBuf,
}
impl HomebinProjectDirs {
pub fn open() -> Result<HomebinProjectDirs> {
project_dirs().map(|dirs| HomebinProjectDirs {
repos_dir: dirs.cache_dir().join("manifest_repos"),
download_dir: dirs.cache_dir().join("downloads"),
})
}
pub fn repos_dir(&self) -> &Path {
&self.repos_dir
}
pub fn download_dir(&self) -> &Path {
&self.download_dir
}
pub fn manifest_download_dir(&self, manifest: &Manifest) -> PathBuf {
self.download_dir
.join(&manifest.info.name)
.join(&manifest.info.version.to_string())
}
}
#[derive(Debug)]
pub struct InstallDirs {
bin_dir: PathBuf,
man_base_dir: PathBuf,
systemd_user_unit_dir: PathBuf,
fish_completion_dir: PathBuf,
}
impl InstallDirs {
pub fn from_base_dirs(dirs: &BaseDirs) -> Result<InstallDirs> {
Ok(InstallDirs {
bin_dir: dirs
.executable_dir()
.with_context(|| {
"Cannot determine executable directory from base dirs".to_string()
})?
.to_path_buf(),
man_base_dir: dirs.data_local_dir().join("man"),
systemd_user_unit_dir: dirs.data_local_dir().join("systemd").join("user"),
fish_completion_dir: dirs.config_dir().join("fish").join("completions"),
})
}
pub fn bin_dir(&self) -> &Path {
&self.bin_dir
}
pub fn man_dir(&self) -> &Path {
&self.man_base_dir
}
pub fn man_section_dir(&self, section: u8) -> PathBuf {
self.man_base_dir.join(format!("man{}", section))
}
pub fn systemd_user_unit_dir(&self) -> &Path {
&self.systemd_user_unit_dir
}
pub fn shell_completion_dir(&self, shell: Shell) -> &Path {
match shell {
Shell::Fish => &self.fish_completion_dir,
}
}
pub fn path(&self, directory: DestinationDirectory) -> Cow<Path> {
match directory {
DestinationDirectory::BinDir => Cow::from(&self.bin_dir),
DestinationDirectory::ManDir(section) => Cow::from(self.man_section_dir(section)),
DestinationDirectory::SystemdUserUnitDir => Cow::from(&self.systemd_user_unit_dir),
DestinationDirectory::CompletionDir(shell) => {
Cow::from(self.shell_completion_dir(shell))
}
}
}
}
#[derive(Debug)]
pub struct ManifestOperationDirs<'a> {
install_dirs: &'a mut InstallDirs,
download_dir: PathBuf,
work_dir: TempDir,
}
impl<'a> ManifestOperationDirs<'a> {
pub fn for_manifest(
dirs: &HomebinProjectDirs,
install_dirs: &'a mut InstallDirs,
manifest: &Manifest,
) -> Result<ManifestOperationDirs<'a>> {
tempdir()
.with_context(|| {
format!(
"Failed to create workdir for manifest {}",
manifest.info.name
)
})
.map(move |work_dir| ManifestOperationDirs {
work_dir,
install_dirs,
download_dir: dirs.manifest_download_dir(manifest),
})
}
pub fn install_dirs(&self) -> &InstallDirs {
self.install_dirs
}
pub fn download_dir(&self) -> &Path {
&self.download_dir
}
pub fn work_dir(&self) -> &Path {
&self.work_dir.path()
}
pub fn path(&self, directory: SourceDirectory) -> &Path {
match directory {
SourceDirectory::Download => &self.download_dir,
SourceDirectory::WorkDir => &self.work_dir.path(),
}
}
pub fn ensure(&self) -> Result<()> {
std::fs::create_dir_all(self.download_dir()).with_context(|| {
format!(
"Failed to create download directory at {}",
self.download_dir().display()
)
})
}
pub fn close(self) -> Result<()> {
self.work_dir
.close()
.with_context(|| "Failed to delete manifest workdir".to_string())
}
}
#[cfg(test)]
mod tests {
use crate::manifest::Shell;
use crate::operations::DestinationDirectory;
use crate::InstallDirs;
use directories::BaseDirs;
use pretty_assertions::assert_eq;
use std::path::Path;
#[test]
fn install_dirs_from_base_dirs() {
std::env::set_var("XDG_CONFIG_HOME", "/test/config");
std::env::set_var("XDG_DATA_HOME", "/test/data_home");
std::env::set_var("XDG_BIN_DIR", "/test/bin");
let dirs = InstallDirs::from_base_dirs(&BaseDirs::new().expect("base dirs"))
.expect("install dirs");
assert_eq!(
dirs.path(DestinationDirectory::BinDir),
Path::new("/test/bin")
);
assert_eq!(
dirs.path(DestinationDirectory::ManDir(4)),
Path::new("/test/data_home/man/man4")
);
assert_eq!(
dirs.path(DestinationDirectory::SystemdUserUnitDir),
Path::new("/test/data_home/systemd/user")
);
assert_eq!(
dirs.path(DestinationDirectory::CompletionDir(Shell::Fish)),
Path::new("/test/config/fish/completions")
);
}
}