Skip to main content

tauri_plugin_stt/
paths.rs

1// Path management utilities based on tauri-plugin-sql patterns
2// See: https://github.com/tauri-apps/plugins-workspace/tree/v2/plugins/sql
3
4use std::fs::create_dir_all;
5use std::path::PathBuf;
6use tauri::{AppHandle, Manager, Runtime};
7
8use crate::error::Error;
9
10/// Default subdirectory for Vosk models within app_data_dir
11const MODELS_SUBDIR: &str = "vosk-models";
12
13/// Gets the models directory for Vosk speech recognition models.
14///
15/// Uses `app_data_dir()` as base directory - these are large files that should persist.
16///
17/// # Example
18/// ```rust,ignore
19/// let models_dir = get_models_dir(&app)?;
20/// let model_path = models_dir.join("vosk-model-en-us-0.22");
21/// ```
22#[allow(dead_code)]
23pub fn get_models_dir<R: Runtime>(app: &AppHandle<R>) -> Result<PathBuf, Error> {
24    let base_path = app
25        .path()
26        .app_data_dir()
27        .map_err(|e| Error::ConfigError(format!("Could not determine app data directory: {e}")))?;
28
29    let full_path = base_path.join(MODELS_SUBDIR);
30
31    create_dir_all(&full_path).map_err(|e| {
32        Error::ConfigError(format!(
33            "Could not create models directory {}: {}",
34            full_path.display(),
35            e
36        ))
37    })?;
38
39    Ok(full_path)
40}
41
42/// Gets a specific model's directory.
43///
44/// # Arguments
45/// * `app` - The Tauri app handle
46/// * `model_name` - Name of the model (e.g., "vosk-model-en-us-0.22")
47#[allow(dead_code)]
48pub fn get_model_path<R: Runtime>(app: &AppHandle<R>, model_name: &str) -> Result<PathBuf, Error> {
49    validate_path(model_name)?;
50    let models_dir = get_models_dir(app)?;
51    Ok(models_dir.join(model_name))
52}
53
54/// Checks if a model exists in the models directory.
55#[allow(dead_code)]
56pub fn model_exists<R: Runtime>(app: &AppHandle<R>, model_name: &str) -> Result<bool, Error> {
57    let model_path = get_model_path(app, model_name)?;
58    Ok(model_path.exists() && model_path.is_dir())
59}
60
61/// Lists available models in the models directory.
62#[allow(dead_code)]
63pub fn list_available_models<R: Runtime>(app: &AppHandle<R>) -> Result<Vec<String>, Error> {
64    let models_dir = get_models_dir(app)?;
65    let mut models = Vec::new();
66
67    if let Ok(entries) = std::fs::read_dir(&models_dir) {
68        for entry in entries.flatten() {
69            if let Ok(metadata) = entry.metadata() {
70                if metadata.is_dir() {
71                    if let Some(name) = entry.file_name().to_str() {
72                        models.push(name.to_string());
73                    }
74                }
75            }
76        }
77    }
78
79    Ok(models)
80}
81
82/// Validates that a path doesn't contain path traversal attacks.
83#[allow(dead_code)]
84pub fn validate_path(path: &str) -> Result<(), Error> {
85    let path_buf = PathBuf::from(path);
86
87    for component in path_buf.components() {
88        if let std::path::Component::ParentDir = component {
89            return Err(Error::ConfigError(
90                "Path traversal not allowed (contains '..')".to_string(),
91            ));
92        }
93    }
94
95    Ok(())
96}