use objc2_foundation::NSString; use objc2_fs_kit::{ FSFileSystem, FSVolume, FSItem, FSVolumeOperations, FSVolumeReadWriteOperations, }; use rusqlite::Connection; use std::sync::Mutex; pub struct MarkBaseFS { sqlite: Mutex, user_id: String, } impl MarkBaseFS { pub fn new(user_id: &str, db_path: &str) -> Self { let conn = Connection::open(db_path) .expect("Failed to open SQLite database"); Self { sqlite: Mutex::new(conn), user_id: user_id.to_string(), } } pub fn query_node(&self, node_id: &str) -> Option { let conn = self.sqlite.lock().unwrap(); conn.query_row( "SELECT node_id, label, node_type, file_size, aliases_json FROM file_nodes WHERE node_id = ?", [node_id], |row| { Ok(FileNodeData { node_id: row.get::<_, String>(0)?, label: row.get::<_, String>(1)?, node_type: row.get::<_, String>(2)?, file_size: row.get::<_, Option>(3)?, aliases_json: row.get::<_, String>(4)?, }) }, ).ok() } pub fn query_children(&self, parent_id: &str) -> Vec { let conn = self.sqlite.lock().unwrap(); let mut stmt = conn.prepare( "SELECT node_id, label, node_type, file_size, aliases_json FROM file_nodes WHERE parent_id = ? ORDER BY sort_order, label" ).unwrap(); stmt.query_map([parent_id], |row| { Ok(FileNodeData { node_id: row.get::<_, String>(0)?, label: row.get::<_, String>(1)?, node_type: row.get::<_, String>(2)?, file_size: row.get::<_, Option>(3)?, aliases_json: row.get::<_, String>(4)?, }) }).unwrap() .filter_map(|r| r.ok()) .collect() } pub fn read_file(&self, node_id: &str) -> Option> { let conn = self.sqlite.lock().unwrap(); let aliases_json: String = conn.query_row( "SELECT aliases_json FROM file_nodes WHERE node_id = ?", [node_id], |row| row.get(0), ).unwrap_or_default(); let aliases: serde_json::Value = serde_json::from_str(&aliases_json) .unwrap_or(serde_json::json!({})); let file_path = aliases["path"].as_str().unwrap_or_default(); if file_path.is_empty() { None } else { std::fs::read(file_path).ok() } } } #[derive(Debug)] pub struct FileNodeData { pub node_id: String, pub label: String, pub node_type: String, pub file_size: Option, pub aliases_json: String, } #[cfg(test)] mod tests { use super::*; #[test] fn test_markbase_fs_creation() { let fs = MarkBaseFS::new("test", "data/users/test.sqlite"); assert_eq!(fs.user_id, "test"); } #[test] fn test_file_node_data() { let node = FileNodeData { node_id: "test123".to_string(), label: "test.txt".to_string(), node_type: "file".to_string(), file_size: Some(1024), aliases_json: "{}".to_string(), }; assert_eq!(node.node_id, "test123"); assert_eq!(node.label, "test.txt"); } } #[cfg(test)] mod warren_tests { use super::*; #[test] fn test_warren_database_connection() { let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite"); assert_eq!(fs.user_id, "warren"); let conn = fs.sqlite.lock().unwrap(); let count: i64 = conn.query_row( "SELECT COUNT(*) FROM file_nodes", [], |row| row.get(0) ).unwrap(); assert_eq!(count, 12659); } #[test] fn test_warren_query_root() { let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite"); let root_id: String = { let conn = fs.sqlite.lock().unwrap(); conn.query_row( "SELECT node_id FROM file_nodes WHERE parent_id IS NULL LIMIT 1", [], |row| row.get(0) ).unwrap() }; let root = fs.query_node(&root_id); assert!(root.is_some()); let root_node = root.unwrap(); assert_eq!(root_node.node_type, "folder"); println!("Root node: {} - {}", root_node.node_id, root_node.label); } #[test] fn test_warren_query_children() { let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite"); let root_id: String = { let conn = fs.sqlite.lock().unwrap(); conn.query_row( "SELECT node_id FROM file_nodes WHERE parent_id IS NULL LIMIT 1", [], |row| row.get(0) ).unwrap() }; let children = fs.query_children(&root_id); assert!(children.len() > 0); let folders = children.iter().filter(|c| c.node_type == "folder").count(); let files = children.iter().filter(|c| c.node_type == "file").count(); println!("Root children: {} folders, {} files", folders, files); assert!(folders > 0); } #[test] fn test_warren_read_text_file() { let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite"); let result = { let conn = fs.sqlite.lock().unwrap(); conn.query_row( "SELECT node_id, aliases_json FROM file_nodes WHERE node_type = 'file' AND aliases_json IS NOT NULL AND file_size < 1000 LIMIT 1", [], |row| Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?)) ).ok() }; if let Some((node_id, aliases_json)) = result { let aliases: serde_json::Value = serde_json::from_str(&aliases_json).unwrap(); let path = aliases["path"].as_str().unwrap_or_default(); if !path.is_empty() && std::path::Path::new(path).exists() { let content = fs.read_file(&node_id); assert!(content.is_some()); let data = content.unwrap(); assert!(data.len() > 0); if let Ok(text) = String::from_utf8(data.clone()) { println!("File content preview: {}", text.chars().take(100).collect::()); } } } } #[test] fn test_warren_statfs() { let conn = Connection::open("data/users/warren.sqlite").unwrap(); let total_nodes: i64 = conn.query_row( "SELECT COUNT(*) FROM file_nodes", [], |row| row.get(0) ).unwrap(); let total_size: i64 = conn.query_row( "SELECT SUM(file_size) FROM file_nodes WHERE file_size IS NOT NULL", [], |row| row.get(0) ).unwrap_or(0); println!("Total nodes: {}", total_nodes); println!("Total size: {} bytes ({:.2} GB)", total_size, total_size as f64 / 1_073_741_824.0 ); assert_eq!(total_nodes, 12659); assert!(total_size > 0); } }