use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum PlatformTag {
Apple,
Linux,
Windows,
}
impl PlatformTag {
#[must_use]
pub fn matches_triple(self, triple: &str) -> bool {
match self {
Self::Apple => triple.contains("apple") || triple.contains("darwin"),
Self::Linux => triple.contains("linux"),
Self::Windows => triple.contains("windows"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PlatformFeature {
pub crate_name: &'static str,
pub feature: &'static str,
pub tag: PlatformTag,
pub note: &'static str,
}
#[must_use]
pub fn registry() -> &'static [PlatformFeature] {
&[
PlatformFeature {
crate_name: "notify",
feature: "fsevent-sys",
tag: PlatformTag::Apple,
note: "Direct alias for the macos_fsevent backend. \
Same cross-target failure mode as macos_fsevent.",
},
PlatformFeature {
crate_name: "notify",
feature: "kqueue",
tag: PlatformTag::Apple,
note: "Direct alias for the macos_kqueue backend. \
Same cross-target failure mode as macos_kqueue.",
},
PlatformFeature {
crate_name: "notify",
feature: "macos_fsevent",
tag: PlatformTag::Apple,
note: "macos_fsevent pulls `mod fsevent;` which depends \
on fsevent-sys (apple-only). Cross-target builds \
fail with E0455 'link kind framework is only \
supported on Apple targets' unless a consumer \
gates with `default-features = false`.",
},
PlatformFeature {
crate_name: "notify",
feature: "macos_kqueue",
tag: PlatformTag::Apple,
note: "macos_kqueue activates the kqueue → kqueue-sys chain. \
kqueue-sys's BSD bindings (kevent struct, EventFilter, \
EventFlag types) won't compile on linux — E0412 \
'cannot find type'. Despite the portable-sounding name, \
this feature is apple-only when set in [dependencies].",
},
]
}
#[must_use]
pub fn lookup(crate_name: &str, feature: &str) -> Option<&'static PlatformFeature> {
registry()
.iter()
.find(|e| e.crate_name == crate_name && e.feature == feature)
}
#[must_use]
pub fn registered_crate_names() -> Vec<&'static str> {
let mut out: Vec<&'static str> = registry().iter().map(|e| e.crate_name).collect();
out.sort();
out.dedup();
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn apple_matches_both_v5_and_short_triples() {
assert!(PlatformTag::Apple.matches_triple("aarch64-apple-darwin"));
assert!(PlatformTag::Apple.matches_triple("x86_64-apple-darwin"));
assert!(PlatformTag::Apple.matches_triple("aarch64-darwin"));
assert!(PlatformTag::Apple.matches_triple("x86_64-darwin"));
assert!(!PlatformTag::Apple.matches_triple("x86_64-unknown-linux-musl"));
assert!(!PlatformTag::Apple.matches_triple("aarch64-unknown-linux-gnu"));
}
#[test]
fn linux_matches_gnu_and_musl() {
assert!(PlatformTag::Linux.matches_triple("x86_64-unknown-linux-musl"));
assert!(PlatformTag::Linux.matches_triple("aarch64-unknown-linux-gnu"));
assert!(!PlatformTag::Linux.matches_triple("aarch64-apple-darwin"));
assert!(!PlatformTag::Linux.matches_triple("x86_64-pc-windows-msvc"));
}
#[test]
fn windows_matches_msvc_and_gnu() {
assert!(PlatformTag::Windows.matches_triple("x86_64-pc-windows-msvc"));
assert!(PlatformTag::Windows.matches_triple("x86_64-pc-windows-gnu"));
assert!(!PlatformTag::Windows.matches_triple("x86_64-unknown-linux-gnu"));
}
#[test]
fn registry_is_sorted_and_deduped() {
let r = registry();
for window in r.windows(2) {
let a = (window[0].crate_name, window[0].feature);
let b = (window[1].crate_name, window[1].feature);
assert!(a <= b, "registry not sorted: {a:?} > {b:?}");
assert!(a != b, "registry has duplicate entry: {a:?}");
}
}
#[test]
fn notify_macos_fsevent_is_apple_tagged() {
let e = lookup("notify", "macos_fsevent").expect("notify/macos_fsevent should be registered");
assert_eq!(e.tag, PlatformTag::Apple);
assert!(!e.tag.matches_triple("x86_64-unknown-linux-musl"));
}
#[test]
fn lookup_returns_none_for_unregistered() {
assert!(lookup("serde", "default").is_none());
assert!(lookup("notify", "no-such-feature").is_none());
}
#[test]
fn registered_names_is_sorted_unique() {
let names = registered_crate_names();
let mut sorted = names.clone();
sorted.sort();
sorted.dedup();
assert_eq!(names, sorted);
}
}