This commit is contained in:
2026-07-25 20:30:24 -04:00
commit 20a6a70c30
9 changed files with 2145 additions and 0 deletions
+117
View File
@@ -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();
}
}
+188
View File
@@ -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
}
}