dataport 0.1.0

Port abstractions for data types
Documentation
// Copyright © 2026 Stephan Kunz
//! Port variants.
#![allow(unused)]

use crate::{
	any_port_value::AnyPortValue,
	bind::{
		BindCommons, BindIn, BindInOut, BindOut,
		bound_value::{BoundValueReadGuard, BoundValueWriteGuard},
		in_::InBound,
		in_out::InOutBound,
		out_::OutBound,
	},
	collections::PortCollection,
	error::Error,
};

// These values are used in communication with Groot2 when creating the tree xml.
// See function `type_str()` below.
const INPUT_TYPE: &str = "input_port";
const OUTPUT_TYPE: &str = "output_port";
const INOUT_TYPE: &str = "inout_port";

/// Implemented set of port variants.
/// - InBound: bound to some other ports value, only readable
/// - InOutBound: bound to some other ports value, read- & writeable
/// - OutBound: bound to some other ports value, only writeable
#[allow(clippy::enum_variant_names)]
#[derive(Debug, Clone)]
pub enum PortVariant {
	InBound(InBound),
	InOutBound(InOutBound),
	OutBound(OutBound),
}

impl PortVariant {
	/// Returns a [`PortVariant::InBound`] with the 'value' set.
	pub fn create_inbound<T: AnyPortValue>(value: T) -> Self {
		Self::InBound(InBound::with_value(value))
	}

	/// Returns a [`PortVariant::InOutBound`] with the 'value' set.
	pub fn create_inoutbound<T: AnyPortValue>(value: T) -> Self {
		Self::InOutBound(InOutBound::with_value(value))
	}

	/// Returns a [`PortVariant::OutBound`] with the 'value' set.
	pub fn create_outbound<T: AnyPortValue>(value: T) -> Self {
		Self::OutBound(OutBound::with_value(value))
	}

	/// Returns a [`PortVariant::InBound`] with the 'value' set and a `FromStr` setter.
	pub fn create_inbound_parseable<T: AnyPortValue + core::str::FromStr>(value: T) -> Self {
		Self::InBound(InBound::with_value_parseable(value))
	}

	/// Returns a [`PortVariant::InOutBound`] with the 'value' set and a `FromStr` setter.
	pub fn create_inoutbound_parseable<T: AnyPortValue + core::str::FromStr>(value: T) -> Self {
		Self::InOutBound(InOutBound::with_value_parseable(value))
	}

	/// Returns a [`PortVariant::OutBound`] with the 'value' set and a `FromStr` setter.
	pub fn create_outbound_parseable<T: AnyPortValue + core::str::FromStr>(value: T) -> Self {
		Self::OutBound(OutBound::with_value_parseable(value))
	}

	/// Binds this port to the port 'bound'.
	/// # Errors
	/// - [`Error::DataType`], if 'self' is not the same type 'T' as 'bound'.
	/// - [`Error::PortType`], if 'bound' is not a valid port type.
	pub fn use_from_bound(&mut self, bound: &dyn BindCommons) -> Result<(), Error> {
		match self {
			Self::InBound(port) => port.use_from_bound(bound),
			Self::InOutBound(port) => port.use_from_bound(bound),
			Self::OutBound(port) => port.use_from_bound(bound),
		}
	}

	/// Binds this port to the port 'variant'.
	/// # Errors
	/// - [`Error::DataType`], if 'self' is not the same type 'T' as 'variant'.
	/// - [`Error::PortType`], if 'variant' is not a valid port type.
	pub fn use_from_variant(&mut self, other: &PortVariant) -> Result<(), Error> {
		match other {
			Self::InBound(bound) => self.use_from_bound(bound),
			Self::InOutBound(bound) => self.use_from_bound(bound),
			Self::OutBound(bound) => self.use_from_bound(bound),
		}
	}

	/// Binds this port to the port 'name' of 'collection'.
	/// # Errors
	/// - [`Error::DataType`], if 'self' is not the same type 'T' as 'name' in 'collection'.
	/// - [`Error::OtherNotFound`], if 'collection' does not contains port 'name'.
	/// - [`Error::PortType`], if 'variant' is not a valid port type.
	pub fn use_from_collection(&mut self, collection: &dyn PortCollection, name: &str) -> Result<(), Error> {
		if let Some(variant) = collection.find(name) {
			self.use_from_variant(variant)
		} else {
			Err(Error::OtherNotFound { name: name.into() })
		}
	}

	/// Returns a clone/copy of the T.
	/// Therefore T must implement [`Clone`].
	/// # Errors
	/// - [`Error::DataType`], if 'self' is not the expected type 'T'.
	/// - [`Error::PortType`], if method is not available on 'self'.
	pub fn get<T: AnyPortValue + Clone>(&self) -> Result<Option<T>, Error> {
		match self {
			Self::InBound(port) => port.get(),
			Self::InOutBound(port) => port.get(),
			Self::OutBound(_) => Err(Error::PortType),
		}
	}

	/// Checks type 'T' of port.
	/// # Errors
	/// - [`Error::DataType`], if 'self' is not the expected type 'T'.
	pub fn is<T: AnyPortValue>(&self) -> bool {
		match self {
			Self::InBound(port) => port.is::<T>(),
			Self::InOutBound(port) => port.is::<T>(),
			Self::OutBound(port) => port.is::<T>(),
		}
	}

	/// Returns an immutable guard to the ports value 'T'.
	/// # Errors
	/// - [`Error::DataType`](crate::error::Error), if port is not the expected port type & type of T.
	pub fn read<T: AnyPortValue>(&self) -> Result<BoundValueReadGuard<T>, Error> {
		match self {
			Self::InBound(port) => port.read(),
			Self::InOutBound(port) => port.read(),
			Self::OutBound(_) => Err(Error::PortType),
		}
	}

	/// Returns an immutable guard to the ports value 'T'.
	/// # Errors
	/// - [`Error::IsLocked`](crate::error::Error), if port is locked.
	/// - [`Error::DataType`](crate::error::Error), if port is not the expected port type & type of T.
	pub fn try_read<T: AnyPortValue>(&self) -> Result<BoundValueReadGuard<T>, Error> {
		match self {
			Self::InBound(port) => port.try_read(),
			Self::InOutBound(port) => port.try_read(),
			Self::OutBound(_) => Err(Error::PortType),
		}
	}

	/// Sets a new value to the T and returns the old T.
	pub fn replace<T: AnyPortValue>(&mut self, value: T) -> Result<Option<T>, Error> {
		match self {
			Self::InOutBound(port) => port.replace(value),
			Self::InBound(_) | Self::OutBound(_) => Err(Error::PortType),
		}
	}

	/// Returns the change sequence number, a number which
	/// - starts at `0` for a port which never contained a value,
	/// - incremets by 1 whenever the ports value changes
	/// - wraps around to `1` when exceeding [`u32::MAX`].
	pub fn sequence_number(&self) -> u32 {
		match self {
			Self::InBound(port) => port.sequence_number(),
			Self::InOutBound(port) => port.sequence_number(),
			Self::OutBound(port) => port.sequence_number(),
		}
	}

	/// Returns the T, removing it from the port.
	pub fn take<T: AnyPortValue>(&mut self) -> Result<Option<T>, Error> {
		match self {
			Self::InOutBound(port) => port.take(),
			Self::InBound(_) | Self::OutBound(_) => Err(Error::PortType),
		}
	}

	/// Sets a new value to the T.
	pub fn set<T: AnyPortValue>(&mut self, value: T) -> Result<(), Error> {
		match self {
			Self::OutBound(port) => port.set(value),
			Self::InOutBound(port) => port.set(value),
			Self::InBound(_) => Err(Error::PortType),
		}
	}

	/// Sets the port with a value parsed from the string `s`.
	///
	/// The port must have been created with a `_parseable` constructor
	/// (e.g. [`Self::create_outbound_parseable`]) so that the `FromStr`
	/// capability was captured at creation time.
	/// # Errors
	/// - [`Error::FromStr`], if parsing or the setter was not registered.
	/// - [`Error::PortType`], if the port variant does not support writing.
	pub fn set_from_str(&mut self, s: &str) -> Result<(), Error> {
		match self {
			Self::OutBound(port) => port.init_from_str(s),
			Self::InOutBound(port) => port.init_from_str(s),
			Self::InBound(port) => port.init_from_str(s), //Err(Error::PortType),
		}
	}

	pub fn write<T: AnyPortValue>(&mut self) -> Result<BoundValueWriteGuard<T>, Error> {
		match self {
			Self::OutBound(port) => port.write(),
			Self::InOutBound(port) => port.write(),
			Self::InBound(_) => Err(Error::PortType),
		}
	}

	pub fn try_write<T: AnyPortValue>(&mut self) -> Result<BoundValueWriteGuard<T>, Error> {
		match self {
			Self::OutBound(port) => port.try_write(),
			Self::InOutBound(port) => port.try_write(),
			Self::InBound(_) => Err(Error::PortType),
		}
	}

	/// Returns the T, removing it from the port.
	pub fn into_inner<T: AnyPortValue>(self) -> Result<Option<T>, Error> {
		match self {
			Self::InBound(port) => port.into_inner(),
			Self::InOutBound(port) => port.into_inner(),
			Self::OutBound(port) => port.into_inner(),
		}
	}

	/// Converts this [`PortVariant`] into a [`PortVariant::InOutBound`],
	/// preserving the data type and sequence number.
	pub fn to_in_out(self) -> PortVariant {
		match self {
			Self::InOutBound(_) => self,
			Self::InBound(port) => Self::InOutBound(InOutBound::from_value(port.value())),
			Self::OutBound(port) => Self::InOutBound(InOutBound::from_value(port.value())),
		}
	}

	/// Get the PortVariant's direction as `<type>_port` str.
	#[must_use]
	pub const fn direction(&self) -> &'static str {
		match self {
			Self::InOutBound(_) => INOUT_TYPE,
			Self::InBound(_) => INPUT_TYPE,
			Self::OutBound(_) => OUTPUT_TYPE,
		}
	}

	/// Get the PortVariant's data type as str..
	#[must_use]
	pub fn data_type(&self) -> &str {
		match self {
			Self::InOutBound(port) => port.data_type(),
			Self::InBound(port) => port.data_type(),
			Self::OutBound(port) => port.data_type(),
		}
	}
}

#[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::<&PortVariant>();
		is_normal::<PortVariant>();
	}
}