Added set_portal to the API

+ Bumped DB version to 3
+ Renamed "tunnel" to "portal" on the location models
+ Added a new migration
+ Cargo + Clippy
main
Joey Hines 2021-12-07 20:33:25 -07:00
parent 3b8c7fc60f
commit 199516f3cc
No known key found for this signature in database
GPG Key ID: 80F567B5C968F91B
17 changed files with 249 additions and 54 deletions

1
Cargo.lock generated
View File

@ -426,6 +426,7 @@ dependencies = [
"geoffrey_models", "geoffrey_models",
"log", "log",
"rand 0.8.4", "rand 0.8.4",
"regex",
"serde 1.0.130", "serde 1.0.130",
"serde_json", "serde_json",
"simple_logger", "simple_logger",

View File

@ -17,4 +17,5 @@ config = "0.11.0"
structopt = "0.3.21" structopt = "0.3.21"
log = "0.4.14" log = "0.4.14"
simple_logger = "1.13.0" simple_logger = "1.13.0"
rand = "0.8.4" rand = "0.8.4"
regex = "1.5.4"

View File

@ -3,6 +3,7 @@ use crate::commands::add_location::AddLocation;
use crate::commands::find::FindCommand; use crate::commands::find::FindCommand;
use crate::commands::register::Register; use crate::commands::register::Register;
use crate::commands::selling::Selling; use crate::commands::selling::Selling;
use crate::commands::set_portal::SetPortal;
use crate::context::Context; use crate::context::Context;
use crate::helper::{get_player_from_req, get_token_from_req}; use crate::helper::{get_player_from_req, get_token_from_req};
use crate::Result; use crate::Result;
@ -25,6 +26,7 @@ pub mod add_token;
pub mod find; pub mod find;
pub mod register; pub mod register;
pub mod selling; pub mod selling;
pub mod set_portal;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
@ -122,6 +124,7 @@ pub fn command_filter(
.or(create_command_filter::<AddLocation>(ctx.clone())) .or(create_command_filter::<AddLocation>(ctx.clone()))
.or(create_command_filter::<Register>(ctx.clone())) .or(create_command_filter::<Register>(ctx.clone()))
.or(create_command_filter::<Selling>(ctx.clone())) .or(create_command_filter::<Selling>(ctx.clone()))
.or(create_command_filter::<AddItem>(ctx)), .or(create_command_filter::<AddItem>(ctx.clone()))
.or(create_command_filter::<SetPortal>(ctx)),
) )
} }

View File

@ -53,7 +53,7 @@ impl Command for Selling {
listing: item.clone(), listing: item.clone(),
shop_name: shop.name.clone(), shop_name: shop.name.clone(),
shop_loc: shop.position, shop_loc: shop.position,
portal: shop.tunnel.clone(), portal: shop.portal.clone(),
}) })
} else { } else {
None None

View File

@ -0,0 +1,48 @@
use crate::commands::{Command, RequestType};
use crate::context::Context;
use crate::Result;
use geoffrey_db::helper::load_location;
use geoffrey_models::models::locations::{Location, LocationDb};
use geoffrey_models::models::parameters::set_portal_params::SetPortalParams;
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 SetPortal {}
impl Command for SetPortal {
type Req = SetPortalParams;
type Resp = Location;
fn command_name() -> String {
"set_portal".to_string()
}
fn request_type() -> RequestType {
RequestType::POST
}
fn command_level() -> CommandLevel {
CommandLevel::REGISTERED
}
fn run_command(ctx: Arc<Context>, req: Self::Req, user: Option<Player>) -> Result<Self::Resp> {
let user = user.unwrap();
let filter = regex::Regex::new(format!(r"(?i)^{}$", req.loc_name).as_str()).unwrap();
let mut location: LocationDb = ctx
.db
.filter(|_, loc: &LocationDb| {
loc.owners().contains(&user.id.unwrap()) && filter.is_match(loc.name.as_str())
})?
.next()
.ok_or(GeoffreyAPIError::EntryNotFound)?;
location.portal = Some(req.portal);
let location = ctx.db.insert(location)?;
load_location(&ctx.db, &location).map_err(|err| err.into())
}
}

View File

@ -1,15 +1,15 @@
use crate::error::{GeoffreyDBError, Result}; use crate::error::{GeoffreyDBError, Result};
use crate::migration::do_migration;
use geoffrey_models::models::db_metadata::DBMetadata;
use geoffrey_models::GeoffreyDatabaseModel; use geoffrey_models::GeoffreyDatabaseModel;
use std::convert::TryInto; use std::convert::TryInto;
use std::path::Path; use std::path::Path;
use geoffrey_models::models::db_metadata::DBMetadata;
use crate::migration::do_migration;
const DB_VERSION: u64 = 2; const DB_VERSION: u64 = 3;
const DB_METADATA_ID: u64 = 1; const DB_METADATA_ID: u64 = 1;
pub struct Database { pub struct Database {
pub(crate) db: sled::Db, pub(crate) db: sled::Db,
} }
impl Database { impl Database {

View File

@ -1,10 +1,10 @@
use crate::migration::Migration;
use crate::database::Database; use crate::database::Database;
use crate::migration::Migration;
use geoffrey_models::models::locations::LocationDb; use geoffrey_models::models::locations::LocationDb;
use geoffrey_models::GeoffreyDatabaseModel; use geoffrey_models::GeoffreyDatabaseModel;
use json::JsonValue; use json::JsonValue;
pub(crate) struct PosAndNetherMigration {} pub(crate) struct PosAndNetherMigration {}
impl Migration for PosAndNetherMigration { impl Migration for PosAndNetherMigration {
fn up(db: &Database) -> crate::error::Result<()> { fn up(db: &Database) -> crate::error::Result<()> {
@ -36,7 +36,7 @@ impl Migration for PosAndNetherMigration {
let (id, loc_ivec) = entry?; let (id, loc_ivec) = entry?;
let mut loc = json::parse(std::str::from_utf8(&loc_ivec).unwrap()).unwrap(); let mut loc = json::parse(std::str::from_utf8(&loc_ivec).unwrap()).unwrap();
loc["position"]["y"] = loc["position"]["z"].clone(); loc["position"]["y"] = loc["position"]["z"].clone();
loc["position"]["z"].clear(); loc["position"]["z"].clear();
// Clear out the tunnel entry, there is not a great way to convert // Clear out the tunnel entry, there is not a great way to convert
@ -46,7 +46,6 @@ impl Migration for PosAndNetherMigration {
} }
Ok(()) Ok(())
} }
fn version() -> u64 { fn version() -> u64 {
@ -57,12 +56,12 @@ impl Migration for PosAndNetherMigration {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::database::Database; use crate::database::Database;
use std::path::Path; use crate::migration::migration_2::PosAndNetherMigration;
use crate::migration::Migration;
use geoffrey_models::models::locations::LocationDb; use geoffrey_models::models::locations::LocationDb;
use geoffrey_models::GeoffreyDatabaseModel; use geoffrey_models::GeoffreyDatabaseModel;
use sled::IVec; use sled::IVec;
use crate::migration::migration_2::PosAndNetherMigration; use std::path::Path;
use crate::migration::Migration;
#[test] #[test]
fn test_migration_2() { fn test_migration_2() {
@ -70,7 +69,8 @@ mod tests {
let loc_tree = db.db.open_tree(LocationDb::tree()).unwrap(); let loc_tree = db.db.open_tree(LocationDb::tree()).unwrap();
let old_loc_format = json::parse(r#"{ let old_loc_format = json::parse(
r#"{
"id": 55, "id": 55,
"name": "Test", "name": "Test",
"position": { "position": {
@ -79,9 +79,13 @@ mod tests {
"dimension": "Overworld" "dimension": "Overworld"
}, },
"loc_data": "Base" "loc_data": "Base"
}"#).unwrap(); }"#,
)
.unwrap();
loc_tree.insert(IVec::from(vec![55]), old_loc_format.to_string().as_bytes()).unwrap(); loc_tree
.insert(IVec::from(vec![55]), old_loc_format.to_string().as_bytes())
.unwrap();
PosAndNetherMigration::up(&db).unwrap(); PosAndNetherMigration::up(&db).unwrap();
@ -95,4 +99,4 @@ mod tests {
std::fs::remove_dir_all("../migration_2_db").unwrap(); std::fs::remove_dir_all("../migration_2_db").unwrap();
} }
} }

View File

@ -0,0 +1,99 @@
use crate::database::Database;
use crate::migration::Migration;
use geoffrey_models::models::locations::LocationDb;
use geoffrey_models::GeoffreyDatabaseModel;
pub(crate) struct TunnelToPortalMigration {}
impl Migration for TunnelToPortalMigration {
fn up(db: &Database) -> crate::error::Result<()> {
let loc_tree = db.db.open_tree(LocationDb::tree())?;
for entry in loc_tree.iter() {
let (id, loc_ivec) = entry?;
let mut loc = json::parse(std::str::from_utf8(&loc_ivec).unwrap()).unwrap();
let tunnel = loc["tunnel"].clone();
loc["portal"] = tunnel;
loc_tree.insert(id, loc.to_string().as_bytes())?;
}
Ok(())
}
fn down(db: &Database) -> crate::error::Result<()> {
let loc_tree = db.db.open_tree(LocationDb::tree())?;
for entry in loc_tree.iter() {
let (id, loc_ivec) = entry?;
let mut loc = json::parse(std::str::from_utf8(&loc_ivec).unwrap()).unwrap();
let portal = loc["portal"].clone();
loc["tunnel"] = portal;
loc_tree.insert(id, loc.to_string().as_bytes())?;
}
Ok(())
}
fn version() -> u64 {
2
}
}
#[cfg(test)]
mod tests {
use crate::database::Database;
use crate::migration::migration_3::TunnelToPortalMigration;
use crate::migration::Migration;
use geoffrey_models::models::locations::LocationDb;
use geoffrey_models::GeoffreyDatabaseModel;
use sled::IVec;
use std::path::Path;
#[test]
fn test_migration_2() {
let db = Database::new(Path::new("../migration_3_db/")).unwrap();
let loc_tree = db.db.open_tree(LocationDb::tree()).unwrap();
let old_loc = json::parse(
r#"{
"id": 55,
"name": "Test",
"position": {
"x": 55,
"y": 55,
"z": 55,
"dimension": "Overworld"
},
"tunnel": {
"x": 8,
"z": 8
},
"loc_data": "Base"
}"#,
)
.unwrap();
loc_tree
.insert(IVec::from(vec![55]), old_loc.to_string().as_bytes())
.unwrap();
TunnelToPortalMigration::up(&db).unwrap();
let new_loc = loc_tree.get(IVec::from(vec![55])).unwrap().unwrap();
let new_loc = json::parse(std::str::from_utf8(&new_loc).unwrap()).unwrap();
assert_eq!(new_loc["portal"]["x"], 8);
assert_eq!(new_loc["portal"]["z"], 8);
drop(db);
std::fs::remove_dir_all("../migration_3_db").unwrap();
}
}

View File

@ -1,9 +1,11 @@
use crate::database::Database; use crate::database::Database;
use crate::error::Result; use crate::error::Result;
use crate::migration::migration_2::PosAndNetherMigration; use crate::migration::migration_2::PosAndNetherMigration;
use crate::migration::migration_3::TunnelToPortalMigration;
use geoffrey_models::models::db_metadata::DBMetadata; use geoffrey_models::models::db_metadata::DBMetadata;
mod migration_2; mod migration_2;
mod migration_3;
trait Migration { trait Migration {
fn up(db: &Database) -> Result<()>; fn up(db: &Database) -> Result<()>;
@ -12,10 +14,11 @@ trait Migration {
} }
fn upgrade(db: &Database, current_version: u64, target_version: u64) -> Result<()> { fn upgrade(db: &Database, current_version: u64, target_version: u64) -> Result<()> {
for ver in current_version+1..=target_version { for ver in current_version + 1..=target_version {
match ver { match ver {
2 => PosAndNetherMigration::up(db)?, 2 => PosAndNetherMigration::up(db)?,
_ => () 3 => TunnelToPortalMigration::up(db)?,
_ => (),
} }
} }
@ -25,8 +28,9 @@ fn upgrade(db: &Database, current_version: u64, target_version: u64) -> Result<(
fn downgrade(db: &Database, current_version: u64, target_version: u64) -> Result<()> { fn downgrade(db: &Database, current_version: u64, target_version: u64) -> Result<()> {
for ver in (target_version..current_version).rev() { for ver in (target_version..current_version).rev() {
match ver { match ver {
2 => PosAndNetherMigration::up(db)?, 2 => PosAndNetherMigration::down(db)?,
_ => () 3 => TunnelToPortalMigration::down(db)?,
_ => (),
} }
} }
@ -36,11 +40,11 @@ fn downgrade(db: &Database, current_version: u64, target_version: u64) -> Result
pub fn do_migration(db: &Database, target_version: u64) -> Result<()> { pub fn do_migration(db: &Database, target_version: u64) -> Result<()> {
let current_version = db.version().unwrap_or(0); let current_version = db.version().unwrap_or(0);
#[allow(clippy::comparison_chain)]
if target_version > current_version { if target_version > current_version {
upgrade(db, current_version, target_version)?; upgrade(db, current_version, target_version)?;
} } else if target_version < current_version {
else if target_version < current_version { downgrade(db, current_version, target_version)?;
downgrade(db, current_version, target_version)?;
} }
let metadata = DBMetadata { let metadata = DBMetadata {

View File

@ -5,7 +5,6 @@ use std::fmt::Debug;
pub mod models; pub mod models;
pub trait GeoffreyDatabaseModel: Serialize + DeserializeOwned + Debug { pub trait GeoffreyDatabaseModel: Serialize + DeserializeOwned + Debug {
fn id(&self) -> Option<u64>; fn id(&self) -> Option<u64>;
fn set_id(&mut self, id: u64); fn set_id(&mut self, id: u64);

View File

@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use crate::GeoffreyDatabaseModel; use crate::GeoffreyDatabaseModel;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq)]
pub struct DBMetadata { pub struct DBMetadata {
@ -17,5 +17,3 @@ impl GeoffreyDatabaseModel for DBMetadata {
"DBMetadata".to_string() "DBMetadata".to_string()
} }
} }

View File

@ -6,7 +6,7 @@ use crate::models::locations::market::{Market, MarketDb};
use crate::models::locations::shop::Shop; use crate::models::locations::shop::Shop;
use crate::models::locations::town::{Town, TownDb}; use crate::models::locations::town::{Town, TownDb};
use crate::models::player::Player; use crate::models::player::Player;
use crate::models::{Position, Portal}; use crate::models::{Portal, Position};
use crate::GeoffreyDatabaseModel; use crate::GeoffreyDatabaseModel;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::str::FromStr; use std::str::FromStr;
@ -111,7 +111,7 @@ pub struct LocationDb {
pub name: String, pub name: String,
pub position: Position, pub position: Position,
owners: HashSet<u64>, owners: HashSet<u64>,
pub tunnel: Option<Portal>, pub portal: Option<Portal>,
pub loc_data: LocationDataDb, pub loc_data: LocationDataDb,
} }
@ -131,7 +131,7 @@ impl LocationDb {
name: name.to_string(), name: name.to_string(),
position, position,
owners, owners,
tunnel, portal: tunnel,
loc_data: loc_type, loc_data: loc_type,
} }
} }
@ -173,7 +173,7 @@ pub struct Location {
pub name: String, pub name: String,
pub position: Position, pub position: Position,
pub owners: Vec<Player>, pub owners: Vec<Player>,
pub tunnel: Option<Portal>, pub portal: Option<Portal>,
pub loc_data: LocationData, pub loc_data: LocationData,
} }
@ -188,7 +188,7 @@ impl Location {
name: location.name, name: location.name,
position: location.position, position: location.position,
owners, owners,
tunnel: location.tunnel, portal: location.portal,
loc_data, loc_data,
} }
} }
@ -228,7 +228,7 @@ mod tests {
); );
let l2 = LocationDb::new( let l2 = LocationDb::new(
"teSt", "teSt",
Position::new(0, 0, 0,Dimension::Overworld), Position::new(0, 0, 0, Dimension::Overworld),
0u64, 0u64,
None, None,
LocationDataDb::Base, LocationDataDb::Base,

View File

@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::str::FromStr; use std::str::FromStr;
pub mod db_metadata;
pub mod item; pub mod item;
pub mod locations; pub mod locations;
pub mod meta; pub mod meta;
@ -9,7 +10,6 @@ pub mod parameters;
pub mod player; pub mod player;
pub mod response; pub mod response;
pub mod token; pub mod token;
pub mod db_metadata;
#[derive(Serialize, Deserialize, Debug, Copy, Clone)] #[derive(Serialize, Deserialize, Debug, Copy, Clone)]
pub enum Dimension { pub enum Dimension {
@ -86,40 +86,36 @@ impl Position {
impl Display for Position { impl Display for Position {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "(x={}, y={}, z={}) {} ", self.x, self.y, self.z, self.dimension) write!(
f,
"(x={}, y={}, z={}) {} ",
self.x, self.y, self.z, self.dimension
)
} }
} }
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Portal { pub struct Portal {
x: i32, x: i32,
z: i32 z: i32,
} }
impl Portal { impl Portal {
pub fn new(x: i32, z: i32) -> Self { pub fn new(x: i32, z: i32) -> Self {
Self { Self { x, z }
x,
z
}
} }
pub fn direction(&self) -> Direction { pub fn direction(&self) -> Direction {
if self.x.abs() > self.z.abs() { if self.x.abs() > self.z.abs() {
if self.x < 0 { if self.x < 0 {
Direction::West Direction::West
} } else {
else {
Direction::East Direction::East
} }
} } else if self.z < 0 {
else { Direction::North
if self.z < 0 { } else {
Direction::North Direction::South
}
else {
Direction::South
}
} }
} }
} }

View File

@ -1,7 +1,7 @@
use crate::models::locations::LocationType; use crate::models::locations::LocationType;
use crate::models::parameters::CommandRequest; use crate::models::parameters::CommandRequest;
use crate::models::player::UserID; use crate::models::player::UserID;
use crate::models::{Position, Portal}; use crate::models::{Portal, Position};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]

View File

@ -4,6 +4,7 @@ pub mod add_token_params;
pub mod find_params; pub mod find_params;
pub mod register_params; pub mod register_params;
pub mod selling_params; pub mod selling_params;
pub mod set_portal_params;
use crate::models::player::{Player, UserID}; use crate::models::player::{Player, UserID};
use crate::models::token::{Permissions, Token}; use crate::models::token::{Permissions, Token};

View File

@ -0,0 +1,41 @@
use crate::models::parameters::CommandRequest;
use crate::models::player::UserID;
use crate::models::Portal;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SetPortalParams {
pub token: String,
pub user_id: UserID,
pub loc_name: String,
pub portal: Portal,
}
impl SetPortalParams {
pub fn new(loc_name: String, portal: Portal) -> Self {
Self {
token: Default::default(),
user_id: Default::default(),
loc_name,
portal,
}
}
}
impl CommandRequest for SetPortalParams {
fn token(&self) -> String {
self.token.clone()
}
fn user_id(&self) -> Option<UserID> {
Some(self.user_id.clone())
}
fn set_user_id(&mut self, user_id: UserID) {
self.user_id = user_id;
}
fn set_token(&mut self, token: String) {
self.token = token;
}
}

View File

@ -1,5 +1,5 @@
use crate::models::item::ItemListing; use crate::models::item::ItemListing;
use crate::models::{Position, Portal}; use crate::models::{Portal, Position};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]