use anyhow::{Context, Result};
use std::fs;
use std::path::{Path, PathBuf};
use uuid::Uuid;
pub const INSTALL_ID_FILE: &str = "analytics-id";
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InstallId {
id: String,
first_run: bool,
}
impl InstallId {
#[must_use]
pub fn as_str(&self) -> &str {
&self.id
}
#[must_use]
pub const fn is_first_run(&self) -> bool {
self.first_run
}
}
pub fn state_dir() -> Result<PathBuf> {
let home = dirs::home_dir().context("cannot determine home directory")?;
Ok(home.join(".mesh-llm"))
}
pub fn load(dir: &Path) -> Option<InstallId> {
let raw = fs::read_to_string(dir.join(INSTALL_ID_FILE)).ok()?;
let trimmed = raw.trim();
Uuid::parse_str(trimmed).ok()?;
Some(InstallId {
id: trimmed.to_owned(),
first_run: false,
})
}
pub fn load_or_create(dir: &Path) -> Result<InstallId> {
let path = dir.join(INSTALL_ID_FILE);
if let Ok(existing) = fs::read_to_string(&path) {
let trimmed = existing.trim();
if Uuid::parse_str(trimmed).is_ok() {
return Ok(InstallId {
id: trimmed.to_owned(),
first_run: false,
});
}
}
fs::create_dir_all(dir)
.with_context(|| format!("failed to create state directory {}", dir.display()))?;
let id = Uuid::new_v4().to_string();
fs::write(&path, format!("{id}\n"))
.with_context(|| format!("failed to write install identifier {}", path.display()))?;
restrict_permissions(&path);
Ok(InstallId {
id,
first_run: true,
})
}
#[cfg(unix)]
fn restrict_permissions(path: &Path) {
use std::os::unix::fs::PermissionsExt;
let _ = fs::set_permissions(path, fs::Permissions::from_mode(0o600));
}
#[cfg(not(unix))]
fn restrict_permissions(_path: &Path) {}
#[cfg(test)]
#[path = "install_id/tests.rs"]
mod tests;