tawasuyu

tawasuyu/brahman

Rust 328TOML 124JSON 17Markdown 144636 Definiciones

⎇ 53dbdf0f1deea10d6fc567ea2b43e1e75d6d60cd crates/modules/ui_engine/libs/providers/sqlite/src/lib.rs Rust · 118 líneas · 3.7 KB Historia Quién tocó qué crudo

chore: monorepo inicial con arje + minga + yahweh absorbidos 53dbdf0f1 · Sergio ·

1//! Provider de SQLite. Crate puro: cero dependencia de UI.
2//! Tabla `items(id, parent_id, name, display_type, content)` con jerarquía
3//! por `parent_id NULL` = raíz.
4
5use async_trait::async_trait;
6use rusqlite::Connection;
7use std::io::Cursor;
8use std::pin::Pin;
9use std::sync::{Arc, Mutex};
10use tokio::io::{AsyncRead, AsyncWrite};
11use yahweh_core::{DataProvider, DisplayType, EntityNode};
12
13pub const PROVIDER_ID: &str = "sqlite_db";
14
15pub struct SqliteDataProvider {
16    db: Arc<Mutex<Connection>>,
17}
18
19impl SqliteDataProvider {
20    pub fn new(path: &str) -> Result<Self, String> {
21        let conn = Connection::open(path).map_err(|e| e.to_string())?;
22
23        conn.execute(
24            "CREATE TABLE IF NOT EXISTS items (
25                id TEXT PRIMARY KEY,
26                parent_id TEXT,
27                name TEXT NOT NULL,
28                display_type TEXT NOT NULL,
29                content BLOB
30            )",
31            [],
32        )
33        .map_err(|e| e.to_string())?;
34
35        // Seed mínimo si la tabla está vacía — para que el DatabaseExplorer
36        // tenga algo que mostrar en una primera ejecución sin pre-config.
37        let count: i64 = conn
38            .query_row("SELECT COUNT(*) FROM items", [], |row| row.get(0))
39            .unwrap_or(0);
40        if count == 0 {
41            let _ = conn.execute(
42                "INSERT INTO items (id, parent_id, name, display_type, content) VALUES \
43                 ('readme', NULL, 'README.md', 'File', ?), \
44                 ('notes',  NULL, 'notes',     'Folder', NULL), \
45                 ('todo',   'notes', 'TODO.md', 'File', ?)",
46                rusqlite::params![
47                    b"# Yahweh\n\nDemo readme stored in SQLite.\n",
48                    b"- TreeView gen\xC3\xA9rico\n- containers swappables\n- layout JSON\n",
49                ],
50            );
51        }
52
53        Ok(Self {
54            db: Arc::new(Mutex::new(conn)),
55        })
56    }
57}
58
59#[async_trait]
60impl DataProvider for SqliteDataProvider {
61    fn provider_id(&self) -> String {
62        PROVIDER_ID.to_string()
63    }
64
65    async fn list_children(&self, parent_id: Option<&str>) -> Result<Vec<EntityNode>, String> {
66        let db = self.db.lock().unwrap();
67        let mut stmt = db
68            .prepare("SELECT id, name, display_type FROM items WHERE parent_id IS ?")
69            .map_err(|e| e.to_string())?;
70
71        let rows = stmt
72            .query_map([parent_id], |row| {
73                let display_type_str: String = row.get(2)?;
74                let display_type = match display_type_str.as_str() {
75                    "Folder" => DisplayType::Folder,
76                    "Stream" => DisplayType::Stream,
77                    _ => DisplayType::File,
78                };
79
80                Ok(EntityNode {
81                    id: row.get(0)?,
82                    name: row.get(1)?,
83                    display_type,
84                    mime_type: None,
85                })
86            })
87            .map_err(|e| e.to_string())?;
88
89        let mut children = Vec::new();
90        for row in rows {
91            children.push(row.map_err(|e| e.to_string())?);
92        }
93        Ok(children)
94    }
95
96    async fn get_read_stream(
97        &self,
98        entity_id: &str,
99    ) -> Result<Pin<Box<dyn AsyncRead + Send>>, String> {
100        let db = self.db.lock().unwrap();
101        let content: Vec<u8> = db
102            .query_row(
103                "SELECT content FROM items WHERE id = ?",
104                [entity_id],
105                |row| row.get(0),
106            )
107            .map_err(|e| e.to_string())?;
108
109        Ok(Box::pin(Cursor::new(content)))
110    }
111
112    async fn get_write_stream(
113        &self,
114        _entity_id: &str,
115    ) -> Result<Pin<Box<dyn AsyncWrite + Send>>, String> {
116        Err("Escritura en streaming no implementada para SQLite (todavía)".to_string())
117    }
118}