diff --git a/src/dump/mod.rs b/src/dump/mod.rs index 3be0d43..f517630 100644 --- a/src/dump/mod.rs +++ b/src/dump/mod.rs @@ -12,12 +12,24 @@ use std::path::Path; use crate::logicbit::LogicVector; use chrono::Local; +/// A trait for iterating over the containing [`Port`]s of a `Model`. +/// +/// Instead of using (non-exiting) reflection, you have to pass all Ports you want to export to the +/// argument `FnMut`. +/// +/// This is mainly used for dumping purposes, because this operations can be quiet expensive. +//TODO: Is this really needed? Let's rethink dumping values. pub trait IterPorts { + /// See [`IterPorts] for a good description. + /// + /// The implementor should pass a short, descripting `&str` as long with the `Port`. + /// Currently only [`Ieee1164`] is supported, in the future other values will be supported too. fn iter_ports(&self, f: F) where F: FnMut(&str, &Port); } +//TODO: Is this really needed? Let's rethink dumping values. pub trait IterValues { fn iter_values(&self, f: F) where diff --git a/src/lib.rs b/src/lib.rs index df6813a..eca9bef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![feature(nll, try_from, vec_remove_item)] +#![warn(missing_docs)] //! Logical is a digital network simulator. It is named after the german word "Logical" which //! describes a puzzle that follows the rules of logic. @@ -90,10 +91,20 @@ pub use self::logicbit::{Ieee1164, Ieee1164Value, LogicVector, Resolve}; pub use self::port::Port; pub use self::signal::Signal; +#[allow(unused)] +use self::direction::{InOut, Input, Output, PortDirection}; + +/// Declares typical structs and trait that are used for indicating directions, e.g. [`Output`], +/// [`Input`], [`InOut`] or [`PortDirection`]. pub mod direction { pub use super::port::{Dir, InOut, Input, MaybeRead, MaybeWrite, Off, Output, PortDirection, Read, Write}; } +/// Simple update trait for signalling passing values from input to an output. Of course the actual +/// behavior depends on the actual struct that implement this. pub trait Updateable { + /// When this trait function is called you should perform any action necessary to update the + /// struct, e.g. reading input values and updating output values. These changes should be + /// instant. fn update(&mut self); } diff --git a/src/logicbit/ieee1164.rs b/src/logicbit/ieee1164.rs index e7ba63e..f0356ee 100644 --- a/src/logicbit/ieee1164.rs +++ b/src/logicbit/ieee1164.rs @@ -11,24 +11,53 @@ macro_rules! expand_op_ieee1164 { }; } +/// Represents an [Ieee1164](https://en.wikipedia.org/wiki/IEEE_1164) value. For a better usage, +/// there are associated constants, which all start with an underscore, e.g. [`Ieee1164::_U`]. +/// +/// The three binary logical operators are defined on this struct so you can use them. +/// +/// # Examples +/// +/// ```rust +/// # use logical::Ieee1164; +/// let a = Ieee1164::_1; +/// let b = Ieee1164::_0; +/// assert_eq!(Ieee1164::_0, a & b); +/// assert_eq!(Ieee1164::_1, a | b); +/// assert_eq!(Ieee1164::_1, a ^ b); +/// ``` #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Ieee1164 { + /// Uninitialized is the default value. It represents an unknown or invalid value Uninitialized, + /// Represents a strong [`Ieee1164Value`] Strong(Ieee1164Value), + /// Represents a weak [`Ieee1164Value`] Weak(Ieee1164Value), + /// Represents high-impedance HighImpedance, + /// Represents a don't-care DontCare, } impl Ieee1164 { + /// Uninitialized is the default for an `Ieee1164`. It represents an unknown or invalid value pub const _U: Ieee1164 = Ieee1164::Uninitialized; + /// Represents a conflicted value between two strong values pub const _X: Ieee1164 = Ieee1164::Strong(Ieee1164Value::Unknown); + /// Represents a strong 1 pub const _1: Ieee1164 = Ieee1164::Strong(Ieee1164Value::One); + /// Represents a strong 0 pub const _0: Ieee1164 = Ieee1164::Strong(Ieee1164Value::Zero); + /// Represents a conflicted value between two weak values pub const _W: Ieee1164 = Ieee1164::Weak(Ieee1164Value::Unknown); + /// Represents a weak 1 pub const _H: Ieee1164 = Ieee1164::Weak(Ieee1164Value::One); + /// Represents a weak 1 pub const _L: Ieee1164 = Ieee1164::Weak(Ieee1164Value::Zero); + /// Represents high-impedance pub const _Z: Ieee1164 = Ieee1164::HighImpedance; + /// Represents a don't-care pub const _D: Ieee1164 = Ieee1164::DontCare; } @@ -73,6 +102,7 @@ impl From for char { } } +// this will make the tables shorter const _U: Ieee1164 = Ieee1164::_U; const _X: Ieee1164 = Ieee1164::_X; const _0: Ieee1164 = Ieee1164::_0; @@ -86,7 +116,7 @@ const _D: Ieee1164 = Ieee1164::_D; #[allow(clippy::trivially_copy_pass_by_ref)] fn and(a: &Ieee1164, b: &Ieee1164) -> Ieee1164 { const TTABLE: [[Ieee1164; 9]; 9] = [ - // U X 0 1 Z W L H - + //U X 0 1 Z W L H - [_U, _U, _0, _U, _U, _U, _0, _U, _U], // U [_U, _X, _0, _X, _X, _X, _0, _X, _X], // X [_0, _0, _0, _0, _0, _0, _0, _0, _0], // 0 @@ -105,7 +135,7 @@ expand_op_ieee1164!(and, BitAnd, bitand); #[allow(clippy::trivially_copy_pass_by_ref)] fn or(a: &Ieee1164, b: &Ieee1164) -> Ieee1164 { const TTABLE: [[Ieee1164; 9]; 9] = [ - // U X 0 1 Z W L H - + //U X 0 1 Z W L H - [_U, _U, _U, _1, _U, _U, _U, _1, _U], // U [_U, _X, _X, _1, _X, _X, _X, _1, _X], // X [_U, _X, _0, _1, _X, _X, _0, _1, _X], // 0 @@ -124,7 +154,7 @@ expand_op_ieee1164!(or, BitOr, bitor); #[allow(clippy::trivially_copy_pass_by_ref)] fn xor(a: &Ieee1164, b: &Ieee1164) -> Ieee1164 { const TTABLE: [[Ieee1164; 9]; 9] = [ - // U X 0 1 Z W L H - + //U X 0 1 Z W L H - [_U, _U, _U, _U, _U, _U, _U, _U, _U], // U [_U, _X, _X, _X, _X, _X, _X, _X, _X], // X [_U, _X, _0, _1, _X, _X, _0, _1, _X], // 0 @@ -142,17 +172,10 @@ expand_op_ieee1164!(xor, BitXor, bitxor); fn not(i: Ieee1164) -> Ieee1164 { match i { - Ieee1164::Uninitialized => Ieee1164::Uninitialized, - Ieee1164::Weak(Ieee1164Value::Zero) | Ieee1164::Strong(Ieee1164Value::Zero) => { - Ieee1164::Strong(Ieee1164Value::One) - } - Ieee1164::Weak(Ieee1164Value::One) | Ieee1164::Strong(Ieee1164Value::One) => { - Ieee1164::Strong(Ieee1164Value::Zero) - } - Ieee1164::Strong(Ieee1164Value::Unknown) - | Ieee1164::HighImpedance - | Ieee1164::Weak(Ieee1164Value::Unknown) - | Ieee1164::DontCare => Ieee1164::Strong(Ieee1164Value::Unknown), + _U => _U, + _L | _0 => _1, + _H | _1 => _0, + _ => Ieee1164::_X, } } impl Not for Ieee1164 { @@ -171,7 +194,7 @@ impl<'a> Not for &'a Ieee1164 { #[allow(clippy::trivially_copy_pass_by_ref)] fn resolve(a: &Ieee1164, b: &Ieee1164) -> Ieee1164 { const TTABLE: [[Ieee1164; 9]; 9] = [ - // U X 0 1 Z W L H - + //U X 0 1 Z W L H - [_U, _U, _U, _U, _U, _U, _U, _U, _U], // U [_U, _X, _X, _X, _X, _X, _X, _X, _X], // X [_U, _X, _0, _X, _0, _0, _0, _0, _X], // 0 @@ -195,69 +218,83 @@ impl fmt::Display for Ieee1164 { impl Ieee1164 { fn to_index(self) -> usize { match self { - Ieee1164::Uninitialized => 0, - Ieee1164::Strong(Ieee1164Value::Unknown) => 1, - Ieee1164::Strong(Ieee1164Value::Zero) => 2, - Ieee1164::Strong(Ieee1164Value::One) => 3, - Ieee1164::HighImpedance => 4, - Ieee1164::Weak(Ieee1164Value::Unknown) => 5, - Ieee1164::Weak(Ieee1164Value::Zero) => 6, - Ieee1164::Weak(Ieee1164Value::One) => 7, - Ieee1164::DontCare => 8, + Ieee1164::_U => 0, + Ieee1164::_X => 1, + Ieee1164::_0 => 2, + Ieee1164::_1 => 3, + Ieee1164::_Z => 4, + Ieee1164::_W => 5, + Ieee1164::_L => 6, + Ieee1164::_H => 7, + Ieee1164::_D => 8, } } } #[allow(non_snake_case)] impl Ieee1164 { + /// Checks whether this is either [`Ieee1164::_U`], [`Ieee1164::_X`], [`Ieee1164::_W`], + /// [`Ieee1164::_Z`] or [`Ieee1164::_D`]. pub fn is_UXZ(self) -> bool { !(self.is_1H() || self.is_0L()) } + /// Checks whether this is either [`Ieee1164::_0`], [`Ieee1164::_1`] pub fn is_01(self) -> bool { self.is_0() || self.is_1() } + /// Checks whether this is either [`Ieee1164::_1`], [`Ieee1164::_H`] pub fn is_1H(self) -> bool { self.is_1() || self.is_H() } + /// Checks whether this is either [`Ieee1164::_0`], [`Ieee1164::_L`] pub fn is_0L(self) -> bool { self.is_0() || self.is_L() } + /// Checks whether this is either [`Ieee1164::_U`] pub fn is_U(self) -> bool { self == _U } + /// Checks whether this is either [`Ieee1164::_X`] pub fn is_X(self) -> bool { self == _X } + /// Checks whether this is either [`Ieee1164::_0`] pub fn is_0(self) -> bool { self == _0 } + /// Checks whether this is either [`Ieee1164::_1`] pub fn is_1(self) -> bool { self == _1 } + /// Checks whether this is either [`Ieee1164::_Z`] pub fn is_Z(self) -> bool { self == _Z } + /// Checks whether this is either [`Ieee1164::_W`] pub fn is_W(self) -> bool { self == _W } + /// Checks whether this is either [`Ieee1164::_L`] pub fn is_L(self) -> bool { self == _L } + /// Checks whether this is either [`Ieee1164::_H`] pub fn is_H(self) -> bool { self == _H } + /// Checks whether this is either [`Ieee1164::_D`] pub fn is_D(self) -> bool { self == _D } @@ -268,7 +305,7 @@ mod tests { use super::*; #[test] - fn add() { + fn and() { assert_eq!(Ieee1164::_0, Ieee1164::_X & Ieee1164::_0); } diff --git a/src/logicbit/logicvector/mod.rs b/src/logicbit/logicvector/mod.rs index 33c3da3..bb9e1f2 100644 --- a/src/logicbit/logicvector/mod.rs +++ b/src/logicbit/logicvector/mod.rs @@ -51,6 +51,17 @@ fn assert_width(width: u8) -> bool { width != 0 && width <= 128 } +/// A logicvector is an vector containing [`Ieee1164`] as values. +/// +/// # Invariant +/// +/// There are the following invariants for this struct. +/// +/// 1. The width is always not equals zero. +/// 2. The width is limited to 128. +/// +/// If any of these limitations are violated a panic will occur. +/// #[derive(Debug, Clone)] pub struct LogicVector { masks: Masks, @@ -58,6 +69,16 @@ pub struct LogicVector { } impl LogicVector { + /// Accepts a [`Ieee1164`] and set that value to the whole range of `width`. + /// + /// # Example + /// + /// ```rust + /// use logical::{Ieee1164, LogicVector}; + /// let lv = LogicVector::from_ieee_value(Ieee1164::_0, 8); + /// assert_eq!(8, lv.width()); + /// assert!(lv.is_000()); + /// ``` pub fn from_ieee_value(value: Ieee1164, width: u8) -> Self { assert!(assert_width(width)); let mut s = Self { @@ -69,6 +90,30 @@ impl LogicVector { s } + /// Tries to convert an integer value with a given width to a `Logicvector`. + /// + /// It will return `None` if the invariants are violated (e.g. the `width` is `0` or greater + /// than `128`), or the binary size of `value` is greater than `width`. + /// + /// # Examples + /// + /// This example is successful because the `width` (`8`) is greater than 0, less than 129 and + /// the bit representation of `42` (`0b101010`) fits into 8 bits. + /// + /// ```rust + /// use logical::LogicVector; + /// let lv = LogicVector::from_int_value(42, 8).unwrap(); + /// assert_eq!(lv.as_u128(), Some(42)); + /// ``` + /// + /// This example will return `None`, because `42` (`0b101010`) does not fit into 5 bits, but + /// need 6 instead. + /// + /// ```rust + /// use logical::LogicVector; + /// let lv = LogicVector::from_int_value(42, 5); + /// assert!(lv.is_none()); + /// ``` pub fn from_int_value(value: u128, width: u8) -> Option { let zeros = value.leading_zeros() as u8; if assert_width(width) && width >= (128 - zeros) { @@ -83,6 +128,12 @@ impl LogicVector { } } + /// Creates a LogicVector with the given width and all values are set to [`Ieee1164::_U`] + /// (undefined). It is a shortcut for + /// + /// ```text + /// LogicVector::from_ieee_value(Ieee1164::_U, width); + /// ``` pub fn with_width(width: u8) -> Self { assert!(assert_width(width)); Self::from_ieee_value(Ieee1164::default(), width) @@ -90,15 +141,81 @@ impl LogicVector { } impl LogicVector { + /// Returns the width of this LogicVector. + /// + /// ```rust + /// # use logical::LogicVector; + /// assert_eq!(7, LogicVector::with_width(7).width()); + /// ``` pub fn width(&self) -> u8 { self.width } + /// Set's the width of this LogicVector. For further information see [`LogicVector::resize`]. + /// This is a shortcut for + /// ```text + /// LogicVector::resize(new_width, Ieee1164::_U); + /// ``` pub fn set_width(&mut self, new_width: u8) { self.resize(new_width, Ieee1164::_U); debug_assert_eq!(Ok(()), self.sanity_check()); } + /// Resizes the LogicVector to `new_width`. There are three possible cases. + /// + /// 1. `new_width == self.width()` + /// - nothing changes + /// 2. `new_width < self.width()` + /// - The LogicVector will get cropped to the new size. The cropped values will be returned + /// as a new LogicVector, whereas the width is equal to the difference between the old + /// and new width. + /// 3. `new_width > self.width()` + /// - The LogicVector will be set to the new_length and the new bits will be set to `value`. + /// + /// # Examples + /// + /// Using the same length will not change anything. + /// + /// ```rust + /// # use logical::{Ieee1164, LogicVector}; + /// let mut lv1 = LogicVector::from_int_value(42, 8).unwrap(); + /// let lv2 = lv1.clone(); + /// let cropped = lv1.resize(8, Ieee1164::_U); + /// + /// assert!(cropped.is_none()); + /// assert_eq!(lv1, lv2); + /// ``` + /// + /// Making the LogicVector smaller will return the cropped values. + /// + /// ```rust + /// # use logical::{Ieee1164, LogicVector}; + /// let mut lv = LogicVector::from_int_value(58, 7).unwrap(); + /// let cropped = lv.resize(4, Ieee1164::_U).unwrap(); + /// + /// assert_eq!(4, lv.width()); + /// assert_eq!(3, cropped.width()); + /// assert_eq!(Some(0b011), cropped.as_u128()); + /// assert_eq!(Some(0b1010), lv.as_u128()); + /// ``` + /// + /// Making the LogicVector greater will set the upper most bits to `value`. + /// + /// ```rust + /// # use logical::{Ieee1164, LogicVector}; + /// let mut lv = LogicVector::from_int_value(42, 6).unwrap(); + /// lv.resize(8, Ieee1164::_1); + /// + /// assert_eq!(Some(0b11101010), lv.as_u128()); + /// ``` + /// + /// ```rust + /// # use logical::{Ieee1164, LogicVector}; + /// let mut lv = LogicVector::from_int_value(42, 8).unwrap(); + /// lv.resize(10, Ieee1164::_1); + /// + /// assert_eq!(Some(0b1100101010), lv.as_u128()); + /// ``` pub fn resize(&mut self, new_width: u8, value: Ieee1164) -> Option { fn resize_mask(old: u8, new: u8) -> u128 { match (old, new) { @@ -151,6 +268,18 @@ impl LogicVector { res } + /// Set's every bit to `value`. + /// + /// ```rust + /// # use logical::{Ieee1164, LogicVector}; + /// let mut lv = LogicVector::with_width(8); + /// assert!(lv.is_UUU()); + /// assert!(!lv.is_ZZZ()); + /// + /// lv.set_all_to(Ieee1164::_Z); + /// assert!(!lv.is_UUU()); + /// assert!(lv.is_ZZZ()); + /// ``` pub fn set_all_to(&mut self, value: Ieee1164) { for mask in &mut self.masks { *mask.1 = if value == mask.0 { @@ -163,12 +292,28 @@ impl LogicVector { } //TODO introduce proper error type + //TODO documentation pub fn set_int_value(&mut self, value: u128) -> Result<(), ()> { std::mem::replace(self, Self::from_int_value(value, self.width()).ok_or(())?); Ok(()) } - // TODO: maybe not pub? + /// Tries to convert this to a `u128`. This will fail if the LogicVector contains any other bits + /// than [`Ieee1164::_0`] or [`Ieee1164::_1`]. + /// + /// ```rust + /// # use logical::LogicVector; + /// let lv = LogicVector::from_int_value(55, 8).unwrap(); + /// assert_eq!(Some(55), lv.as_u128()); + /// ``` + /// + /// ```rust + /// # use logical::{Ieee1164, LogicVector}; + /// let mut lv = LogicVector::from_int_value(55, 8).unwrap(); + /// assert_eq!(Some(55), lv.as_u128()); + /// lv.set(7, Ieee1164::_X); + /// assert_eq!(None, lv.as_u128()); + /// ``` pub fn as_u128(&self) -> Option { if self.has_UXZ() { None @@ -334,67 +479,82 @@ impl PartialEq for LogicVector { } } -#[allow(non_snake_case)] -impl LogicVector { - fn contains(&self, value: Ieee1164) -> bool { - self.masks[value] != 0 - } - - fn is_only(&self, value: Ieee1164) -> bool { - self.masks[value] == std::u128::MAX & gen_mask_from_width(self.width) - } - - pub fn has_U(&self) -> bool { - self.contains(Ieee1164::Uninitialized) - } - - pub fn has_X(&self) -> bool { - self.contains(Ieee1164::Strong(Ieee1164Value::Unknown)) - } +macro_rules! doc_comment { + ($x:expr, $($tt:tt)*) => { + #[doc = $x] + $($tt)* + }; +} - pub fn has_0(&self) -> bool { - self.contains(Ieee1164::Strong(Ieee1164Value::Zero)) - } +macro_rules! gen_has { + ($name:ident, $value:expr) => { + doc_comment! { + concat!("This is a shortcut for [`LogicVector::has_ieee1164`]. - pub fn has_1(&self) -> bool { - self.contains(Ieee1164::Strong(Ieee1164Value::One)) - } - - pub fn has_Z(&self) -> bool { - self.contains(Ieee1164::HighImpedance) - } +```text +LogicVector::has_ieee1164(", stringify!($value), ") +```"), + pub fn $name(&self) -> bool { + self.has_ieee1164($value) + } + } + }; +} - pub fn has_W(&self) -> bool { - self.contains(Ieee1164::Weak(Ieee1164Value::Unknown)) - } +macro_rules! gen_is { + ($name:ident, $value:expr) => { + doc_comment! { + concat!("This is a shortcut for [`LogicVector::is_ieee1164`]. - pub fn has_D(&self) -> bool { - self.contains(Ieee1164::DontCare) - } +```text +LogicVector::is_ieee1164(", stringify!($value), "); +```"), + pub fn $name(&self) -> bool { + self.is_ieee1164($value) + } + } + }; +} - pub fn has_L(&self) -> bool { - self.contains(Ieee1164::Weak(Ieee1164Value::Zero)) +#[allow(non_snake_case)] +impl LogicVector { + /// Checks if any bit is set to `value`. + /// + /// Returns true if so, false if that bit is not present in this LogicVector. + pub fn has_ieee1164(&self, value: Ieee1164) -> bool { + self.masks[value] != 0 } - pub fn has_H(&self) -> bool { - self.contains(Ieee1164::Weak(Ieee1164Value::One)) + /// Checks if all bits are set to `value`. + /// + /// Returns true if so, false if even one single bit is not set to `value` in this LogicVector. + pub fn is_ieee1164(&self, value: Ieee1164) -> bool { + self.masks[value] == std::u128::MAX & mask_from_width(self.width) } pub fn has_UXZ(&self) -> bool { - self.has_U() || self.has_D() || self.has_W() || self.has_X() || self.has_Z() - } - - pub fn is_000(&self) -> bool { - self.is_only(Ieee1164::_0) - } - - pub fn is_111(&self) -> bool { - self.is_only(Ieee1164::_1) - } - - pub fn is_ZZZ(&self) -> bool { - self.is_only(Ieee1164::_Z) - } + self.has_U() || self.has_X() || self.has_W() || self.has_Z() || self.has_D() + } + + gen_has!(has_U, Ieee1164::_U); + gen_has!(has_X, Ieee1164::_X); + gen_has!(has_0, Ieee1164::_0); + gen_has!(has_1, Ieee1164::_1); + gen_has!(has_W, Ieee1164::_W); + gen_has!(has_H, Ieee1164::_H); + gen_has!(has_L, Ieee1164::_L); + gen_has!(has_Z, Ieee1164::_Z); + gen_has!(has_D, Ieee1164::_D); + + gen_is!(is_UUU, Ieee1164::_U); + gen_is!(is_XXX, Ieee1164::_X); + gen_is!(is_000, Ieee1164::_0); + gen_is!(is_111, Ieee1164::_1); + gen_is!(is_WWW, Ieee1164::_W); + gen_is!(is_HHH, Ieee1164::_H); + gen_is!(is_LLL, Ieee1164::_L); + gen_is!(is_ZZZ, Ieee1164::_Z); + gen_is!(is_DDD, Ieee1164::_D); } #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] diff --git a/src/models/gates/mod.rs b/src/models/gates/mod.rs index 11eadaa..1340ffa 100644 --- a/src/models/gates/mod.rs +++ b/src/models/gates/mod.rs @@ -40,8 +40,11 @@ macro_rules! create_simple_2i1o_gate { ($name:ident, $func:ident) => { #[derive(Debug, Default, Clone)] //TODO: remove Clone! pub struct $name { + /// First input port pub a: Port, + /// Second input port pub b: Port, + /// Output port pub z: Port, _private: (), } diff --git a/src/port/pport.rs b/src/port/pport.rs index d348d18..94e65bd 100644 --- a/src/port/pport.rs +++ b/src/port/pport.rs @@ -10,20 +10,22 @@ use crate::port::portconnector::PortConnector; use crate::Ieee1164; use std::sync::Weak; +#[allow(unused)] +use crate::{models::gates::AndGate, Signal}; + +/// A `Port` is the connection between a model (e.g. an [`AndGate`]) and a signal. +/// +/// A `Port` has a Direction, either [`Input`], [`Output`] or [`InOut`]. Depending on the direction +/// you can read, write or do both on the `Port`. You can't for example read on an [`Output`] port, +/// but only write to it. +/// +/// You can `clone` a `Port` as often as you want, or even [`drop`], it doesn't affect other ports. #[derive(Debug, Clone)] pub struct Port { pub(crate) inner: Arc>, _marker: PhantomData, } -impl PartialEq for Port { - fn eq(&self, other: &Port) -> bool { - Arc::ptr_eq(&self.inner, &other.inner) - } -} - -impl Eq for Port {} - impl Default for Port { fn default() -> Self { Port::new(T::default()) @@ -31,6 +33,39 @@ impl Default for Port { } impl Port { + /// Create a new Port with an inner value. + /// + /// # Examples + /// + /// ```rust + /// use logical::Port; + /// use logical::direction::Input; + /// + /// let port = Port::::new(3u32); + /// assert_eq!(3u32, port.value()); + /// ``` + /// + /// You can't however read the value of an [`Output`] `Port`. + /// + /// ```rust,compile_fail,E0599 + /// use logical::Port; + /// use logical::direction::Output; + /// + /// let port = Port::::new(3u32); + /// assert_eq!(3u32, port.value()); + /// ``` + /// + /// But of course, you can do both an an [`InOut`] `Port`. + /// + /// ```rust + /// use logical::Port; + /// use logical::direction::InOut; + /// + /// let mut port = Port::::new(3u32); + /// assert_eq!(3u32, port.value()); + /// port.replace(5); + /// assert_eq!(5, port.value()); + /// ``` pub fn new(value: T) -> Self { Port { inner: Arc::new(InnerPort { @@ -41,13 +76,39 @@ impl Port { } } + /// Create a Port with an already exiting `InnerPort`. This is only useful, if you have to + /// convert a Port from one Direction to another and can't use the `TryFrom` trait. + /// Otherwise please you clone!! pub(crate) fn new_with_arc(arc: Arc>) -> Self { Port { inner: arc, _marker: PhantomData, } } +} + +impl Port +where + D: PortDirection, +{ + pub(crate) fn _connect(&mut self, _signal: WeakSignal) { + //FIXME + //std::mem::replace(&mut self.inner.signal, signal); + } + /// Returns whether this `Port` is connected to a [`Signal`]. + /// + /// ```rust + /// # use logical::{Ieee1164, Port, Signal}; + /// # use logical::direction::Output; + /// let port = Port::<_, Output>::new(Ieee1164::_U); + /// assert!(!port.is_connected()); + /// + /// let mut signal = Signal::default(); + /// signal.connect(&port); + /// //assert!(port.is_connected()); + /// ``` + // FIXME! pub fn is_connected(&self) -> bool { self.inner.signal.upgrade().is_some() } @@ -59,6 +120,15 @@ where W: MaybeWrite, Dir: PortDirection, { + /// Returns a copy of the inner value. `Clone` is needed, because on how the values are + /// internally stored. If `T` is not `Clone` use [`Port::with_value`]. + /// + /// ```rust + /// # use logical::Port; + /// # use logical::direction::Input; + /// let port = Port::<_, Input>::new(5u32); + /// assert_eq!(5, port.value()); + /// ``` pub fn value(&self) -> T { self.inner.value.read().unwrap().clone() } @@ -69,6 +139,15 @@ where W: MaybeWrite, Dir: PortDirection, { + /// Accepts a [`FnOnce`] which accepts `&T` and executes it with the inner value. + /// This function is useful, if `T` does not implement `Clone`. + /// + /// ```rust + /// # use logical::Port; + /// # use logical::direction::Input; + /// let port = Port::<_, Input>::new(5u32); + /// port.with_value(|value| assert_eq!(&5, value)); + /// ``` pub fn with_value(&self, f: F) { f(&self.inner.value.read().unwrap()); } @@ -79,10 +158,32 @@ where R: MaybeRead, Dir: PortDirection, { - pub fn replace(&mut self, value: T) { - *self.inner.value.write().unwrap() = value; + /// Replaces the internal value with `value` and returns the old value. + /// + /// If you intend to modify the inner value, use `with_value_mut` instead. + /// + /// ```rust + /// # use logical::Port; + /// # use logical::direction::Output; + /// let mut port = Port::<_, Output>::new(5u32); + /// port.replace(9u32); + /// ``` + pub fn replace(&mut self, value: T) -> T { + std::mem::replace(&mut self.inner.value.write().unwrap(), value) } + /// Accepts a `FnOnce` which accepts a `&mut T`, so you can modify the inner values, instead of + /// replacing it. + /// + /// ```rust + /// # use logical::Port; + /// # use logical::direction::Output; + /// let mut port = Port::<_, Output>::new(String::from("ABC")); + /// port.with_value_mut(|value| { + /// value.push('D'); + /// assert_eq!("ABCD", value); + /// }); + /// ``` pub fn with_value_mut(&mut self, f: F) { f(&mut self.inner.value.write().unwrap()); } @@ -166,6 +267,14 @@ where } } +impl PartialEq for Port { + fn eq(&self, other: &Port) -> bool { + Arc::ptr_eq(&self.inner, &other.inner) + } +} + +impl Eq for Port {} + impl IterValues for Port where D: PortDirection, diff --git a/src/signal.rs b/src/signal.rs index 1a42c64..2b2560d 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -72,13 +72,13 @@ impl Signal { if D::IS_INPUT || D::IS_INOUT { let connector = port.try_into().unwrap(); if !out_guard.contains(&connector) { - out_guard.push(connector) + out_guard.push(connector); } } if D::IS_OUTPUT || D::IS_INOUT { let connector = port.try_into().unwrap(); if !in_guard.contains(&connector) { - in_guard.push(connector) + in_guard.push(connector); } }