use std::fmt;
use std::hash::{Hash, Hasher};
use std::sync::{Arc, RwLock};
use super::uoid::Uoid;
pub struct KeyData {
pub uoid: Uoid,
pub start_pos: u32,
pub data_len: u32,
pub active_refs: u16,
}
impl KeyData {
pub fn name(&self) -> &str {
&self.uoid.object_name
}
pub fn class_type(&self) -> u16 {
self.uoid.class_type
}
}
impl fmt::Debug for KeyData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"KeyData({:?}, pos={}, len={})",
self.uoid, self.start_pos, self.data_len
)
}
}
#[derive(Clone)]
pub struct Key(Option<Arc<RwLock<KeyData>>>);
impl Key {
pub fn null() -> Self {
Key(None)
}
pub fn from_uoid(uoid: Uoid) -> Self {
Key(Some(Arc::new(RwLock::new(KeyData {
uoid,
start_pos: 0,
data_len: 0,
active_refs: 0,
}))))
}
pub fn from_uoid_with_pos(uoid: Uoid, start_pos: u32, data_len: u32) -> Self {
Key(Some(Arc::new(RwLock::new(KeyData {
uoid,
start_pos,
data_len,
active_refs: 0,
}))))
}
pub fn is_null(&self) -> bool {
self.0.is_none()
}
pub fn is_some(&self) -> bool {
self.0.is_some()
}
pub fn inner(&self) -> Option<&Arc<RwLock<KeyData>>> {
self.0.as_ref()
}
pub fn uoid(&self) -> Uoid {
self.0
.as_ref()
.expect("uoid() called on null key")
.read()
.unwrap()
.uoid
.clone()
}
pub fn name(&self) -> String {
self.0
.as_ref()
.expect("name() called on null key")
.read()
.unwrap()
.uoid
.object_name
.clone()
}
pub fn class_type(&self) -> u16 {
self.0
.as_ref()
.expect("class_type() called on null key")
.read()
.unwrap()
.uoid
.class_type
}
pub fn try_name(&self) -> Option<String> {
self.0
.as_ref()
.map(|arc| arc.read().unwrap().uoid.object_name.clone())
}
}
impl Default for Key {
fn default() -> Self {
Key::null()
}
}
impl PartialEq for Key {
fn eq(&self, other: &Self) -> bool {
match (&self.0, &other.0) {
(None, None) => true,
(Some(a), Some(b)) => Arc::ptr_eq(a, b),
_ => false,
}
}
}
impl Eq for Key {}
impl Hash for Key {
fn hash<H: Hasher>(&self, state: &mut H) {
match &self.0 {
None => 0u8.hash(state),
Some(arc) => {
1u8.hash(state);
Arc::as_ptr(arc).hash(state);
}
}
}
}
impl PartialOrd for Key {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Key {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match (&self.0, &other.0) {
(None, None) => std::cmp::Ordering::Equal,
(None, Some(_)) => std::cmp::Ordering::Less,
(Some(_), None) => std::cmp::Ordering::Greater,
(Some(a), Some(b)) => Arc::as_ptr(a).cmp(&Arc::as_ptr(b)),
}
}
}
impl fmt::Debug for Key {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
None => write!(f, "Key(nil)"),
Some(arc) => {
let data = arc.read().unwrap();
write!(f, "Key({:?})", data.uoid)
}
}
}
}
impl fmt::Display for Key {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
None => write!(f, "(nil)"),
Some(arc) => {
let data = arc.read().unwrap();
write!(f, "{}", data.uoid)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::location::Location;
#[test]
fn test_null_key() {
let k = Key::null();
assert!(k.is_null());
assert!(!k.is_some());
}
#[test]
fn test_key_from_uoid() {
let uoid = Uoid::new(Location::new(100, 0), 0x004C, "TestDrawable".into());
let k = Key::from_uoid(uoid);
assert!(!k.is_null());
assert_eq!(k.name(), "TestDrawable");
assert_eq!(k.class_type(), 0x004C);
}
#[test]
fn test_key_clone_shares_data() {
let uoid = Uoid::new(Location::new(100, 0), 0x0001, "Object".into());
let k1 = Key::from_uoid(uoid);
let k2 = k1.clone();
assert_eq!(k1, k2);
}
#[test]
fn test_key_with_pos() {
let uoid = Uoid::new(Location::new(100, 0), 0x0004, "Texture".into());
let k = Key::from_uoid_with_pos(uoid, 1000, 500);
let arc = k.inner().unwrap();
let data = arc.read().unwrap();
assert_eq!(data.start_pos, 1000);
assert_eq!(data.data_len, 500);
}
#[test]
fn test_null_keys_equal() {
assert_eq!(Key::null(), Key::null());
}
#[test]
fn test_different_keys_not_equal() {
let k1 = Key::from_uoid(Uoid::new(Location::new(1, 0), 1, "A".into()));
let k2 = Key::from_uoid(Uoid::new(Location::new(1, 0), 1, "A".into()));
assert_ne!(k1, k2);
}
}