use std::{
cmp::Ordering,
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
str::FromStr,
};
use arrayvec::ArrayString;
use js_sys::JsString;
use serde::{Deserialize, Serialize};
use wasm_bindgen::{JsCast, JsValue};
use crate::{game, js_collections::JsCollectionFromValue, objects::RoomObject, traits::MaybeHasId};
mod errors;
mod raw;
pub use errors::*;
pub use raw::*;
#[derive(Serialize, Deserialize)]
#[serde(transparent, bound = "")]
pub struct ObjectId<T> {
raw: RawObjectId,
#[serde(skip)]
phantom: PhantomData<fn() -> T>,
}
impl<T> Copy for ObjectId<T> {}
impl<T> Clone for ObjectId<T> {
fn clone(&self) -> ObjectId<T> {
*self
}
}
impl<T> fmt::Debug for ObjectId<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.raw.fmt(f)
}
}
impl<T> PartialEq for ObjectId<T> {
fn eq(&self, o: &ObjectId<T>) -> bool {
self.raw.eq(&o.raw)
}
}
impl<T> Eq for ObjectId<T> {}
impl<T> Hash for ObjectId<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.raw.hash(state)
}
}
impl<T> PartialOrd<ObjectId<T>> for ObjectId<T> {
#[inline]
fn partial_cmp(&self, other: &ObjectId<T>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T> Ord for ObjectId<T> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.raw.cmp(&other.raw)
}
}
impl<T> FromStr for ObjectId<T> {
type Err = RawObjectIdParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let raw: RawObjectId = s.parse()?;
Ok(raw.into())
}
}
impl<T> fmt::Display for ObjectId<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.raw.fmt(f)
}
}
impl<T> ObjectId<T> {
pub fn into_type<U>(self) -> ObjectId<U> {
RawObjectId::from(self).into()
}
pub fn from_packed(packed: u128) -> Self {
RawObjectId::from_packed(packed).into()
}
pub fn to_u128(self) -> u128 {
self.raw.into()
}
pub fn to_array_string(&self) -> ArrayString<24> {
self.raw.to_array_string()
}
pub fn try_resolve(self) -> Result<Option<T>, RoomObject>
where
T: MaybeHasId + JsCast,
{
match game::get_object_by_id_erased(&self.raw) {
Some(v) => v.dyn_into().map(|v| Some(v)),
None => Ok(None),
}
}
pub fn resolve(self) -> Option<T>
where
T: MaybeHasId + JsCast,
{
game::get_object_by_id_typed(&self)
}
}
impl<T> PartialEq<RawObjectId> for ObjectId<T> {
#[inline]
fn eq(&self, other: &RawObjectId) -> bool {
self.raw == *other
}
}
impl<T> PartialEq<ObjectId<T>> for RawObjectId {
#[inline]
fn eq(&self, other: &ObjectId<T>) -> bool {
*self == other.raw
}
}
impl<T> PartialOrd<RawObjectId> for ObjectId<T> {
#[inline]
fn partial_cmp(&self, other: &RawObjectId) -> Option<Ordering> {
Some(self.raw.cmp(other))
}
}
impl<T> PartialOrd<ObjectId<T>> for RawObjectId {
#[inline]
fn partial_cmp(&self, other: &ObjectId<T>) -> Option<Ordering> {
Some(self.cmp(&other.raw))
}
}
impl<T> From<RawObjectId> for ObjectId<T> {
fn from(raw: RawObjectId) -> Self {
ObjectId {
raw,
phantom: PhantomData,
}
}
}
impl<T> From<ObjectId<T>> for RawObjectId {
fn from(id: ObjectId<T>) -> Self {
id.raw
}
}
impl<T> From<ObjectId<T>> for ArrayString<24> {
fn from(id: ObjectId<T>) -> Self {
id.to_array_string()
}
}
impl<T> From<ObjectId<T>> for String {
fn from(id: ObjectId<T>) -> Self {
id.to_string()
}
}
impl<T> From<ObjectId<T>> for u128 {
fn from(id: ObjectId<T>) -> Self {
id.raw.into()
}
}
impl<T> From<u128> for ObjectId<T> {
fn from(packed: u128) -> Self {
Self::from_packed(packed)
}
}
impl<T> JsCollectionFromValue for ObjectId<T> {
fn from_value(val: JsValue) -> Self {
let val: JsString = val.unchecked_into();
let val: String = val.into();
val.parse().expect("valid id string")
}
}