oxios_kernel/skill/
requirements.rs1#![allow(missing_docs)]
2use super::types::*;
5
6fn has_bin(bin: &str) -> bool {
7 std::process::Command::new("which")
8 .arg(bin)
9 .output()
10 .map(|o| o.status.success())
11 .unwrap_or(false)
12}
13
14pub fn current_platform() -> &'static str {
15 if cfg!(target_os = "macos") {
16 "darwin"
17 } else if cfg!(target_os = "windows") {
18 "windows"
19 } else {
20 "linux"
21 }
22}
23
24pub fn check_requirements(metadata: &SkillMetadata) -> RequirementsCheck {
25 let platform = current_platform();
26 let missing_bins: Vec<String> = metadata
27 .requires
28 .bins
29 .iter()
30 .filter(|b| !has_bin(b))
31 .cloned()
32 .collect();
33 let missing_any_bins = if metadata.requires.any_bins.is_empty()
34 || metadata.requires.any_bins.iter().any(|b| has_bin(b))
35 {
36 Vec::new()
37 } else {
38 metadata.requires.any_bins.clone()
39 };
40 let missing_env: Vec<String> = metadata
41 .requires
42 .env
43 .iter()
44 .filter(|e| std::env::var(e).is_err())
45 .cloned()
46 .collect();
47 let config_checks: Vec<ConfigCheck> = metadata
48 .requires
49 .config
50 .iter()
51 .map(|path| ConfigCheck {
52 path: path.clone(),
53 satisfied: true,
54 })
55 .collect();
56 let missing_config: Vec<String> = config_checks
57 .iter()
58 .filter(|c| !c.satisfied)
59 .map(|c| c.path.clone())
60 .collect();
61 let missing_os = if metadata.os.is_empty() || metadata.os.iter().any(|o| o == platform) {
62 Vec::new()
63 } else {
64 metadata.os.clone()
65 };
66 let eligible = metadata.always
67 || (missing_bins.is_empty()
68 && missing_any_bins.is_empty()
69 && missing_env.is_empty()
70 && missing_config.is_empty()
71 && missing_os.is_empty());
72 RequirementsCheck {
73 missing_bins,
74 missing_any_bins,
75 missing_env,
76 missing_config,
77 missing_os,
78 eligible,
79 config_checks,
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 #[test]
87 fn test_no_reqs() {
88 assert!(check_requirements(&SkillMetadata::default()).eligible);
89 }
90 #[test]
91 fn test_always() {
92 let mut m = SkillMetadata::default();
93 m.always = true;
94 m.requires.bins = vec!["nonexistent-xyz".into()];
95 assert!(check_requirements(&m).eligible);
96 }
97 #[test]
98 fn test_existing_bin() {
99 let mut m = SkillMetadata::default();
100 m.requires.bins = vec!["echo".into()];
101 assert!(check_requirements(&m).eligible);
102 }
103 #[test]
104 fn test_missing_bin() {
105 let mut m = SkillMetadata::default();
106 m.requires.bins = vec!["nonexistent-xyz".into()];
107 assert!(!check_requirements(&m).eligible);
108 }
109 #[test]
110 fn test_missing_env() {
111 let mut m = SkillMetadata::default();
112 m.requires.env = vec!["OXIOS_TEST_MISSING".into()];
113 assert!(!check_requirements(&m).eligible);
114 }
115}