use std::process::Command;
const MIN_NATIVE_FSKIT_MAJOR_VERSION: u32 = 26;
pub const EXTENSION_BUNDLE_ID: &str = "sh.heddle.HeddleHost.HeddleFSModule";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Readiness {
Ready,
NeedsApproval,
NotInstalled,
UnsupportedMacOS,
Unknown,
}
pub fn probe() -> Readiness {
match macos_major_version() {
Some(major) if major < MIN_NATIVE_FSKIT_MAJOR_VERSION => {
return Readiness::UnsupportedMacOS;
}
Some(_) => {}
None => return Readiness::Unknown,
}
let output = match Command::new("pluginkit")
.args(["-m", "-p", "com.apple.fskit.fsmodule"])
.output()
{
Ok(o) => o,
Err(_) => return Readiness::Unknown,
};
if !output.status.success() {
return Readiness::Unknown;
}
let stdout = String::from_utf8_lossy(&output.stdout);
for line in stdout.lines() {
let trimmed = line.trim_start();
if !trimmed.contains(EXTENSION_BUNDLE_ID) {
continue;
}
let enabled = line.trim_start().starts_with('+');
return if enabled {
Readiness::Ready
} else {
Readiness::NeedsApproval
};
}
Readiness::NotInstalled
}
pub fn open_settings() {
let url = "x-apple.systempreferences:com.apple.LoginItems-Settings.extension?Extensions";
let _ = Command::new("open").arg(url).status();
}
pub fn setup_hint() -> &'static str {
"Heddle FSKit extension not enabled.\n\
Opening System Settings — toggle \"Heddle\" on under \
File System Extensions, then re-run."
}
pub fn unsupported_macos_hint() -> &'static str {
"Heddle native FSKit mounts require macOS 26.0 or newer.\n\
Using the NFS fallback for this virtualized workspace."
}
fn macos_major_version() -> Option<u32> {
let output = Command::new("sw_vers")
.arg("-productVersion")
.output()
.ok()?;
if !output.status.success() {
return None;
}
let version = String::from_utf8(output.stdout).ok()?;
parse_macos_major_version(&version)
}
fn parse_macos_major_version(version: &str) -> Option<u32> {
version.trim().split('.').next()?.parse().ok()
}
#[cfg(test)]
mod tests {
use super::parse_macos_major_version;
#[test]
fn parses_macos_major_versions() {
assert_eq!(parse_macos_major_version("15.4"), Some(15));
assert_eq!(parse_macos_major_version("26.0.1"), Some(26));
assert_eq!(parse_macos_major_version(""), None);
assert_eq!(parse_macos_major_version("not-a-version"), None);
}
}