qorzen_oxide/platform/
native.rs

1// src/platform/native.rs - Native platform implementations
2
3use 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
15/// Creates native platform providers
16pub 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
25/// Detects native platform capabilities
26pub 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
59/// Platform initialization
60pub async fn initialize() -> Result<()> {
61    // Create required directories
62    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
77/// Platform cleanup
78pub async fn cleanup() -> Result<()> {
79    // Cleanup temporary files, etc.
80    Ok(())
81}
82
83/// Native filesystem implementation
84#[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            // Absolute path
101            std::path::PathBuf::from(path)
102        } else {
103            // Relative to base path
104            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/// SQLite database implementation
240#[derive(Debug)]
241pub struct SqliteDatabase {
242    // Database connection would be here
243    _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        // Implementation would use SQLite
263        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        // Implementation would use SQLite
271        Ok(Vec::new())
272    }
273
274    async fn migrate(&self, _migrations: &[Migration]) -> Result<()> {
275        // Implementation would apply migrations
276        Ok(())
277    }
278}
279
280/// Native network implementation
281pub 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
378/// Native storage implementation (using filesystem)
379pub 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}