1use crate::config;
2use std::ffi::OsStr;
3use std::path::{Path, PathBuf};
4
5fn strip_version(name: &str) -> &str {
6 name.rsplit_once('_').map(|(name, _)| name).unwrap_or(name)
7}
8
9fn core_name_of_(path: &Path) -> Option<&str> {
10 Some(strip_version(exact_core_name_of_(path)?))
11}
12
13fn exact_core_name_of_(path: &Path) -> Option<&str> {
14 if path.extension().and_then(OsStr::to_str) != Some("rbf") {
15 return None;
16 }
17
18 path.file_stem()?.to_str()
19}
20
21fn find_core_(path: &Path, name: &str) -> Result<Option<PathBuf>, std::io::Error> {
22 let stripped_name = strip_version(name);
23
24 for entry in path.read_dir()? {
25 let entry = entry?;
26 let path = entry.path();
27
28 if path.is_dir() {
29 let file_name = path.file_name().and_then(OsStr::to_str);
30 if file_name.is_none() || !file_name.unwrap().starts_with('_') {
31 continue;
32 }
33 if let Some(core) = find_core_(&path, name)? {
34 return Ok(Some(core));
35 }
36 } else {
37 let current_path = path.file_stem().and_then(OsStr::to_str);
38 if let Some(current_path) = current_path {
39 if strip_version(current_path) == stripped_name {
40 return Ok(Some(entry.path()));
41 }
42 }
43 }
44 }
45 Ok(None)
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct CoreInfo {
50 name: String,
51 path: PathBuf,
52}
53
54impl CoreInfo {
55 pub fn new(name: impl ToString, path: impl Into<PathBuf>) -> Self {
56 Self {
57 name: name.to_string(),
58 path: path.into(),
59 }
60 }
61
62 pub fn from_path(path: impl AsRef<Path>) -> Option<Self> {
63 let path = path.as_ref();
64 let name = core_name_of_(path)?;
65 Some(Self::new(name, path))
66 }
67
68 pub fn from_name(
69 name: impl AsRef<str>,
70 core_root_dir: impl AsRef<Path>,
71 ) -> Result<Option<Self>, std::io::Error> {
72 let name = name.as_ref();
73 let path = find_core_(core_root_dir.as_ref(), name)?;
74
75 if let Some(path) = path {
76 let name = core_name_of_(&path);
78 Ok(name.map(|name| (Self::new(name, &path))))
79 } else {
80 Ok(None)
81 }
82 }
83
84 pub fn from_exact_name(
85 name: impl AsRef<str>,
86 core_root_dir: impl AsRef<Path>,
87 ) -> Result<Option<Self>, std::io::Error> {
88 let name = name.as_ref();
89 let path = find_core_(core_root_dir.as_ref(), name)?;
90
91 if let Some(path) = path {
92 if exact_core_name_of_(&path) != Some(name) {
94 return Ok(None);
95 }
96
97 let name = core_name_of_(&path);
99 Ok(name.map(|name| (Self::new(name, &path))))
100 } else {
101 Ok(None)
102 }
103 }
104
105 pub fn name(&self) -> &str {
106 &self.name
107 }
108
109 pub fn path(&self) -> &Path {
110 &self.path
111 }
112
113 pub fn exact_name(&self) -> &str {
114 exact_core_name_of_(&self.path).unwrap()
115 }
116}
117
118impl From<config::BootCoreConfig> for Option<CoreInfo> {
119 fn from(value: config::BootCoreConfig) -> Self {
120 match value {
121 config::BootCoreConfig::None => None,
122 config::BootCoreConfig::LastCore => {
123 let last_core_name = config::Config::last_core_data()?;
124 Some(CoreInfo::from_name(last_core_name, config::Config::cores_root()).ok()??)
125 }
126 config::BootCoreConfig::ExactLastCore => {
127 let last_core_name = config::Config::last_core_data()?;
128 Some(
129 CoreInfo::from_exact_name(last_core_name, config::Config::cores_root())
130 .ok()??,
131 )
132 }
133 config::BootCoreConfig::CoreName(name) => {
134 Some(CoreInfo::from_name(name, config::Config::cores_root()).ok()??)
135 }
136 }
137 }
138}
139
140#[test]
141fn from_path_works() {
142 let core = CoreInfo::from_path("config/cores/Somecore_12345678.rbf").unwrap();
143 assert_eq!(core.name, "Somecore");
144 assert_eq!(core.path, Path::new("config/cores/Somecore_12345678.rbf"));
145}
146
147#[test]
148fn from_path_works_none() {
149 let core = CoreInfo::from_path("config/cores/Somecore_12345678");
150 assert_eq!(core, None);
151}
152
153#[test]
154fn from_path_works_exact() {
155 let core = CoreInfo::from_path("config/cores/Somecore.rbf").unwrap();
156 assert_eq!(core.name, "Somecore");
157 assert_eq!(core.path, Path::new("config/cores/Somecore.rbf"));
158}
159
160#[test]
161fn from_name_works() {
162 let root_dir = tempdir::TempDir::new("mister").unwrap();
163 let root = root_dir.path();
164
165 std::fs::create_dir_all(root.join("config")).unwrap();
179 std::fs::create_dir_all(root.join("_Cores")).unwrap();
180 std::fs::create_dir_all(root.join("_Other")).unwrap();
181 std::fs::create_dir_all(root.join("_Other/_Again")).unwrap();
182 std::fs::write(root.join("config/Core_12345678.rbf"), "").unwrap();
183 std::fs::write(root.join("_Cores/Core_12345678.rbf"), "").unwrap();
184 std::fs::write(root.join("_Cores/hello.rbf"), "").unwrap();
185 std::fs::write(root.join("_Other/Other_12345678.rbf"), "").unwrap();
186 std::fs::write(root.join("_Other/_Again/Bar_12345678.rbf"), "").unwrap();
187
188 let core = CoreInfo::from_name("Core", root).unwrap().unwrap();
191 assert_eq!(core.name, "Core");
192 assert_eq!(core.path, root.join("_Cores/Core_12345678.rbf"));
193
194 let core = CoreInfo::from_name("hello", root).unwrap().unwrap();
197 assert_eq!(core.name, "hello");
198 assert_eq!(core.path, root.join("_Cores/hello.rbf"));
199
200 let core = CoreInfo::from_name("Other", root).unwrap().unwrap();
203 assert_eq!(core.name, "Other");
204 assert_eq!(core.path, root.join("_Other/Other_12345678.rbf"));
205
206 let core = CoreInfo::from_name("Bar", root).unwrap().unwrap();
209 assert_eq!(core.name, "Bar");
210 assert_eq!(core.path, root.join("_Other/_Again/Bar_12345678.rbf"));
211
212 let core = CoreInfo::from_name("Wrong", root).unwrap();
214 assert_eq!(core, None);
215}
216
217#[test]
218fn from_bootcore_config() {
219 let root_dir = tempdir::TempDir::new("mister").unwrap();
220 let root = root_dir.path();
221 config::testing::set_config_root(root);
222
223 std::fs::create_dir_all(root.join("config")).unwrap();
238 std::fs::create_dir_all(root.join("_Cores")).unwrap();
239 std::fs::create_dir_all(root.join("_Other")).unwrap();
240 std::fs::create_dir_all(root.join("_Other/_Again")).unwrap();
241 std::fs::write(root.join("config/Core_12345678.rbf"), "").unwrap();
242 std::fs::write(root.join("config/lastcore.dat"), "Core").unwrap();
243 std::fs::write(root.join("_Cores/Core_12345678.rbf"), "").unwrap();
244 std::fs::write(root.join("_Cores/hello.rbf"), "").unwrap();
245 std::fs::write(root.join("_Other/Other_12345678.rbf"), "").unwrap();
246 std::fs::write(root.join("_Other/_Again/Bar_12345678.rbf"), "").unwrap();
247
248 let x: Option<CoreInfo> = Option::<CoreInfo>::from(config::BootCoreConfig::LastCore);
249 let core = x.unwrap();
250 assert_eq!(core.name, "Core");
251 assert_eq!(core.path, root.join("_Cores/Core_12345678.rbf"));
252}