dataport 0.1.0

Port abstractions for data types
Documentation
// Copyright © 2026 Stephan Kunz
//! A bound input type port implementing [`BindIn`].

use core::str::FromStr;

use alloc::boxed::Box;

use crate::{
	Arc, PortCollection, PortCollectionAccessors, PortCommons, RwLock,
	any_port_value::AnyPortValue,
	bind::{
		BindCommons, BindIn,
		bound_value::{self, BoundValue, BoundValuePtr, BoundValueReadGuard},
		sequence_number::SequenceNumber,
	},
	error::Error,
	port_variant::PortVariant,
};

/// @TODO:
#[derive(Debug, Clone)]
pub struct InBound(BoundValuePtr);

impl InBound {
	pub fn new<T: AnyPortValue>() -> Self {
		Self(Arc::new(RwLock::new((
			Box::new(BoundValue::<T>::empty()),
			SequenceNumber::default(),
			None,
			core::any::type_name::<T>(),
		))))
	}

	/// Returns a new `InBound` with an empty value and a `FromStr` setter.
	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>(),
		))))
	}

	/// Returns a new `InBound` with a value and a `FromStr` setter.
	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 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 InBound {
	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 InBound {
	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 InBound {
	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())
	}
}

#[cfg(test)]
mod tests {
	use super::*;

	const fn is_normal<T: Sized + Send + Sync>() {}

	// check, that the auto traits are available.
	#[test]
	const fn normal_types() {
		is_normal::<&InBound>();
		is_normal::<InBound>();
	}

	#[test]
	fn into_inner() {
		let port = InBound::new::<i32>();
		assert_eq!(port.into_inner::<f64>(), Err(Error::DataType));
	}

	#[test]
	fn into_inner_empty() {
		let port = InBound::new::<i32>();
		assert_eq!(port.into_inner::<i32>(), Ok(None));
	}

	#[test]
	fn into_inner_type_mismatch_with_value() {
		let port = InBound::with_value(42i32);
		assert_eq!(port.into_inner::<alloc::string::String>(), Err(Error::DataType));
	}

	#[test]
	fn into_inner_type_mismatch_different_numeric() {
		let port = InBound::with_value(3.24f64);
		assert_eq!(port.into_inner::<u8>(), Err(Error::DataType));
	}
}