Monday | 15 APR 2024
[ previous ]
[ next ]

Rust, Fuse, Sqlite - Sqlite

Title:
Date: 2023-02-10
Tags:  

Some rusqlite code. I'll hopefully go over this at some point. Or not.

use std::fs;
use std::collections::HashMap;
use std::time::SystemTime;

use rusqlite::Connection;

#[derive(Debug)]
enum FType {
    File, Directory
}

#[derive(Debug)]
struct Entry {
    ino: u64,
    parent: u64,
    id_col: String,
    table: String,
    name: String,
    ftype: FType,
}

#[derive(Debug)]
struct FS {
    db: String,
    modified: SystemTime,
    counter: u64,
    directories: HashMap<u64, Entry>,
    files: HashMap<u64, Entry>,
}

impl FS {
    fn new(db: String) -> FS {
        let metadata = fs::metadata(&db).unwrap();
        let modified = metadata.modified().unwrap();

        FS {
            db,
            modified,
            counter: 0,
            directories: HashMap::new(),
            files: HashMap::new(),
        }
    }

    fn get_directories(&mut self) {
        let conn = Connection::open(self.db.clone()).unwrap();

        let mut stmt = conn.prepare(
            "select name from sqlite_master
            where type = 'table'
            and name not like 'sqlite_%' order by name;"
        ).unwrap();

        self.counter = 1;

        let rows = stmt.query_map([], |row| {
            self.counter = self.counter + 1;
            Ok(Entry {
                ino: self.counter,
                parent: 1,
                id_col: "".to_string(),
                table: "".to_string(),
                name: row.get(0)?,
                ftype: FType::Directory,
            })
        }).unwrap();

        for row in rows {
            if let Ok(r) = row {
                self.directories.insert(r.ino, r);
            }
        }
    }

    fn get_all_files(&mut self) {
        for (ino, entry) in &self.directories {
            let table = &entry.name;
            let conn = Connection::open(self.db.clone()).unwrap();

            let query = format!("select * from \"{}\";",table);
            let mut stmt = conn.prepare(&query).unwrap();

            let mut id_col = "".to_string();

            let rows = stmt.query_map([], |row| {
                self.counter = self.counter + 1;

                if id_col == "" {
                    id_col = row.as_ref().column_name(0).unwrap().to_string();
                }

                Ok(Entry {
                    ino: self.counter,
                    parent: ino.clone(),
                    name: row.get(0)?,
                    id_col: id_col.clone(),
                    table: table.clone(),
                    ftype: FType::File,
                })
            }).unwrap();

            for row in rows {
                match row {
                    Ok(r) => {
                        self.files.insert(r.ino, r);
                    },
                    Err(err) => {
                        println!("rusqlite error: {}", err);
                    }
                }
            }
        }
    }

    fn fetch(&mut self) {
        self.directories = HashMap::new();
        self.files = HashMap::new();
        self.get_directories();
        self.get_all_files();
    }

    fn read(&self, table: &str, id_col: &str, id: &str) {
        let query = format!("select * from {} where {} = \"{}\";", table, id_col, id);

        let conn = Connection::open(self.db.clone()).unwrap();
        let mut stmt = conn.prepare(&query).unwrap();
        let column_count = stmt.column_count();

        let mut rows = stmt.query(()).unwrap();
        let row = rows.next().unwrap().unwrap();

        let mut row_map = serde_json::map::Map::new();

        for column_index in 0..column_count {
            let key = row.as_ref().column_name(column_index).unwrap().to_string();
            let str_val: String = row.get(column_index).unwrap();
            let val = serde_json::Value::from(str_val);
            row_map.insert(key, val);
        }

        row_map.remove(id_col);

        println!("{}", serde_json::to_string(&row_map).unwrap());
    }

    fn write(&mut self, table: &str, id_col: &str, id: &str, text :&str) {
        let mut columns = vec!();
        let mut values = vec!();
        let json: serde_json::Value = match serde_json::from_str(text) {
            Ok(json) => json,
            Err(err) => {
                println!("Failed to write - {} on {}: {}", id, table,err);
                return;
            }
        };

        for (key, val) in json.as_object().unwrap() {
            columns.push(key.to_string());
            values.push(val.to_string());
        }

        let columns = columns.join(",");
        let values = values.join(",");
        let query = format!("REPLACE INTO {} ({},{}) VALUES (\"{}\",{});", table, id_col, columns, id, values);
        let conn = Connection::open(self.db.clone()).unwrap();
        let res = conn.execute(&query, ());

        match res {
            Ok(r) => {
                self.fetch();
                println!("Successfully written: {} on {}.", id, table);
            },
            Err(err) => {
                println!("Failed to write - {} on {}: {}", id, table,err);
            }
        }
    }

    fn print_directories(&self) {
        for (ino, entry) in &self.directories {
            println!("{:?}: {:?}", ino, entry);
        }
    }

    fn print_files(&self) {
        for (ino, entry) in &self.files {
            println!("{:?}: {:?}", ino, entry);
        }
    }

    fn info(&self) {
        let dirs = self.directories.keys().len();
        let files = self.files.keys().len();

        println!("Last modified {:?}", self.modified);
        println!("{} directoriess found", dirs);
        println!("{} files found", files);
        println!("{} is the last inode", self.counter);
    }
}

fn main() {
    let db = "example.db".to_string();
    let mut fs = FS::new(db);

    fs.fetch();
    fs.read("mytable", "title", "Post 1");

    let text = r#"{"body":"Content for Post 1"}"#;
    fs.write("mytable", "title", "Post 1", text);

    let text = r#"{"body":"Content for Post 4. This is the end"}"#;
    fs.write("mytable", "title", "Post 4", text);

    println!("{}", text);
}