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 + fmtmain
parent
9bf31f17f6
commit
b92308da67
|
@ -3,3 +3,4 @@
|
|||
/database
|
||||
/test_database
|
||||
config.toml
|
||||
*.http
|
||||
|
|
|
@ -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<AddLocationParams>;
|
||||
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<Context>, req: Self::Req) -> Result<Self::Resp> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<FindParams>;
|
||||
type Resp = Vec<Location>;
|
||||
|
||||
fn command_name() -> String {
|
||||
"find".to_string()
|
||||
}
|
||||
|
||||
fn request_type() -> RequestType {
|
||||
RequestType::GET
|
||||
}
|
||||
|
||||
fn command_level() -> CommandLevel {
|
||||
CommandLevel::ALL
|
||||
}
|
||||
|
||||
fn run_command(ctx: Arc<Context>, req: Self::Req) -> Result<Self::Resp> {
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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<Context>, req: Self::Req) -> Result<Self::Resp>;
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_return)]
|
||||
pub fn create_command_filter<T: Command>(ctx: Arc<Context>) -> 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<Context>, req: T::Req| {
|
||||
let reply = T::run_command(ctx, req);
|
||||
if let Ok(reply) = reply {
|
||||
warp::reply::json(&APIResponse::Response::<T::Resp>(reply))
|
||||
} else {
|
||||
warp::reply::json(&APIResponse::<T::Resp>::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<Context>,
|
||||
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
||||
warp::path("command").and(
|
||||
create_command_filter::<FindCommand>(ctx.clone())
|
||||
.or(create_command_filter::<AddLocation>(ctx.clone()))
|
||||
.or(create_command_filter::<Register>(ctx)),
|
||||
)
|
||||
}
|
|
@ -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<RegisterParameters>;
|
||||
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<Context>, req: Self::Req) -> crate::Result<Self::Resp> {
|
||||
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()))
|
||||
}
|
||||
}
|
|
@ -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<Self, ConfigError> {
|
||||
let mut cfg = Config::new();
|
||||
cfg.merge(File::from(config_path.to_path_buf()))?;
|
||||
|
||||
cfg.try_into()
|
||||
}
|
||||
}
|
|
@ -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<Arc<Self>> {
|
||||
let ctx = Self {
|
||||
db: Database::new(cfg.db_path.as_path()).unwrap(),
|
||||
cfg,
|
||||
};
|
||||
|
||||
Ok(Arc::new(ctx))
|
||||
}
|
||||
}
|
|
@ -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<T>(db: &Database, req: &CommandRequest<T>) -> Result<Option<Player>> {
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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<T> = std::result::Result<T, GeoffreyAPIError>;
|
||||
|
||||
#[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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Self> {
|
||||
let db = sled::open(db_path)?;
|
||||
|
||||
Ok(Self {
|
||||
db,
|
||||
})
|
||||
Ok(Self { db })
|
||||
}
|
||||
|
||||
fn get_tree<T>(&self) -> Result<sled::Tree> where T: GeoffreyDatabaseModel {
|
||||
fn get_tree<T>(&self) -> Result<sled::Tree>
|
||||
where
|
||||
T: GeoffreyDatabaseModel,
|
||||
{
|
||||
Ok(self.db.open_tree::<String>(T::tree())?)
|
||||
}
|
||||
|
||||
pub fn insert<T>(&self, mut model: T) -> Result<T> where T: GeoffreyDatabaseModel {
|
||||
pub fn insert<T>(&self, mut model: T) -> Result<T>
|
||||
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::<T>()?;
|
||||
|
@ -46,25 +48,34 @@ impl Database {
|
|||
Ok(model)
|
||||
}
|
||||
|
||||
|
||||
pub fn get<T>(&self, id: u64) -> Result<Option<T>> where T: GeoffreyDatabaseModel {
|
||||
pub fn get<T>(&self, id: u64) -> Result<Option<T>>
|
||||
where
|
||||
T: GeoffreyDatabaseModel,
|
||||
{
|
||||
let tree = self.get_tree::<T>()?;
|
||||
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<T>(&self) -> Result<()> where T: GeoffreyDatabaseModel {
|
||||
pub fn clear_tree<T>(&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<impl Iterator<Item=T> + 'a> where T: GeoffreyDatabaseModel {
|
||||
pub fn filter<'a, T>(
|
||||
&self,
|
||||
f: impl Fn(u64, &T) -> bool + 'a,
|
||||
) -> Result<impl Iterator<Item = T> + '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<T>(&self) -> Result<sled::Iter> where T: GeoffreyDatabaseModel {
|
||||
pub fn tree_iter<T>(&self) -> Result<sled::Iter>
|
||||
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>(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::<Location>(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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
pub mod database;
|
||||
pub mod error;
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Utc>
|
||||
pub restocked_time: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl ItemListing {
|
||||
fn new(item: &str, price: u32, count_per_price: u32, restocked_time: DateTime<Utc>) -> Self {
|
||||
Self {
|
||||
item: Item {name: item.to_string()},
|
||||
item: Item {
|
||||
name: item.to_string(),
|
||||
},
|
||||
price,
|
||||
count_per_price,
|
||||
restocked_time,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<u64>,
|
||||
pub items_produced: HashSet<Item>
|
||||
pub items_produced: HashSet<Item>,
|
||||
}
|
||||
|
||||
impl GeoffreyDatabaseModel for FarmData {
|
||||
|
|
|
@ -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<u64>,
|
||||
pub shops: HashSet<u64>
|
||||
pub shops: HashSet<u64>,
|
||||
}
|
||||
|
||||
impl GeoffreyDatabaseModel for Market {
|
||||
|
@ -21,4 +21,3 @@ impl GeoffreyDatabaseModel for Market {
|
|||
"market".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<u64>,
|
||||
pub tunnel: Option<Tunnel>,
|
||||
loc_type: LocationType
|
||||
loc_type: LocationType,
|
||||
}
|
||||
|
||||
impl Location {
|
||||
pub fn new (name: &str, position: Position, owner: u64, tunnel: Option<Tunnel>, loc_type: LocationType) -> Self {
|
||||
pub fn new(
|
||||
name: &str,
|
||||
position: Position,
|
||||
owner: u64,
|
||||
tunnel: Option<Tunnel>,
|
||||
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<u64> {
|
||||
pub fn owners(&self) -> Vec<u64> {
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<u64>,
|
||||
pub residents: HashSet<u64>
|
||||
pub residents: HashSet<u64>,
|
||||
}
|
||||
|
||||
impl GeoffreyDatabaseModel for Town {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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<Tunnel>,
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct FindParams {
|
||||
pub query: String,
|
||||
}
|
|
@ -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<T> {
|
||||
pub user: Option<UserID>,
|
||||
pub arguments: T,
|
||||
pub token: u64,
|
||||
}
|
||||
|
||||
impl<T> CommandRequest<T> {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -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<u64>,
|
||||
pub name: String,
|
||||
pub user_ids: Vec<UserID>
|
||||
pub user_ids: Vec<UserID>,
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum GeoffreyAPIError {
|
||||
PlayerNotFound,
|
||||
LocationNotFound,
|
||||
PermissionInsufficient,
|
||||
DatabaseError(String),
|
||||
}
|
|
@ -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<T> {
|
||||
Response(T),
|
||||
Error(GeoffreyAPIError),
|
||||
}
|
|
@ -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<u64>,
|
||||
permission: u64,
|
||||
pub created: DateTime<Utc>,
|
||||
pub modified: DateTime<Utc>
|
||||
pub modified: DateTime<Utc>,
|
||||
}
|
||||
|
||||
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<u64> {
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue