Backup
This commit is contained in:
parent
33a2e3156e
commit
7bb564f91e
16 changed files with 1877 additions and 89 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
|||
use nix
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
target
|
||||
.direnv
|
1288
Cargo.lock
generated
1288
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -8,17 +8,22 @@ license = "MIT License"
|
|||
# Used by library
|
||||
macaddr = "1.0.1"
|
||||
log = "0.4"
|
||||
actix-web = "4"
|
||||
# Used by server runtime
|
||||
fern = { optional = true, version = "0.7" }
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
stopwatch = "0.0.7"
|
||||
rand = "0.9"
|
||||
|
||||
[features]
|
||||
default = ["client", "server"]
|
||||
bin-deps = ["fern"]
|
||||
client = []
|
||||
server = []
|
||||
|
||||
[[bin]]
|
||||
name = "matchmaker_server"
|
||||
path = "src/main.rs"
|
||||
required-features = []
|
||||
required-features = ["server"]
|
49
docs/WEBAPI_V1.md
Normal file
49
docs/WEBAPI_V1.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
# Web API Documentation - Draft
|
||||
|
||||
## /api/info
|
||||
|
||||
### GET
|
||||
|
||||
```json
|
||||
{
|
||||
"supportedVersions": [
|
||||
"<VERSION IDENTIFIER>"
|
||||
],
|
||||
"partitions": [
|
||||
"us",
|
||||
"uk"
|
||||
// ...
|
||||
],
|
||||
"heartbeatTimeoutDuration": 7
|
||||
}
|
||||
```
|
||||
|
||||
## /api/query
|
||||
|
||||
### POST
|
||||
|
||||
```json
|
||||
{
|
||||
// if this is unspecified/null, infer a default partition
|
||||
"partition": "us",
|
||||
""
|
||||
// Validated against a server-side maximum and minimum
|
||||
"count": 7,
|
||||
// Validated to prevent out-of-bounds issues.
|
||||
// This allows reading a slice of the instances,
|
||||
// allowing pagination.
|
||||
"offset": 0
|
||||
}
|
||||
```
|
||||
|
||||
## /api/connect
|
||||
|
||||
|
||||
|
||||
## /api/heartbeat
|
||||
|
||||
```json
|
||||
{
|
||||
"token":
|
||||
}
|
||||
```
|
4
rust-toolchain.toml
Normal file
4
rust-toolchain.toml
Normal file
|
@ -0,0 +1,4 @@
|
|||
[toolchain]
|
||||
channel = "1.81"
|
||||
components = ["rustfmt"]
|
||||
profile = "minimal"
|
42
shell.nix
42
shell.nix
|
@ -1,4 +1,40 @@
|
|||
{ pkgs ? import <nixpkgs> {} }:
|
||||
pkgs.mkShell {
|
||||
buildInputs = [ pkgs.cargo pkgs.rustc ];
|
||||
}
|
||||
let
|
||||
overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml));
|
||||
libPath = with pkgs; lib.makeLibraryPath [
|
||||
# load external libraries that you need in your rust project here
|
||||
];
|
||||
in
|
||||
pkgs.mkShell rec {
|
||||
buildInputs = with pkgs; [
|
||||
clang
|
||||
# Replace llvmPackages with llvmPackages_X, where X is the latest LLVM version (at the time of writing, 16)
|
||||
llvmPackages.bintools
|
||||
rustup
|
||||
];
|
||||
RUSTC_VERSION = overrides.toolchain.channel;
|
||||
# https://github.com/rust-lang/rust-bindgen#environment-variables
|
||||
LIBCLANG_PATH = pkgs.lib.makeLibraryPath [ pkgs.llvmPackages_latest.libclang.lib ];
|
||||
shellHook = ''
|
||||
export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin
|
||||
export PATH=$PATH:''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-x86_64-unknown-linux-gnu/bin/
|
||||
'';
|
||||
# Add precompiled library to rustc search path
|
||||
RUSTFLAGS = (builtins.map (a: ''-L ${a}/lib'') [
|
||||
# add libraries here (e.g. pkgs.libvmi)
|
||||
]);
|
||||
LD_LIBRARY_PATH = libPath;
|
||||
# Add glibc, clang, glib, and other headers to bindgen search path
|
||||
BINDGEN_EXTRA_CLANG_ARGS =
|
||||
# Includes normal include path
|
||||
(builtins.map (a: ''-I"${a}/include"'') [
|
||||
# add dev libraries here (e.g. pkgs.libvmi.dev)
|
||||
pkgs.glibc.dev
|
||||
])
|
||||
# Includes with special directory paths
|
||||
++ [
|
||||
''-I"${pkgs.llvmPackages_latest.libclang.lib}/lib/clang/${pkgs.llvmPackages_latest.libclang.version}/include"''
|
||||
''-I"${pkgs.glib.dev}/include/glib-2.0"''
|
||||
''-I${pkgs.glib.out}/lib/glib-2.0/include/''
|
||||
];
|
||||
}
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
use auth::AuthGuard;
|
||||
use util::Address;
|
||||
|
||||
pub mod util;
|
||||
pub mod auth;
|
||||
|
||||
pub enum DataStorageError {
|
||||
AlreadyExists,
|
||||
AntiSpam(String)
|
||||
}
|
||||
|
||||
pub trait DataStorageItem {
|
||||
type Id;
|
||||
type Address: Address;
|
||||
fn id(&self) -> Self::Id;
|
||||
fn address_info(&self) -> Self::Address;
|
||||
}
|
||||
|
||||
pub trait DataStorage<Auth: AuthGuard, Item: DataStorageItem>: IntoIterator<Item = Item> {
|
||||
fn update_instance(&mut self, address: &impl Address, guard: Option<Auth>)
|
||||
-> Result<Item::Id, DataStorageError>;
|
||||
|
||||
fn send_heartbeat(&mut self, address: &impl Address)
|
||||
-> Result<(),DataStorageError>;
|
||||
|
||||
fn peek_instance(&self, id: Item::Id);
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
use std::net::IpAddr;
|
||||
|
||||
use macaddr::MacAddr;
|
||||
|
||||
pub trait Address: PartialEq {
|
||||
fn mac(&self) -> MacAddr;
|
||||
fn ip(&self) -> IpAddr;
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct SimpleAddress {
|
||||
mac: MacAddr,
|
||||
ip: IpAddr
|
||||
}
|
||||
|
||||
impl SimpleAddress {
|
||||
fn new(ip: IpAddr, mac: MacAddr) -> SimpleAddress {
|
||||
return SimpleAddress {
|
||||
ip,
|
||||
mac
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Address for SimpleAddress {
|
||||
fn mac(&self) -> MacAddr {
|
||||
self.mac
|
||||
}
|
||||
|
||||
fn ip(&self) -> IpAddr {
|
||||
self.ip
|
||||
}
|
||||
}
|
14
src/lib.rs
14
src/lib.rs
|
@ -1,9 +1,15 @@
|
|||
pub mod data;
|
||||
pub mod server;
|
||||
|
||||
pub enum MatchmakerError {
|
||||
AlreadyExists,
|
||||
InternalError(String),
|
||||
LimitReached(String),
|
||||
Unauthorized,
|
||||
AntiSpam(String),
|
||||
}
|
||||
|
||||
pub trait Matchmaker {
|
||||
fn run(&mut self);
|
||||
}
|
||||
|
||||
struct DefaultMatchmaker {
|
||||
|
||||
}
|
||||
struct DefaultMatchmaker {}
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
fn main() {
|
||||
|
||||
}
|
||||
fn main() {}
|
||||
|
|
33
src/server/access.rs
Normal file
33
src/server/access.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* -------------------------------------------------------------------------- */
|
||||
/* Interfaces */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::util::Address;
|
||||
|
||||
pub struct AccessInfo {
|
||||
address: Address,
|
||||
}
|
||||
|
||||
pub trait AccessGuard {
|
||||
fn check(&self, info: &AccessInfo);
|
||||
}
|
||||
|
||||
pub enum AccessAction {
|
||||
Allow,
|
||||
Block,
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Rate Limiting */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
struct RateLimitAccessor {}
|
||||
|
||||
pub struct RateLimitAccessGuard {
|
||||
max_read: i32,
|
||||
max_write: i32,
|
||||
max_accessors: i32,
|
||||
accessors: HashMap<Address, RateLimitAccessor>,
|
||||
}
|
|
@ -185,7 +185,7 @@ mod tests {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CodeAuthKey {
|
||||
key: u64
|
||||
key: u64,
|
||||
}
|
||||
|
||||
impl CodeAuthKey {
|
||||
|
@ -197,7 +197,7 @@ impl CodeAuthKey {
|
|||
|
||||
pub fn new<T: Hash + ?Sized>(code: &T) -> CodeAuthKey {
|
||||
CodeAuthKey {
|
||||
key: Self::_generate_key(code)
|
||||
key: Self::_generate_key(code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -211,14 +211,12 @@ impl PartialEq for CodeAuthKey {
|
|||
}
|
||||
|
||||
pub struct CodeAuthGuard {
|
||||
reference_key: CodeAuthKey
|
||||
reference_key: CodeAuthKey,
|
||||
}
|
||||
|
||||
impl CodeAuthGuard {
|
||||
fn new(key: CodeAuthKey) -> Self {
|
||||
Self {
|
||||
reference_key: key
|
||||
}
|
||||
Self { reference_key: key }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,15 +231,14 @@ impl AuthGuard for CodeAuthGuard {
|
|||
/* ------------------------------- Unit Tests ------------------------------- */
|
||||
#[cfg(test)]
|
||||
mod code_authguard_tests {
|
||||
use super::*;
|
||||
use rand::{distr::Alphanumeric, prelude::*};
|
||||
use stopwatch::Stopwatch;
|
||||
use super::*;
|
||||
|
||||
fn _create_rng_string() -> String {
|
||||
let mut rng = rand::rng();
|
||||
let count = rng.random_range(5..100);
|
||||
rng
|
||||
.sample_iter(Alphanumeric)
|
||||
rng.sample_iter(Alphanumeric)
|
||||
.take(count)
|
||||
.map(char::from)
|
||||
.collect()
|
25
src/server/mod.rs
Normal file
25
src/server/mod.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use auth::AuthGuard;
|
||||
use util::Address;
|
||||
|
||||
use crate::MatchmakerError;
|
||||
|
||||
pub mod access;
|
||||
pub mod auth;
|
||||
pub mod storage;
|
||||
pub mod util;
|
||||
|
||||
pub trait DataStorageItem {
|
||||
type Id;
|
||||
fn id(&self) -> Self::Id;
|
||||
fn address_info(&self) -> Address;
|
||||
}
|
||||
|
||||
pub trait DataStorage<Auth: AuthGuard, Item: DataStorageItem>: IntoIterator<Item = Item> {
|
||||
fn reserve_instance(
|
||||
&mut self,
|
||||
address: &Address,
|
||||
guard: Option<Auth>,
|
||||
) -> Result<Item::Id, MatchmakerError>;
|
||||
|
||||
fn remove_instance(&mut self, id: Item::Id);
|
||||
}
|
375
src/server/storage.rs
Normal file
375
src/server/storage.rs
Normal file
|
@ -0,0 +1,375 @@
|
|||
use std::{
|
||||
collections::{BTreeMap, HashMap}, iter::Map, net::IpAddr, sync::RwLock
|
||||
};
|
||||
|
||||
use super::util::Address;
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Instance Storage */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InstanceStorageError {
|
||||
NotFound,
|
||||
Full
|
||||
}
|
||||
|
||||
pub trait Instance {
|
||||
type Id;
|
||||
fn id(&self) -> Self::Id;
|
||||
fn ip(&self) -> IpAddr;
|
||||
}
|
||||
|
||||
pub trait InstanceStorage<Item: Instance>: IntoIterator<Item = Item> {
|
||||
// Create
|
||||
|
||||
// Request
|
||||
fn get_by_address(&self, address: &Address) -> Result<&Item, InstanceStorageError>;
|
||||
fn get_by_address_mut(
|
||||
&mut self,
|
||||
address: &Address,
|
||||
) -> Result<&mut Item, InstanceStorageError>;
|
||||
fn get_by_id(&self, id: Item::Id) -> Result<&Item, InstanceStorageError>;
|
||||
fn get_by_id_mut(&mut self, id: Item::Id) -> Result<&mut Item, InstanceStorageError>;
|
||||
// Delete
|
||||
fn remove_by_address(&mut self, address: &Address) -> Result<(), InstanceStorageError>;
|
||||
fn remove_by_id(&mut self, id: Item::Id) -> Result<(), InstanceStorageError>;
|
||||
}
|
||||
|
||||
/* ------------------------- Default Implementation ------------------------- */
|
||||
pub struct BasicInstance {
|
||||
id: u32,
|
||||
ip: IpAddr
|
||||
}
|
||||
|
||||
impl Instance for BasicInstance {
|
||||
type Id = u32;
|
||||
fn id(&self) -> Self::Id {
|
||||
self.id
|
||||
}
|
||||
fn ip(&self) -> IpAddr {
|
||||
self.ip
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BasicInstanceStorage {
|
||||
max_instances: u32,
|
||||
instances: BTreeMap<Address, BasicInstance>,
|
||||
}
|
||||
|
||||
impl BasicInstanceStorage {
|
||||
fn new(max_instances: u32) -> Self {
|
||||
Self {
|
||||
instances: BTreeMap::new(),
|
||||
max_instances
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InstanceStorage<BasicInstance> for BasicInstanceStorage {
|
||||
fn get_by_address(&self, address: &Address) -> Result<&BasicInstance, InstanceStorageError> {
|
||||
match self.instances.get(address) {
|
||||
Some(v) => {
|
||||
Ok(v)
|
||||
},
|
||||
None => Err(InstanceStorageError::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_by_address_mut(
|
||||
&mut self,
|
||||
address: &Address,
|
||||
) -> Result<&mut BasicInstance, InstanceStorageError> {
|
||||
match self.instances.get_mut(address) {
|
||||
Some(v) => {
|
||||
Ok(v)
|
||||
},
|
||||
None => Err(InstanceStorageError::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_by_id(&self, id: <BasicInstance as Instance>::Id) -> Result<&BasicInstance, InstanceStorageError> {
|
||||
let search_result = self.instances
|
||||
.iter()
|
||||
.find(|v| v.1.id == id);
|
||||
match search_result {
|
||||
Some(v) => {
|
||||
Ok(v.1)
|
||||
},
|
||||
None => {
|
||||
Err(InstanceStorageError::NotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_by_id_mut(&mut self, id: <BasicInstance as Instance>::Id) -> Result<&mut BasicInstance, InstanceStorageError> {
|
||||
let search_result = self.instances
|
||||
.iter_mut()
|
||||
.find(|v| v.1.id == id);
|
||||
match search_result {
|
||||
Some(v) => {
|
||||
Ok(v.1)
|
||||
},
|
||||
None => {
|
||||
Err(InstanceStorageError::NotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_by_address(&mut self, address: &Address) -> Result<(), InstanceStorageError> {
|
||||
match self.instances.remove(address) {
|
||||
Some(_) => {
|
||||
Ok(())
|
||||
},
|
||||
None => Err(InstanceStorageError::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_by_id(&mut self, id: <BasicInstance as Instance>::Id) -> Result<(), InstanceStorageError> {
|
||||
let original_len = self.instances.len();
|
||||
self.instances.retain(|_, v| v.id != id);
|
||||
|
||||
if self.instances.len() == original_len {
|
||||
return Err(InstanceStorageError::NotFound);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for BasicInstanceStorage {
|
||||
type Item = BasicInstance;
|
||||
type IntoIter = std::vec::IntoIter<BasicInstance>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.instances
|
||||
.into_iter()
|
||||
.map(|(_address, instance)| instance)
|
||||
.collect::<Vec<BasicInstance>>()
|
||||
.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod basic_instance_test {
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
|
||||
use macaddr::{MacAddr, MacAddr6};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn _create_sample_address(id: u8) -> Address {
|
||||
let ip: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, id));
|
||||
let mac: MacAddr = MacAddr::V6(MacAddr6::new(id, id, id, id, id, id));
|
||||
|
||||
Address::new(ip, mac)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_by_address_found() {
|
||||
let mut storage = BasicInstanceStorage::new(10);
|
||||
let instance = BasicInstance { id: 42, ip: IpAddr::V4(Ipv4Addr::LOCALHOST) };
|
||||
let addr = _create_sample_address(1);
|
||||
storage.instances.insert(addr.clone(), instance);
|
||||
let fetched = storage.get_by_address(&addr).unwrap();
|
||||
assert_eq!(fetched.id, 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_by_address_not_found() {
|
||||
let storage = BasicInstanceStorage::new(10);
|
||||
let addr = _create_sample_address(1);
|
||||
let result = storage.get_by_address(&addr);
|
||||
assert!(matches!(result, Err(InstanceStorageError::NotFound)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_by_address_mut() {
|
||||
let mut storage = BasicInstanceStorage::new(10);
|
||||
let addr = _create_sample_address(1);
|
||||
let instance = BasicInstance { id: 42, ip: IpAddr::V4(Ipv4Addr::LOCALHOST) };
|
||||
storage.instances.insert(addr.clone(), instance);
|
||||
{
|
||||
let fetched_mut = storage.get_by_address_mut(&addr).unwrap();
|
||||
fetched_mut.id = 100;
|
||||
}
|
||||
let fetched = storage.get_by_address(&addr).unwrap();
|
||||
assert_eq!(fetched.id, 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_by_id_found() {
|
||||
let mut storage = BasicInstanceStorage::new(10);
|
||||
let addr = _create_sample_address(1);
|
||||
let instance = BasicInstance { id: 42, ip: IpAddr::V4(Ipv4Addr::LOCALHOST) };
|
||||
storage.instances.insert(addr, instance);
|
||||
let fetched = storage.get_by_id(42).unwrap();
|
||||
assert_eq!(fetched.id, 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_by_id_not_found() {
|
||||
let storage = BasicInstanceStorage::new(10);
|
||||
let result = storage.get_by_id(42);
|
||||
assert!(matches!(result, Err(InstanceStorageError::NotFound)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_by_id_mut() {
|
||||
let mut storage = BasicInstanceStorage::new(10);
|
||||
let addr = _create_sample_address(1);
|
||||
let instance = BasicInstance { id: 42, ip: IpAddr::V4(Ipv4Addr::LOCALHOST) };
|
||||
storage.instances.insert(addr, instance);
|
||||
{
|
||||
let fetched_mut = storage.get_by_id_mut(42);
|
||||
assert!(fetched_mut.is_ok());
|
||||
let fetched_mut = fetched_mut.unwrap();
|
||||
fetched_mut.id = 99;
|
||||
}
|
||||
let fetched = storage.get_by_id(42);
|
||||
assert!(fetched.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_by_address() {
|
||||
let mut storage = BasicInstanceStorage::new(10);
|
||||
let addr = _create_sample_address(1);
|
||||
storage.instances.insert(addr.clone(), BasicInstance { id: 42, ip: IpAddr::V4(Ipv4Addr::LOCALHOST) });
|
||||
assert!(storage.remove_by_address(&addr).is_ok());
|
||||
assert!(storage.instances.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_by_address_not_found() {
|
||||
let mut storage = BasicInstanceStorage::new(10);
|
||||
let addr = _create_sample_address(1);
|
||||
let result = storage.remove_by_address(&addr);
|
||||
assert!(matches!(result, Err(InstanceStorageError::NotFound)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_by_id() {
|
||||
let mut storage = BasicInstanceStorage::new(10);
|
||||
let addr = _create_sample_address(1);
|
||||
storage.instances.insert(addr, BasicInstance { id: 42, ip: IpAddr::V4(Ipv4Addr::LOCALHOST) });
|
||||
assert!(storage.remove_by_id(42).is_ok());
|
||||
assert!(storage.instances.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_by_id_not_found() {
|
||||
let mut storage = BasicInstanceStorage::new(10);
|
||||
let result = storage.remove_by_id(42);
|
||||
assert!(matches!(result, Err(InstanceStorageError::NotFound)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_iterator() {
|
||||
let mut storage = BasicInstanceStorage::new(10);
|
||||
storage.instances.insert(_create_sample_address(1), BasicInstance { id: 42, ip: IpAddr::V4(Ipv4Addr::LOCALHOST) });
|
||||
storage.instances.insert(_create_sample_address(2), BasicInstance { id: 43, ip: IpAddr::V4(Ipv4Addr::LOCALHOST) });
|
||||
let instances: Vec<_> = storage.into_iter().collect();
|
||||
assert_eq!(instances.len(), 2);
|
||||
let ids: Vec<u32> = instances.into_iter().map(|inst| inst.id).collect();
|
||||
assert!(ids.contains(&42));
|
||||
assert!(ids.contains(&43));
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Attribute System */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
// TODO: Optimize the heck out of this - This is effectively a placeholder
|
||||
// It will likely be *very* space inefficient.
|
||||
|
||||
/* -------------------------------- Metadata -------------------------------- */
|
||||
#[repr(u8)]
|
||||
#[derive(PartialEq)]
|
||||
enum AttributeType {
|
||||
Float,
|
||||
Integer,
|
||||
Boolean,
|
||||
String,
|
||||
}
|
||||
|
||||
enum AttributeValue {
|
||||
Float(f32),
|
||||
Integer(i32),
|
||||
Boolean(bool),
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl AttributeValue {
|
||||
fn get_type(&self) -> AttributeType {
|
||||
match self {
|
||||
Float => AttributeType::Float,
|
||||
Integer => AttributeType::Integer,
|
||||
Boolean => AttributeType::Boolean,
|
||||
String => AttributeType::String,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum AttributeError {
|
||||
InvalidType(AttributeType),
|
||||
InvalidKey(String)
|
||||
}
|
||||
|
||||
/* --------------------------------- Schema --------------------------------- */
|
||||
|
||||
// TODO: Builder pattern to create this
|
||||
pub struct AttributeSchema {
|
||||
attribute_schema: Map<String, AttributeSchemaElement>,
|
||||
}
|
||||
|
||||
pub struct AttributeSchemaElement {
|
||||
default_type_and_value: AttributeValue,
|
||||
}
|
||||
|
||||
/* ---------------------------------- List ---------------------------------- */
|
||||
|
||||
struct AttributeListElement {
|
||||
value: AttributeValue,
|
||||
}
|
||||
|
||||
pub struct AttributeList<'a> {
|
||||
schema: &'a AttributeSchema,
|
||||
data: RwLock<HashMap<String, AttributeListElement>>,
|
||||
}
|
||||
|
||||
impl<'a> AttributeList<'a> {
|
||||
fn new(schema: &'a AttributeSchema) -> Self {
|
||||
Self {
|
||||
schema,
|
||||
// TODO: Populate with initial values
|
||||
data: RwLock::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_attribute(&mut self, key: &str, value: AttributeValue) -> Result<(), AttributeError> {
|
||||
// ! Naive unwrap is present
|
||||
let mut write = self.data.write().unwrap();
|
||||
{
|
||||
let element_read = &write[key];
|
||||
if value.get_type() != element_read.value.get_type() {
|
||||
return Err(AttributeError::InvalidType(value.get_type()));
|
||||
}
|
||||
}
|
||||
{
|
||||
// ! Double query
|
||||
let element = write.get_mut(key);
|
||||
match element {
|
||||
Some(v) => {
|
||||
v.value = value;
|
||||
},
|
||||
None => {
|
||||
// TODO: Verify that this is the best thing to do
|
||||
return Err(AttributeError::InvalidKey(key.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
33
src/server/util.rs
Normal file
33
src/server/util.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use std::net::IpAddr;
|
||||
|
||||
use macaddr::MacAddr;
|
||||
|
||||
#[derive(Debug, Hash, PartialOrd, Ord, Clone)]
|
||||
pub struct Address {
|
||||
mac: MacAddr,
|
||||
ip: IpAddr,
|
||||
}
|
||||
|
||||
impl Address {
|
||||
pub fn new(ip: IpAddr, mac: MacAddr) -> Self {
|
||||
return Self { ip, mac };
|
||||
}
|
||||
|
||||
pub fn mac(&self) -> MacAddr {
|
||||
self.mac
|
||||
}
|
||||
|
||||
pub fn ip(&self) -> IpAddr {
|
||||
self.ip
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Address {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ip == other.ip
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Address {
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue