deleting colors(+cats) works
This commit is contained in:
parent
22fa06e2b6
commit
29ebb7b31a
|
@ -0,0 +1,174 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{env, fs};
|
||||||
|
|
||||||
|
use rusqlite::Connection;
|
||||||
|
pub const DBNAME: &str = "cats.db";
|
||||||
|
pub const TABLE_CAT_COLOR: &str = "cat_colors";
|
||||||
|
pub const TABLE_CAT: &str = "cats";
|
||||||
|
|
||||||
|
|
||||||
|
pub fn connect() -> anyhow::Result<Connection> {
|
||||||
|
let mut wd = env::current_dir()?;
|
||||||
|
let mut dbpath: Option<PathBuf> = 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(conn: &Connection) -> anyhow::Result<()> {
|
||||||
|
conn.execute(
|
||||||
|
&format!(
|
||||||
|
"CREATE TABLE IF NOT EXISTS {TABLE_CAT_COLOR} (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL UNIQUE COLLATE NOCASE
|
||||||
|
)"
|
||||||
|
),
|
||||||
|
(),
|
||||||
|
)?;
|
||||||
|
conn.execute(
|
||||||
|
&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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// color names are unique, so we use the color name instead of the id because it's easier
|
||||||
|
pub fn check_if_color_exists(conn: &Connection, color: &str) -> anyhow::Result<bool> {
|
||||||
|
let mut stmt = conn.prepare(&format!(
|
||||||
|
"SELECT EXISTS( SELECT 1 FROM {TABLE_CAT_COLOR} WHERE name = (LOWER(?1)))"
|
||||||
|
))?;
|
||||||
|
|
||||||
|
// we need the return or the temporary result is dropped somehow
|
||||||
|
#[allow(clippy::needless_return)]
|
||||||
|
return Ok(stmt
|
||||||
|
.query([color.to_string()])?
|
||||||
|
.next()?
|
||||||
|
.unwrap()
|
||||||
|
.get::<usize, usize>(0)?
|
||||||
|
== 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// note that cat names are not UNIQUE, so we need to use the id instead
|
||||||
|
pub fn check_if_cat_exists(conn: &Connection, id: usize) -> anyhow::Result<bool> {
|
||||||
|
let mut stmt = conn.prepare(&format!(
|
||||||
|
"SELECT EXISTS( SELECT 1 FROM {TABLE_CAT} WHERE id = (?1))"
|
||||||
|
))?;
|
||||||
|
|
||||||
|
// we need the return or the temporary result is dropped somehow
|
||||||
|
#[allow(clippy::needless_return)]
|
||||||
|
return Ok(stmt.query([id])?.next()?.unwrap().get::<usize, usize>(0)? == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a new color to the cat_colors table
|
||||||
|
///
|
||||||
|
/// Will fail if the sql fails
|
||||||
|
///
|
||||||
|
/// If the color already exists, will return Ok(id)
|
||||||
|
///
|
||||||
|
/// The table stores the name in lowercase
|
||||||
|
///
|
||||||
|
/// Returns the id of the color
|
||||||
|
pub fn new_color(conn: &Connection, color: &str) -> anyhow::Result<usize> {
|
||||||
|
if check_if_color_exists(conn, color)? {
|
||||||
|
return get_color_id(conn, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.execute(
|
||||||
|
&format!("INSERT INTO {TABLE_CAT_COLOR} (name) VALUES (LOWER(?1))"),
|
||||||
|
[color.to_string()],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
get_color_id(conn, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_color_id(conn: &Connection, color: &str) -> anyhow::Result<usize> {
|
||||||
|
Ok(conn.query_row(
|
||||||
|
&format!("SELECT id FROM {TABLE_CAT_COLOR} WHERE name = (LOWER(?1))"),
|
||||||
|
[color.to_string()],
|
||||||
|
|row| row.get::<usize, usize>(0),
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_color_by_id(conn: &Connection, id: usize) -> anyhow::Result<Option<String>> {
|
||||||
|
let maybe = conn.query_row(
|
||||||
|
&format!("SELECT name FROM {TABLE_CAT_COLOR} WHERE id = (?1)"),
|
||||||
|
[id.to_string()],
|
||||||
|
|row| row.get::<usize, String>(0),
|
||||||
|
);
|
||||||
|
if let Result::Ok(color) = maybe {
|
||||||
|
Ok(Some(color))
|
||||||
|
} else {
|
||||||
|
let err: rusqlite::Error = maybe.unwrap_err();
|
||||||
|
if matches!(err, rusqlite::Error::QueryReturnedNoRows) {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Err(err.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_cat(conn: &Connection, name: &str, color_id: usize) -> anyhow::Result<usize> {
|
||||||
|
conn.execute(
|
||||||
|
&format!("INSERT INTO {TABLE_CAT} (name, color_id) VALUES (LOWER(?1), ?2)"),
|
||||||
|
[name.to_string(), color_id.to_string()],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(conn.last_insert_rowid() as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cat_color(conn: &Connection, id: usize) -> anyhow::Result<String> {
|
||||||
|
Ok(conn.query_row(
|
||||||
|
&format!("SELECT cc.name FROM {TABLE_CAT_COLOR} cc, {TABLE_CAT} c WHERE c.id = (?1) AND c.color_id = cc.id"),
|
||||||
|
[id.to_string()],
|
||||||
|
|row| row.get::<usize, String>(0),
|
||||||
|
)?)
|
||||||
|
}
|
|
@ -1,189 +1,21 @@
|
||||||
|
use std::io;
|
||||||
/// This demo application uses a sqlite file to store some data. It does *not* use ORM (that would
|
/// This demo application uses a sqlite file to store some data. It does *not* use ORM (that would
|
||||||
/// be done with the `diesel` crate.)!
|
/// be done with the `diesel` crate.)!
|
||||||
///
|
///
|
||||||
/// A very useful ressource is the
|
/// A very useful ressource is the
|
||||||
/// [rust-cookbook](https://rust-lang-nursery.github.io/rust-cookbook/database/sqlite.html).
|
/// [rust-cookbook](https://rust-lang-nursery.github.io/rust-cookbook/database/sqlite.html).
|
||||||
use std::io::{BufRead, Write};
|
use std::io::{BufRead, Write};
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::{env, fs, io};
|
|
||||||
|
|
||||||
use rusqlite::{Connection, Rows};
|
use rusqlite::{Connection, Rows};
|
||||||
|
|
||||||
const DBNAME: &str = "cats.db";
|
mod db;
|
||||||
const TABLE_CAT_COLOR: &str = "cat_colors";
|
use db::*;
|
||||||
const TABLE_CAT: &str = "cats";
|
|
||||||
|
|
||||||
const USAGE_DELETE: &str = "Usage:
|
const USAGE_DELETE: &str = "Usage:
|
||||||
> D cat 15 (to delete cat with id 15)
|
> D cat 15 (to delete cat with id 15)
|
||||||
> D cat 15 16 (to delete cat with id 15 and 16)
|
> D cat 15 16 (to delete cat with id 15 and 16)
|
||||||
> D color 5 (to delete color with id 5)";
|
> D color 5 (to delete color with id 5)";
|
||||||
|
|
||||||
fn connect() -> anyhow::Result<Connection> {
|
|
||||||
let mut wd = env::current_dir()?;
|
|
||||||
let mut dbpath: Option<PathBuf> = 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(
|
|
||||||
&format!(
|
|
||||||
"CREATE TABLE IF NOT EXISTS {TABLE_CAT_COLOR} (
|
|
||||||
id INTEGER PRIMARY KEY,
|
|
||||||
name TEXT NOT NULL UNIQUE COLLATE NOCASE
|
|
||||||
)"
|
|
||||||
),
|
|
||||||
(),
|
|
||||||
)?;
|
|
||||||
conn.execute(
|
|
||||||
&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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// color names are unique, so we use the color name instead of the id because it's easier
|
|
||||||
fn check_if_color_exists(conn: &Connection, color: &str) -> anyhow::Result<bool> {
|
|
||||||
let mut stmt = conn.prepare(&format!(
|
|
||||||
"SELECT EXISTS( SELECT 1 FROM {TABLE_CAT_COLOR} WHERE name = (LOWER(?1)))"
|
|
||||||
))?;
|
|
||||||
|
|
||||||
// we need the return or the temporary result is dropped somehow
|
|
||||||
#[allow(clippy::needless_return)]
|
|
||||||
return Ok(stmt
|
|
||||||
.query([color.to_string()])?
|
|
||||||
.next()?
|
|
||||||
.unwrap()
|
|
||||||
.get::<usize, usize>(0)?
|
|
||||||
== 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// note that cat names are not UNIQUE, so we need to use the id instead
|
|
||||||
fn check_if_cat_exists(conn: &Connection, id: usize) -> anyhow::Result<bool> {
|
|
||||||
let mut stmt = conn.prepare(&format!(
|
|
||||||
"SELECT EXISTS( SELECT 1 FROM {TABLE_CAT} WHERE id = (?1))"
|
|
||||||
))?;
|
|
||||||
|
|
||||||
// we need the return or the temporary result is dropped somehow
|
|
||||||
#[allow(clippy::needless_return)]
|
|
||||||
return Ok(stmt.query([id])?.next()?.unwrap().get::<usize, usize>(0)? == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a new color to the cat_colors table
|
|
||||||
///
|
|
||||||
/// Will fail if the sql fails
|
|
||||||
///
|
|
||||||
/// If the color already exists, will return Ok(id)
|
|
||||||
///
|
|
||||||
/// The table stores the name in lowercase
|
|
||||||
///
|
|
||||||
/// Returns the id of the color
|
|
||||||
fn new_color(conn: &Connection, color: &str) -> anyhow::Result<usize> {
|
|
||||||
if check_if_color_exists(conn, color)? {
|
|
||||||
return get_color_id(conn, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.execute(
|
|
||||||
&format!("INSERT INTO {TABLE_CAT_COLOR} (name) VALUES (LOWER(?1))"),
|
|
||||||
[color.to_string()],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
get_color_id(conn, color)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_color_id(conn: &Connection, color: &str) -> anyhow::Result<usize> {
|
|
||||||
Ok(conn.query_row(
|
|
||||||
&format!("SELECT id FROM {TABLE_CAT_COLOR} WHERE name = (LOWER(?1))"),
|
|
||||||
[color.to_string()],
|
|
||||||
|row| row.get::<usize, usize>(0),
|
|
||||||
)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_color_by_id(conn: &Connection, id: usize) -> anyhow::Result<Option<String>> {
|
|
||||||
let maybe = conn.query_row(
|
|
||||||
&format!("SELECT name FROM {TABLE_CAT_COLOR} WHERE id = (?1)"),
|
|
||||||
[id.to_string()],
|
|
||||||
|row| row.get::<usize, String>(0),
|
|
||||||
);
|
|
||||||
if let Result::Ok(color) = maybe {
|
|
||||||
Ok(Some(color))
|
|
||||||
} else {
|
|
||||||
let err: rusqlite::Error = maybe.unwrap_err();
|
|
||||||
if matches!(err, rusqlite::Error::QueryReturnedNoRows) {
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
Err(err.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_cat(conn: &Connection, name: &str, color_id: usize) -> anyhow::Result<usize> {
|
|
||||||
conn.execute(
|
|
||||||
&format!("INSERT INTO {TABLE_CAT} (name, color_id) VALUES (LOWER(?1), ?2)"),
|
|
||||||
[name.to_string(), color_id.to_string()],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(conn.last_insert_rowid() as usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_cat_color(conn: &Connection, id: usize) -> anyhow::Result<String> {
|
|
||||||
Ok(conn.query_row(
|
|
||||||
&format!("SELECT cc.name FROM {TABLE_CAT_COLOR} cc, {TABLE_CAT} c WHERE c.id = (?1) AND c.color_id = cc.id"),
|
|
||||||
[id.to_string()],
|
|
||||||
|row| row.get::<usize, String>(0),
|
|
||||||
)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn interactive_add_cat(conn: &Connection) -> anyhow::Result<usize> {
|
fn interactive_add_cat(conn: &Connection) -> anyhow::Result<usize> {
|
||||||
let stdin = io::stdin();
|
let stdin = io::stdin();
|
||||||
print!("the name of your cat?\n> ");
|
print!("the name of your cat?\n> ");
|
||||||
|
@ -234,6 +66,97 @@ fn interactive_find_cat(conn: &Connection) -> anyhow::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn interactive_delete(conn: &Connection, buf: &mut String) -> anyhow::Result<()> {
|
||||||
|
let words: Vec<&str> = buf.split(' ').collect();
|
||||||
|
if words.len() < 2 {
|
||||||
|
println!("{USAGE_DELETE}");
|
||||||
|
} else {
|
||||||
|
let stdin = io::stdin();
|
||||||
|
let mode = words[1];
|
||||||
|
let mut nums: Vec<usize> = Vec::new();
|
||||||
|
for word in words[2..].iter() {
|
||||||
|
nums.push(match word.parse() {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Could not parse '{word}' to id: {e}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
match mode {
|
||||||
|
"CAT" => {
|
||||||
|
let mut stmt = conn.prepare(&format!("DELETE FROM {TABLE_CAT} WHERE id = (?1)"))?;
|
||||||
|
for n in nums {
|
||||||
|
stmt.execute([n])?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"COLOR" => {
|
||||||
|
// Cats have colors, so if we delete a color, we need to delete cats with
|
||||||
|
// that color too.
|
||||||
|
let mut stmt_how_many_cats_with_color = conn.prepare(&format!(
|
||||||
|
"SELECT COUNT(1) FROM {TABLE_CAT} c, {TABLE_CAT_COLOR}
|
||||||
|
cc WHERE c.color_id = (?1) AND cc.id = (?1)"
|
||||||
|
))?;
|
||||||
|
// FIXME: this must still be wrong?
|
||||||
|
let mut stmt_cats_with_color = conn.prepare(&format!(
|
||||||
|
"SELECT c.* FROM {TABLE_CAT} c, {TABLE_CAT_COLOR} cc
|
||||||
|
WHERE c.color_id = (?1) AND cc.id = (?1)"
|
||||||
|
))?;
|
||||||
|
// works: `SELECT cats.* FROM cats, cat_colors WHERE cats.color_id = 2 AND cat_colors.id = 2;`
|
||||||
|
|
||||||
|
for color_id in &nums {
|
||||||
|
let cats_amount: usize = stmt_how_many_cats_with_color
|
||||||
|
.query_row([color_id], |row| row.get::<_, usize>(0))?;
|
||||||
|
|
||||||
|
if cats_amount > 0 {
|
||||||
|
let mut cats = stmt_cats_with_color.query([color_id])?; // Get the cats
|
||||||
|
// that would be deleted
|
||||||
|
println!(
|
||||||
|
"\nYou are about to also delete these cats,\n\
|
||||||
|
as they have the color id {color_id}. Type 'YES' to confirm."
|
||||||
|
);
|
||||||
|
print_cats(conn, &mut cats)?;
|
||||||
|
drop(cats); // we need to renew this, because Rows is not Clone and we have
|
||||||
|
// consumed the Rows in print_cats. The Rows index can not be reset,
|
||||||
|
// probably because of a sqlite API limitation.
|
||||||
|
|
||||||
|
let mut cats = stmt_cats_with_color.query([color_id])?;
|
||||||
|
let mut cat_ids: Vec<usize> = Vec::new();
|
||||||
|
|
||||||
|
while let Some(cat) = cats.next()? {
|
||||||
|
cat_ids.push(cat.get::<_, usize>(0)?);
|
||||||
|
}
|
||||||
|
buf.clear();
|
||||||
|
let _ = stdin.lock().read_line(buf); // wait for enter as confirmation
|
||||||
|
*buf = buf.trim().to_string();
|
||||||
|
*buf = buf.to_uppercase().to_string();
|
||||||
|
dbg!(&buf);
|
||||||
|
if buf.as_str() != "YES" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut stmt =
|
||||||
|
conn.prepare(&format!("DELETE FROM {TABLE_CAT} WHERE id = (?1)"))?;
|
||||||
|
for cat_id in cat_ids {
|
||||||
|
stmt.execute([cat_id])?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut stmt =
|
||||||
|
conn.prepare(&format!("DELETE FROM {TABLE_CAT_COLOR} WHERE id = (?1)"))?;
|
||||||
|
for n in &nums {
|
||||||
|
stmt.execute([n])?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("deleted ids {nums:?}");
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("{USAGE_DELETE}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn print_colors(_conn: &Connection, colors: &mut Rows) -> anyhow::Result<()> {
|
fn print_colors(_conn: &Connection, colors: &mut Rows) -> anyhow::Result<()> {
|
||||||
println!("{: <14}| {: <19}", "id", "name");
|
println!("{: <14}| {: <19}", "id", "name");
|
||||||
println!("{:=^80}", "");
|
println!("{:=^80}", "");
|
||||||
|
@ -247,6 +170,9 @@ fn print_colors(_conn: &Connection, colors: &mut Rows) -> anyhow::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Print [Rows] of cats.
|
||||||
|
///
|
||||||
|
/// This needs all columns of the [TABLE_CAT], otherwise it will error.
|
||||||
fn print_cats(conn: &Connection, cats: &mut Rows) -> anyhow::Result<()> {
|
fn print_cats(conn: &Connection, cats: &mut Rows) -> anyhow::Result<()> {
|
||||||
println!(
|
println!(
|
||||||
"{: <14}| {: <19}| {: <19} -> {: <19}",
|
"{: <14}| {: <19}| {: <19} -> {: <19}",
|
||||||
|
@ -289,7 +215,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
buf.clear();
|
buf.clear();
|
||||||
print!("{}[2J", 27 as char); // clear terminal
|
// print!("{}[2J", 27 as char); // clear terminal
|
||||||
io::stdout().flush()?;
|
io::stdout().flush()?;
|
||||||
print!("(A)dd a cat, (F)ind a cat, (P)rint out all data, (D)elete data, or (E)xit?\n> ");
|
print!("(A)dd a cat, (F)ind a cat, (P)rint out all data, (D)elete data, or (E)xit?\n> ");
|
||||||
io::stdout().flush()?;
|
io::stdout().flush()?;
|
||||||
|
@ -303,87 +229,12 @@ fn main() -> anyhow::Result<()> {
|
||||||
} else if buf.starts_with('P') {
|
} else if buf.starts_with('P') {
|
||||||
print_all_data(&conn)?;
|
print_all_data(&conn)?;
|
||||||
} else if buf.starts_with('D') {
|
} else if buf.starts_with('D') {
|
||||||
let words: Vec<&str> = buf.split(' ').collect();
|
interactive_delete(&conn, &mut buf)?;
|
||||||
dbg!(&words);
|
|
||||||
if words.len() < 2 {
|
|
||||||
println!("{USAGE_DELETE}");
|
|
||||||
} else {
|
|
||||||
let mode = words[1];
|
|
||||||
let nums: Vec<usize> = words[2..]
|
|
||||||
.iter()
|
|
||||||
.map(|word| {
|
|
||||||
word.parse::<usize>()
|
|
||||||
.expect("could not parse db integer to rust integer")
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
dbg!(&nums);
|
|
||||||
match mode {
|
|
||||||
"CAT" => {
|
|
||||||
let mut stmt =
|
|
||||||
conn.prepare(&format!("DELETE FROM {TABLE_CAT} WHERE id = (?1)"))?;
|
|
||||||
for n in nums {
|
|
||||||
stmt.execute([n])?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"COLOR" => {
|
|
||||||
// Cats have colors, so if we delete a color, we need to delete cats with
|
|
||||||
// that color too.
|
|
||||||
let mut stmt_how_many_cats_with_color = conn.prepare(&format!(
|
|
||||||
"SELECT cc.id FROM {TABLE_CAT} c, {TABLE_CAT_COLOR}
|
|
||||||
cc WHERE c.color_id = (?1)"
|
|
||||||
))?;
|
|
||||||
let mut stmt_cats_with_color = conn.prepare(&format!(
|
|
||||||
"SELECT cc.id FROM {TABLE_CAT} c, {TABLE_CAT_COLOR}
|
|
||||||
cc WHERE c.color_id = (?1)"
|
|
||||||
))?;
|
|
||||||
|
|
||||||
for color_id in &nums {
|
|
||||||
let cats_amount: usize = stmt_how_many_cats_with_color
|
|
||||||
.query_row([color_id], |row| row.get::<_, usize>(0))?;
|
|
||||||
|
|
||||||
if cats_amount > 0 {
|
|
||||||
// FIXME: cats is empty?
|
|
||||||
let mut cats = stmt_cats_with_color.query([color_id])?;
|
|
||||||
let mut cat_ids: Vec<usize> = Vec::new();
|
|
||||||
|
|
||||||
while let Some(cat) = cats.next()? {
|
|
||||||
cat_ids.push(cat.get::<_, usize>(0)?);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("You are about to also delete these cats, as they have the color id {color_id}. Type 'YES' to confirm");
|
|
||||||
print_cats(&conn, &mut cats)?;
|
|
||||||
buf.clear();
|
|
||||||
let _ = stdin.lock().read_line(&mut buf); // wait for enter as confirmation
|
|
||||||
buf = buf.trim().to_string();
|
|
||||||
buf = buf.to_uppercase().to_string();
|
|
||||||
if buf.as_str() != "YES" {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// FIXME: I DELETED ALL THE CATS OH NO
|
|
||||||
let mut stmt = conn
|
|
||||||
.prepare(&format!("DELETE FROM {TABLE_CAT} WHERE id = (?1)"))?;
|
|
||||||
for cat_id in cat_ids {
|
|
||||||
stmt.execute([cat_id])?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut stmt = conn.prepare(&format!(
|
|
||||||
"DELETE FROM {TABLE_CAT_COLOR} WHERE id = (?1)"
|
|
||||||
))?;
|
|
||||||
let _ = nums.iter().map(|n| stmt.execute([n]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
println!("{USAGE_DELETE}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if buf.starts_with('E') {
|
} else if buf.starts_with('E') {
|
||||||
println!("Goodbye");
|
println!("Goodbye");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
println!("\n(Enter to continue)");
|
println!("\n");
|
||||||
let _ = stdin.lock().read_line(&mut buf); // wait for enter as confirmation
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in New Issue