use std::{env, fs, path::PathBuf};
use anyhow::{anyhow, Context, Result};
use crate::Channel;
const RIALO_HOME_ENV: &str = "RIALO_HOME";
#[derive(Debug, Clone)]
pub struct RialoDirs {
home: PathBuf,
releases: PathBuf,
toolchains: PathBuf,
bin: PathBuf,
downloads: PathBuf,
tmp: PathBuf,
current_file: PathBuf,
}
impl RialoDirs {
pub fn new(explicit_home: Option<&PathBuf>) -> Result<Self> {
let home = match explicit_home {
Some(path) => path.clone(),
None => resolve_rialo_home_dir()?,
};
let home = if home.is_absolute() {
home
} else {
std::env::current_dir()
.with_context(|| "failed to get current working directory")?
.join(home)
};
Ok(Self {
releases: home.join("releases"),
toolchains: home.join("toolchains"),
bin: home.join("bin"),
downloads: home.join("downloads"),
tmp: home.join("tmp"),
current_file: home.join("current.json"),
home,
})
}
pub fn home(&self) -> &PathBuf {
&self.home
}
pub fn releases(&self) -> &PathBuf {
&self.releases
}
pub fn toolchains(&self) -> &PathBuf {
&self.toolchains
}
pub fn bin(&self) -> &PathBuf {
&self.bin
}
pub fn tmp(&self) -> &PathBuf {
&self.tmp
}
pub fn downloads(&self) -> &PathBuf {
&self.downloads
}
pub fn current_file(&self) -> &PathBuf {
&self.current_file
}
pub fn ensure_layout(&self) -> Result<()> {
self.migrate_from_old_layout()?;
fs::create_dir_all(&self.releases).with_context(|| {
format!(
"failed to create releases dir at {}",
self.releases.display()
)
})?;
fs::create_dir_all(&self.toolchains).with_context(|| {
format!(
"failed to create toolchains dir at {}",
self.toolchains.display()
)
})?;
fs::create_dir_all(&self.bin)
.with_context(|| format!("failed to create bin dir at {}", self.bin.display()))?;
fs::create_dir_all(&self.downloads).with_context(|| {
format!(
"failed to create downloads dir at {}",
self.downloads.display()
)
})?;
self.write_cache_tag()?;
fs::create_dir_all(&self.tmp)
.with_context(|| format!("failed to create tmp dir at {}", self.tmp.display()))?;
Ok(())
}
fn migrate_from_old_layout(&self) -> Result<()> {
let old_toolchains = self.home.join("toolchains");
let channels = Channel::KNOWN;
if channels
.iter()
.all(|c| !old_toolchains.join(c.as_str()).exists())
{
return Ok(());
}
match fs::create_dir_all(&self.releases) {
Ok(..) => eprintln!("Created {}", self.releases.display()),
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {}
err => err.with_context(|| format!("failed to create {}", self.releases.display()))?,
}
eprintln!("Migrating from old directory layout...");
for channel in Channel::KNOWN {
let src = old_toolchains.join(channel.as_str());
let dst = self.releases.join(channel.as_str());
match fs::rename(&src, &dst) {
Ok(..) => eprintln!("Renamed {} -> {}", src.display(), dst.display()),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
err => err.with_context(|| {
format!("failed to rename {} to {}", src.display(), dst.display())
})?,
}
}
eprintln!("Migration complete!");
Ok(())
}
fn write_cache_tag(&self) -> Result<()> {
let tag_path = self.downloads.join("CACHEDIR.TAG");
if tag_path.exists() {
return Ok(());
}
const CONTENT: &str =
"Signature: 8a477f597d28d172789f06886806bc55\n# rialoman download cache\n";
fs::write(&tag_path, CONTENT)
.with_context(|| format!("failed to write {}", tag_path.display()))
}
}
fn resolve_rialo_home_dir() -> Result<PathBuf> {
if let Some(env_home) = env::var_os(RIALO_HOME_ENV) {
return Ok(PathBuf::from(env_home));
}
if let Some(xdg_data_home) = env::var_os("XDG_DATA_HOME") {
let mut buf = PathBuf::from(xdg_data_home);
buf.push("rialo");
return Ok(buf);
}
let mut buf = dirs::home_dir().ok_or_else(|| anyhow!("failed to determine home directory"))?;
buf.extend([".local", "share", "rialo"]);
Ok(buf)
}