init
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
Generated
+1462
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "todo-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
http = "1.2.0"
|
||||||
|
openssl = "0.10.68"
|
||||||
|
reqwest = { version = "0.12.9", features = ["blocking"] }
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
use reqwest::blocking;
|
||||||
|
|
||||||
|
const COMMANDS : [&str; 7] = [
|
||||||
|
"help",
|
||||||
|
"version",
|
||||||
|
"show",
|
||||||
|
"add",
|
||||||
|
"rm",
|
||||||
|
"mv",
|
||||||
|
"setdb"
|
||||||
|
];
|
||||||
|
|
||||||
|
fn dbgprint(s: &str) {
|
||||||
|
if false {
|
||||||
|
println!("{}", s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo:
|
||||||
|
// * add desc
|
||||||
|
// * add multiple tags support
|
||||||
|
// * add "inbox default"
|
||||||
|
// * add nice layout printout
|
||||||
|
fn help_menu() -> String {
|
||||||
|
let cmds = vec![
|
||||||
|
"./todo help | --help | -h",
|
||||||
|
"./todo version | --version",
|
||||||
|
"./todo show [-b]" ,
|
||||||
|
"./todo add taskname",
|
||||||
|
"./todo rm taskname",
|
||||||
|
// "./todo add taskname [desc] [tag] (use to edit, collision --force)",
|
||||||
|
// "./todo add -b bucketname [category] (used to edit, collision --force)",
|
||||||
|
// "./todo rm -b bucketname (also removes tasks)",
|
||||||
|
// "./todo rm -b bucketname -s target-bucket",
|
||||||
|
// "./todo mv taskname bucketname",
|
||||||
|
// "./todo mv bucketname newbucketname"
|
||||||
|
// "./todo setdb filepath",
|
||||||
|
];
|
||||||
|
|
||||||
|
cmds.join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_get(url: &String) -> Result<String, String> {
|
||||||
|
println!("Sending get: {}", url);
|
||||||
|
let body = match reqwest::blocking::get(url) {
|
||||||
|
Ok(res) => {
|
||||||
|
if let Ok(inner) = res.text() {
|
||||||
|
inner
|
||||||
|
} else {
|
||||||
|
format!("error getting test")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => format!("Failed to get")
|
||||||
|
};
|
||||||
|
Ok(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_post(url: &String, data: &String) -> Result<String, String> {
|
||||||
|
let client = reqwest::blocking::Client::new();
|
||||||
|
if let Ok(res) = client.post(url)
|
||||||
|
.body(data.clone())
|
||||||
|
.send() {
|
||||||
|
return Ok(format!(""))
|
||||||
|
}
|
||||||
|
Err(format!("failed to post"))
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Command {
|
||||||
|
cmd: String,
|
||||||
|
args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parser(args: Vec<String>) -> Result<Command,String> {
|
||||||
|
if !COMMANDS.contains(&&args[1].as_str()) {
|
||||||
|
println!("UNKNOWN COMMAND, try one of the following");
|
||||||
|
return Err(format!("Unknown command"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Command { cmd: args[1].clone(), args: args.to_vec()})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handler(command: &Command, url: &String) -> Result<String,String> {
|
||||||
|
// these commands do not require db access
|
||||||
|
if command.cmd == "help"
|
||||||
|
|| command.args.contains(&String::from("--help"))
|
||||||
|
|| command.args.contains(&String::from("-h")) {
|
||||||
|
return Ok(help_menu());
|
||||||
|
} else if command.cmd == "version"
|
||||||
|
|| command.args.contains(&String::from("--version")) {
|
||||||
|
let version = env!("CARGO_PKG_VERSION");
|
||||||
|
return Ok(format!("{}", version));
|
||||||
|
}
|
||||||
|
|
||||||
|
// probably need to hit the server
|
||||||
|
if command.cmd == "show" {
|
||||||
|
let url_with_query = url.clone() + &format!("?action=show");
|
||||||
|
send_get(&url_with_query)
|
||||||
|
} else if command.cmd == "add" {
|
||||||
|
if command.args.len() < 3 {
|
||||||
|
return Err(format!("ERR: Not enough args for adding"));
|
||||||
|
}
|
||||||
|
let name = &command.args[2];
|
||||||
|
// let mut bucket = "";
|
||||||
|
// if command.args.len() > 3 {
|
||||||
|
// bucket = &command.args[3];
|
||||||
|
// }
|
||||||
|
let url_with_query = url.clone() + &format!("?action=add");
|
||||||
|
send_post(&url_with_query, &format!("{}", name))
|
||||||
|
} else if command.cmd == "rm" {
|
||||||
|
if command.args.len() < 3 {
|
||||||
|
println!("ERR: Not enough args for removing");
|
||||||
|
return Err(format!("Err: Not enough args for removing"));
|
||||||
|
}
|
||||||
|
let name = &command.args[2];
|
||||||
|
let url_with_query = url.clone() + &format!("?action=rm");
|
||||||
|
send_post(&url_with_query, &format!("{}", name))
|
||||||
|
} else {
|
||||||
|
Ok(help_menu())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args : Vec<String> = std::env::args().collect();
|
||||||
|
let url = format!("http://localhost:8001/");
|
||||||
|
let parsed = parser(args);
|
||||||
|
if let Ok(cmd) = parsed {
|
||||||
|
println!("{}", handler(&cmd, &url).unwrap());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
println!("parsing failed");
|
||||||
|
println!("{}", help_menu());
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
todo.sqlite
|
||||||
Generated
+226
@@ -0,0 +1,226 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ascii"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chunked_transfer"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-iterator"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-streaming-iterator"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashlink"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httpdate"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libsqlite3-sys"
|
||||||
|
version = "0.30.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.20.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rusqlite"
|
||||||
|
version = "0.32.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"fallible-iterator",
|
||||||
|
"fallible-streaming-iterator",
|
||||||
|
"hashlink",
|
||||||
|
"libsqlite3-sys",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny_http"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82"
|
||||||
|
dependencies = [
|
||||||
|
"ascii",
|
||||||
|
"chunked_transfer",
|
||||||
|
"httpdate",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "todo"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"rusqlite",
|
||||||
|
"tiny_http",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "todo"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rusqlite = {version = "0.32.1", features = ["bundled"]}
|
||||||
|
tiny_http = "0.12.0"
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
mod todo_db;
|
||||||
|
|
||||||
|
use todo_db::TodoDb;
|
||||||
|
use tiny_http::{Server, Response, Header};
|
||||||
|
|
||||||
|
fn test_task_and_bucket(db: &TodoDb) {
|
||||||
|
let _ = db.add_task("eat lunch", "", "tomorrow");
|
||||||
|
let _ = db.add_bucket("today", "schedule");
|
||||||
|
let _ = db.add_bucket("this week", "schedule");
|
||||||
|
let _ = db.add_bucket("property research", "project");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_db(db: &TodoDb) {
|
||||||
|
let tasks = db.all_tasks();
|
||||||
|
let buckets = db.all_buckets();
|
||||||
|
for b in buckets.iter() {
|
||||||
|
println!("{}", b.name);
|
||||||
|
for t in tasks.iter() {
|
||||||
|
if t.tag.contains(&b.name) {
|
||||||
|
println!("{}", t.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Command {
|
||||||
|
cmd: String,
|
||||||
|
args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_query(query_string: &String) -> Result<Command, String> {
|
||||||
|
let mut result = Err(format!("No action found"));
|
||||||
|
for (key, value) in query_string.split('&').map(|s| {
|
||||||
|
let mut parts = s.splitn(3, '=');
|
||||||
|
(parts.next().unwrap_or(""), parts.next().unwrap_or(""))
|
||||||
|
}) {
|
||||||
|
match key {
|
||||||
|
"action" => {
|
||||||
|
println!("found action");
|
||||||
|
match value {
|
||||||
|
"show" => {
|
||||||
|
result = Ok(Command { cmd: format!("show"), args: vec![] });
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
"add" => {
|
||||||
|
result = Ok(Command { cmd: format!("add"), args: vec![] });
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
"rm" => {
|
||||||
|
result = Ok(Command { cmd: format!("rm"), args: vec![] });
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handler(cmd: &Command, db: &TodoDb) -> String {
|
||||||
|
match cmd.cmd.as_str() {
|
||||||
|
"show" => db.all_tables(),
|
||||||
|
"add" => {
|
||||||
|
if let Ok(_) = db.add_task(cmd.args[0].as_str(), "", "") {
|
||||||
|
return format!("");
|
||||||
|
}
|
||||||
|
format!("Failed to add")
|
||||||
|
},
|
||||||
|
"rm" => {
|
||||||
|
if let Ok(_) = db.remove_item(cmd.args[0].as_str(), "") {
|
||||||
|
return format!("");
|
||||||
|
}
|
||||||
|
format!("Failed to remove")
|
||||||
|
}
|
||||||
|
_ => format!(""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// let args : Vec<String> = std::env::args().collect();
|
||||||
|
let filename = "todo.sqlite";
|
||||||
|
|
||||||
|
// if args.len() < 2 {
|
||||||
|
// let db = TodoDb::new(true, filename);
|
||||||
|
// println!("{}", db.all_tables());
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
let db_url = "localhost:8001";
|
||||||
|
let db = TodoDb::new(true, filename);
|
||||||
|
|
||||||
|
let server = Server::http(db_url).unwrap();
|
||||||
|
|
||||||
|
// one request at a time
|
||||||
|
for request in server.incoming_requests() {
|
||||||
|
println!("Incoming request...");
|
||||||
|
|
||||||
|
// parse the request
|
||||||
|
let url = request.url();
|
||||||
|
let query_string = url.split('?').nth(1).unwrap_or("");
|
||||||
|
let mut response_body = format!("");
|
||||||
|
if let Ok(cmd) = parse_query(&format!("{}", query_string)) {
|
||||||
|
// hit the db
|
||||||
|
response_body = handler(&cmd, &db);
|
||||||
|
} else {
|
||||||
|
println!("problem parsing the query string");
|
||||||
|
}
|
||||||
|
|
||||||
|
// repond to client
|
||||||
|
let response = Response::from_string(response_body).with_status_code(200);
|
||||||
|
request.respond(response).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
use rusqlite::{Connection, Result};
|
||||||
|
|
||||||
|
pub struct TodoDb {
|
||||||
|
conn: Connection,
|
||||||
|
// persistent: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct Task {
|
||||||
|
pub id: u32,
|
||||||
|
pub name: String,
|
||||||
|
pub desc: String,
|
||||||
|
pub tag: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct Bucket {
|
||||||
|
pub id: u32,
|
||||||
|
pub name: String,
|
||||||
|
pub category: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TodoDb {
|
||||||
|
pub fn new(persistent: bool, filename: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
conn: TodoDb::init(persistent, filename).unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(persistent: bool, filename: &str) -> Result<Connection> {
|
||||||
|
let conn = if !persistent {
|
||||||
|
Connection::open_in_memory()?
|
||||||
|
} else {
|
||||||
|
Connection::open(filename)? // todo: check for error
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(_) = conn.execute(
|
||||||
|
"CREATE TABLE task (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
desc TEXT,
|
||||||
|
tag TEXT
|
||||||
|
)",
|
||||||
|
(),
|
||||||
|
) {}
|
||||||
|
|
||||||
|
if let Ok(_) = conn.execute(
|
||||||
|
"CREATE TABLE bucket (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
category TEXT
|
||||||
|
)",
|
||||||
|
(),
|
||||||
|
) {}
|
||||||
|
|
||||||
|
Ok(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_task(&self, name: &str, desc: &str, tag: &str) -> Result<u32, String> {
|
||||||
|
let id = self.unique_id(String::from("task"));
|
||||||
|
println!("Adding task: {:?}", name);
|
||||||
|
if self.conn.execute(
|
||||||
|
"INSERT INTO task (id, name, desc, tag) VALUES (?1, ?2, ?3, ?4)",
|
||||||
|
(id, name, desc, tag),
|
||||||
|
).is_err() {
|
||||||
|
return Err(format!("Failed to add task {}", name));
|
||||||
|
};
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_bucket(&self, name: &str, category: &str) -> Result<u32, String> {
|
||||||
|
let id = self.unique_id(String::from("bucket"));
|
||||||
|
println!("Adding bucket: {:?}", name);
|
||||||
|
if self.conn.execute(
|
||||||
|
"INSERT INTO bucket (id, name, category) VALUES (?1, ?2, ?3)",
|
||||||
|
(id, name, category),
|
||||||
|
).is_err() {
|
||||||
|
return Err(format!("Failed to add task {}", name));
|
||||||
|
};
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_item(&self, name: &str, table: &str) -> Result<u32, String> {
|
||||||
|
println!("deleting: {:?} from {:?}", name, table);
|
||||||
|
|
||||||
|
let query = format!("DELETE FROM {} WHERE name = '{}';", table, name);
|
||||||
|
if self.conn.execute(&query, ()).is_err() {
|
||||||
|
return Err(format!("Failed to remove {}", name));
|
||||||
|
};
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unique_id(&self, table: String) -> u32 {
|
||||||
|
let query = format!("SELECT id FROM {} ORDER BY id", table);
|
||||||
|
let mut stmt = self.conn.prepare(&query).unwrap();
|
||||||
|
let ids_iter = stmt.query_map([], |row| {
|
||||||
|
Ok(row.get::<_, u32>(0).unwrap())
|
||||||
|
}).unwrap();
|
||||||
|
let mut count = 0;
|
||||||
|
for id in ids_iter {
|
||||||
|
if count < id.unwrap() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
count
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_tasks(&self) -> Vec<Task> {
|
||||||
|
let mut stmt = self.conn.prepare("SELECT id, name, desc, tag FROM task").unwrap();
|
||||||
|
let iter = stmt.query_map([], |row| {
|
||||||
|
Ok(Task {
|
||||||
|
id: row.get(0)?,
|
||||||
|
name: row.get(1)?,
|
||||||
|
desc: row.get(2)?,
|
||||||
|
tag: row.get(3)?,
|
||||||
|
})
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
for i in iter {
|
||||||
|
vec.push(i.unwrap());
|
||||||
|
}
|
||||||
|
vec
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_buckets(&self) -> Vec<Bucket> {
|
||||||
|
let mut stmt = self.conn.prepare("SELECT id, name, category FROM bucket").unwrap();
|
||||||
|
let iter = stmt.query_map([], |row| {
|
||||||
|
Ok(Bucket {
|
||||||
|
id: row.get(0)?,
|
||||||
|
name: row.get(1)?,
|
||||||
|
category: row.get(2)?,
|
||||||
|
})
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
for i in iter {
|
||||||
|
vec.push(i.unwrap());
|
||||||
|
}
|
||||||
|
vec
|
||||||
|
}
|
||||||
|
|
||||||
|
// could DB read fail?
|
||||||
|
pub fn all_tables(&self) -> String {
|
||||||
|
let mut result = "".to_string();
|
||||||
|
|
||||||
|
// table names
|
||||||
|
let tables = self.table_names();
|
||||||
|
result += "Tables:\n";
|
||||||
|
for table in tables {
|
||||||
|
result += format!("{:?}\n", table).as_str();
|
||||||
|
}
|
||||||
|
result += "\n";
|
||||||
|
|
||||||
|
// Tasks
|
||||||
|
result += format!("Tasks:\n").as_str();
|
||||||
|
let tasks = self.all_tasks();
|
||||||
|
for task in tasks {
|
||||||
|
result += format!("{:?}\n", task).as_str();
|
||||||
|
}
|
||||||
|
result += "\n";
|
||||||
|
|
||||||
|
// buckets
|
||||||
|
result += format!("Buckets:\n").as_str();
|
||||||
|
let buckets = self.all_buckets();
|
||||||
|
for bucket in buckets {
|
||||||
|
result += format!("{:?}\n", bucket).as_str();
|
||||||
|
}
|
||||||
|
result += "\n";
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn table_names(&self) -> Vec<String> {
|
||||||
|
let mut stmt = self.conn.prepare(
|
||||||
|
"SELECT name FROM sqlite_schema WHERE type = 'table'").unwrap();
|
||||||
|
let tables = stmt.query_map([], |row| {
|
||||||
|
Ok(row.get::<_,String>(0)?.to_string())
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
for table in tables {
|
||||||
|
vec.push(table.unwrap());
|
||||||
|
}
|
||||||
|
vec
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user