diff options
-rw-r--r-- | README.md | 46 | ||||
-rw-r--r-- | TODO | 6 | ||||
-rw-r--r-- | src/database.rs | 103 | ||||
-rw-r--r-- | src/database/types.rs | 139 | ||||
-rw-r--r-- | src/main.rs | 52 |
5 files changed, 185 insertions, 161 deletions
@@ -2,56 +2,60 @@ The api is available on `/api`. GET `/` -Gets information about the server + Gets information about the server - Return type: JSON - Returns: <information> GET `/ping` -Pings the server + Pings the server - Return type: JSON - Returns: pong -GET `/create_user/<name>` -Creates a new user with name +POST `/create_user/` + Creates a new user with name +- Data type: JSON +- Data: `String` of wanted username - Return type: JSON -- Returns: <user_id> +- Returns: `UID` of user GET `/get_user/<id>` -Gets a user from id + Gets a user from id - Return type: JSON - Returns: <user> GET `/get_user_by_name/<name>` -Gets a user from name + Gets a user from name - Return type: JSON - Returns: <user> GET `/get_message/<id>` -Gets a message by id + Gets a message by id - Return type: JSON - Returns: <message> GET `/get_message_id_newest` -Gets the newest message id + Gets the newest message id - Return type: JSON - Returns: <message_id> GET `/get_message_id_list/<from_id>/<to_id>` -Gets a list of messages from_id to to_id inclusive + Gets a list of messages from_id to to_id inclusive - Return type: JSON - Returns: <Array<message_id>> -GET `/send_message/<user_id>/<message>` -Sends a message +POST `/send_message` + Sends a message +- Data type: JSON +- Data: + - `"sender": u64` + - `"message": String` + - `"reply_to": Option<u64>` - Return type: JSON -- Returns: <message_id> - -GET `/send_reply/<user_id>/<message_id>/<message>` -Sends a reply to a message -- Return type: JSON -- Returns: <message_id> +- Returns: `UID` of message -GET `/delete_message/<message_id>` -Deletes a message +DELETE `/delete_message` + Deletes a message +- Data type: JSON +- Data: `u64` - Return type: JSON -- Returns: Deleted
\ No newline at end of file +- Returns: `Result`
\ No newline at end of file @@ -1,4 +1,3 @@ -POST Authentication Delete user @@ -7,7 +6,6 @@ Add Channels Add DM'ing Federation would be cool -Rework message format - sender should just be an id Limit get_message_id_list Add get_message_list @@ -15,4 +13,6 @@ Add get_message_list ACLs Config -/api - list functions/routes
\ No newline at end of file +/api - list functions/routes + +Delete is a bit funky
\ No newline at end of file diff --git a/src/database.rs b/src/database.rs index af7de68..84c402f 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,8 +1,7 @@ pub mod r#types; use sqlite::Connection; - -use types::{Message, User}; +use super::*; pub struct Database { @@ -18,24 +17,20 @@ impl Database { let msg = row.read::<&str, _>("message"); let reply_to = row.read::<Option<i64>, _>("reply_to"); let reply_to = match reply_to { - Some(n) => Some(self.i64_into_usize(n)), + Some(n) => Some(n.into()), None => None }; - let date = row.read::<i64, _>("date"); - let date = types::Date::new(date); - + let date = row.read::<i64, _>("date").into(); let deleted = row.read::<i64, _>("deleted"); - let deleted = match deleted { - 0 => false, - _ => true, + let deleted = if deleted < 0 { + false + } else { + true }; - let sender = row.read::<i64, _>("sender"); - let sender = self.get_user(sender as usize).unwrap(); - - let id = row.read::<i64, _>("id"); - let id = self.i64_into_usize(id); + let sender = row.read::<i64, _>("sender").into(); + let id = row.read::<i64, _>("id").into(); let msg = Message::construct(String::from(msg), sender, id, reply_to, date, deleted); @@ -44,49 +39,45 @@ impl Database { None } - pub fn get_message_id_list(&self, id_start: usize, id_end: usize) -> Vec<usize> { - let query = "SELECT id FROM messages WHERE deleted is false AND id BETWEEN :id_start AND :id_end"; + pub fn get_message_id_list(&self, id_start: UID, id_end: UID) -> Vec<UID> { + let query = "SELECT id FROM messages WHERE id BETWEEN :id_start AND :id_end"; let statement = self.db.prepare(query).unwrap().into_iter().bind::<&[(_, sqlite::Value)]>(&[ - (":id_start", (id_start as i64).into()), - (":id_end", (id_end as i64).into()), + (":id_start", id_start.into()), + (":id_end", id_end.into()), ]).unwrap(); - let mut message_id_vec: Vec<usize> = vec![]; + let mut message_id_vec: Vec<UID> = vec![]; for row in statement.map(|row| row.unwrap()) { - let id = row.read::<i64, _>("id"); - let id = self.i64_into_usize(id); + let id = row.read::<i64, _>("id").into(); message_id_vec.push(id); } return message_id_vec; } - pub fn get_next_message_id(&self) -> usize { - self.get_table_length("messages") as usize + pub fn get_next_message_id(&self) -> UID { + UID::from(self.get_table_length("messages")) } - pub fn get_message_newest_id(&self) -> Option<usize> { - match self.get_table_length("messages") as usize { + pub fn get_message_newest_id(&self) -> Option<UID> { + match self.get_table_length("messages") { 0 => None, - n => Some(n - 1), + n => Some(UID::new(n - 1)), } - } - pub fn send_message(&mut self, msg: &Message) -> Result<usize, &'static str> { - let id = msg.id(); - + pub fn send_message(&mut self, msg: &Message) -> Result<UID, &'static str> { let query = "INSERT INTO messages (date, sender, message, id, reply_to, deleted) VALUES (:date, :sender, :message, :id, :reply_to, :deleted)"; let statement = self.db.prepare(query).unwrap().into_iter() .bind::<&[(_, sqlite::Value)]>(&[ - (":date", msg.date_as_i64().into()), - (":sender", msg.user_id().into()), + (":date", msg.date().into()), + (":sender", msg.sender().into()), (":message", msg.message().into()), (":reply_to", match msg.reply_to() { Some(n) => n.into(), None => ().into() }), - (":id", (id as i64).into()), + (":id", msg.id().into()), (":deleted", 0.into()), ]); @@ -101,16 +92,16 @@ impl Database { } } - Ok(id) + Ok(msg.id()) } - pub fn delete_message(&mut self, id: usize) -> Result<&'static str, &'static str> { + pub fn delete_message(&mut self, id: UID) -> Result<&'static str, &'static str> { let query = "UPDATE messages SET message=:message, deleted=:deleted WHERE id IS :id AND deleted IS false"; let statement = self.db.prepare(query).unwrap().into_iter() .bind::<&[(_, sqlite::Value)]>(&[ (":message", "".into()), (":deleted", 1.into()), - (":id", (id as i64).into()), + (":id", id.into()), ]); let change_count = self.db.change_count(); @@ -133,16 +124,14 @@ impl Database { } } - pub fn get_user(&self, id: usize) -> Option<User> { + pub fn get_user(&self, id: UID) -> Option<User> { let query = "SELECT * FROM users WHERE id IS :id"; - let statement = self.db.prepare(query).unwrap().into_iter().bind::<&[(_, sqlite::Value)]>(&[(":id", (id as i64).into())]).unwrap(); + let statement = self.db.prepare(query).unwrap().into_iter().bind::<&[(_, sqlite::Value)]>(&[(":id", id.into())]).unwrap(); for row in statement.map(|row| row.unwrap()) { let username = row.read::<&str, _>("username"); - let id = row.read::<i64, _>("id"); - - let id = self.i64_into_usize(id); + let id = row.read::<i64, _>("id").into(); return Some(User::new(String::from(username), id)) } @@ -155,9 +144,7 @@ impl Database { for row in statement.map(|row| row.unwrap()) { let username = row.read::<&str, _>("username"); - let id = row.read::<i64, _>("id"); - - let id = self.i64_into_usize(id); + let id = row.read::<i64, _>("id").into(); return Some(User::new(String::from(username), id)) } @@ -165,12 +152,12 @@ impl Database { None } - pub fn create_user(&mut self, name: String) -> Result<usize, &'static str> { - let id = self.get_user_count(); + pub fn create_user(&mut self, name: String) -> Result<UID, &'static str> { + let id: UID = self.get_user_count().into(); let query = "INSERT INTO users (id, username) VALUES (:id, :name)"; let statement = self.db.prepare(query).unwrap().into_iter() - .bind::<&[(_, sqlite::Value)]>(&[(":id", (id as i64).into()),(":name", name.into())]); + .bind::<&[(_, sqlite::Value)]>(&[(":id", id.into()),(":name", name.into())]); for x in statement.unwrap() { match x { @@ -187,33 +174,29 @@ impl Database { Ok(id) } - pub fn get_user_count(&self) -> usize { + pub fn get_user_count(&self) -> u64 { self.get_table_length("users") } - fn get_table_length(&self, table: &'static str) -> usize { + fn get_table_length(&self, table: &'static str) -> u64 { let query = format!("SELECT COUNT(*) FROM {}", table); let statement = self.db.prepare(query).unwrap().into_iter(); for row in statement.map(|row| row.unwrap()) { let c = row.read::<i64, _>("COUNT(*)"); - - let c = self.i64_into_usize(c); - + + let c = if c < 0 { + 0 + } else { + c as u64 + }; + return c; } 0 } - fn i64_into_usize(&self, i: i64) -> usize { - if i < 0 { - 0 - } else { - i as usize - } - } - pub fn new() -> Database { let db = match Connection::open("db.sqlite") { Ok(n) => n, diff --git a/src/database/types.rs b/src/database/types.rs index 0d89adb..1a8ab86 100644 --- a/src/database/types.rs +++ b/src/database/types.rs @@ -1,59 +1,99 @@ -use serde::Serialize; +use serde::{Serialize, Deserialize}; use std::time::SystemTime; -#[derive(Serialize, Clone, Debug)] -pub struct Date { - date: u64, -} +#[derive(Deserialize, Serialize, Copy, Clone, Debug)] +// Date is the seconds from UNIX_EPOCH +pub struct Date(u64); impl Date { pub fn now() -> Date { - Date{ - date: SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() - } + Date( + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH).unwrap() + .as_secs() + ) } - pub fn new(s: i64) -> Date { - Date { - date: s as u64, +} +impl std::convert::From<i64> for Date { + fn from(t: i64) -> Date { + if t < 0 { + Date(0) + } else { + Date(t as u64) } } } +impl std::convert::From<Date> for sqlite::Value { + fn from(t: Date) -> sqlite::Value { + (t.0 as i64).into() + } +} #[derive(Serialize, Clone, Debug)] pub struct User { username: String, - id: usize + id: UID, } impl User { - pub fn new(name: String, id: usize) -> User { - User{ - username: name, - id: id + pub fn new(username: String, id: UID) -> User { + User { + username, + id, } } + + pub fn id(&self) -> UID { + self.id + } } -#[derive(Serialize, Clone, Debug)] +#[derive(Deserialize, Serialize, Copy, Clone, Debug)] +pub struct UID(u64); +impl UID { + pub fn new(id: u64) -> UID { + UID(id) + } +} +impl std::convert::From<u64> for UID { + fn from(t: u64) -> UID { + UID(t) + } +} +impl std::convert::From<i64> for UID { + fn from(t: i64) -> UID { + if t < 0 { + UID(0) + } else { + UID(t as u64) + } + } +} +impl std::convert::From<UID> for sqlite::Value { + fn from(t: UID) -> sqlite::Value { + (t.0 as i64).into() + } +} + +#[derive(Deserialize, Serialize, Clone, Debug)] pub struct Message { - date: Date, + id: UID, + sender: UID, message: String, - sender: User, - id: usize, - reply_to: Option<usize>, // Message ID + reply_to: Option<UID>, deleted: bool, + date: Date, } impl Message { - pub fn new(msg: String, sender: User, id: usize, reply_to: Option<usize>) -> Message { + pub fn new(message: String, sender: &User, id: UID, reply_to: Option<UID>) -> Message { Message { - date: Date::now(), - sender: sender, - message: msg, - id: id, - reply_to: reply_to, + id, + sender: sender.id(), + message, + reply_to, deleted: false, + date: Date::now(), } } - - pub fn construct(msg: String, sender: User, id: usize, reply_to: Option<usize>, date: Date, deleted: bool) -> Message { + pub fn construct(msg: String, sender: UID, id: UID, reply_to: Option<UID>, date: Date, deleted: bool) -> Message { Message { date: date, sender: sender, @@ -64,24 +104,20 @@ impl Message { } } - pub fn id(&self) -> usize { + pub fn id(&self) -> UID { self.id } - - pub fn date_as_i64(&self) -> i64 { - self.date.date as i64 - } - pub fn user_id(&self) -> i64 { - self.sender.id as i64 + pub fn sender(&self) -> UID { + self.sender } pub fn message(&self) -> String { self.message.clone() } - pub fn reply_to(&self) -> Option<i64> { - match self.reply_to { - Some(n) => Some(n as i64), - None => None - } + pub fn reply_to(&self) -> Option<UID> { + self.reply_to + } + pub fn date(&self) -> Date { + self.date } } @@ -89,7 +125,7 @@ impl Message { pub struct Info { name: String, version: &'static str, - users: usize, + users: u64, } impl Info { pub fn get(db: std::sync::MutexGuard<crate::database::Database>) -> Info { @@ -99,4 +135,23 @@ impl Info { users: db.get_user_count(), } } +} + +#[derive(Deserialize, Serialize)] +pub struct ReceiveMessage { + sender: UID, + message: String, + reply_to: Option<UID>, +} +impl ReceiveMessage { + pub fn fill(&self, id: UID) -> Message { + Message { + id, + sender: self.sender, + message: self.message.clone(), + reply_to: self.reply_to, + deleted: false, + date: Date::now(), + } + } }
\ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 3e12e1f..9ed4ddc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ mod database; use rocket::serde::json::Json; use rocket::State; use std::sync::Mutex; -use database::types::{Message, User, Info}; +use database::types::{Message, User, Info, UID, ReceiveMessage}; struct SharedDB { @@ -25,9 +25,9 @@ fn get_message(id: usize, db: &State<SharedDB>) -> Json<Response<Message, &'stat } } #[get("/get_message_id_list/<id_start>/<id_end>")] -fn get_message_id_list(id_start: usize, id_end: usize, db: &State<SharedDB>) -> Json<Response<Vec<usize>, &'static str>> { +fn get_message_id_list(id_start: u64, id_end: u64, db: &State<SharedDB>) -> Json<Response<Vec<UID>, &'static str>> { let lock = db.sdb.lock().unwrap(); - let list = lock.get_message_id_list(id_start, id_end); + let list = lock.get_message_id_list(id_start.into(), id_end.into()); if list.len() == 0 { Json(Response(Err("No messages"))) @@ -36,7 +36,7 @@ fn get_message_id_list(id_start: usize, id_end: usize, db: &State<SharedDB>) -> } } #[get("/get_message_id_newest")] -fn get_message_id_newest(db: &State<SharedDB>) -> Json<Response<usize, &'static str>> { +fn get_message_id_newest(db: &State<SharedDB>) -> Json<Response<UID, &'static str>> { let lock = db.sdb.lock().unwrap(); match lock.get_message_newest_id() { Some(n) => Json(Response(Ok(n))), @@ -44,45 +44,28 @@ fn get_message_id_newest(db: &State<SharedDB>) -> Json<Response<usize, &'static } } -#[get("/send_message/<user_id>/<msg>")] -fn send_message(user_id: usize, msg: String, db: &State<SharedDB>) -> Json<Response<usize, &'static str>> { +#[post("/send_message", format = "application/json", data = "<message>")] +fn send_message(message: Json<ReceiveMessage>, db: &State<SharedDB>) -> Json<Response<UID, &'static str>> { let mut lock = db.sdb.lock().unwrap(); - let user = match lock.get_user(user_id) { - Some(n) => n, - None => return Json(Response(Err("User not found"))) - }; - let msg = Message::new(msg, user, lock.get_next_message_id(), None); + let id = lock.get_next_message_id(); + let message = message.fill(id); - let send = lock.send_message(&msg); + let send = lock.send_message(&message); Json(Response(send)) } -#[get("/send_reply/<user_id>/<message_id>/<msg>")] -fn send_reply(user_id: usize, message_id: usize, msg: String, db: &State<SharedDB>) -> Json<Response<usize, &'static str>> { +#[delete("/delete_message", format = "application/json", data = "<id>")] +fn delete_message(id: Json<UID>, db: &State<SharedDB>) -> Json<Response<&'static str, &'static str>> { let mut lock = db.sdb.lock().unwrap(); - let user = match lock.get_user(user_id) { - Some(n) => n, - None => return Json(Response(Err("User not found"))) - }; - let msg = Message::new(msg, user, lock.get_next_message_id(), Some(message_id)); - - let send = lock.send_message(&msg); - Json(Response(send)) -} - -#[get("/delete_message/<id>")] -fn delete_message(id: usize, db: &State<SharedDB>) -> Json<Response<&'static str, &'static str>> { - let mut lock = db.sdb.lock().unwrap(); - - Json(Response(lock.delete_message(id))) + Json(Response(lock.delete_message(id.0))) } #[get("/get_user/<id>")] -fn get_user(id: usize, db: &State<SharedDB>) -> Json<Response<User, &'static str>> { +fn get_user(id: u64, db: &State<SharedDB>) -> Json<Response<User, &'static str>> { let lock = db.sdb.lock().unwrap(); - match lock.get_user(id) { + match lock.get_user(id.into()) { Some(n) => Json(Response(Ok(n))), None => Json(Response(Err("User not found"))) } @@ -95,11 +78,11 @@ fn get_user_by_name(name: String, db: &State<SharedDB>) -> Json<Response<User, & None => Json(Response(Err("User not found"))) } } -#[get("/create_user/<name>")] -fn create_user(name: String, db: &State<SharedDB>) -> Json<Response<usize, &'static str>> { +#[post("/create_user", format = "application/json", data = "<name>")] +fn create_user(name: Json<String>, db: &State<SharedDB>) -> Json<Response<UID, &'static str>> { let mut lock = db.sdb.lock().unwrap(); - Json(Response(lock.create_user(name))) + Json(Response(lock.create_user(name.0))) } #[get("/ping")] @@ -131,7 +114,6 @@ fn rocket() -> _ { create_user, get_user_by_name, get_message_id_newest, - send_reply, delete_message, ]) .mount("/", routes![api_index]) |