commit fa2a8c862607216d340ea97c74a1c3cbb70f84e9
parent f662dd6fb7bef74fe87554e43bb79224aef8c122
Author: triesap <tyson@radroots.org>
Date: Tue, 11 Aug 2026 06:35:35 +0000
runtime-paths: define linux service host roots
- resolve the service-host profile only on explicit Linux
- reject macOS Windows mobile and unknown host platforms
- stop classifying unknown targets as Linux service hosts
- preserve tested interactive mobile and repo-local behavior
Diffstat:
4 files changed, 72 insertions(+), 102 deletions(-)
diff --git a/crates/runtime_paths/src/error.rs b/crates/runtime_paths/src/error.rs
@@ -12,9 +12,6 @@ pub enum RadrootsRuntimePathsError {
#[error("interactive_user on windows requires APPDATA and LOCALAPPDATA roots")]
MissingWindowsUserDirs,
- #[error("service_host on windows requires a ProgramData root")]
- MissingWindowsProgramDataDir,
-
#[error("repo_local requires an explicit repo-local base root")]
MissingRepoLocalRoot,
diff --git a/crates/runtime_paths/src/lib.rs b/crates/runtime_paths/src/lib.rs
@@ -136,7 +136,7 @@ mod tests {
}
#[test]
- fn service_host_unix_uses_canonical_service_roots() {
+ fn service_host_linux_uses_canonical_service_roots() {
let resolver =
RadrootsPathResolver::new(RadrootsPlatform::Linux, RadrootsHostEnvironment::default());
@@ -161,45 +161,28 @@ mod tests {
}
#[test]
- fn service_host_windows_uses_programdata_roots() {
- let resolver = RadrootsPathResolver::new(
+ fn service_host_is_unsupported_outside_linux() {
+ for platform in [
+ RadrootsPlatform::Macos,
RadrootsPlatform::Windows,
- RadrootsHostEnvironment {
- programdata_dir: Some(PathBuf::from(r"C:\ProgramData")),
- ..RadrootsHostEnvironment::default()
- },
- );
-
- let roots = resolver
- .resolve(
- RadrootsPathProfile::ServiceHost,
- &RadrootsPathOverrides::default(),
- )
- .expect("resolve service_host roots");
-
- assert_eq!(
- roots,
- RadrootsPaths {
- config: PathBuf::from(r"C:\ProgramData")
- .join("Radroots")
- .join("config"),
- data: PathBuf::from(r"C:\ProgramData")
- .join("Radroots")
- .join("data"),
- cache: PathBuf::from(r"C:\ProgramData")
- .join("Radroots")
- .join("cache"),
- logs: PathBuf::from(r"C:\ProgramData")
- .join("Radroots")
- .join("logs"),
- run: PathBuf::from(r"C:\ProgramData")
- .join("Radroots")
- .join("run"),
- secrets: PathBuf::from(r"C:\ProgramData")
- .join("Radroots")
- .join("secrets"),
- }
- );
+ RadrootsPlatform::Android,
+ RadrootsPlatform::Ios,
+ RadrootsPlatform::Other,
+ ] {
+ let resolver = RadrootsPathResolver::new(platform, RadrootsHostEnvironment::default());
+ assert_eq!(
+ resolver
+ .resolve(
+ RadrootsPathProfile::ServiceHost,
+ &RadrootsPathOverrides::default(),
+ )
+ .expect_err("service_host must be unsupported outside linux"),
+ RadrootsRuntimePathsError::UnsupportedProfilePlatform {
+ profile: RadrootsPathProfile::ServiceHost,
+ platform,
+ }
+ );
+ }
}
#[test]
diff --git a/crates/runtime_paths/src/platform.rs b/crates/runtime_paths/src/platform.rs
@@ -8,6 +8,7 @@ pub enum RadrootsPlatform {
Windows,
Android,
Ios,
+ Other,
}
impl RadrootsPlatform {
@@ -36,17 +37,24 @@ impl RadrootsPlatform {
}
#[must_use]
- #[cfg(all(
- not(target_os = "android"),
- not(target_os = "ios"),
- not(target_os = "macos"),
- not(target_os = "windows")
- ))]
+ #[cfg(target_os = "linux")]
pub fn current() -> Self {
Self::Linux
}
#[must_use]
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "windows"
+ )))]
+ pub fn current() -> Self {
+ Self::Other
+ }
+
+ #[must_use]
pub fn is_unix_like(self) -> bool {
matches!(self, Self::Linux | Self::Macos)
}
@@ -60,6 +68,7 @@ impl fmt::Display for RadrootsPlatform {
Self::Windows => "windows",
Self::Android => "android",
Self::Ios => "ios",
+ Self::Other => "other",
})
}
}
@@ -88,7 +97,6 @@ pub struct RadrootsHostEnvironment {
pub home_dir: Option<PathBuf>,
pub appdata_dir: Option<PathBuf>,
pub localappdata_dir: Option<PathBuf>,
- pub programdata_dir: Option<PathBuf>,
}
impl RadrootsHostEnvironment {
@@ -98,7 +106,6 @@ impl RadrootsHostEnvironment {
home_dir: std::env::var_os("HOME").map(PathBuf::from),
appdata_dir: std::env::var_os("APPDATA").map(PathBuf::from),
localappdata_dir: std::env::var_os("LOCALAPPDATA").map(PathBuf::from),
- programdata_dir: std::env::var_os("ProgramData").map(PathBuf::from),
}
}
}
@@ -119,13 +126,16 @@ mod tests {
let expected = RadrootsPlatform::Macos;
#[cfg(target_os = "windows")]
let expected = RadrootsPlatform::Windows;
- #[cfg(all(
- not(target_os = "android"),
- not(target_os = "ios"),
- not(target_os = "macos"),
- not(target_os = "windows")
- ))]
+ #[cfg(target_os = "linux")]
let expected = RadrootsPlatform::Linux;
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "windows"
+ )))]
+ let expected = RadrootsPlatform::Other;
assert_eq!(RadrootsPlatform::current(), expected);
}
@@ -137,6 +147,7 @@ mod tests {
assert!(!RadrootsPlatform::Windows.is_unix_like());
assert!(!RadrootsPlatform::Android.is_unix_like());
assert!(!RadrootsPlatform::Ios.is_unix_like());
+ assert!(!RadrootsPlatform::Other.is_unix_like());
}
#[test]
@@ -146,6 +157,7 @@ mod tests {
assert_eq!(RadrootsPlatform::Windows.to_string(), "windows");
assert_eq!(RadrootsPlatform::Android.to_string(), "android");
assert_eq!(RadrootsPlatform::Ios.to_string(), "ios");
+ assert_eq!(RadrootsPlatform::Other.to_string(), "other");
assert_eq!(
RadrootsPathProfile::InteractiveUser.to_string(),
@@ -171,9 +183,5 @@ mod tests {
env.localappdata_dir,
std::env::var_os("LOCALAPPDATA").map(PathBuf::from)
);
- assert_eq!(
- env.programdata_dir,
- std::env::var_os("ProgramData").map(PathBuf::from)
- );
}
}
diff --git a/crates/runtime_paths/src/roots.rs b/crates/runtime_paths/src/roots.rs
@@ -113,10 +113,15 @@ impl RadrootsPathResolver {
.mobile_roots
.clone()
.ok_or(RadrootsRuntimePathsError::MissingMobileRoots),
- _ => Err(RadrootsRuntimePathsError::UnsupportedProfilePlatform {
- profile,
- platform: self.platform,
- }),
+ RadrootsPlatform::Linux
+ | RadrootsPlatform::Macos
+ | RadrootsPlatform::Windows
+ | RadrootsPlatform::Other => {
+ Err(RadrootsRuntimePathsError::UnsupportedProfilePlatform {
+ profile,
+ platform: self.platform,
+ })
+ }
},
}
}
@@ -153,7 +158,7 @@ impl RadrootsPathResolver {
secrets: config_root.join("secrets"),
})
}
- RadrootsPlatform::Android | RadrootsPlatform::Ios => {
+ RadrootsPlatform::Android | RadrootsPlatform::Ios | RadrootsPlatform::Other => {
Err(RadrootsRuntimePathsError::UnsupportedProfilePlatform {
profile: RadrootsPathProfile::InteractiveUser,
platform: self.platform,
@@ -164,23 +169,7 @@ impl RadrootsPathResolver {
fn resolve_service_host(&self) -> Result<RadrootsPaths, RadrootsRuntimePathsError> {
match self.platform {
- RadrootsPlatform::Windows => {
- let programdata = self
- .host_environment
- .programdata_dir
- .as_ref()
- .ok_or(RadrootsRuntimePathsError::MissingWindowsProgramDataDir)?;
- let base = programdata.join("Radroots");
- Ok(RadrootsPaths {
- config: base.join("config"),
- data: base.join("data"),
- cache: base.join("cache"),
- logs: base.join("logs"),
- run: base.join("run"),
- secrets: base.join("secrets"),
- })
- }
- RadrootsPlatform::Linux | RadrootsPlatform::Macos => Ok(RadrootsPaths {
+ RadrootsPlatform::Linux => Ok(RadrootsPaths {
config: PathBuf::from("/etc/radroots"),
data: PathBuf::from("/var/lib/radroots"),
cache: PathBuf::from("/var/cache/radroots"),
@@ -188,7 +177,11 @@ impl RadrootsPathResolver {
run: PathBuf::from("/run/radroots"),
secrets: PathBuf::from("/etc/radroots/secrets"),
}),
- RadrootsPlatform::Android | RadrootsPlatform::Ios => {
+ RadrootsPlatform::Macos
+ | RadrootsPlatform::Windows
+ | RadrootsPlatform::Android
+ | RadrootsPlatform::Ios
+ | RadrootsPlatform::Other => {
Err(RadrootsRuntimePathsError::UnsupportedProfilePlatform {
profile: RadrootsPathProfile::ServiceHost,
platform: self.platform,
@@ -277,32 +270,21 @@ mod tests {
}
#[test]
- fn service_host_windows_requires_programdata() {
- let resolver = RadrootsPathResolver::new(
+ fn service_host_is_rejected_outside_linux() {
+ for platform in [
+ RadrootsPlatform::Macos,
RadrootsPlatform::Windows,
- RadrootsHostEnvironment::default(),
- );
-
- let err = resolver
- .resolve(
- RadrootsPathProfile::ServiceHost,
- &RadrootsPathOverrides::default(),
- )
- .expect_err("service_host on windows should require programdata");
-
- assert_eq!(err, RadrootsRuntimePathsError::MissingWindowsProgramDataDir);
- }
-
- #[test]
- fn service_host_is_rejected_on_mobile_platforms() {
- for platform in [RadrootsPlatform::Android, RadrootsPlatform::Ios] {
+ RadrootsPlatform::Android,
+ RadrootsPlatform::Ios,
+ RadrootsPlatform::Other,
+ ] {
let resolver = RadrootsPathResolver::new(platform, RadrootsHostEnvironment::default());
let err = resolver
.resolve(
RadrootsPathProfile::ServiceHost,
&RadrootsPathOverrides::default(),
)
- .expect_err("service_host should be unsupported on mobile");
+ .expect_err("service_host should be unsupported outside linux");
assert_eq!(
err,
RadrootsRuntimePathsError::UnsupportedProfilePlatform {