dataport 0.1.0

Port abstractions for data types
Documentation
// Copyright © 2026 Stephan Kunz
//! Internal port value representation and its read & write guards.

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,
};

/// Type for an optional setter function that parses a value from a string.
pub(crate) type FromStrSetter = fn(&str, &mut dyn AnyPortValue) -> Result<(), Error>;

/// Helper to create a [`FromStrSetter`] for a specific type `T`.
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)
	}
}

/// The tuple stored inside a [`BoundValuePtr`]'s `RwLock`.
/// The 4th element is the fully-qualified name of the inner data type `T`
/// (as returned by [`core::any::type_name`]), captured at construction time.
pub(crate) type BoundTuple = (Box<dyn AnyPortValue>, SequenceNumber, Option<FromStrSetter>, &'static str);

/// Type definition for a pointer to a [`BoundValue`].
pub(crate) type BoundValuePtr = Arc<RwLock<BoundTuple>>;

/// Returns the last `::`-separated segment of a fully-qualified type name,
/// so e.g. `"alloc::string::String"` becomes `"String"`.
pub(crate) fn short_type_name(full: &'static str) -> &'static str {
	full.rsplit("::").next().unwrap_or(full)
}

/// Internal representation of a ports value.
/// The `PortValue` is shared between the bound ports.
#[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()
	}
}

// @TODO: check wether we really need to store value
/// Read-Locked port value guard.
/// Until this value is dropped, a read lock is held on the ports value.
///
/// Implements [`Deref`], providing read access to the locked `T`.
#[must_use = "a `BoundValueReadGuard` should be used"]
pub struct BoundValueReadGuard<T> {
	/// Read guard holding the lock on the value; only its `Drop` is relevant.
	/// Its lifetime is transmuted to `'static`; kept valid by `_value` below.
	/// Declared first so it is dropped — releasing the lock — before `_value`.
	_guard: RwLockReadGuard<'static, BoundTuple>,
	/// Keeps the `RwLock` borrowed by `guard` alive at a stable address.
	_value: BoundValuePtr,
	/// Immutable pointer to the locked content.
	ptr_t: *const T,
}

impl<T> Deref for BoundValueReadGuard<T> {
	type Target = T;

	fn deref(&self) -> &Self::Target {
		// SAFETY: Self referencing to locked content of the `Arc` `Entry`, valid until self is dropped
		#[allow(unsafe_code)]
		unsafe {
			&*self.ptr_t
		}
	}
}

impl<T: 'static> BoundValueReadGuard<T> {
	/// Returns a read guard to the T.
	/// # Errors
	/// - [`Error::NoValueSet`] if the port does not yet contain a value.
	pub(crate) fn new(value: BoundValuePtr) -> Result<Self, Error> {
		let guard = value.read();
		// SAFETY: `guard` borrows the `RwLock` owned by `value`. `value` is moved
		// into the returned struct and, due to field order, dropped strictly after
		// `guard`, so the `RwLock` outlives the guard.
		#[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,
		})
	}

	/// Returns a read guard to the T.
	/// # Errors
	/// - [`Error::IsLocked`]  if the entry is locked by someone else.
	/// - [`Error::NoValueSet`] if the port does not yet contain a value.
	pub(crate) fn try_new(value: BoundValuePtr) -> Result<Self, Error> {
		if let Some(guard) = value.clone().try_read() {
			// SAFETY: see `new`.
			#[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)
		}
	}
}

// @TODO: check wether we really need to store value
/// Write-Locked port value guard.
/// Until this value is dropped, a write lock is held on the ports value.
///
/// Implements [`Deref`] & [`DerefMut`], providing access to the locked `T`.
#[must_use = "a `BoundValueWriteGuard` should be used"]
pub struct BoundValueWriteGuard<T> {
	/// Write guard holding the lock on the value.
	/// Its lifetime is transmuted to `'static`; kept valid by `_value` below.
	/// Declared first so it is dropped — releasing the lock — before `_value`.
	guard: RwLockWriteGuard<'static, BoundTuple>,
	/// Keeps the `RwLock` borrowed by `guard` alive at a stable address.
	_value: BoundValuePtr,
	/// Mutable pointer to the locked content.
	ptr_t: *mut T,
	/// Change flag.
	modified: bool,
}

impl<T> Deref for BoundValueWriteGuard<T> {
	type Target = T;

	fn deref(&self) -> &Self::Target {
		// SAFETY: Self referencing to locked content of the `Arc` `Entry`, valid until self is dropped
		#[allow(unsafe_code)]
		unsafe {
			&*self.ptr_t
		}
	}
}

impl<T> DerefMut for BoundValueWriteGuard<T> {
	fn deref_mut(&mut self) -> &mut Self::Target {
		// once dereferenced mutable we assume a modification
		self.modified = true;
		// SAFETY: Self referencing to locked content of the `Arc` `Entry`, valid until self is dropped
		#[allow(unsafe_code)]
		unsafe {
			&mut *self.ptr_t
		}
	}
}

impl<T> Drop for BoundValueWriteGuard<T> {
	fn drop(&mut self) {
		// if modified, increment the sequence id before the lock is released
		if self.modified {
			self.guard.1.increment();
		}
		// `guard` is dropped next, releasing the write lock, then `_value`.
	}
}

impl<T: 'static> BoundValueWriteGuard<T> {
	/// Returns a write guard to the T.
	/// # Errors
	/// - [`Error::NoValueSet`] if the port does not yet contain a value.
	pub(crate) fn new(value: BoundValuePtr) -> Result<Self, Error> {
		let guard = value.write();
		// SAFETY: see `BoundValueReadGuard::new`.
		#[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,
		})
	}

	/// Returns a write guard to the T.
	/// # Errors
	/// - [`Error::IsLocked`]  if the entry is locked by someone else.
	/// - [`Error::NoValueSet`] if the port does not yet contain a value.
	pub(crate) fn try_new(value: BoundValuePtr) -> Result<Self, Error> {
		if let Some(guard) = value.clone().try_write() {
			// SAFETY: see `BoundValueReadGuard::new`.
			#[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>() {}

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

	#[test]
	fn from_str_setter_type_mismatch_returns_data_type_error() {
		// Create a PortValue<f64> but call from_str_setter::<i32> on it.
		// The downcast to PortValue<i32> will fail, hitting Err(Error::DataType).
		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));
	}
}