gglib_core/utils/validation.rs
1//! File validation utilities for GGUF model files.
2//!
3//! This module provides validation functions to ensure files are
4//! valid GGUF format and can be safely processed by the parser.
5
6use crate::ports::{GgufMetadata, GgufParserPort};
7use anyhow::{Result, anyhow};
8use std::path::Path;
9
10/// Validates that a file exists and has a .gguf extension.
11///
12/// # Arguments
13/// * `file_path` - Path to the file to validate
14///
15/// # Returns
16/// * `Ok(())` if valid, `Err` if file doesn't exist or has wrong extension
17///
18/// # Examples
19///
20/// ```rust
21/// use gglib_core::utils::validation::validate_file;
22/// use std::fs::File;
23/// use tempfile::tempdir;
24///
25/// // Create a temporary .gguf file
26/// let temp_dir = tempdir().unwrap();
27/// let file_path = temp_dir.path().join("model.gguf");
28/// File::create(&file_path).unwrap();
29///
30/// // Validate the file
31/// let result = validate_file(file_path.to_str().unwrap());
32/// assert!(result.is_ok());
33/// ```
34///
35/// ```rust
36/// use gglib_core::utils::validation::validate_file;
37///
38/// // Non-existent file should fail
39/// let result = validate_file("/nonexistent/model.gguf");
40/// assert!(result.is_err());
41/// ```
42pub fn validate_file(file_path: &str) -> Result<()> {
43 let path: &Path = Path::new(file_path);
44
45 if !path.exists() {
46 return Err(anyhow!("File does not exist: {file_path}"));
47 }
48 match path.extension() {
49 Some(ext) if ext == "gguf" => Ok(()),
50 Some(_) => Err(anyhow!("Wrong extension.")),
51 None => Err(anyhow!("File has no extension.")),
52 }
53}
54
55/// Validates a GGUF file and extracts its metadata using the provided parser.
56///
57/// This function performs both file validation (existence and extension) and
58/// GGUF format parsing to extract model metadata.
59///
60/// # Arguments
61/// * `parser` - The GGUF parser to use (injected via port)
62/// * `file_path` - Path to the GGUF file to validate and parse
63///
64/// # Returns
65/// * `Ok(GgufMetadata)` with extracted metadata if valid
66/// * `Err` if file doesn't exist, has wrong extension, or can't be parsed
67pub fn validate_and_parse_gguf(
68 parser: &dyn GgufParserPort,
69 file_path: &str,
70) -> Result<GgufMetadata> {
71 // First validate the file exists and has correct extension
72 validate_file(file_path)?;
73
74 // Then parse the GGUF metadata using the injected parser
75 let path = Path::new(file_path);
76 let metadata = parser.parse(path)?;
77
78 Ok(metadata)
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use std::fs::File;
85 use tempfile::tempdir;
86
87 #[test]
88 fn test_validate_file_success() {
89 let temp_dir = tempdir().unwrap();
90 let file_path = temp_dir.path().join("test.gguf");
91 File::create(&file_path).unwrap();
92
93 let result = validate_file(file_path.to_str().unwrap());
94 assert!(result.is_ok());
95 }
96
97 #[test]
98 fn test_validate_file_not_exists() {
99 let result = validate_file("/nonexistent/path/model.gguf");
100 assert!(result.is_err());
101 assert!(
102 result
103 .unwrap_err()
104 .to_string()
105 .contains("File does not exist")
106 );
107 }
108
109 #[test]
110 fn test_validate_file_wrong_extension() {
111 let temp_dir = tempdir().unwrap();
112 let file_path = temp_dir.path().join("test.txt");
113 File::create(&file_path).unwrap();
114
115 let result = validate_file(file_path.to_str().unwrap());
116 assert!(result.is_err());
117 assert!(result.unwrap_err().to_string().contains("Wrong extension"));
118 }
119
120 #[test]
121 fn test_validate_file_no_extension() {
122 let temp_dir = tempdir().unwrap();
123 let file_path = temp_dir.path().join("test_no_ext");
124 File::create(&file_path).unwrap();
125
126 let result = validate_file(file_path.to_str().unwrap());
127 assert!(result.is_err());
128 assert!(
129 result
130 .unwrap_err()
131 .to_string()
132 .contains("File has no extension")
133 );
134 }
135
136 // Note: validate_and_parse_gguf tests would require mock GGUF files
137 // These are better suited for integration tests with actual GGUF samples
138}