use core::ops::{Deref, DerefMut};
use alloc::boxed::Box;
use crate::{
ConstString, PortVec,
any_port_value::AnyPortValue,
bind::{
BindCommons,
bound_value::{BoundValueReadGuard, BoundValueWriteGuard},
},
collections::{PortCollection, PortCollectionAccessors, PortCollectionAccessorsCommon},
error::Error,
port_variant::PortVariant,
};
#[derive(Clone)]
#[repr(transparent)]
pub struct PortArray<const S: usize>(pub [(ConstString, PortVariant); S]);
impl<const S: usize> PortArray<S> {
pub fn from_array(ports: [(ConstString, PortVariant); S]) -> Self {
Self(ports)
}
#[allow(clippy::unwrap_used)]
pub fn from_vec(mut ports: PortVec) -> Self {
let mut drain = ports.inner_mut().drain(..);
let array: [(ConstString, PortVariant); S] = core::array::from_fn(|_| drain.next().unwrap());
Self(array)
}
}
impl PortArray<0> {
pub fn empty() -> Self {
Self::from_array([])
}
}
impl<const S: usize> core::fmt::Debug for PortArray<S> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("PortArray").field(&self.0).finish()
}
}
impl<const S: usize> Deref for PortArray<S> {
type Target = [(ConstString, PortVariant)];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<const S: usize> DerefMut for PortArray<S> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<const S: usize> PortCollection for PortArray<S> {
fn find(&self, name: &str) -> Option<&PortVariant> {
self.0
.iter()
.find(|port| &*port.0 == name)
.map(|v| &v.1 as _)
}
fn find_mut(&mut self, name: &str) -> Option<&mut PortVariant> {
self.0
.iter_mut()
.find(|port| &*port.0 == name)
.map(|v| &mut v.1 as _)
}
fn set_from_str(&mut self, name: &str, value: &str) -> Result<(), Error> {
if let Some(port) = self.find_mut(name) {
port.set_from_str(value)
} else {
Err(Error::NotFound { name: name.into() })
}
}
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (&'a str, &'a PortVariant)> + 'a> {
Box::new(self.0.iter().map(|(name, port)| (&**name, port)))
}
fn iter_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = (&'a str, &'a mut PortVariant)> + 'a> {
Box::new(
self.0
.iter_mut()
.map(|(name, port)| (&**name, port)),
)
}
}
impl<const S: usize> PortCollectionAccessorsCommon for PortArray<S> {
fn contains_name(&self, name: &str) -> bool {
self.find(name).is_some()
}
fn give_to_bound(&self, name: &str, bound: &mut dyn BindCommons) -> Result<(), Error> {
self.find(name)
.map_or(Err(Error::NotFound { name: name.into() }), |port| {
bound.use_from_variant(port)
})
}
fn give_to_collection(
&self,
name: &str,
other_collection: &mut dyn PortCollection,
other_name: &str,
) -> Result<(), Error> {
self.find(name)
.map_or(Err(Error::NotFound { name: name.into() }), |port| {
other_collection
.find_mut(other_name)
.map_or(Err(Error::OtherNotFound { name: other_name.into() }), |variant| {
variant.use_from_variant(port)
})
})
}
fn give_to_variant(&self, name: &str, variant: &mut PortVariant) -> Result<(), Error> {
self.find(name)
.map_or(Err(Error::NotFound { name: name.into() }), |port| {
variant.use_from_variant(port)
})
}
fn sequence_number(&self, name: &str) -> Result<u32, Error> {
if let Some(port) = self.find(name) {
Ok(port.sequence_number())
} else {
Err(Error::NotFound { name: name.into() })
}
}
fn use_from_bound(&mut self, name: &str, bound: &dyn BindCommons) -> Result<(), Error> {
self.find_mut(name)
.map_or(Err(Error::NotFound { name: name.into() }), |port| port.use_from_bound(bound))
}
fn use_from_collection(
&mut self,
name: &str,
other_collection: &dyn PortCollection,
other_name: &str,
) -> Result<(), Error> {
self.find_mut(name)
.map_or(Err(Error::NotFound { name: name.into() }), |port| {
port.use_from_collection(other_collection, other_name)
})
}
fn use_from_variant(&mut self, name: &str, variant: &PortVariant) -> Result<(), Error> {
self.find_mut(name)
.map_or(Err(Error::NotFound { name: name.into() }), |port| {
port.use_from_variant(variant)
})
}
}
impl<const S: usize> PortCollectionAccessors for PortArray<S> {
fn contains<T: AnyPortValue>(&self, name: &str) -> Result<bool, Error> {
if let Some(p) = self.find(name) {
if p.is::<T>() { Ok(true) } else { Err(Error::DataType) }
} else {
Ok(false)
}
}
fn get<T>(&self, name: &str) -> Result<T, Error>
where
T: AnyPortValue + Clone,
{
if let Some(port) = self.find(name) {
match port.get::<T>()? {
Some(t) => Ok(t),
None => Err(Error::NoValueSet),
}
} else {
Err(Error::NotFound { name: name.into() })
}
}
fn read<T: AnyPortValue>(&self, name: &str) -> Result<BoundValueReadGuard<T>, Error> {
if let Some(port) = self.find(name) {
port.read()
} else {
Err(Error::NotFound { name: name.into() })
}
}
fn try_read<T: AnyPortValue>(&self, name: &str) -> Result<BoundValueReadGuard<T>, Error> {
if let Some(port) = self.find(name) {
port.try_read()
} else {
Err(Error::NotFound { name: name.into() })
}
}
fn replace<T: AnyPortValue>(&mut self, name: &str, value: T) -> Result<Option<T>, Error> {
if let Some(port) = self.find_mut(name) {
port.replace(value)
} else {
Err(Error::NotFound { name: name.into() })
}
}
fn set<T: AnyPortValue>(&mut self, name: &str, value: T) -> Result<(), Error> {
if let Some(port) = self.find_mut(name) {
port.set(value)
} else {
Err(Error::NotFound { name: name.into() })
}
}
fn take<T: AnyPortValue>(&mut self, name: &str) -> Result<Option<T>, Error> {
if let Some(port) = self.find_mut(name) {
port.take()
} else {
Err(Error::NotFound { name: name.into() })
}
}
fn write<T: AnyPortValue>(&mut self, name: &str) -> Result<BoundValueWriteGuard<T>, Error> {
if let Some(port) = self.find_mut(name) {
port.write()
} else {
Err(Error::NotFound { name: name.into() })
}
}
fn try_write<T: AnyPortValue>(&mut self, name: &str) -> Result<BoundValueWriteGuard<T>, Error> {
if let Some(port) = self.find_mut(name) {
port.try_write()
} else {
Err(Error::NotFound { name: name.into() })
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const fn is_normal<T: Sized + Send + Sync>() {}
#[test]
const fn normal_types() {
is_normal::<&PortArray<2>>();
is_normal::<PortArray<4>>();
}
#[test]
fn set_from_str_existing_port() {
let port = crate::PortVariant::create_outbound_parseable(0i32);
let mut array = PortArray::from_array([("val".into(), port)]);
assert!(array.set_from_str("val", "42").is_ok());
}
#[test]
fn set_from_str_missing_port() {
let mut array = PortArray::<0>::empty();
assert_eq!(
array.set_from_str("missing", "42"),
Err(crate::error::Error::NotFound { name: "missing".into() })
);
}
#[test]
fn contains_missing_name_returns_false() {
let array = PortArray::<0>::empty();
assert_eq!(array.contains::<i32>("nonexistent"), Ok(false));
}
#[test]
fn contains_missing_name_in_nonempty_array() {
let port = crate::PortVariant::create_outbound(42i32);
let array = PortArray::from_array([("exists".into(), port)]);
assert_eq!(array.contains::<i32>("other"), Ok(false));
}
#[test]
fn contains_wrong_type_returns_data_type_error() {
let port = crate::PortVariant::create_outbound(42i32);
let array = PortArray::from_array([("val".into(), port)]);
assert_eq!(array.contains::<f64>("val"), Err(crate::error::Error::DataType));
}
#[test]
fn get_returns_value() {
let port = crate::PortVariant::create_inbound(42i32);
let array = PortArray::from_array([("val".into(), port)]);
assert_eq!(array.get::<i32>("val"), Ok(42));
}
#[test]
fn get_empty_port_returns_no_value_set() {
let port = crate::PortVariant::InBound(crate::bind::in_::InBound::new::<i32>());
let array = PortArray::from_array([("empty".into(), port)]);
assert_eq!(array.get::<i32>("empty"), Err(crate::error::Error::NoValueSet));
}
#[test]
fn contains_missing_with_multiple_ports() {
let array = PortArray::from_array([
("a".into(), crate::PortVariant::create_outbound(1i32)),
("b".into(), crate::PortVariant::create_inbound(2i32)),
]);
assert_eq!(array.contains::<i32>("c"), Ok(false));
}
#[test]
fn get_returns_string_value() {
let port = crate::PortVariant::create_inbound(alloc::string::String::from("hello"));
let array = PortArray::from_array([("s".into(), port)]);
assert_eq!(
array.get::<alloc::string::String>("s"),
Ok(alloc::string::String::from("hello"))
);
}
#[test]
fn get_returns_f64_value() {
let port = crate::PortVariant::create_inoutbound(3.24f64);
let array = PortArray::from_array([("pi".into(), port)]);
assert_eq!(array.get::<f64>("pi"), Ok(3.24));
}
#[test]
fn get_empty_inoutbound_returns_no_value_set() {
let port = crate::PortVariant::InOutBound(crate::bind::in_out::InOutBound::new::<f64>());
let array = PortArray::from_array([("empty".into(), port)]);
assert_eq!(array.get::<f64>("empty"), Err(crate::error::Error::NoValueSet));
}
#[test]
fn iter_empty_yields_nothing() {
let array = PortArray::<0>::empty();
assert_eq!(array.iter().count(), 0);
}
#[test]
fn iter_yields_all_entries_in_order() {
let array = PortArray::from_array([
("a".into(), crate::PortVariant::create_inbound(1i32)),
("b".into(), crate::PortVariant::create_outbound(2i32)),
("c".into(), crate::PortVariant::create_inoutbound(3i32)),
]);
let names: alloc::vec::Vec<&str> = array.iter().map(|(n, _)| n).collect();
assert_eq!(names, ["a", "b", "c"]);
assert_eq!(array.iter().count(), 3);
}
#[test]
fn iter_yields_matching_port_refs() {
let array = PortArray::from_array([("x".into(), crate::PortVariant::create_inbound(42i32))]);
let (name, port) = array.iter().next().unwrap();
assert_eq!(name, "x");
assert_eq!(port.get::<i32>().unwrap(), Some(42));
}
#[test]
fn iter_mut_empty_yields_nothing() {
let mut array = PortArray::<0>::empty();
assert_eq!(array.iter_mut().count(), 0);
}
#[test]
fn iter_mut_yields_all_entries_in_order() {
let mut array = PortArray::from_array([
("a".into(), crate::PortVariant::create_inbound(1i32)),
("b".into(), crate::PortVariant::create_outbound(2i32)),
("c".into(), crate::PortVariant::create_inoutbound(3i32)),
]);
let names: alloc::vec::Vec<&str> = array.iter_mut().map(|(n, _)| n).collect();
assert_eq!(names, ["a", "b", "c"]);
assert_eq!(array.iter_mut().count(), 3);
}
#[test]
fn iter_mut_allows_mutation_of_port_values() {
let mut array = PortArray::from_array([
("a".into(), crate::PortVariant::create_inoutbound(1i32)),
("b".into(), crate::PortVariant::create_inoutbound(2i32)),
]);
for (_, port) in array.iter_mut() {
let current = port.get::<i32>().unwrap().unwrap();
port.set(current * 10).unwrap();
}
assert_eq!(array.get::<i32>("a"), Ok(10));
assert_eq!(array.get::<i32>("b"), Ok(20));
}
#[test]
fn iter_mut_yields_matching_port_refs() {
let mut array = PortArray::from_array([("x".into(), crate::PortVariant::create_inoutbound(42i32))]);
{
let (name, port) = array.iter_mut().next().unwrap();
assert_eq!(name, "x");
port.set(99i32).unwrap();
}
assert_eq!(array.get::<i32>("x"), Ok(99));
}
}