use core::ops::{Deref, DerefMut};
use alloc::boxed::Box;
use crate::{
Arc, RwLock, RwLockReadGuard, RwLockWriteGuard, any_port_value::AnyPortValue, bind::sequence_number::SequenceNumber,
error::Error,
};
pub(crate) type FromStrSetter = fn(&str, &mut dyn AnyPortValue) -> Result<(), Error>;
pub(crate) fn from_str_setter<T: AnyPortValue + core::str::FromStr>(
s: &str,
any: &mut dyn AnyPortValue,
) -> Result<(), Error> {
let v = T::from_str(s).map_err(|_| Error::FromStr)?;
if let Some(pv) = any.as_mut_any().downcast_mut::<BoundValue<T>>() {
pv.set(v);
Ok(())
} else {
Err(Error::DataType)
}
}
pub(crate) type BoundTuple = (Box<dyn AnyPortValue>, SequenceNumber, Option<FromStrSetter>, &'static str);
pub(crate) type BoundValuePtr = Arc<RwLock<BoundTuple>>;
pub(crate) fn short_type_name(full: &'static str) -> &'static str {
full.rsplit("::").next().unwrap_or(full)
}
#[derive(Default)]
#[repr(transparent)]
pub(crate) struct BoundValue<T>(Option<T>);
impl<T> BoundValue<T> {
pub(crate) fn empty() -> Self {
Self(None)
}
pub(crate) fn new(value: T) -> Self {
Self(Some(value))
}
pub(crate) fn replace(&mut self, value: T) -> Option<T> {
self.0.replace(value)
}
pub(crate) fn set(&mut self, value: T) {
self.0 = Some(value)
}
pub(crate) fn take(&mut self) -> Option<T> {
self.0.take()
}
}
impl<T: Clone> BoundValue<T> {
pub(crate) fn get(&self) -> Option<T> {
self.0.clone()
}
}
impl<T: core::fmt::Debug> core::fmt::Debug for BoundValue<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("PortValue").field(&self.0).finish()
}
}
#[must_use = "a `BoundValueReadGuard` should be used"]
pub struct BoundValueReadGuard<T> {
_guard: RwLockReadGuard<'static, BoundTuple>,
_value: BoundValuePtr,
ptr_t: *const T,
}
impl<T> Deref for BoundValueReadGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
#[allow(unsafe_code)]
unsafe {
&*self.ptr_t
}
}
}
impl<T: 'static> BoundValueReadGuard<T> {
pub(crate) fn new(value: BoundValuePtr) -> Result<Self, Error> {
let guard = value.read();
#[allow(unsafe_code)]
let guard: RwLockReadGuard<'static, BoundTuple> =
unsafe { core::mem::transmute::<RwLockReadGuard<'_, BoundTuple>, RwLockReadGuard<'static, BoundTuple>>(guard) };
let ptr_t = if let Some(v) = guard
.0
.as_ref()
.as_any()
.downcast_ref::<BoundValue<T>>()
{
if let Some(inner) = &v.0 {
inner as *const T
} else {
return Err(Error::NoValueSet);
}
} else {
return Err(Error::DataType);
};
Ok(Self {
_guard: guard,
_value: value,
ptr_t,
})
}
pub(crate) fn try_new(value: BoundValuePtr) -> Result<Self, Error> {
if let Some(guard) = value.clone().try_read() {
#[allow(unsafe_code)]
let guard: RwLockReadGuard<'static, BoundTuple> = unsafe {
core::mem::transmute::<RwLockReadGuard<'_, BoundTuple>, RwLockReadGuard<'static, BoundTuple>>(guard)
};
let ptr_t = if let Some(v) = guard
.0
.as_ref()
.as_any()
.downcast_ref::<BoundValue<T>>()
{
if let Some(inner) = &v.0 {
inner as *const T
} else {
return Err(Error::NoValueSet);
}
} else {
return Err(Error::DataType);
};
Ok(Self {
_guard: guard,
_value: value,
ptr_t,
})
} else {
Err(Error::IsLocked)
}
}
}
#[must_use = "a `BoundValueWriteGuard` should be used"]
pub struct BoundValueWriteGuard<T> {
guard: RwLockWriteGuard<'static, BoundTuple>,
_value: BoundValuePtr,
ptr_t: *mut T,
modified: bool,
}
impl<T> Deref for BoundValueWriteGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
#[allow(unsafe_code)]
unsafe {
&*self.ptr_t
}
}
}
impl<T> DerefMut for BoundValueWriteGuard<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.modified = true;
#[allow(unsafe_code)]
unsafe {
&mut *self.ptr_t
}
}
}
impl<T> Drop for BoundValueWriteGuard<T> {
fn drop(&mut self) {
if self.modified {
self.guard.1.increment();
}
}
}
impl<T: 'static> BoundValueWriteGuard<T> {
pub(crate) fn new(value: BoundValuePtr) -> Result<Self, Error> {
let guard = value.write();
#[allow(unsafe_code)]
let mut guard: RwLockWriteGuard<'static, BoundTuple> = unsafe {
core::mem::transmute::<RwLockWriteGuard<'_, BoundTuple>, RwLockWriteGuard<'static, BoundTuple>>(guard)
};
let ptr_t = if let Some(v) = guard
.0
.as_mut()
.as_mut_any()
.downcast_mut::<BoundValue<T>>()
{
if let Some(inner) = &mut v.0 {
inner as *mut T
} else {
return Err(Error::NoValueSet);
}
} else {
return Err(Error::DataType);
};
Ok(Self {
guard,
_value: value,
ptr_t,
modified: false,
})
}
pub(crate) fn try_new(value: BoundValuePtr) -> Result<Self, Error> {
if let Some(guard) = value.clone().try_write() {
#[allow(unsafe_code)]
let mut guard: RwLockWriteGuard<'static, BoundTuple> = unsafe {
core::mem::transmute::<RwLockWriteGuard<'_, BoundTuple>, RwLockWriteGuard<'static, BoundTuple>>(guard)
};
let ptr_t = if let Some(v) = guard
.0
.as_mut()
.as_mut_any()
.downcast_mut::<BoundValue<T>>()
{
if let Some(inner) = &mut v.0 {
inner as *mut T
} else {
return Err(Error::NoValueSet);
}
} else {
return Err(Error::DataType);
};
Ok(Self {
guard,
_value: value,
ptr_t,
modified: false,
})
} else {
Err(Error::IsLocked)
}
}
}
#[cfg(test)]
mod tests {
use alloc::string::String;
use super::*;
const fn is_normal<T: Sized + Send + Sync>() {}
#[test]
const fn normal_types() {
is_normal::<&BoundValue<i32>>();
is_normal::<BoundValue<String>>();
}
#[test]
fn from_str_setter_type_mismatch_returns_data_type_error() {
let mut pv: Box<dyn AnyPortValue> = Box::new(BoundValue::new(1.0f64));
let result = from_str_setter::<i32>("42", pv.as_mut());
assert_eq!(result, Err(Error::DataType));
}
}