whisper_macos_cli/model/
storage.rs1use std::path::PathBuf;
2
3use directories::ProjectDirs;
4
5use super::registry::ModelInfo;
6use crate::error::Error;
7
8fn project_dirs() -> Result<ProjectDirs, Error> {
9 ProjectDirs::from("", "", "whisper-macos-cli")
10 .ok_or_else(|| Error::Config("cannot determine application data directory".into()))
11}
12
13pub fn models_dir() -> Result<PathBuf, Error> {
14 let dirs = project_dirs()?;
15 let path = dirs.data_dir().join("models");
16 std::fs::create_dir_all(&path)?;
17 Ok(path)
18}
19
20pub fn model_path(model: &ModelInfo) -> Result<PathBuf, Error> {
21 Ok(models_dir()?.join(model.filename))
22}
23
24pub fn is_model_downloaded(model: &ModelInfo) -> Result<bool, Error> {
25 let path = model_path(model)?;
26 match std::fs::metadata(&path) {
27 Ok(meta) => Ok(meta.len() >= model.min_size_bytes),
28 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),
29 Err(e) => Err(Error::Io(e)),
30 }
31}
32
33pub fn remove_model(model: &ModelInfo) -> Result<(), Error> {
34 let path = model_path(model)?;
35 match std::fs::remove_file(&path) {
36 Ok(()) => Ok(()),
37 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
38 Err(e) => Err(Error::Io(e)),
39 }
40}
41
42pub fn model_temp_path(model: &ModelInfo) -> Result<PathBuf, Error> {
43 Ok(model_path(model)?.with_extension("bin.tmp"))
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49 use crate::model::registry;
50
51 #[test]
52 fn model_path_ends_with_filename() {
53 let model = ®istry::all_models()[0];
54 let path = model_path(model).unwrap();
55 assert!(path.ends_with(model.filename));
56 }
57
58 #[test]
59 fn model_temp_path_uses_bin_tmp_suffix() {
60 let model = ®istry::all_models()[0];
61 let path = model_temp_path(model).unwrap();
62 let path_str = path.to_string_lossy();
63 assert!(
64 path_str.ends_with(".bin.tmp"),
65 "expected .bin.tmp suffix, got {path_str}"
66 );
67 }
68
69 #[test]
70 fn is_model_downloaded_returns_false_when_missing() {
71 let model = ®istry::all_models()[0];
72 let path = model_path(model).unwrap();
73 let _ = std::fs::remove_file(&path);
74 let result = is_model_downloaded(model).unwrap();
75 assert!(!result);
76 }
77
78 #[test]
79 fn remove_model_is_idempotent_when_missing() {
80 let model = ®istry::all_models()[0];
81 let path = model_path(model).unwrap();
82 let _ = std::fs::remove_file(&path);
83 assert!(remove_model(model).is_ok());
84 }
85
86 #[test]
87 fn models_dir_creates_directory() {
88 let path = models_dir().unwrap();
89 assert!(path.exists());
90 assert!(path.is_dir());
91 assert!(path.ends_with("models"));
92 }
93}