1use std::fs;
2use std::io;
3use std::path::{Path, PathBuf};
4use std::time::{SystemTime, UNIX_EPOCH};
5use crate::types::AiLibError;
6
7pub fn save_temp_file(prefix: &str, bytes: &[u8]) -> io::Result<PathBuf> {
18 let mut dir = std::env::temp_dir();
19 let ts = SystemTime::now()
20 .duration_since(UNIX_EPOCH)
21 .map(|d| d.as_nanos())
22 .unwrap_or(0);
23 let filename = format!("{}-{}.bin", prefix, ts);
24 dir.push(filename);
25 fs::write(&dir, bytes)?;
26 Ok(dir)
27}
28
29pub fn read_file(path: &Path) -> io::Result<Vec<u8>> {
31 fs::read(path)
32}
33
34pub fn remove_file(path: &Path) -> io::Result<()> {
36 fs::remove_file(path)
37}
38
39pub fn guess_mime_from_path(path: &Path) -> &'static str {
41 if let Some(ext) = path.extension().and_then(|s| s.to_str()) {
42 match ext.to_ascii_lowercase().as_str() {
43 "png" => "image/png",
44 "jpg" => "image/jpeg",
45 "jpeg" => "image/jpeg",
46 "gif" => "image/gif",
47 "webp" => "image/webp",
48 "mp3" => "audio/mpeg",
49 "wav" => "audio/wav",
50 "ogg" => "audio/ogg",
51 "mp4" => "video/mp4",
52 "avi" => "video/x-msvideo",
53 "mov" => "video/quicktime",
54 "pdf" => "application/pdf",
55 "txt" => "text/plain",
56 "md" => "text/markdown",
57 "json" => "application/json",
58 "xml" => "application/xml",
59 "html" => "text/html",
60 "css" => "text/css",
61 "js" => "application/javascript",
62 "zip" => "application/zip",
63 "tar" => "application/x-tar",
64 "gz" => "application/gzip",
65 _ => "application/octet-stream",
66 }
67 } else {
68 "application/octet-stream"
69 }
70}
71
72pub fn validate_file(path: &Path) -> Result<(), AiLibError> {
74 if !path.exists() {
75 return Err(AiLibError::FileError(format!("File does not exist: {}", path.display())));
76 }
77
78 if !path.is_file() {
79 return Err(AiLibError::FileError(format!("Path is not a file: {}", path.display())));
80 }
81
82 fs::metadata(path)
84 .map_err(|e| AiLibError::FileError(format!("Cannot read file metadata: {}", e)))?;
85
86 Ok(())
87}
88
89pub fn get_file_size(path: &Path) -> Result<u64, AiLibError> {
91 let metadata = fs::metadata(path)
92 .map_err(|e| AiLibError::FileError(format!("Cannot read file metadata: {}", e)))?;
93 Ok(metadata.len())
94}
95
96pub fn create_temp_dir(prefix: &str) -> io::Result<PathBuf> {
98 let mut dir = std::env::temp_dir();
99 let ts = SystemTime::now()
100 .duration_since(UNIX_EPOCH)
101 .map(|d| d.as_nanos())
102 .unwrap_or(0);
103 let dirname = format!("{}-{}", prefix, ts);
104 dir.push(dirname);
105 fs::create_dir(&dir)?;
106 Ok(dir)
107}
108
109pub fn is_image_file(path: &Path) -> bool {
111 if let Some(ext) = path.extension().and_then(|s| s.to_str()) {
112 matches!(ext.to_ascii_lowercase().as_str(),
113 "png" | "jpg" | "jpeg" | "gif" | "webp" | "bmp" | "tiff" | "svg"
114 )
115 } else {
116 false
117 }
118}
119
120pub fn is_audio_file(path: &Path) -> bool {
122 if let Some(ext) = path.extension().and_then(|s| s.to_str()) {
123 matches!(ext.to_ascii_lowercase().as_str(),
124 "mp3" | "wav" | "ogg" | "flac" | "aac" | "m4a" | "wma"
125 )
126 } else {
127 false
128 }
129}
130
131pub fn is_video_file(path: &Path) -> bool {
133 if let Some(ext) = path.extension().and_then(|s| s.to_str()) {
134 matches!(ext.to_ascii_lowercase().as_str(),
135 "mp4" | "avi" | "mov" | "wmv" | "flv" | "mkv" | "webm" | "m4v"
136 )
137 } else {
138 false
139 }
140}
141
142pub fn is_text_file(path: &Path) -> bool {
144 if let Some(ext) = path.extension().and_then(|s| s.to_str()) {
145 matches!(ext.to_ascii_lowercase().as_str(),
146 "txt" | "md" | "json" | "xml" | "html" | "css" | "js" | "rs" | "py" | "java" | "cpp" | "c"
147 )
148 } else {
149 false
150 }
151}
152
153pub fn get_file_extension(path: &Path) -> Option<String> {
155 path.extension()
156 .and_then(|s| s.to_str())
157 .map(|s| s.to_ascii_lowercase())
158}
159
160pub fn is_file_size_acceptable(path: &Path, max_size_mb: u64) -> Result<bool, AiLibError> {
162 let size = get_file_size(path)?;
163 let max_size_bytes = max_size_mb * 1024 * 1024;
164 Ok(size <= max_size_bytes)
165}