use std::{io, path, process::exit}; use io::Write; use clap::Parser; use cli::CliArgs; use rusqlite::{Connection, params_from_iter}; const DB_PATH: &str = ".tags"; mod cli; fn main() { let args = CliArgs::parse(); match args.commands { cli::Commands::Init => { if !has_database() { let conn = Connection::open(DB_PATH).unwrap(); init_db(&conn); } else { panic!("Database is already initialized in this folder"); } exit(0); } cli::Commands::Tags(args) => { let conn = Connection::open(DB_PATH).unwrap(); if args.list { let mut w = io::stdout(); let tags = list_tags(&conn); for tag in tags { writeln!(&mut w, "{}", tag).unwrap(); } w.flush().unwrap(); return; } match args.commands { Some(cli::TagsCommands::Add { add }) => add_tags(&conn, add), Some(cli::TagsCommands::Remove { remove }) => remove_tags(&conn, remove), _ => (), }; } } } fn list_tags(conn: &Connection) -> Vec { let mut stmt = conn.prepare("SELECT name FROM tag").unwrap(); let result = stmt.query_map([], |row| row.get(0)).unwrap(); let mut tags = Vec::new(); for name in result { tags.push(name.unwrap()); } tags } fn remove_tags(conn: &Connection, tags: Vec) { let mut query = r#"DELETE FROM tag WHERE name IN ("#.to_string(); for (i, _tag) in tags.iter().enumerate() { query.push('?'); if i < tags.len() - 1 { query.push(','); } } query.push(')'); conn.execute(&query, params_from_iter(tags)).unwrap(); } fn add_tags(conn: &Connection, tags: Vec) { let mut query = r#"INSERT INTO tag(name) VALUES"#.to_string(); for (i, _tag) in tags.iter().enumerate() { query.push_str("(?)"); if i < tags.len() - 1 { query.push(','); } } conn.execute(&query, params_from_iter(tags)).unwrap(); } fn init_db(conn: &Connection) { conn.execute( r#"CREATE TABLE IF NOT EXISTS tag( id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(255) NOT NULL UNIQUE );"#, (), ) .unwrap(); conn.execute( r#"CREATE TABLE IF NOT EXISTS file( id INT NOT NULL PRIMARY KEY, path VARCHAR(255) NOT NULL UNIQUE );"#, (), ) .unwrap(); conn.execute( r#"CREATE TABLE IF NOT EXISTS file_tag( tag_id INT REFERENCES tag(id), file_id INT REFERENCES file(id) );"#, (), ) .unwrap(); } pub fn has_database() -> bool { path::Path::new(DB_PATH).exists() }