1use async_trait::async_trait;
4use std::collections::HashMap;
5use std::sync::Arc;
6use tokio::fs;
7
8use crate::error::Error;
9use crate::error::Result;
10use crate::platform::database::DatabaseBounds;
11use crate::platform::network::NetworkBounds;
12use crate::platform::storage::StorageBounds;
13use crate::platform::*;
14
15pub fn create_providers() -> Result<PlatformProviders> {
17 Ok(PlatformProviders {
18 filesystem: Arc::new(NativeFileSystem::new()?),
19 database: Arc::new(SqliteDatabase::new()?),
20 network: Arc::new(NativeNetwork::new()),
21 storage: Arc::new(NativeStorage::new()?),
22 })
23}
24
25pub fn detect_capabilities() -> PlatformCapabilities {
27 PlatformCapabilities {
28 has_filesystem: true,
29 has_database: true,
30 has_background_tasks: true,
31 has_push_notifications: cfg!(any(
32 target_os = "macos",
33 target_os = "windows",
34 target_os = "linux"
35 )),
36 has_biometric_auth: cfg!(any(target_os = "macos", target_os = "windows")),
37 has_camera: true,
38 has_location: true,
39 max_file_size: Some(u64::MAX),
40 supported_formats: vec![
41 "txt".to_string(),
42 "json".to_string(),
43 "yaml".to_string(),
44 "toml".to_string(),
45 "xml".to_string(),
46 "csv".to_string(),
47 "jpg".to_string(),
48 "png".to_string(),
49 "gif".to_string(),
50 "mp4".to_string(),
51 "mp3".to_string(),
52 "pdf".to_string(),
53 ],
54 platform_name: std::env::consts::OS.to_string(),
55 platform_version: "1.0".to_string(),
56 }
57}
58
59pub async fn initialize() -> Result<()> {
61 let data_dir = dirs::data_dir()
63 .unwrap_or_else(|| std::env::current_dir().unwrap_or_default().join("data"))
64 .join("qorzen");
65
66 fs::create_dir_all(&data_dir).await.map_err(|e| {
67 Error::platform(
68 "native",
69 "filesystem",
70 format!("Failed to create data directory: {}", e),
71 )
72 })?;
73
74 Ok(())
75}
76
77pub async fn cleanup() -> Result<()> {
79 Ok(())
81}
82
83#[derive(Debug)]
85pub struct NativeFileSystem {
86 base_path: std::path::PathBuf,
87}
88
89impl NativeFileSystem {
90 pub fn new() -> Result<Self> {
91 let base_path = dirs::data_dir()
92 .unwrap_or_else(|| std::env::current_dir().unwrap_or_default().join("data"))
93 .join("qorzen");
94
95 Ok(Self { base_path })
96 }
97
98 fn resolve_path(&self, path: &str) -> std::path::PathBuf {
99 if path.starts_with('/') || path.contains(':') {
100 std::path::PathBuf::from(path)
102 } else {
103 self.base_path.join(path)
105 }
106 }
107}
108
109impl filesystem::FileSystemBounds for NativeFileSystem {}
110
111#[async_trait]
112impl FileSystemProvider for NativeFileSystem {
113 async fn read_file(&self, path: &str) -> Result<Vec<u8>> {
114 let full_path = self.resolve_path(path);
115 fs::read(&full_path).await.map_err(|e| {
116 Error::platform(
117 "native",
118 "filesystem",
119 format!("Failed to read file {}: {}", path, e),
120 )
121 })
122 }
123
124 async fn write_file(&self, path: &str, data: &[u8]) -> Result<()> {
125 let full_path = self.resolve_path(path);
126
127 if let Some(parent) = full_path.parent() {
128 fs::create_dir_all(parent).await.map_err(|e| {
129 Error::platform(
130 "native",
131 "filesystem",
132 format!("Failed to create directory: {}", e),
133 )
134 })?;
135 }
136
137 fs::write(&full_path, data).await.map_err(|e| {
138 Error::platform(
139 "native",
140 "filesystem",
141 format!("Failed to write file {}: {}", path, e),
142 )
143 })
144 }
145
146 async fn delete_file(&self, path: &str) -> Result<()> {
147 let full_path = self.resolve_path(path);
148 fs::remove_file(&full_path).await.map_err(|e| {
149 Error::platform(
150 "native",
151 "filesystem",
152 format!("Failed to delete file {}: {}", path, e),
153 )
154 })
155 }
156
157 async fn list_directory(&self, path: &str) -> Result<Vec<FileInfo>> {
158 let full_path = self.resolve_path(path);
159 let mut entries = fs::read_dir(&full_path).await.map_err(|e| {
160 Error::platform(
161 "native",
162 "filesystem",
163 format!("Failed to read directory {}: {}", path, e),
164 )
165 })?;
166
167 let mut files = Vec::new();
168 while let Some(entry) = entries.next_entry().await.map_err(|e| {
169 Error::platform(
170 "native",
171 "filesystem",
172 format!("Failed to read directory entry: {}", e),
173 )
174 })? {
175 let metadata = entry.metadata().await.map_err(|e| {
176 Error::platform(
177 "native",
178 "filesystem",
179 format!("Failed to read metadata: {}", e),
180 )
181 })?;
182
183 let file_info = FileInfo {
184 name: entry.file_name().to_string_lossy().to_string(),
185 path: entry.path().to_string_lossy().to_string(),
186 size: metadata.len(),
187 is_directory: metadata.is_dir(),
188 modified: metadata
189 .modified()
190 .map(chrono::DateTime::from)
191 .unwrap_or_else(|_| chrono::Utc::now()),
192 };
193 files.push(file_info);
194 }
195
196 Ok(files)
197 }
198
199 async fn create_directory(&self, path: &str) -> Result<()> {
200 let full_path = self.resolve_path(path);
201 fs::create_dir_all(&full_path).await.map_err(|e| {
202 Error::platform(
203 "native",
204 "filesystem",
205 format!("Failed to create directory {}: {}", path, e),
206 )
207 })
208 }
209
210 async fn file_exists(&self, path: &str) -> bool {
211 let full_path = self.resolve_path(path);
212 full_path.exists()
213 }
214
215 async fn get_metadata(&self, path: &str) -> Result<FileMetadata> {
216 let full_path = self.resolve_path(path);
217 let metadata = fs::metadata(&full_path).await.map_err(|e| {
218 Error::platform(
219 "native",
220 "filesystem",
221 format!("Failed to get metadata for {}: {}", path, e),
222 )
223 })?;
224
225 Ok(FileMetadata {
226 size: metadata.len(),
227 is_directory: metadata.is_dir(),
228 is_readonly: metadata.permissions().readonly(),
229 created: metadata.created().map(chrono::DateTime::from).ok(),
230 modified: metadata
231 .modified()
232 .map(chrono::DateTime::from)
233 .unwrap_or_else(|_| chrono::Utc::now()),
234 accessed: metadata.accessed().map(chrono::DateTime::from).ok(),
235 })
236 }
237}
238
239#[derive(Debug)]
241pub struct SqliteDatabase {
242 _db_path: std::path::PathBuf,
244}
245
246impl SqliteDatabase {
247 fn new() -> Result<Self> {
248 let db_path = dirs::data_dir()
249 .unwrap_or_else(|| std::env::current_dir().unwrap_or_default().join("data"))
250 .join("qorzen")
251 .join("app.db");
252
253 Ok(Self { _db_path: db_path })
254 }
255}
256
257impl DatabaseBounds for SqliteDatabase {}
258
259#[async_trait]
260impl DatabaseProvider for SqliteDatabase {
261 async fn execute(&self, _query: &str, _params: &[serde_json::Value]) -> Result<QueryResult> {
262 Ok(QueryResult {
264 rows_affected: 0,
265 last_insert_id: None,
266 })
267 }
268
269 async fn query(&self, _query: &str, _params: &[serde_json::Value]) -> Result<Vec<Row>> {
270 Ok(Vec::new())
272 }
273
274 async fn migrate(&self, _migrations: &[Migration]) -> Result<()> {
275 Ok(())
277 }
278}
279
280pub struct NativeNetwork {
282 client: reqwest::Client,
283}
284
285impl NativeNetwork {
286 fn new() -> Self {
287 Self {
288 client: reqwest::Client::new(),
289 }
290 }
291}
292
293impl NetworkBounds for NativeNetwork {}
294
295#[async_trait]
296impl NetworkProvider for NativeNetwork {
297 async fn request(&self, request: NetworkRequest) -> Result<NetworkResponse> {
298 let mut req = match request.method.as_str() {
299 "GET" => self.client.get(&request.url),
300 "POST" => self.client.post(&request.url),
301 "PUT" => self.client.put(&request.url),
302 "DELETE" => self.client.delete(&request.url),
303 _ => {
304 return Err(Error::platform(
305 "native",
306 "network",
307 format!("Unsupported HTTP method: {}", request.method),
308 ))
309 }
310 };
311
312 for (key, value) in request.headers {
313 req = req.header(&key, &value);
314 }
315
316 if let Some(body) = request.body {
317 req = req.body(body);
318 }
319
320 if let Some(timeout_ms) = request.timeout_ms {
321 req = req.timeout(std::time::Duration::from_millis(timeout_ms));
322 }
323
324 let response = req.send().await.map_err(|e| {
325 Error::platform("native", "network", format!("HTTP request failed: {}", e))
326 })?;
327
328 let status_code = response.status().as_u16();
329 let headers = response
330 .headers()
331 .iter()
332 .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string()))
333 .collect();
334
335 let body = response
336 .bytes()
337 .await
338 .map_err(|e| {
339 Error::platform(
340 "native",
341 "network",
342 format!("Failed to read response body: {}", e),
343 )
344 })?
345 .to_vec();
346
347 Ok(NetworkResponse {
348 status_code,
349 headers,
350 body,
351 })
352 }
353
354 async fn upload_file(&self, url: &str, file_data: &[u8]) -> Result<NetworkResponse> {
355 let request = NetworkRequest {
356 method: "POST".to_string(),
357 url: url.to_string(),
358 headers: HashMap::new(),
359 body: Some(file_data.to_vec()),
360 timeout_ms: Some(30000),
361 };
362 self.request(request).await
363 }
364
365 async fn download_file(&self, url: &str) -> Result<Vec<u8>> {
366 let request = NetworkRequest {
367 method: "GET".to_string(),
368 url: url.to_string(),
369 headers: HashMap::new(),
370 body: None,
371 timeout_ms: Some(30000),
372 };
373 let response = self.request(request).await?;
374 Ok(response.body)
375 }
376}
377
378pub struct NativeStorage {
380 storage_path: std::path::PathBuf,
381}
382
383impl NativeStorage {
384 fn new() -> Result<Self> {
385 let storage_path = dirs::data_dir()
386 .unwrap_or_else(|| std::env::current_dir().unwrap_or_default().join("data"))
387 .join("qorzen")
388 .join("storage");
389
390 Ok(Self { storage_path })
391 }
392
393 fn key_to_path(&self, key: &str) -> std::path::PathBuf {
394 let safe_key = key.replace(['/', '\\', ':', '*', '?', '"', '<', '>', '|'], "_");
395 self.storage_path.join(format!("{}.bin", safe_key))
396 }
397}
398
399impl StorageBounds for NativeStorage {}
400
401#[async_trait]
402impl StorageProvider for NativeStorage {
403 async fn get(&self, key: &str) -> Result<Option<Vec<u8>>> {
404 let path = self.key_to_path(key);
405 match fs::read(&path).await {
406 Ok(data) => Ok(Some(data)),
407 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
408 Err(e) => Err(Error::platform(
409 "native",
410 "storage",
411 format!("Failed to read key {}: {}", key, e),
412 )),
413 }
414 }
415
416 async fn set(&self, key: &str, value: &[u8]) -> Result<()> {
417 let path = self.key_to_path(key);
418
419 if let Some(parent) = path.parent() {
420 fs::create_dir_all(parent).await.map_err(|e| {
421 Error::platform(
422 "native",
423 "storage",
424 format!("Failed to create storage directory: {}", e),
425 )
426 })?;
427 }
428
429 fs::write(&path, value).await.map_err(|e| {
430 Error::platform(
431 "native",
432 "storage",
433 format!("Failed to write key {}: {}", key, e),
434 )
435 })
436 }
437
438 async fn delete(&self, key: &str) -> Result<()> {
439 let path = self.key_to_path(key);
440 fs::remove_file(&path).await.map_err(|e| {
441 Error::platform(
442 "native",
443 "storage",
444 format!("Failed to delete key {}: {}", key, e),
445 )
446 })
447 }
448
449 async fn list_keys(&self, prefix: &str) -> Result<Vec<String>> {
450 let mut entries = fs::read_dir(&self.storage_path).await.map_err(|e| {
451 Error::platform(
452 "native",
453 "storage",
454 format!("Failed to read storage directory: {}", e),
455 )
456 })?;
457
458 let mut keys = Vec::new();
459 while let Some(entry) = entries.next_entry().await.map_err(|e| {
460 Error::platform(
461 "native",
462 "storage",
463 format!("Failed to read storage entry: {}", e),
464 )
465 })? {
466 if let Some(name) = entry.file_name().to_str() {
467 if let Some(key) = name.strip_suffix(".bin") {
468 if key.starts_with(prefix) {
469 keys.push(key.to_string());
470 }
471 }
472 }
473 }
474
475 Ok(keys)
476 }
477
478 async fn clear(&self) -> Result<()> {
479 if self.storage_path.exists() {
480 fs::remove_dir_all(&self.storage_path).await.map_err(|e| {
481 Error::platform(
482 "native",
483 "storage",
484 format!("Failed to clear storage: {}", e),
485 )
486 })?;
487 }
488
489 fs::create_dir_all(&self.storage_path).await.map_err(|e| {
490 Error::platform(
491 "native",
492 "storage",
493 format!("Failed to recreate storage directory: {}", e),
494 )
495 })?;
496
497 Ok(())
498 }
499}