use crate::{Interface, MessageGroup, NoMessage};
use std::cmp::Ordering;
pub const SERVER_ID_LIMIT: u32 = 0xFF00_0000;
pub trait ObjectMetadata: Clone {
fn child(&self) -> Self;
}
impl ObjectMetadata for () {
fn child(&self) {}
}
#[derive(Clone)]
pub struct Object<Meta: ObjectMetadata> {
pub interface: &'static str,
pub version: u32,
pub requests: &'static [crate::wire::MessageDesc],
pub events: &'static [crate::wire::MessageDesc],
pub meta: Meta,
pub childs_from_events: fn(u16, u32, &Meta) -> Option<Object<Meta>>,
pub childs_from_requests: fn(u16, u32, &Meta) -> Option<Object<Meta>>,
}
impl<Meta: ObjectMetadata + std::fmt::Debug> std::fmt::Debug for Object<Meta> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Object")
.field("interface", &self.interface)
.field("version", &self.version)
.field("requests", &self.requests)
.field("events", &self.events)
.field("meta", &self.meta)
.finish()
}
}
impl<Meta: ObjectMetadata> Object<Meta> {
pub fn from_interface<I: Interface>(version: u32, meta: Meta) -> Object<Meta> {
Object {
interface: I::NAME,
version,
requests: I::Request::MESSAGES,
events: I::Event::MESSAGES,
meta,
childs_from_events: childs_from::<I::Event, Meta>,
childs_from_requests: childs_from::<I::Request, Meta>,
}
}
pub fn event_child(&self, opcode: u16) -> Option<Object<Meta>> {
(self.childs_from_events)(opcode, self.version, &self.meta)
}
pub fn request_child(&self, opcode: u16) -> Option<Object<Meta>> {
(self.childs_from_requests)(opcode, self.version, &self.meta)
}
pub fn is_interface<I: Interface>(&self) -> bool {
self.interface == I::NAME
}
pub fn placeholder(meta: Meta) -> Object<Meta> {
Object {
interface: "",
version: 0,
requests: &[],
events: &[],
meta,
childs_from_events: childs_from::<NoMessage, Meta>,
childs_from_requests: childs_from::<NoMessage, Meta>,
}
}
}
fn childs_from<M: MessageGroup, Meta: ObjectMetadata>(
opcode: u16,
version: u32,
meta: &Meta,
) -> Option<Object<Meta>> {
M::child(opcode, version, meta)
}
#[derive(Default, Debug)]
pub struct ObjectMap<Meta: ObjectMetadata> {
client_objects: Vec<Option<Object<Meta>>>,
server_objects: Vec<Option<Object<Meta>>>,
}
impl<Meta: ObjectMetadata> ObjectMap<Meta> {
pub fn new() -> ObjectMap<Meta> {
ObjectMap { client_objects: Vec::new(), server_objects: Vec::new() }
}
pub fn find(&self, id: u32) -> Option<Object<Meta>> {
if id == 0 {
None
} else if id >= SERVER_ID_LIMIT {
self.server_objects.get((id - SERVER_ID_LIMIT) as usize).and_then(Clone::clone)
} else {
self.client_objects.get((id - 1) as usize).and_then(Clone::clone)
}
}
pub fn remove(&mut self, id: u32) {
if id == 0 {
} else if id >= SERVER_ID_LIMIT {
if let Some(place) = self.server_objects.get_mut((id - SERVER_ID_LIMIT) as usize) {
*place = None;
}
} else if let Some(place) = self.client_objects.get_mut((id - 1) as usize) {
*place = None;
}
}
#[allow(clippy::result_unit_err)]
pub fn insert_at(&mut self, id: u32, object: Object<Meta>) -> Result<(), ()> {
if id == 0 {
Err(())
} else if id >= SERVER_ID_LIMIT {
insert_in_at(&mut self.server_objects, (id - SERVER_ID_LIMIT) as usize, object)
} else {
insert_in_at(&mut self.client_objects, (id - 1) as usize, object)
}
}
pub fn client_insert_new(&mut self, object: Object<Meta>) -> u32 {
insert_in(&mut self.client_objects, object) + 1
}
pub fn server_insert_new(&mut self, object: Object<Meta>) -> u32 {
insert_in(&mut self.server_objects, object) + SERVER_ID_LIMIT
}
#[allow(clippy::result_unit_err)]
pub fn with<T, F: FnOnce(&mut Object<Meta>) -> T>(&mut self, id: u32, f: F) -> Result<T, ()> {
if id == 0 {
Err(())
} else if id >= SERVER_ID_LIMIT {
if let Some(&mut Some(ref mut obj)) =
self.server_objects.get_mut((id - SERVER_ID_LIMIT) as usize)
{
Ok(f(obj))
} else {
Err(())
}
} else if let Some(&mut Some(ref mut obj)) = self.client_objects.get_mut((id - 1) as usize)
{
Ok(f(obj))
} else {
Err(())
}
}
pub fn with_all<F: FnMut(u32, &mut Object<Meta>)>(&mut self, mut f: F) {
for (id, place) in self.client_objects.iter_mut().enumerate() {
if let Some(ref mut obj) = *place {
f(id as u32 + 1, obj);
}
}
for (id, place) in self.server_objects.iter_mut().enumerate() {
if let Some(ref mut obj) = *place {
f(id as u32 + SERVER_ID_LIMIT, obj);
}
}
}
}
fn insert_in<Meta: ObjectMetadata>(
store: &mut Vec<Option<Object<Meta>>>,
object: Object<Meta>,
) -> u32 {
match store.iter().position(Option::is_none) {
Some(id) => {
store[id] = Some(object);
id as u32
}
None => {
store.push(Some(object));
(store.len() - 1) as u32
}
}
}
fn insert_in_at<Meta: ObjectMetadata>(
store: &mut Vec<Option<Object<Meta>>>,
id: usize,
object: Object<Meta>,
) -> Result<(), ()> {
match id.cmp(&store.len()) {
Ordering::Greater => Err(()),
Ordering::Equal => {
store.push(Some(object));
Ok(())
}
Ordering::Less => {
let previous = &mut store[id];
if !previous.is_none() {
return Err(());
}
*previous = Some(object);
Ok(())
}
}
}