diff --git a/.gitignore b/.gitignore index 04ce869..47c0057 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ members/c-bindings/lib/cmake_install.cmake members/c-bindings/lib/libtest.so members/c-bindings/lib/libtest.so.1 members/c-bindings/lib/libtest.so.1.0.1 +members/sqlite-demo/data/cats.db diff --git a/members/sqlite-demo/cats.db b/members/sqlite-demo/cats.db deleted file mode 100644 index 9a80e9f..0000000 Binary files a/members/sqlite-demo/cats.db and /dev/null differ diff --git a/members/sqlite-demo/src/main.rs b/members/sqlite-demo/src/main.rs index 07cbd7c..b820c8f 100644 --- a/members/sqlite-demo/src/main.rs +++ b/members/sqlite-demo/src/main.rs @@ -1,34 +1,116 @@ +use std::path::PathBuf; +use std::{env, fs}; + use anyhow::Ok; /// This demo application uses a sqlite file to store some data. It does *not* use ORM (that would /// be done with the `diesel` crate.)! /// /// A very useful ressource is the /// [rust-cookbook](https://rust-lang-nursery.github.io/rust-cookbook/database/sqlite.html). -use rusqlite::{Connection, Result}; +use rusqlite::Connection; + +const DBNAME: &str = "cats.db"; +const TABLE_CAT_COLOR: &str = "cat_colors"; +const TABLE_CAT: &str = "cats"; + +fn connect() -> anyhow::Result { + let mut wd = env::current_dir()?; + let mut dbpath: Option = None; + + // does the current directory have a data/cats.db ? (the file need not exist, we can create it + // if data exists) + { + let mut wddata = wd.clone(); + wddata.push("data"); + if wddata.exists() { + println!("found {DBNAME} in {:?}", &wddata); + wddata.push(DBNAME); + dbpath = Some(wddata); + } + } + + // otherwise, does the current or any higher directories contain a cats.db ? + 'search_dir: while dbpath.is_none() { + for entry in fs::read_dir(&wd)? { + let entry = entry?; + if entry.file_name() == DBNAME { + println!("found {DBNAME} in {:?}", wd); + dbpath = Some(entry.path()); + break 'search_dir; + } + } + if !wd.pop() { + // we are at the root! + break 'search_dir; + } + } + + // if all fails, use $PWD/data/cats.db + if dbpath.is_none() { + println!( + "No {DBNAME} found, using {:?}/data/{DBNAME}", + env::current_dir()? + ); + fs::create_dir("data")?; + let mut path = env::current_dir()?; + path.push("data"); + path.push(DBNAME); + dbpath = Some(path); + } + + let conn = Connection::open(dbpath.unwrap())?; + + Ok(conn) +} fn setup(conn: &Connection) -> anyhow::Result<()> { conn.execute( - "create table if not exists cat_colors ( - id integer primary key, - name text not null unique - )", + &format!( + "CREATE TABLE IF NOT EXISTS {TABLE_CAT_COLOR} ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL UNIQUE COLLATE NOCASE + )" + ), (), )?; conn.execute( - "create table if not exists cats ( - id integer primary key, - name text not null, - color_id integer not null references cat_colors(id) - )", + &format!( + "CREATE TABLE IF NOT EXISTS {TABLE_CAT} ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL COLLATE NOCASE, + COLOR_ID INTEGER NOT NULL REFERENCES CAT_COLORS(ID) + )" + ), (), )?; Ok(()) } +/// Add a new color to the cat_colors table +/// +/// Will fail if the sql fails +/// +/// If the color already exists, will return Ok(()) +/// +/// The table stores the name in lowercase fn new_color(conn: &Connection, color: &str) -> anyhow::Result<()> { + // check if the color already exists, so we can just try adding a color and not worry if it + // already exists + + let mut stmt = conn.prepare(&format!( + "SELECT EXISTS( SELECT 1 FROM {TABLE_CAT_COLOR} WHERE name = (?1))" + ))?; + if let Some(row) = stmt.query([color.to_string()])?.next()? { + if row.get::(0)? == 1 { + // the color already exists + return Ok(()); + } + }; + + // do the inserting conn.execute( - "INSERT INTO cat_colors (name) values (?1)", + "INSERT INTO cat_colors (name) VALUES (?1)", [color.to_string()], )?; @@ -36,11 +118,12 @@ fn new_color(conn: &Connection, color: &str) -> anyhow::Result<()> { } fn main() -> anyhow::Result<()> { - let conn = Connection::open("cats.db")?; + let conn = connect()?; setup(&conn)?; new_color(&conn, "black")?; new_color(&conn, "red")?; + new_color(&conn, "white")?; Ok(()) }