From b92308da67cfa78bd3c975861747f30932875ca1 Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Sun, 3 Oct 2021 14:03:32 -0600 Subject: [PATCH] First pass of an API + Implemented register, find, and add_location + Bunch of changes to the DB and the Models to make this work + API models are defined in GeoffreyModels so things that call the API don't need to define their own types + Still needs a lot of work, and need to design the api a bit more + Clippy + fmt --- .gitignore | 1 + geoffrey_api/src/commands/add_location.rs | 48 +++++++++ geoffrey_api/src/commands/find.rs | 43 ++++++++ geoffrey_api/src/commands/mod.rs | 64 ++++++++++++ geoffrey_api/src/commands/register.rs | 35 +++++++ geoffrey_api/src/config.rs | 18 ++++ geoffrey_api/src/context.rs | 20 ++++ geoffrey_api/src/helper/mod.rs | 16 +++ geoffrey_api/src/main.rs | 24 ++--- geoffrey_db/src/database.rs | 97 ++++++++++++------- geoffrey_db/src/error.rs | 2 +- geoffrey_db/src/lib.rs | 2 +- geoffrey_models/src/lib.rs | 3 +- geoffrey_models/src/models/item.rs | 9 +- geoffrey_models/src/models/locations/farm.rs | 4 +- .../src/models/locations/market.rs | 5 +- geoffrey_models/src/models/locations/mod.rs | 59 ++++++++--- geoffrey_models/src/models/locations/shop.rs | 3 +- geoffrey_models/src/models/locations/town.rs | 4 +- geoffrey_models/src/models/meta.rs | 4 +- geoffrey_models/src/models/mod.rs | 24 +++-- .../models/parameters/add_location_params.rs | 11 +++ .../src/models/parameters/find_params.rs | 6 ++ geoffrey_models/src/models/parameters/mod.rs | 38 ++++++++ .../src/models/parameters/register_params.rs | 8 ++ geoffrey_models/src/models/player.rs | 19 ++-- .../src/models/response/api_error.rs | 9 ++ geoffrey_models/src/models/response/mod.rs | 10 ++ geoffrey_models/src/models/token.rs | 38 ++++---- 29 files changed, 507 insertions(+), 117 deletions(-) create mode 100644 geoffrey_api/src/commands/add_location.rs create mode 100644 geoffrey_api/src/commands/find.rs create mode 100644 geoffrey_api/src/commands/mod.rs create mode 100644 geoffrey_api/src/commands/register.rs create mode 100644 geoffrey_api/src/config.rs create mode 100644 geoffrey_api/src/context.rs create mode 100644 geoffrey_api/src/helper/mod.rs create mode 100644 geoffrey_models/src/models/parameters/add_location_params.rs create mode 100644 geoffrey_models/src/models/parameters/find_params.rs create mode 100644 geoffrey_models/src/models/parameters/mod.rs create mode 100644 geoffrey_models/src/models/parameters/register_params.rs create mode 100644 geoffrey_models/src/models/response/api_error.rs create mode 100644 geoffrey_models/src/models/response/mod.rs diff --git a/.gitignore b/.gitignore index 40e9e7b..fe0dde8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /database /test_database config.toml +*.http diff --git a/geoffrey_api/src/commands/add_location.rs b/geoffrey_api/src/commands/add_location.rs new file mode 100644 index 0000000..1ff0147 --- /dev/null +++ b/geoffrey_api/src/commands/add_location.rs @@ -0,0 +1,48 @@ +use crate::commands::{Command, RequestType}; +use crate::context::Context; +use crate::helper::get_player_from_req; +use crate::Result; +use geoffrey_models::models::locations::Location; +use geoffrey_models::models::parameters::add_location_params::AddLocationParams; +use geoffrey_models::models::parameters::CommandRequest; +use geoffrey_models::models::response::api_error::GeoffreyAPIError; +use geoffrey_models::models::CommandLevel; +use std::sync::Arc; + +pub struct AddLocation {} + +impl Command for AddLocation { + type Req = CommandRequest; + type Resp = Location; + + fn command_name() -> String { + "add_location".to_string() + } + + fn request_type() -> RequestType { + RequestType::POST + } + + fn command_level() -> CommandLevel { + CommandLevel::REGISTERED + } + + fn run_command(ctx: Arc, req: Self::Req) -> Result { + if let Some(player) = get_player_from_req(&ctx.db, &req)? { + let args = &req.arguments; + let location = Location::new( + args.name.as_str(), + args.position, + player.id.unwrap(), + args.tunnel.clone(), + args.loc_type.clone(), + ); + + ctx.db + .insert(location) + .map_err(|err| GeoffreyAPIError::DatabaseError(err.to_string())) + } else { + Err(GeoffreyAPIError::PlayerNotFound) + } + } +} diff --git a/geoffrey_api/src/commands/find.rs b/geoffrey_api/src/commands/find.rs new file mode 100644 index 0000000..20cd859 --- /dev/null +++ b/geoffrey_api/src/commands/find.rs @@ -0,0 +1,43 @@ +use crate::commands::{Command, RequestType}; +use crate::context::Context; +use crate::Result; +use geoffrey_models::models::locations::Location; +use geoffrey_models::models::parameters::find_params::FindParams; +use geoffrey_models::models::parameters::CommandRequest; +use geoffrey_models::models::response::api_error::GeoffreyAPIError; +use geoffrey_models::models::CommandLevel; +use std::sync::Arc; + +pub struct FindCommand {} + +impl Command for FindCommand { + type Req = CommandRequest; + type Resp = Vec; + + fn command_name() -> String { + "find".to_string() + } + + fn request_type() -> RequestType { + RequestType::GET + } + + fn command_level() -> CommandLevel { + CommandLevel::ALL + } + + fn run_command(ctx: Arc, req: Self::Req) -> Result { + let locations = ctx + .db + .filter(|_, loc: &Location| { + let name = loc.name.to_lowercase(); + let query = req.arguments.query.to_lowercase(); + + name.contains(&query) + }) + .map_err(|err| GeoffreyAPIError::DatabaseError(err.to_string()))? + .collect(); + + Ok(locations) + } +} diff --git a/geoffrey_api/src/commands/mod.rs b/geoffrey_api/src/commands/mod.rs new file mode 100644 index 0000000..741744d --- /dev/null +++ b/geoffrey_api/src/commands/mod.rs @@ -0,0 +1,64 @@ +use crate::commands::add_location::AddLocation; +use crate::commands::find::FindCommand; +use crate::commands::register::Register; +use crate::context::Context; +use crate::Result; +use geoffrey_models::models::response::APIResponse; +use geoffrey_models::models::CommandLevel; +use serde::de::DeserializeOwned; +use serde::Serialize; +use std::sync::Arc; +use warp::filters::BoxedFilter; +use warp::Filter; + +pub mod add_location; +pub mod find; +pub mod register; + +#[derive(Debug, Clone, PartialEq)] +#[allow(clippy::upper_case_acronyms)] +pub enum RequestType { + POST, + GET, +} + +pub trait Command { + type Req: Serialize + DeserializeOwned + Send + 'static; + type Resp: Serialize + DeserializeOwned + Send; + + fn command_name() -> String; + fn request_type() -> RequestType; + fn command_level() -> CommandLevel; + fn run_command(ctx: Arc, req: Self::Req) -> Result; +} + +#[allow(clippy::needless_return)] +pub fn create_command_filter(ctx: Arc) -> BoxedFilter<(impl warp::Reply,)> { + let filter = warp::path(T::command_name()) + .and(warp::any().map(move || ctx.clone())) + .and(warp::body::json()) + .map(|ctx: Arc, req: T::Req| { + let reply = T::run_command(ctx, req); + if let Ok(reply) = reply { + warp::reply::json(&APIResponse::Response::(reply)) + } else { + warp::reply::json(&APIResponse::::Error(reply.err().unwrap())) + } + }); + + if T::request_type() == RequestType::POST { + return filter.and(warp::post()).boxed(); + } else { + return filter.and(warp::get()).boxed(); + } +} + +pub fn command_filter( + ctx: Arc, +) -> impl Filter + Clone { + warp::path("command").and( + create_command_filter::(ctx.clone()) + .or(create_command_filter::(ctx.clone())) + .or(create_command_filter::(ctx)), + ) +} diff --git a/geoffrey_api/src/commands/register.rs b/geoffrey_api/src/commands/register.rs new file mode 100644 index 0000000..4ec6d43 --- /dev/null +++ b/geoffrey_api/src/commands/register.rs @@ -0,0 +1,35 @@ +use crate::commands::{Command, RequestType}; +use crate::context::Context; +use geoffrey_models::models::parameters::register_params::RegisterParameters; +use geoffrey_models::models::parameters::CommandRequest; +use geoffrey_models::models::player::Player; +use geoffrey_models::models::response::api_error::GeoffreyAPIError; +use geoffrey_models::models::CommandLevel; +use std::sync::Arc; + +pub struct Register {} + +impl Command for Register { + type Req = CommandRequest; + type Resp = Player; + + fn command_name() -> String { + "register".to_string() + } + + fn request_type() -> RequestType { + RequestType::POST + } + + fn command_level() -> CommandLevel { + CommandLevel::ALL + } + + fn run_command(ctx: Arc, req: Self::Req) -> crate::Result { + let player = Player::new(req.arguments.username.as_str(), req.arguments.user_id); + + ctx.db + .insert(player) + .map_err(|err| GeoffreyAPIError::DatabaseError(err.to_string())) + } +} diff --git a/geoffrey_api/src/config.rs b/geoffrey_api/src/config.rs new file mode 100644 index 0000000..2ba58f2 --- /dev/null +++ b/geoffrey_api/src/config.rs @@ -0,0 +1,18 @@ +use config::{Config, ConfigError, File}; +use serde::Deserialize; +use std::path::{Path, PathBuf}; + +#[derive(Debug, Deserialize, Clone)] +pub struct GeoffreyAPIConfig { + pub db_path: PathBuf, + pub host: String, +} + +impl GeoffreyAPIConfig { + pub fn new(config_path: &Path) -> Result { + let mut cfg = Config::new(); + cfg.merge(File::from(config_path.to_path_buf()))?; + + cfg.try_into() + } +} diff --git a/geoffrey_api/src/context.rs b/geoffrey_api/src/context.rs new file mode 100644 index 0000000..9174762 --- /dev/null +++ b/geoffrey_api/src/context.rs @@ -0,0 +1,20 @@ +use crate::config::GeoffreyAPIConfig; +use crate::Result; +use geoffrey_db::database::Database; +use std::sync::Arc; + +pub struct Context { + pub db: Database, + pub cfg: GeoffreyAPIConfig, +} + +impl Context { + pub fn new(cfg: GeoffreyAPIConfig) -> Result> { + let ctx = Self { + db: Database::new(cfg.db_path.as_path()).unwrap(), + cfg, + }; + + Ok(Arc::new(ctx)) + } +} diff --git a/geoffrey_api/src/helper/mod.rs b/geoffrey_api/src/helper/mod.rs new file mode 100644 index 0000000..70d7d9a --- /dev/null +++ b/geoffrey_api/src/helper/mod.rs @@ -0,0 +1,16 @@ +use crate::Result; +use geoffrey_db::database::Database; +use geoffrey_models::models::parameters::CommandRequest; +use geoffrey_models::models::player::Player; +use geoffrey_models::models::response::api_error::GeoffreyAPIError; + +pub fn get_player_from_req(db: &Database, req: &CommandRequest) -> Result> { + if let Some(user_id) = &req.user { + Ok(db + .filter(|_, player: &Player| player.has_user_id(user_id)) + .map_err(|err| GeoffreyAPIError::DatabaseError(err.to_string()))? + .next()) + } else { + Ok(None) + } +} diff --git a/geoffrey_api/src/main.rs b/geoffrey_api/src/main.rs index e4f948a..57ea752 100644 --- a/geoffrey_api/src/main.rs +++ b/geoffrey_api/src/main.rs @@ -1,16 +1,18 @@ mod commands; -mod error; -mod context; mod config; -mod responses; +mod context; +mod helper; -use structopt::StructOpt; -use std::path::PathBuf; -use crate::config::GeoffreyAPIConfig; -use std::net::SocketAddr; -use std::str::FromStr; -use crate::context::Context; use crate::commands::command_filter; +use crate::config::GeoffreyAPIConfig; +use crate::context::Context; +use geoffrey_models::models::response::api_error::GeoffreyAPIError; +use std::net::SocketAddr; +use std::path::PathBuf; +use std::str::FromStr; +use structopt::StructOpt; + +pub type Result = std::result::Result; #[derive(Debug, StructOpt, Clone)] #[structopt(name = "GeoffreyAPI", about = "Geoffrey Central API")] @@ -23,7 +25,7 @@ struct Args { async fn main() { let args: Args = Args::from_args(); - let cfg= GeoffreyAPIConfig::new(args.config.as_path()).unwrap(); + let cfg = GeoffreyAPIConfig::new(args.config.as_path()).unwrap(); let ctx = Context::new(cfg).unwrap(); @@ -32,4 +34,4 @@ async fn main() { warp::serve(api) .run(SocketAddr::from_str(ctx.cfg.host.as_str()).unwrap()) .await; -} \ No newline at end of file +} diff --git a/geoffrey_db/src/database.rs b/geoffrey_db/src/database.rs index e958879..af33b8a 100644 --- a/geoffrey_db/src/database.rs +++ b/geoffrey_db/src/database.rs @@ -1,7 +1,7 @@ -use crate::error::{Result, GeoffreyDBError}; -use std::path::Path; +use crate::error::{GeoffreyDBError, Result}; use geoffrey_models::GeoffreyDatabaseModel; use std::convert::TryInto; +use std::path::Path; pub struct Database { db: sled::Db, @@ -11,16 +11,20 @@ impl Database { pub fn new(db_path: &Path) -> Result { let db = sled::open(db_path)?; - Ok(Self { - db, - }) + Ok(Self { db }) } - fn get_tree(&self) -> Result where T: GeoffreyDatabaseModel { + fn get_tree(&self) -> Result + where + T: GeoffreyDatabaseModel, + { Ok(self.db.open_tree::(T::tree())?) } - pub fn insert(&self, mut model: T) -> Result where T: GeoffreyDatabaseModel { + pub fn insert(&self, mut model: T) -> Result + where + T: GeoffreyDatabaseModel, + { let id = match model.id() { Some(id) => id, None => { @@ -30,12 +34,10 @@ impl Database { } }; - let match_count = self.filter(|_, o: &T| { - !o.check_unique(&model) - })?.count(); + let match_count = self.filter(|_, o: &T| !o.check_unique(&model))?.count(); if match_count > 0 { - return Err(GeoffreyDBError::NotUnique) + return Err(GeoffreyDBError::NotUnique); } let tree = self.get_tree::()?; @@ -46,25 +48,34 @@ impl Database { Ok(model) } - - pub fn get(&self, id: u64) -> Result> where T: GeoffreyDatabaseModel { + pub fn get(&self, id: u64) -> Result> + where + T: GeoffreyDatabaseModel, + { let tree = self.get_tree::()?; let id_bytes = id.to_be_bytes(); if let Some(bytes) = tree.get(id_bytes)? { Ok(Some(T::try_from_bytes(&bytes)?)) - } - else { + } else { Ok(None) } - } - pub fn clear_tree(&self) -> Result<()> where T: GeoffreyDatabaseModel { + pub fn clear_tree(&self) -> Result<()> + where + T: GeoffreyDatabaseModel, + { Ok(self.db.open_tree(T::tree())?.clear()?) } - pub fn filter<'a, T>(&self, f: impl Fn(u64, &T) -> bool + 'a) -> Result + 'a> where T: GeoffreyDatabaseModel { + pub fn filter<'a, T>( + &self, + f: impl Fn(u64, &T) -> bool + 'a, + ) -> Result + 'a> + where + T: GeoffreyDatabaseModel, + { let tree = self.db.open_tree(T::tree())?; Ok(tree.iter().filter_map(move |e| { @@ -74,31 +85,32 @@ impl Database { if f(id, &data) { Some(data) - } - else { + } else { None } - } - else { + } else { None } })) } - pub fn tree_iter(&self) -> Result where T: GeoffreyDatabaseModel { + pub fn tree_iter(&self) -> Result + where + T: GeoffreyDatabaseModel, + { Ok(self.db.open_tree(T::tree()).map(|tree| tree.iter())?) } } #[cfg(test)] mod tests { - use crate::database::{Database}; - use std::path::Path; - use geoffrey_models::models::player::{Player, UserID}; - use lazy_static::lazy_static; - use geoffrey_models::GeoffreyDatabaseModel; + use crate::database::Database; use geoffrey_models::models::locations::{Location, LocationType}; - use geoffrey_models::models::{Position, Dimension}; + use geoffrey_models::models::player::{Player, UserID}; + use geoffrey_models::models::{Dimension, Position}; + use geoffrey_models::GeoffreyDatabaseModel; + use lazy_static::lazy_static; + use std::path::Path; use std::time::Instant; lazy_static! { @@ -120,7 +132,6 @@ mod tests { assert!(p2.id().is_some()); assert_eq!(player.name, p2.name); - cleanup(); } @@ -152,13 +163,22 @@ mod tests { fn test_filter() { let player = Player::new("CoolZero123", UserID::DiscordUUID(0u64)); let player = DB.insert::(player.clone()).unwrap(); - let loc = Location::new("Test Shop", Position::new(0, 0, Dimension::Overworld), player.id.unwrap(), None, LocationType::Shop(0)); + let loc = Location::new( + "Test Shop", + Position::new(0, 0, Dimension::Overworld), + player.id.unwrap(), + None, + LocationType::Shop(0), + ); let loc = DB.insert::(loc.clone()).unwrap(); - let count = DB.filter(|id: u64, l: &Location| { - assert_eq!(id, l.id().unwrap()); - loc.id().unwrap() == id - }).unwrap().count(); + let count = DB + .filter(|id: u64, l: &Location| { + assert_eq!(id, l.id().unwrap()); + loc.id().unwrap() == id + }) + .unwrap() + .count(); assert_eq!(count, 1); DB.db.flush().unwrap(); @@ -178,9 +198,12 @@ mod tests { DB.db.flush().unwrap(); let sec_elapsed = timer.elapsed().as_secs_f32(); - println!("Completed in {}s. {} inserts per second", sec_elapsed, insert_count as f32/sec_elapsed); + println!( + "Completed in {}s. {} inserts per second", + sec_elapsed, + insert_count as f32 / sec_elapsed + ); cleanup() } } - diff --git a/geoffrey_db/src/error.rs b/geoffrey_db/src/error.rs index 173f18f..36d086d 100644 --- a/geoffrey_db/src/error.rs +++ b/geoffrey_db/src/error.rs @@ -14,7 +14,7 @@ impl std::fmt::Display for GeoffreyDBError { match self { GeoffreyDBError::SledError(e) => write!(f, "Sled Error: {}", e), GeoffreyDBError::SerdeJsonError(e) => write!(f, "Serde JSON Error: {}", e), - GeoffreyDBError::NotUnique => write!(f, "Entry is not unique.") + GeoffreyDBError::NotUnique => write!(f, "Entry is not unique."), } } } diff --git a/geoffrey_db/src/lib.rs b/geoffrey_db/src/lib.rs index cad3bef..0b6be88 100644 --- a/geoffrey_db/src/lib.rs +++ b/geoffrey_db/src/lib.rs @@ -1,4 +1,4 @@ #![allow(dead_code)] + pub mod database; pub mod error; - diff --git a/geoffrey_models/src/lib.rs b/geoffrey_models/src/lib.rs index cb31f64..dd322be 100644 --- a/geoffrey_models/src/lib.rs +++ b/geoffrey_models/src/lib.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] -use serde::Serialize; use serde::de::DeserializeOwned; +use serde::Serialize; pub mod models; @@ -31,4 +31,3 @@ pub trait GeoffreyDatabaseModel: Serialize + DeserializeOwned { serde_json::from_slice(b) } } - diff --git a/geoffrey_models/src/models/item.rs b/geoffrey_models/src/models/item.rs index ccd2d1d..66cd98f 100644 --- a/geoffrey_models/src/models/item.rs +++ b/geoffrey_models/src/models/item.rs @@ -9,7 +9,7 @@ pub struct Item { impl Item { pub fn new(name: &str) -> Self { Self { - name: name.to_string() + name: name.to_string(), } } } @@ -19,17 +19,18 @@ pub struct ItemListing { pub item: Item, pub price: u32, pub count_per_price: u32, - pub restocked_time: DateTime + pub restocked_time: DateTime, } impl ItemListing { fn new(item: &str, price: u32, count_per_price: u32, restocked_time: DateTime) -> Self { Self { - item: Item {name: item.to_string()}, + item: Item { + name: item.to_string(), + }, price, count_per_price, restocked_time, } } } - diff --git a/geoffrey_models/src/models/locations/farm.rs b/geoffrey_models/src/models/locations/farm.rs index acb5005..1d897bd 100644 --- a/geoffrey_models/src/models/locations/farm.rs +++ b/geoffrey_models/src/models/locations/farm.rs @@ -1,12 +1,12 @@ -use serde::{Serialize, Deserialize}; use crate::models::item::Item; use crate::GeoffreyDatabaseModel; +use serde::{Deserialize, Serialize}; use std::collections::HashSet; #[derive(Serialize, Deserialize, Debug, Clone, Default)] pub struct FarmData { id: Option, - pub items_produced: HashSet + pub items_produced: HashSet, } impl GeoffreyDatabaseModel for FarmData { diff --git a/geoffrey_models/src/models/locations/market.rs b/geoffrey_models/src/models/locations/market.rs index e58f9fa..68ef9d6 100644 --- a/geoffrey_models/src/models/locations/market.rs +++ b/geoffrey_models/src/models/locations/market.rs @@ -1,11 +1,11 @@ -use serde::{Serialize, Deserialize}; use crate::GeoffreyDatabaseModel; +use serde::{Deserialize, Serialize}; use std::collections::HashSet; #[derive(Serialize, Deserialize, Debug, Clone, Default)] pub struct Market { pub id: Option, - pub shops: HashSet + pub shops: HashSet, } impl GeoffreyDatabaseModel for Market { @@ -21,4 +21,3 @@ impl GeoffreyDatabaseModel for Market { "market".to_string() } } - diff --git a/geoffrey_models/src/models/locations/mod.rs b/geoffrey_models/src/models/locations/mod.rs index a8e66e8..69c97f3 100644 --- a/geoffrey_models/src/models/locations/mod.rs +++ b/geoffrey_models/src/models/locations/mod.rs @@ -1,15 +1,14 @@ -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; use std::collections::HashSet; -use crate::GeoffreyDatabaseModel; use crate::models::{Position, Tunnel}; +use crate::GeoffreyDatabaseModel; pub mod farm; pub mod market; pub mod shop; pub mod town; - #[derive(Serialize, Deserialize, Debug, Clone)] pub enum LocationType { Base, @@ -27,11 +26,17 @@ pub struct Location { pub position: Position, owners: HashSet, pub tunnel: Option, - loc_type: LocationType + loc_type: LocationType, } impl Location { - pub fn new (name: &str, position: Position, owner: u64, tunnel: Option, loc_type: LocationType) -> Self { + pub fn new( + name: &str, + position: Position, + owner: u64, + tunnel: Option, + loc_type: LocationType, + ) -> Self { let mut owners = HashSet::new(); owners.insert(owner); @@ -41,19 +46,19 @@ impl Location { position, owners, tunnel, - loc_type + loc_type, } } - fn owners(&self) -> Vec { + pub fn owners(&self) -> Vec { self.owners.iter().cloned().collect() } - fn add_owner(&mut self, owner: u64) { + pub fn add_owner(&mut self, owner: u64) { self.owners.insert(owner); } - fn remove_owner(&mut self, owner: u64) { + pub fn remove_owner(&mut self, owner: u64) { self.owners.remove(&owner); } } @@ -78,19 +83,43 @@ impl GeoffreyDatabaseModel for Location { #[cfg(test)] mod tests { - use crate::GeoffreyDatabaseModel; use crate::models::locations::{Location, LocationType}; - use crate::models::{Position, Dimension}; + use crate::models::{Dimension, Position}; + use crate::GeoffreyDatabaseModel; #[test] fn test_location_check_unique() { - let l1 = Location::new("Test", Position::new(0, 0, Dimension::Overworld), 0u64, None, LocationType::Base); - let l2 = Location::new("NotTest", Position::new(0, 0, Dimension::Overworld), 0u64, None, LocationType::Base); + let l1 = Location::new( + "Test", + Position::new(0, 0, Dimension::Overworld), + 0u64, + None, + LocationType::Base, + ); + let l2 = Location::new( + "NotTest", + Position::new(0, 0, Dimension::Overworld), + 0u64, + None, + LocationType::Base, + ); assert!(l1.check_unique(&l2)); - let l1 = Location::new("Test", Position::new(0, 0, Dimension::Overworld), 0u64, None, LocationType::Base); - let l2 = Location::new("teSt", Position::new(0, 0, Dimension::Overworld), 0u64, None, LocationType::Base); + let l1 = Location::new( + "Test", + Position::new(0, 0, Dimension::Overworld), + 0u64, + None, + LocationType::Base, + ); + let l2 = Location::new( + "teSt", + Position::new(0, 0, Dimension::Overworld), + 0u64, + None, + LocationType::Base, + ); assert!(!l1.check_unique(&l2)); } diff --git a/geoffrey_models/src/models/locations/shop.rs b/geoffrey_models/src/models/locations/shop.rs index e2a3eb3..a3e0dc9 100644 --- a/geoffrey_models/src/models/locations/shop.rs +++ b/geoffrey_models/src/models/locations/shop.rs @@ -2,8 +2,8 @@ use std::collections::HashSet; use serde::{Deserialize, Serialize}; -use crate::GeoffreyDatabaseModel; use crate::models::item::ItemListing; +use crate::GeoffreyDatabaseModel; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Shop { @@ -24,4 +24,3 @@ impl GeoffreyDatabaseModel for Shop { "shop".to_string() } } - diff --git a/geoffrey_models/src/models/locations/town.rs b/geoffrey_models/src/models/locations/town.rs index deb4cbd..4413d56 100644 --- a/geoffrey_models/src/models/locations/town.rs +++ b/geoffrey_models/src/models/locations/town.rs @@ -1,11 +1,11 @@ -use serde::{Deserialize, Serialize}; use crate::GeoffreyDatabaseModel; +use serde::{Deserialize, Serialize}; use std::collections::HashSet; #[derive(Serialize, Deserialize, Debug, Clone, Default)] pub struct Town { id: Option, - pub residents: HashSet + pub residents: HashSet, } impl GeoffreyDatabaseModel for Town { diff --git a/geoffrey_models/src/models/meta.rs b/geoffrey_models/src/models/meta.rs index 25804cf..b60ae08 100644 --- a/geoffrey_models/src/models/meta.rs +++ b/geoffrey_models/src/models/meta.rs @@ -1,5 +1,5 @@ -use serde::{Deserialize, Serialize}; use crate::GeoffreyDatabaseModel; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone)] struct Meta { @@ -20,4 +20,4 @@ impl GeoffreyDatabaseModel for Meta { fn tree() -> String { "meta".to_string() } -} \ No newline at end of file +} diff --git a/geoffrey_models/src/models/mod.rs b/geoffrey_models/src/models/mod.rs index b6f2403..933b40d 100644 --- a/geoffrey_models/src/models/mod.rs +++ b/geoffrey_models/src/models/mod.rs @@ -1,16 +1,18 @@ use serde::{Deserialize, Serialize}; -pub mod player; pub mod item; -pub mod token; -pub mod meta; pub mod locations; +pub mod meta; +pub mod parameters; +pub mod player; +pub mod response; +pub mod token; #[derive(Serialize, Deserialize, Debug, Copy, Clone)] pub enum Dimension { Overworld, Nether, - TheEnd + TheEnd, } impl Default for Dimension { @@ -36,17 +38,19 @@ pub struct Position { impl Position { pub fn new(x: i32, y: i32, dimension: Dimension) -> Self { - Self { - x, - y, - dimension - } + Self { x, y, dimension } } } #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Tunnel { direction: Direction, - number: i32 + number: i32, } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Ord, PartialOrd, Eq, Hash)] +pub enum CommandLevel { + ALL = 0, + REGISTERED = 1, + ADMIN = 2, +} diff --git a/geoffrey_models/src/models/parameters/add_location_params.rs b/geoffrey_models/src/models/parameters/add_location_params.rs new file mode 100644 index 0000000..f7b74d9 --- /dev/null +++ b/geoffrey_models/src/models/parameters/add_location_params.rs @@ -0,0 +1,11 @@ +use crate::models::locations::LocationType; +use crate::models::{Position, Tunnel}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct AddLocationParams { + pub name: String, + pub position: Position, + pub loc_type: LocationType, + pub tunnel: Option, +} diff --git a/geoffrey_models/src/models/parameters/find_params.rs b/geoffrey_models/src/models/parameters/find_params.rs new file mode 100644 index 0000000..a03b593 --- /dev/null +++ b/geoffrey_models/src/models/parameters/find_params.rs @@ -0,0 +1,6 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct FindParams { + pub query: String, +} diff --git a/geoffrey_models/src/models/parameters/mod.rs b/geoffrey_models/src/models/parameters/mod.rs new file mode 100644 index 0000000..845a48e --- /dev/null +++ b/geoffrey_models/src/models/parameters/mod.rs @@ -0,0 +1,38 @@ +pub mod add_location_params; +pub mod find_params; +pub mod register_params; + +use crate::models::player::{Player, UserID}; +use crate::models::token::{Permissions, Token}; +use crate::models::CommandLevel; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct CommandRequest { + pub user: Option, + pub arguments: T, + pub token: u64, +} + +impl CommandRequest { + fn has_user_id(&self) -> bool { + self.user.is_some() + } + + fn check_permission( + &self, + player: &Player, + command_level: &CommandLevel, + token: &Token, + ) -> bool { + if player.auth_level >= *command_level { + if *command_level == CommandLevel::ADMIN { + token.check_permission(Permissions::Admin) + } else { + true + } + } else { + false + } + } +} diff --git a/geoffrey_models/src/models/parameters/register_params.rs b/geoffrey_models/src/models/parameters/register_params.rs new file mode 100644 index 0000000..0c88735 --- /dev/null +++ b/geoffrey_models/src/models/parameters/register_params.rs @@ -0,0 +1,8 @@ +use crate::models::player::UserID; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct RegisterParameters { + pub username: String, + pub user_id: UserID, +} diff --git a/geoffrey_models/src/models/player.rs b/geoffrey_models/src/models/player.rs index 0892121..d70b2ec 100644 --- a/geoffrey_models/src/models/player.rs +++ b/geoffrey_models/src/models/player.rs @@ -1,17 +1,19 @@ -use serde::{Deserialize, Serialize}; +use crate::models::CommandLevel; use crate::GeoffreyDatabaseModel; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)] pub enum UserID { - DiscordUUID(u64), - MinecraftUUID(String), + DiscordUUID { discord_uuid: u64 }, + MinecraftUUID { mc_uuid: String }, } #[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)] pub struct Player { pub id: Option, pub name: String, - pub user_ids: Vec + pub user_ids: Vec, + pub auth_level: CommandLevel, } impl Player { @@ -19,9 +21,14 @@ impl Player { Self { id: None, name: name.to_string(), - user_ids: vec![user_id] + user_ids: vec![user_id], + auth_level: CommandLevel::REGISTERED, } } + + pub fn has_user_id(&self, user_id: &UserID) -> bool { + self.user_ids.iter().any(|id| id == user_id) + } } impl GeoffreyDatabaseModel for Player { @@ -66,4 +73,4 @@ mod tests { assert!(!p1.check_unique(&p2)); } -} \ No newline at end of file +} diff --git a/geoffrey_models/src/models/response/api_error.rs b/geoffrey_models/src/models/response/api_error.rs new file mode 100644 index 0000000..c27f10e --- /dev/null +++ b/geoffrey_models/src/models/response/api_error.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub enum GeoffreyAPIError { + PlayerNotFound, + LocationNotFound, + PermissionInsufficient, + DatabaseError(String), +} diff --git a/geoffrey_models/src/models/response/mod.rs b/geoffrey_models/src/models/response/mod.rs new file mode 100644 index 0000000..6f95cf2 --- /dev/null +++ b/geoffrey_models/src/models/response/mod.rs @@ -0,0 +1,10 @@ +use crate::models::response::api_error::GeoffreyAPIError; +use serde::{Deserialize, Serialize}; + +pub mod api_error; + +#[derive(Debug, Serialize, Deserialize)] +pub enum APIResponse { + Response(T), + Error(GeoffreyAPIError), +} diff --git a/geoffrey_models/src/models/token.rs b/geoffrey_models/src/models/token.rs index a79311a..592800a 100644 --- a/geoffrey_models/src/models/token.rs +++ b/geoffrey_models/src/models/token.rs @@ -1,6 +1,6 @@ -use serde::{Deserialize, Serialize}; -use chrono::{DateTime, Utc}; use crate::GeoffreyDatabaseModel; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; pub enum Permissions { ModelGet = 0, @@ -11,29 +11,20 @@ pub enum Permissions { } #[derive(Serialize, Deserialize, Debug, Clone)] -struct Token { +pub struct Token { pub id: Option, permission: u64, pub created: DateTime, - pub modified: DateTime + pub modified: DateTime, } impl Token { - pub fn new() -> Self { - Self { - id: None, - permission: 0, - created: Utc::now(), - modified: Utc::now() - } - } - pub fn set_permission(&mut self, permission: Permissions) { - self.permission = self.permission | (1u64 << permission as u32); + self.permission |= 1u64 << permission as u32; } pub fn clear_permission(&mut self, permission: Permissions) { - self.permission = self.permission & !(1u64 << permission as u32); + self.permission &= !(1u64 << permission as u32); } pub fn check_permission(&self, permission: Permissions) -> bool { @@ -45,6 +36,17 @@ impl Token { } } +impl Default for Token { + fn default() -> Self { + Self { + id: None, + permission: 0, + created: Utc::now(), + modified: Utc::now(), + } + } +} + impl GeoffreyDatabaseModel for Token { fn id(&self) -> Option { self.id @@ -67,18 +69,16 @@ impl PartialEq for Token { #[cfg(test)] mod tests { - use crate::models::token::{Token, Permissions}; + use crate::models::token::{Permissions, Token}; #[test] fn test_token() { - let mut token = Token::new(); + let mut token = Token::default(); token.set_permission(Permissions::ModelGet); assert_eq!(token.permission, 0x1u64); token.set_permission(Permissions::ModelPost); assert_eq!(token.permission, 0x3u64); - } } -