Refactor ahead of API addition

+ This commit started as an experiment in a GraphQL api. It didn't turn out well...
main
Joey Hines 2021-05-08 15:42:47 -05:00
parent 2bf1b8ab1c
commit 9d104db0d6
No known key found for this signature in database
GPG Key ID: 80F567B5C968F91B
21 changed files with 1164 additions and 406 deletions

2
.gitignore vendored
View File

@ -1,3 +1,3 @@
/target /target
.idea .idea
database/ /database

899
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
[workspace] [workspace]
members = [ members = [
"geoffrey_models", "geoffrey_models",
"geoffrey_db" "geoffrey_db",
"geoffrey_api"
] ]

View File

@ -0,0 +1,15 @@
[package]
name = "geoffrey_api"
version = "0.1.0"
authors = ["Joey Hines <joey@ahines.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tokio = { version = "1", features = ["full"] }
warp = "0.3"
serde = "1.0.124"
serde_json = "1.0.64"
geoffrey_models = { path = "../geoffrey_models" }
geoffrey_db = { path = "../geoffrey_db" }

View File

@ -0,0 +1,12 @@
use warp::Filter;
#[tokio::main]
async fn main() {
// GET /hello/warp => 200 OK with body "Hello, warp!"
let hello = warp::path!("hello" / String)
.map(|name| format!("Hello, {}!", name));
warp::serve(hello)
.run(([127, 0, 0, 1], 3030))
.await;
}

View File

@ -1,7 +1,8 @@
use crate::error::Result; use crate::error::Result;
use std::path::Path; use std::path::Path;
use geoffrey_models::GeoffreyDatabaseModel; use geoffrey_models::GeoffreyDatabaseModel;
use crate::u64_to_bytes; use crate::{u64_from_bytes, u64_to_bytes};
use sled::Iter;
pub struct Database { pub struct Database {
db: sled::Db, db: sled::Db,
@ -51,6 +52,31 @@ impl Database {
} }
} }
pub fn filter<T, F>(&self, f: F) -> Result<Vec<T>> where T: GeoffreyDatabaseModel, F: Fn(u64, &T) -> bool {
let tree = self.db.open_tree(T::tree())?;
Ok(tree.iter().filter_map(|e| {
if let Ok((id, data)) = e {
let id = u64_from_bytes(&mut id.clone());
let data = T::try_from_bytes(&data).unwrap();
if f(id, &data) {
Some(data)
}
else {
None
}
}
else {
None
}
}).collect())
}
pub fn tree_iter<T>(&self) -> Result<Iter> where T: GeoffreyDatabaseModel {
Ok(self.db.open_tree(T::tree()).map(|tree| tree.iter())?)
}
} }
#[cfg(test)] #[cfg(test)]
@ -59,12 +85,10 @@ mod tests {
use std::path::Path; use std::path::Path;
use geoffrey_models::models::player::{Player, UserID}; use geoffrey_models::models::player::{Player, UserID};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use geoffrey_models::models::shop::Shop; use geoffrey_models::GeoffreyDatabaseModel;
use geoffrey_models::models::{Location, Position};
lazy_static! { lazy_static! {
static ref DB: Database = Database::new(Path::new("../database")).unwrap(); static ref DB: Database = Database::new(Path::new("../locations")).unwrap();
} }
fn cleanup() { fn cleanup() {
@ -73,41 +97,26 @@ mod tests {
#[test] #[test]
fn test_insert() { fn test_insert() {
let player = Player::new("CoolZero123", UserID::DiscordUUID(0)); let player = Player::new("CoolZero123", UserID::DiscordUUID(0u64));
let p2 = DB.insert::<Player>(player.clone()).unwrap(); let p2 = DB.insert::<Player>(player.clone()).unwrap();
assert!(p2.id.is_some()); assert!(p2.id().is_some());
assert_eq!(player.name, p2.name); assert_eq!(player.name, p2.name);
cleanup(); cleanup();
} }
#[test] #[test]
fn test_get() { fn test_get() {
let player = Player::new("CoolZero123", UserID::DiscordUUID(0)); let player = Player::new("CoolZero123", UserID::DiscordUUID(0u64));
let p2 = DB.insert::<Player>(player.clone()).unwrap(); let p2 = DB.insert::<Player>(player.clone()).unwrap();
let p3 = DB.get::<Player>(p2.id.unwrap()).unwrap(); let p3 = DB.get::<Player>(p2.id().unwrap()).unwrap();
assert!(p3.is_some()); assert!(p3.is_some());
assert_eq!(p3.unwrap().name, player.name); assert_eq!(p3.unwrap().name, player.name);
cleanup(); cleanup();
} }
#[test]
fn test_shop() {
let player = Player::new("CoolZero123", UserID::DiscordUUID(0));
let player= DB.insert::<Player>(player.clone()).unwrap();
let shop = Shop::new("cool shop123", Position::default(), player.id.unwrap(), None);
let shop2 = DB.insert::<Shop>(shop.clone()).unwrap();
assert_eq!(shop.name(), shop2.name());
let shop3 = DB.get::<Shop>(shop2.id.unwrap()).unwrap().unwrap();
assert_eq!(shop3.id, shop2.id);
cleanup();
}
} }

View File

@ -1,5 +1,6 @@
#![allow(dead_code)] #![allow(dead_code)]
use byteorder::{WriteBytesExt, BigEndian}; use byteorder::{WriteBytesExt, BigEndian, ReadBytesExt};
use std::io::Cursor;
pub mod database; pub mod database;
pub mod error; pub mod error;
@ -10,3 +11,8 @@ pub fn u64_to_bytes(n: u64) -> Vec<u8> {
id_bytes id_bytes
} }
pub fn u64_from_bytes(bytes: &mut [u8]) -> u64 {
let mut cursor = Cursor::new(bytes);
cursor.read_u64::<BigEndian>().unwrap()
}

View File

@ -1,9 +1,11 @@
#![allow(dead_code)] #![allow(dead_code)]
use serde::{Serialize}; use serde::Serialize;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
pub mod models; pub mod models;
const DB_VERSION: u64 = 1;
pub trait GeoffreyDatabaseModel: Serialize + DeserializeOwned { pub trait GeoffreyDatabaseModel: Serialize + DeserializeOwned {
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,40 +0,0 @@
use serde::{Serialize, Deserialize};
use crate::models::{LocationData, Location, Position, Tunnel};
use crate::GeoffreyDatabaseModel;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Attraction {
pub id: Option<u64>,
pub location_data: LocationData
}
impl GeoffreyDatabaseModel for Attraction {
fn id(&self) -> Option<u64> {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = Some(id)
}
fn tree() -> String {
"attraction".to_string()
}
}
impl Location for Attraction {
fn new(name: &str, position: Position, owner: u64, tunnel: Option<Tunnel>) -> Self {
Self {
id: None,
location_data: LocationData::new(name, position, owner, tunnel)
}
}
fn mut_location_data(&mut self) -> &mut LocationData {
&mut self.location_data
}
fn location_data(&self) -> &LocationData {
&self.location_data
}
}

View File

@ -1,40 +0,0 @@
use serde::{Serialize, Deserialize};
use crate::models::{LocationData, Location, Position, Tunnel};
use crate::GeoffreyDatabaseModel;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Base {
pub id: Option<u64>,
pub location_data: LocationData
}
impl GeoffreyDatabaseModel for Base {
fn id(&self) -> Option<u64> {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = Some(id)
}
fn tree() -> String {
"base".to_string()
}
}
impl Location for Base {
fn new(name: &str, position: Position, owner: u64, tunnel: Option<Tunnel>) -> Self {
Self {
id: None,
location_data: LocationData::new(name, position, owner, tunnel)
}
}
fn mut_location_data(&mut self) -> &mut LocationData {
&mut self.location_data
}
fn location_data(&self) -> &LocationData {
&self.location_data
}
}

View File

@ -1,44 +0,0 @@
use serde::{Serialize, Deserialize};
use crate::models::{LocationData, Location, Position, Tunnel};
use crate::models::item::Item;
use crate::GeoffreyDatabaseModel;
use std::collections::HashSet;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Farm {
pub id: Option<u64>,
pub location_data: LocationData,
pub items_produced: HashSet<Item>
}
impl GeoffreyDatabaseModel for Farm {
fn id(&self) -> Option<u64> {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = Some(id)
}
fn tree() -> String {
"farm".to_string()
}
}
impl Location for Farm {
fn new(name: &str, position: Position, owner: u64, tunnel: Option<Tunnel>) -> Self {
Self {
id: None,
location_data: LocationData::new(name, position, owner, tunnel),
items_produced: HashSet::new(),
}
}
fn mut_location_data(&mut self) -> &mut LocationData {
&mut self.location_data
}
fn location_data(&self) -> &LocationData {
&self.location_data
}
}

View File

@ -1,3 +1,4 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq)]
@ -13,3 +14,22 @@ impl Item {
} }
} }
#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq)]
pub struct ItemListing {
pub item: Item,
pub price: u32,
pub count_per_price: u32,
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()},
price,
count_per_price,
restocked_time,
}
}
}

View File

@ -0,0 +1,24 @@
use serde::{Serialize, Deserialize};
use crate::models::item::Item;
use crate::GeoffreyDatabaseModel;
use std::collections::HashSet;
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct FarmData {
id: Option<u64>,
pub items_produced: HashSet<Item>
}
impl GeoffreyDatabaseModel for FarmData {
fn id(&self) -> Option<u64> {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = Some(id)
}
fn tree() -> String {
"farm".to_string()
}
}

View File

@ -0,0 +1,24 @@
use serde::{Serialize, Deserialize};
use crate::GeoffreyDatabaseModel;
use std::collections::HashSet;
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct Market {
pub id: Option<u64>,
pub shops: HashSet<u64>
}
impl GeoffreyDatabaseModel for Market {
fn id(&self) -> Option<u64> {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = Some(id)
}
fn tree() -> String {
"market".to_string()
}
}

View File

@ -0,0 +1,73 @@
use serde::{Serialize, Deserialize};
use std::collections::HashSet;
use crate::GeoffreyDatabaseModel;
use crate::models::{Position, Tunnel};
pub mod farm;
pub mod market;
pub mod shop;
pub mod town;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum LocationType {
Base,
Shop(u64),
Attraction,
Town(u64),
Farm(u64),
Market(u64),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Location {
id: Option<u64>,
pub name: String,
pub position: Position,
owners: HashSet<u64>,
pub tunnel: Option<Tunnel>,
loc_type: LocationType
}
impl Location {
pub fn new (name: &str, position: Position, owner: u64, tunnel: Option<Tunnel>, loc_type: LocationType) -> Self {
let mut owners = HashSet::new();
owners.insert(owner);
Self {
id: None,
name: name.to_string(),
position,
owners,
tunnel,
loc_type
}
}
fn owners(&self) -> Vec<u64> {
self.owners.iter().cloned().collect()
}
fn add_owner(&mut self, owner: u64) {
self.owners.insert(owner);
}
fn remove_owner(&mut self, owner: u64) {
self.owners.remove(&owner);
}
}
impl GeoffreyDatabaseModel for Location {
fn id(&self) -> Option<u64> {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = Some(id)
}
fn tree() -> String {
"location".to_string()
}
}

View File

@ -0,0 +1,27 @@
use std::collections::HashSet;
use serde::{Deserialize, Serialize};
use crate::GeoffreyDatabaseModel;
use crate::models::item::ItemListing;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Shop {
id: Option<u64>,
pub item_listings: HashSet<ItemListing>,
}
impl GeoffreyDatabaseModel for Shop {
fn id(&self) -> Option<u64> {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = Some(id);
}
fn tree() -> String {
"shop".to_string()
}
}

View File

@ -0,0 +1,23 @@
use serde::{Deserialize, Serialize};
use crate::GeoffreyDatabaseModel;
use std::collections::HashSet;
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct Town {
id: Option<u64>,
pub residents: HashSet<u64>
}
impl GeoffreyDatabaseModel for Town {
fn id(&self) -> Option<u64> {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = Some(id)
}
fn tree() -> String {
"town".to_string()
}
}

View File

@ -1,44 +0,0 @@
use serde::{Serialize, Deserialize};
use crate::models::{LocationData, Location, Position, Tunnel};
use crate::GeoffreyDatabaseModel;
use std::collections::HashSet;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Market {
pub id: Option<u64>,
pub location_data: LocationData,
pub shops: HashSet<u64>
}
impl GeoffreyDatabaseModel for Market {
fn id(&self) -> Option<u64> {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = Some(id)
}
fn tree() -> String {
"farm".to_string()
}
}
impl Location for Market {
fn new(name: &str, position: Position, owner: u64, tunnel: Option<Tunnel>) -> Self {
Self {
id: None,
location_data: LocationData::new(name, position, owner, tunnel),
shops: HashSet::new()
}
}
fn mut_location_data(&mut self) -> &mut LocationData {
&mut self.location_data
}
fn location_data(&self) -> &LocationData {
&self.location_data
}
}

View File

@ -1,17 +1,10 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde::de::DeserializeOwned;
use std::collections::HashSet;
pub mod player; pub mod player;
pub mod shop;
pub mod item; pub mod item;
pub mod town;
pub mod farm;
pub mod market;
pub mod token; pub mod token;
pub mod meta; pub mod meta;
mod base; pub mod locations;
pub mod attraction;
#[derive(Serialize, Deserialize, Debug, Copy, Clone)] #[derive(Serialize, Deserialize, Debug, Copy, Clone)]
pub enum Dimension { pub enum Dimension {
@ -36,13 +29,13 @@ pub enum Direction {
#[derive(Default, Serialize, Deserialize, Debug, Copy, Clone)] #[derive(Default, Serialize, Deserialize, Debug, Copy, Clone)]
pub struct Position { pub struct Position {
pub x: i64, pub x: i32,
pub y: i64, pub y: i32,
pub dimension: Dimension, pub dimension: Dimension,
} }
impl Position { impl Position {
pub fn new(x: i64, y: i64, dimension: Dimension) -> Self { pub fn new(x: i32, y: i32, dimension: Dimension) -> Self {
Self { Self {
x, x,
y, y,
@ -54,71 +47,6 @@ impl Position {
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Tunnel { pub struct Tunnel {
direction: Direction, direction: Direction,
number: i64 number: i32
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct LocationData {
name: String,
position: Position,
owners: HashSet<u64>,
tunnel: Option<Tunnel>,
}
impl LocationData {
pub fn new (name: &str, position: Position, owner: u64, tunnel: Option<Tunnel>) -> Self {
let mut owners = HashSet::new();
owners.insert(owner);
Self {
name: name.to_string(),
position,
owners,
tunnel,
}
}
}
pub trait Location: Serialize + DeserializeOwned + Clone {
fn new (name: &str, position: Position, owner: u64, tunnel: Option<Tunnel>) -> Self;
fn mut_location_data(&mut self) -> &mut LocationData;
fn location_data(&self) -> &LocationData;
fn name(&self) -> String {
self.location_data().name.clone()
}
fn owners(&self) -> Vec<u64> {
self.location_data().owners.iter().cloned().collect()
}
fn position(&self) -> Position {
self.location_data().position.clone()
}
fn tunnel(&self) -> Option<Tunnel> {
self.location_data().tunnel.clone()
}
fn add_owner(&mut self, owner: u64) {
self.mut_location_data().owners.insert(owner);
}
fn remove_owner(&mut self, owner: u64) {
self.mut_location_data().owners.remove(&owner);
}
fn set_position(&mut self, position: Position) {
self.mut_location_data().position = position;
}
fn set_tunnel(&mut self, tunnel: Tunnel) {
self.mut_location_data().tunnel = Some(tunnel);
}
fn set_name(&mut self, name: &str) {
self.mut_location_data().name = name.to_string();
}
} }

View File

@ -1,82 +0,0 @@
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
use crate::models::item::Item;
use std::collections::HashSet;
use crate::models::{LocationData, Location, Tunnel, Position};
use crate::GeoffreyDatabaseModel;
#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq)]
pub struct ItemListing {
pub item: Item,
pub price: u64,
pub count_per_price: u64,
pub restocked_time: DateTime<Utc>
}
impl ItemListing {
fn new(item: &str, price: u64, count_per_price: u64, restocked_time: DateTime<Utc>) -> Self {
Self {
item: Item {name: item.to_string()},
price,
count_per_price,
restocked_time,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Shop {
pub id: Option<u64>,
pub item_listings: HashSet<ItemListing>,
pub location_data: LocationData
}
impl Location for Shop {
fn new(name: &str, position: Position, owner: u64, tunnel: Option<Tunnel>) -> Self {
Self {
id: None,
item_listings: Default::default(),
location_data: LocationData::new(name, position, owner, tunnel)
}
}
fn mut_location_data(&mut self) -> &mut LocationData {
&mut self.location_data
}
fn location_data(&self) -> &LocationData {
&self.location_data
}
}
impl GeoffreyDatabaseModel for Shop {
fn id(&self) -> Option<u64> {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = Some(id);
}
fn tree() -> String {
"shop".to_string()
}
}
#[cfg(test)]
mod tests {
use crate::models::{Position, Dimension, LocationData, Location};
use crate::models::player::{Player, UserID};
use crate::models::shop::Shop;
fn create_shop() -> Shop {
let player = Player::new("CoolZero123", UserID::DiscordUUID(0));
let shop = Shop::new("Cool Shop 123", Position::default(), player, None);
}
#[test]
fn test_new_shop() {
}
}

View File

@ -1,45 +0,0 @@
use serde::{Deserialize, Serialize};
use crate::models::{LocationData, Location, Position, Tunnel};
use crate::models::player::Player;
use crate::GeoffreyDatabaseModel;
use std::collections::HashSet;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Town {
pub id: Option<u64>,
pub location_data: LocationData,
pub residents: HashSet<Player>
}
impl GeoffreyDatabaseModel for Town {
fn id(&self) -> Option<u64> {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = Some(id)
}
fn tree() -> String {
"town".to_string()
}
}
impl Location for Town {
fn new(name: &str, position: Position, owner: u64, tunnel: Option<Tunnel>) -> Self {
Self {
id: None,
location_data: LocationData::new(name, position, owner, tunnel),
residents: HashSet::new(),
}
}
fn mut_location_data(&mut self) -> &mut LocationData {
&mut self.location_data
}
fn location_data(&self) -> &LocationData {
&self.location_data
}
}