Skip to main content

aube_runtime/
paths.rs

1//! On-disk layout for aube-managed Node installs and caches. Follows
2//! the same XDG / `%LOCALAPPDATA%` / home-fallback convention as
3//! `aube-store/src/dirs.rs` (kept separate so this crate doesn't pull
4//! in the CAS machinery for three path joins).
5//!
6//! ```text
7//! $XDG_DATA_HOME/aube/nodejs/
8//! ├── 24.1.0/              # native layout: unix bin/node, win node.exe
9//! ├── .downloads/          # in-flight archive downloads
10//! ├── .tmp/                # extraction staging (rename source)
11//! └── .locks/              # per-version fslock files
12//! ```
13
14use std::path::PathBuf;
15
16/// Root of aube-managed Node installs. `AUBE_RUNTIME_DIR` overrides
17/// for tests and unusual setups.
18pub fn runtime_dir() -> Option<PathBuf> {
19    if let Some(dir) = std::env::var_os("AUBE_RUNTIME_DIR")
20        && !dir.is_empty()
21    {
22        return Some(PathBuf::from(dir));
23    }
24    #[cfg(windows)]
25    if let Ok(local) = std::env::var("LOCALAPPDATA") {
26        return Some(PathBuf::from(local).join("aube/nodejs"));
27    }
28    let data_home = match aube_util::env::xdg_data_home() {
29        Some(xdg) => xdg,
30        None => aube_util::env::home_dir()?.join(".local/share"),
31    };
32    Some(data_home.join("aube/nodejs"))
33}
34
35/// The install dir for an exact version: `<runtime_dir>/24.1.0/`.
36pub fn install_dir(version: &node_semver::Version) -> Option<PathBuf> {
37    runtime_dir().map(|d| d.join(version.to_string()))
38}
39
40pub(crate) fn downloads_dir() -> Option<PathBuf> {
41    runtime_dir().map(|d| d.join(".downloads"))
42}
43
44pub(crate) fn staging_dir() -> Option<PathBuf> {
45    runtime_dir().map(|d| d.join(".tmp"))
46}
47
48pub(crate) fn locks_dir() -> Option<PathBuf> {
49    runtime_dir().map(|d| d.join(".locks"))
50}
51
52/// Cache directory shared with the rest of aube
53/// (`$XDG_CACHE_HOME/aube`), mirroring `aube-store`'s `cache_dir`.
54pub(crate) fn cache_dir() -> Option<PathBuf> {
55    if let Some(xdg) = aube_util::env::xdg_cache_home() {
56        return Some(xdg.join("aube"));
57    }
58    #[cfg(windows)]
59    if let Ok(local) = std::env::var("LOCALAPPDATA") {
60        return Some(PathBuf::from(local).join("aube"));
61    }
62    aube_util::env::home_dir().map(|h| h.join(".cache/aube"))
63}
64
65/// Disk cache for the dist index, segmented by mirror origin so two
66/// mirrors never serve each other's entries:
67/// `$XDG_CACHE_HOME/aube/node-index/origin-<sha256-16>/index.json`.
68pub(crate) fn index_cache_path(mirror_base: &str) -> Option<PathBuf> {
69    cache_dir().map(|d| {
70        d.join("node-index")
71            .join(origin_segment(mirror_base))
72            .join("index.json")
73    })
74}
75
76/// Disk cache for a release's SHASUMS256.txt (immutable once
77/// published — cached forever):
78/// `$XDG_CACHE_HOME/aube/node-shasums/origin-<sha256-16>/v24.1.0.txt`.
79pub(crate) fn shasums_cache_path(
80    mirror_base: &str,
81    version: &node_semver::Version,
82) -> Option<PathBuf> {
83    cache_dir().map(|d| {
84        d.join("node-shasums")
85            .join(origin_segment(mirror_base))
86            .join(format!("v{version}.txt"))
87    })
88}
89
90fn origin_segment(mirror_base: &str) -> String {
91    use sha2::Digest;
92    let digest = sha2::Sha256::digest(mirror_base.as_bytes());
93    format!("origin-{}", hex::encode(&digest[..8]))
94}