use core::str::FromStr;
use alloc::boxed::Box;
use crate::{
Arc, PortCollection, PortCollectionAccessors, PortCommons, RwLock,
any_port_value::AnyPortValue,
bind::{
BindCommons, BindIn, BindInOut, BindOut,
bound_value::{self, BoundValue, BoundValuePtr, BoundValueReadGuard, BoundValueWriteGuard},
sequence_number::SequenceNumber,
},
error::Error,
port_variant::PortVariant,
};
#[derive(Debug, Clone)]
pub struct InOutBound(BoundValuePtr);
impl InOutBound {
pub fn new<T: AnyPortValue>() -> Self {
Self(Arc::new(RwLock::new((
Box::new(BoundValue::<T>::empty()),
SequenceNumber::default(),
None,
core::any::type_name::<T>(),
))))
}
pub fn new_parseable<T: AnyPortValue + core::str::FromStr>() -> Self {
Self(Arc::new(RwLock::new((
Box::new(BoundValue::<T>::empty()),
SequenceNumber::default(),
Some(bound_value::from_str_setter::<T> as bound_value::FromStrSetter),
core::any::type_name::<T>(),
))))
}
pub fn with_value<T: AnyPortValue>(value: T) -> Self {
let mut sq = SequenceNumber::default();
sq.increment();
Self(Arc::new(RwLock::new((
Box::new(BoundValue::new(value)),
sq,
None,
core::any::type_name::<T>(),
))))
}
pub fn with_value_parseable<T: AnyPortValue + core::str::FromStr>(value: T) -> Self {
let mut sq = SequenceNumber::default();
sq.increment();
Self(Arc::new(RwLock::new((
Box::new(BoundValue::new(value)),
sq,
Some(bound_value::from_str_setter::<T> as bound_value::FromStrSetter),
core::any::type_name::<T>(),
))))
}
pub(crate) fn init_from_str(&mut self, s: &str) -> Result<(), Error> {
let any_value = &mut *self.0.write();
if let Some(setter) = any_value.2 {
setter(s, any_value.0.as_mut())?;
any_value.1.increment();
Ok(())
} else {
Err(Error::FromStr)
}
}
pub fn from_collection<T: AnyPortValue>(
collection: &impl PortCollectionAccessors,
name: impl Into<Arc<str>>,
) -> Result<Self, Error> {
let name = name.into();
let mut port = Self::new::<T>();
collection.give_to_bound(&name, &mut port)?;
Ok(port)
}
pub fn from_collection_parseable<T: AnyPortValue + FromStr>(
collection: &impl PortCollectionAccessors,
name: impl Into<Arc<str>>,
) -> Result<Self, Error> {
let name = name.into();
let mut port = Self::new_parseable::<T>();
collection.give_to_bound(&name, &mut port)?;
Ok(port)
}
pub(crate) fn from_value(ptr: BoundValuePtr) -> Self {
Self(ptr)
}
pub(crate) fn is<T: AnyPortValue>(&self) -> bool {
self.0
.read()
.0
.as_ref()
.as_any()
.downcast_ref::<BoundValue<T>>()
.is_some()
}
pub(crate) fn set_value(&mut self, value: BoundValuePtr) -> Result<(), Error> {
let x = self.0.read().0.type_id();
let y = value.read().0.type_id();
if x == y {
self.0 = value;
Ok(())
} else {
Err(Error::DataType)
}
}
pub(crate) fn into_inner<T: AnyPortValue>(self) -> Result<Option<T>, Error> {
let any_value = &mut *self.0.write();
let p = &mut any_value.0;
let p_mut = p.as_mut();
if let Some(t_ref) = p_mut.as_mut_any().downcast_mut::<BoundValue<T>>() {
any_value.1.increment();
Ok(t_ref.take())
} else {
Err(Error::DataType)
}
}
pub(crate) fn data_type(&self) -> &str {
bound_value::short_type_name(self.0.read().3)
}
}
impl PortCommons for InOutBound {
fn use_from_variant(&mut self, other: &PortVariant) -> Result<(), Error> {
match other {
PortVariant::InBound(port) => self.use_from_bound(port),
PortVariant::InOutBound(port) => self.use_from_bound(port),
PortVariant::OutBound(port) => self.use_from_bound(port),
}
}
fn use_from_collection(&mut self, name: &str, collection: &dyn PortCollection) -> Result<(), Error> {
if let Some(variant) = collection.find(name) {
self.use_from_variant(variant)
} else {
Err(Error::OtherNotFound { name: name.into() })
}
}
}
impl BindCommons for InOutBound {
fn sequence_number(&self) -> u32 {
self.0.read().1.value()
}
fn use_from_bound(&mut self, other: &dyn BindCommons) -> Result<(), Error> {
self.set_value(other.value())
}
fn value(&self) -> BoundValuePtr {
self.0.clone()
}
}
impl<T: AnyPortValue> BindIn<T> for InOutBound {
fn get(&self) -> Result<Option<T>, Error>
where
T: Clone,
{
let any_value = &*self.0.read();
if let Some(t_ref) = any_value
.0
.as_ref()
.as_any()
.downcast_ref::<BoundValue<T>>()
{
Ok(t_ref.get())
} else {
Err(Error::DataType)
}
}
fn read(&self) -> Result<BoundValueReadGuard<T>, Error> {
BoundValueReadGuard::new(self.0.clone())
}
fn try_read(&self) -> Result<BoundValueReadGuard<T>, Error> {
BoundValueReadGuard::try_new(self.0.clone())
}
}
impl<T: AnyPortValue> BindInOut<T> for InOutBound {
fn replace(&mut self, value: T) -> Result<Option<T>, Error> {
let any_value = &mut *self.0.write();
let p = &mut any_value.0;
let p_mut = p.as_mut();
if let Some(t_ref) = p_mut.as_mut_any().downcast_mut::<BoundValue<T>>() {
any_value.1.increment();
Ok(t_ref.replace(value))
} else {
Err(Error::DataType)
}
}
fn take(&mut self) -> Result<Option<T>, Error> {
let any_value = &mut *self.0.write();
let p = &mut any_value.0;
let p_mut = p.as_mut();
if let Some(t_ref) = p_mut.as_mut_any().downcast_mut::<BoundValue<T>>() {
any_value.1.increment();
Ok(t_ref.take())
} else {
Err(Error::DataType)
}
}
}
impl<T: AnyPortValue> BindOut<T> for InOutBound {
fn set(&mut self, value: T) -> Result<(), Error> {
let any_value = &mut *self.0.write();
let p = &mut any_value.0;
let p_mut = p.as_mut();
if let Some(t_ref) = p_mut.as_mut_any().downcast_mut::<BoundValue<T>>() {
t_ref.set(value);
any_value.1.increment();
Ok(())
} else {
Err(Error::DataType)
}
}
fn write(&mut self) -> Result<BoundValueWriteGuard<T>, Error> {
BoundValueWriteGuard::new(self.0.clone())
}
fn try_write(&mut self) -> Result<BoundValueWriteGuard<T>, Error> {
BoundValueWriteGuard::try_new(self.0.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
const fn is_normal<T: Sized + Send + Sync>() {}
#[test]
const fn normal_types() {
is_normal::<&InOutBound>();
is_normal::<InOutBound>();
}
#[test]
fn into_inner() {
let port = InOutBound::new::<i32>();
assert_eq!(port.into_inner::<f64>(), Err(Error::DataType));
}
#[test]
fn into_inner_empty() {
let port = InOutBound::new::<i32>();
assert_eq!(port.into_inner::<i32>(), Ok(None));
}
#[test]
fn into_inner_type_mismatch_with_value() {
let port = InOutBound::with_value(42i32);
assert_eq!(port.into_inner::<alloc::string::String>(), Err(Error::DataType));
}
#[test]
fn into_inner_type_mismatch_different_numeric() {
let port = InOutBound::with_value(3.24f64);
assert_eq!(port.into_inner::<u8>(), Err(Error::DataType));
}
}