1use std::path::{Path, PathBuf};
19
20use crate::probe::{Toolbox, Version};
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum Origin {
25 Override,
27 Path,
29 Sibling,
31}
32
33impl Origin {
34 pub fn token(self) -> &'static str {
35 match self {
36 Origin::Override => "override",
37 Origin::Path => "path",
38 Origin::Sibling => "sibling",
39 }
40 }
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum Skew {
48 Match,
50 Minor,
52 Major,
54}
55
56impl Skew {
57 pub fn classify(driver: Version, compiler: Version) -> Skew {
59 if driver.major != compiler.major {
60 Skew::Major
61 } else if driver.minor != compiler.minor {
62 Skew::Minor
63 } else {
64 Skew::Match
65 }
66 }
67
68 pub fn token(self) -> &'static str {
69 match self {
70 Skew::Match => "match",
71 Skew::Minor => "minor",
72 Skew::Major => "major",
73 }
74 }
75}
76
77#[derive(Debug, Clone)]
79pub struct Compiler {
80 pub path: Option<PathBuf>,
83 pub origin: Option<Origin>,
84 pub version: Option<Version>,
85 pub skew: Option<Skew>,
87}
88
89impl Compiler {
90 pub fn is_resolved(&self) -> bool {
91 self.path.is_some()
92 }
93
94 pub fn has_major_skew(&self) -> bool {
96 self.skew == Some(Skew::Major)
97 }
98}
99
100pub fn resolve(
104 tb: &dyn Toolbox,
105 override_path: Option<&Path>,
106 bynk_bin_dir: Option<&Path>,
107 driver: Version,
108) -> Compiler {
109 let (path, origin) = locate(tb, override_path, bynk_bin_dir);
110 let version = path.as_deref().and_then(|p| tb.version(p));
111 let skew = version.map(|v| Skew::classify(driver, v));
112 Compiler {
113 path,
114 origin,
115 version,
116 skew,
117 }
118}
119
120fn locate(
121 tb: &dyn Toolbox,
122 override_path: Option<&Path>,
123 bynk_bin_dir: Option<&Path>,
124) -> (Option<PathBuf>, Option<Origin>) {
125 if let Some(ovr) = override_path {
126 if let Some(p) = tb.in_dir(ovr.parent().unwrap_or(Path::new(".")), file_stem(ovr)) {
130 return (Some(p), Some(Origin::Override));
131 }
132 return (Some(ovr.to_path_buf()), Some(Origin::Override));
133 }
134 if let Some(p) = tb.on_path("bynkc") {
135 return (Some(p), Some(Origin::Path));
136 }
137 if let Some(dir) = bynk_bin_dir
138 && let Some(p) = tb.in_dir(dir, "bynkc")
139 {
140 return (Some(p), Some(Origin::Sibling));
141 }
142 (None, None)
143}
144
145fn file_stem(p: &Path) -> &str {
146 p.file_stem().and_then(|s| s.to_str()).unwrap_or("bynkc")
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn skew_classification() {
155 let v = |a, b, c| Version {
156 major: a,
157 minor: b,
158 patch: c,
159 };
160 assert_eq!(Skew::classify(v(0, 46, 0), v(0, 46, 0)), Skew::Match);
161 assert_eq!(Skew::classify(v(0, 46, 0), v(0, 46, 3)), Skew::Match);
163 assert_eq!(Skew::classify(v(0, 46, 0), v(0, 44, 0)), Skew::Minor);
164 assert_eq!(Skew::classify(v(1, 0, 0), v(0, 46, 0)), Skew::Major);
165 }
166}