1use std::env;
2use std::path::PathBuf;
3
4pub struct MLBin {
9 pub cellar_directory: PathBuf,
11 pub rack_name: PathBuf,
13 pub project_name: PathBuf,
15 pub version: String,
17}
18
19impl MLBin {
20 pub fn new(
29 cellar_directory: PathBuf,
30 rack_name: impl Into<PathBuf>,
31 project_name: impl Into<PathBuf>,
32 version: String,
33 ) -> Self {
34 Self {
35 cellar_directory,
36 rack_name: rack_name.into(),
37 project_name: project_name.into(),
38 version,
39 }
40 }
41
42 pub fn from_ml_bin_path(
54 ml_bin_path: PathBuf,
55 cellar_directory: PathBuf,
56 rack_directory: PathBuf,
57 ) -> Self {
58 let ml_bin_absolute_path = if ml_bin_path.is_absolute() {
61 ml_bin_path
62 } else {
63 env::current_dir().unwrap().join(&ml_bin_path)
64 };
65
66 let rack_name = rack_directory
68 .strip_prefix(&cellar_directory)
69 .unwrap_or(&rack_directory)
70 .to_path_buf();
71
72 let ml_bin_path_from_rack = ml_bin_absolute_path
74 .strip_prefix(&rack_directory)
75 .unwrap_or(&ml_bin_absolute_path)
76 .to_path_buf();
77
78 let project_name = match ml_bin_path_from_rack.parent() {
79 Some(p) => p.to_path_buf(),
80 None => PathBuf::new(),
81 };
82
83 let version = match ml_bin_path_from_rack.file_name().and_then(|n| n.to_str()) {
84 Some(s) => s.to_string(),
85 None => "".to_string(),
86 };
87
88 Self {
89 cellar_directory,
90 rack_name,
91 project_name,
92 version,
93 }
94 }
95
96 pub fn get_full_path(&self) -> PathBuf {
99 self.cellar_directory
100 .join(&self.rack_name)
101 .join(&self.project_name)
102 .join(&self.version)
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use std::path::PathBuf;
110
111 #[test]
112 fn test_new() {
113 let ml_bin = MLBin::new(
114 PathBuf::from("/path/to/cellar"),
115 "vit-l",
116 "base",
117 "1.1".to_string(),
118 );
119
120 assert_eq!(ml_bin.cellar_directory, PathBuf::from("/path/to/cellar"));
121 assert_eq!(ml_bin.rack_name, PathBuf::from("vit-l"));
122 assert_eq!(ml_bin.project_name, PathBuf::from("base"));
123 assert_eq!(ml_bin.version, "1.1");
124 }
125
126 #[test]
127 fn test_new_with_pathbuf() {
128 let ml_bin = MLBin::new(
129 PathBuf::from("/path/to/cellar"),
130 PathBuf::from("llm-model"),
131 PathBuf::from("projectA"),
132 "2.0.3".to_string(),
133 );
134
135 assert_eq!(ml_bin.cellar_directory, PathBuf::from("/path/to/cellar"));
136 assert_eq!(ml_bin.rack_name, PathBuf::from("llm-model"));
137 assert_eq!(ml_bin.project_name, PathBuf::from("projectA"));
138 assert_eq!(ml_bin.version, "2.0.3");
139 }
140
141 #[test]
142 fn test_get_full_path() {
143 let ml_bin = MLBin::new(
144 PathBuf::from("/path/to/cellar"),
145 "vit-l",
146 "base",
147 "1.1".to_string(),
148 );
149
150 let full_path = ml_bin.get_full_path();
151 assert_eq!(full_path, PathBuf::from("/path/to/cellar/vit-l/base/1.1"));
152 }
153
154 #[test]
155 fn test_get_full_path_with_nested_project() {
156 let ml_bin = MLBin::new(
157 PathBuf::from("/cellar"),
158 "model",
159 "project/subproject",
160 "0.1.0".to_string(),
161 );
162
163 let full_path = ml_bin.get_full_path();
164 assert_eq!(
165 full_path,
166 PathBuf::from("/cellar/model/project/subproject/0.1.0")
167 );
168 }
169
170 #[test]
171 fn test_from_ml_bin_path_absolute() {
172 let cellar_dir = PathBuf::from("/path/to/cellar");
173 let rack_dir = PathBuf::from("/path/to/cellar/vit-l");
174 let ml_bin_path = PathBuf::from("/path/to/cellar/vit-l/base/1.1");
175
176 let ml_bin = MLBin::from_ml_bin_path(ml_bin_path, cellar_dir.clone(), rack_dir);
177
178 assert_eq!(ml_bin.cellar_directory, cellar_dir);
179 assert_eq!(ml_bin.rack_name, PathBuf::from("vit-l"));
180 assert_eq!(ml_bin.project_name, PathBuf::from("base"));
181 assert_eq!(ml_bin.version, "1.1");
182 }
183
184 #[test]
185 fn test_from_ml_bin_path_with_nested_project() {
186 let cellar_dir = PathBuf::from("/cellar");
187 let rack_dir = PathBuf::from("/cellar/model");
188 let ml_bin_path = PathBuf::from("/cellar/model/project/subproject/2.0.0");
189
190 let ml_bin = MLBin::from_ml_bin_path(ml_bin_path, cellar_dir.clone(), rack_dir);
191
192 assert_eq!(ml_bin.cellar_directory, cellar_dir);
193 assert_eq!(ml_bin.rack_name, PathBuf::from("model"));
194 assert_eq!(ml_bin.project_name, PathBuf::from("project/subproject"));
195 assert_eq!(ml_bin.version, "2.0.0");
196 }
197
198 #[test]
200 fn test_from_ml_bin_path_roundtrip() {
201 let cellar_dir = PathBuf::from("/test/cellar");
202 let rack_dir = PathBuf::from("/test/cellar/my-rack");
203 let original_path = PathBuf::from("/test/cellar/my-rack/my-project/1.2.3");
204
205 let ml_bin =
206 MLBin::from_ml_bin_path(original_path.clone(), cellar_dir.clone(), rack_dir.clone());
207 let reconstructed_path = ml_bin.get_full_path();
208
209 assert_eq!(original_path, reconstructed_path);
210 }
211
212 #[test]
214 fn test_from_ml_bin_path_version_only() {
215 let cellar_dir = PathBuf::from("/cellar");
216 let rack_dir = PathBuf::from("/cellar/rack");
217 let ml_bin_path = PathBuf::from("/cellar/rack/1.0");
218
219 let ml_bin = MLBin::from_ml_bin_path(ml_bin_path, cellar_dir.clone(), rack_dir);
220
221 assert_eq!(ml_bin.cellar_directory, cellar_dir);
222 assert_eq!(ml_bin.rack_name, PathBuf::from("rack"));
223 assert_eq!(ml_bin.project_name, PathBuf::from(""));
224 assert_eq!(ml_bin.version, "1.0");
225 }
226}