diff --git a/src/_macros.rs b/src/_macros.rs index 3794a650..f58767e7 100644 --- a/src/_macros.rs +++ b/src/_macros.rs @@ -65,277 +65,6 @@ macro_rules! iterator_for_nodeiterator { }; } -macro_rules! impl_id_traits { - ($idtype: ty) => { - impl $idtype { - /// NULL value for this type. - pub const NULL: $idtype = Self($crate::sys::TSK_NULL); - - /// Return `true` is `self == Self::NULL` - pub fn is_null(&self) -> bool { - *self == Self::NULL - } - - /// Convenience function to convert to usize. - /// - /// Works via [`TryFrom`]. - /// - /// # Returns - /// - /// * `None` if the underlying value is negative. - /// * `Some(usize)` otherwise. - pub fn to_usize(&self) -> Option { - usize::try_from(*self).ok() - } - - /// Convenience function to convert to usize. - /// Implemented via `as`. - /// Negative values with therefore wrap. - pub fn as_usize(&self) -> usize { - self.0 as usize - } - } - - impl std::fmt::Display for $idtype { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match *self == Self::NULL { - false => write!(f, "{}", self.0), - true => write!(f, "NULL"), - } - } - } - - impl From<$crate::sys::bindings::tsk_id_t> for $idtype { - fn from(value: $crate::sys::bindings::tsk_id_t) -> Self { - Self(value) - } - } - - impl TryFrom<$idtype> for usize { - type Error = crate::TskitError; - fn try_from(value: $idtype) -> Result { - match value.0.try_into() { - Ok(value) => Ok(value), - Err(_) => Err(crate::TskitError::RangeError(format!( - "could not convert {:?} to usize", - value - ))), - } - } - } - - impl From<$idtype> for $crate::sys::bindings::tsk_id_t { - fn from(value: $idtype) -> Self { - value.0 - } - } - - impl TryFrom<$idtype> for $crate::SizeType { - type Error = $crate::TskitError; - - fn try_from(value: $idtype) -> Result { - $crate::SizeType::try_from(value.0) - } - } - - impl PartialEq<$crate::sys::bindings::tsk_id_t> for $idtype { - fn eq(&self, other: &$crate::sys::bindings::tsk_id_t) -> bool { - self.0 == *other - } - } - - impl PartialEq<$idtype> for $crate::sys::bindings::tsk_id_t { - fn eq(&self, other: &$idtype) -> bool { - *self == other.0 - } - } - - impl PartialOrd<$crate::sys::bindings::tsk_id_t> for $idtype { - fn partial_cmp( - &self, - other: &$crate::sys::bindings::tsk_id_t, - ) -> Option { - self.0.partial_cmp(other) - } - } - - impl PartialOrd<$idtype> for $crate::sys::bindings::tsk_id_t { - fn partial_cmp(&self, other: &$idtype) -> Option { - self.partial_cmp(&other.0) - } - } - - impl Default for $idtype { - fn default() -> Self { - Self::NULL - } - } - }; -} - -macro_rules! impl_size_type_comparisons_for_row_ids { - ($idtype: ty) => { - impl PartialEq<$idtype> for $crate::SizeType { - fn eq(&self, other: &$idtype) -> bool { - self.0 == other.0 as $crate::sys::bindings::tsk_size_t - } - } - - impl PartialEq<$crate::SizeType> for $idtype { - fn eq(&self, other: &$crate::SizeType) -> bool { - (self.0 as $crate::sys::bindings::tsk_size_t) == other.0 - } - } - - impl PartialOrd<$idtype> for $crate::SizeType { - fn partial_cmp(&self, other: &$idtype) -> Option { - self.0 - .partial_cmp(&(other.0 as $crate::sys::bindings::tsk_size_t)) - } - } - - impl PartialOrd<$crate::SizeType> for $idtype { - fn partial_cmp(&self, other: &$crate::SizeType) -> Option { - (self.0 as $crate::sys::bindings::tsk_size_t).partial_cmp(&other.0) - } - } - }; -} - -macro_rules! impl_f64_newtypes { - ($type: ty) => { - impl std::fmt::Display for $type { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.0) - } - } - - impl PartialEq for $type { - fn eq(&self, other: &f64) -> bool { - self.0.eq(other) - } - } - - impl PartialEq<$type> for f64 { - fn eq(&self, other: &$type) -> bool { - self.eq(&other.0) - } - } - - impl PartialOrd for $type { - fn partial_cmp(&self, other: &f64) -> Option { - self.0.partial_cmp(other) - } - } - - impl PartialOrd<$type> for f64 { - fn partial_cmp(&self, other: &$type) -> Option { - self.partial_cmp(&other.0) - } - } - - impl From for $type { - fn from(value: f64) -> Self { - Self(value) - } - } - - impl From<$type> for f64 { - fn from(value: $type) -> Self { - value.0 - } - } - - impl std::ops::Sub for $type { - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - Self(self.0 - rhs.0) - } - } - - impl std::ops::SubAssign for $type { - fn sub_assign(&mut self, rhs: Self) { - self.0 -= rhs.0 - } - } - - impl std::ops::Add for $type { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - Self(self.0 + rhs.0) - } - } - - impl std::ops::AddAssign for $type { - fn add_assign(&mut self, rhs: Self) { - self.0 += rhs.0 - } - } - - impl std::ops::Mul for $type { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - Self(self.0 * rhs.0) - } - } - - impl std::ops::MulAssign for $type { - fn mul_assign(&mut self, rhs: Self) { - self.0.mul_assign(&rhs.0) - } - } - - impl std::ops::Div for $type { - type Output = Self; - - fn div(self, rhs: Self) -> Self::Output { - Self(self.0 / rhs.0) - } - } - - impl std::ops::DivAssign for $type { - fn div_assign(&mut self, rhs: Self) { - self.0.div_assign(&rhs.0) - } - } - }; -} - -macro_rules! impl_time_position_arithmetic { - ($lhs: ty, $rhs:ty) => { - impl std::ops::Mul<$rhs> for $lhs { - type Output = $lhs; - - fn mul(self, other: $rhs) -> Self { - Self(self.0.mul(&other.0)) - } - } - - impl std::ops::MulAssign<$rhs> for $lhs { - fn mul_assign(&mut self, other: $rhs) { - self.0.mul_assign(&other.0) - } - } - - impl std::ops::Div<$rhs> for $lhs { - type Output = $lhs; - - fn div(self, other: $rhs) -> Self { - Self(self.0.div(&other.0)) - } - } - - impl std::ops::DivAssign<$rhs> for $lhs { - fn div_assign(&mut self, other: $rhs) { - self.0.div_assign(&other.0) - } - } - }; -} - /// Convenience macro to handle implementing /// [`crate::metadata::MetadataRoundtrip`] #[macro_export] diff --git a/src/newtypes.rs b/src/newtypes.rs index 9ac147ef..39be14d1 100644 --- a/src/newtypes.rs +++ b/src/newtypes.rs @@ -1,8 +1,30 @@ -use crate::sys::bindings; -use crate::TskitError; +/// An edge ID +/// +/// This is an integer referring to a row of an [``EdgeTable``](crate::EdgeTable). +/// +/// The features for this type follow the same pattern as for [``NodeId``] +pub use crate::sys::newtypes::EdgeId; + +/// An individual ID +/// +/// This is an integer referring to a row of an [``IndividualTable``](crate::IndividualTable). +/// +/// The features for this type follow the same pattern as for [``NodeId``] +pub use crate::sys::newtypes::IndividualId; + +/// A mutation ID +/// +/// This is an integer referring to a row of an [``MutationTable``](crate::MutationTable). +/// +/// The features for this type follow the same pattern as for [``NodeId``] +pub use crate::sys::newtypes::MutationId; -use bindings::tsk_id_t; -use bindings::tsk_size_t; +/// A migration ID +/// +/// This is an integer referring to a row of an [``MigrationTable``](crate::MigrationTable). +/// +/// The features for this type follow the same pattern as for [``NodeId``] +pub use crate::sys::newtypes::MigrationId; /// A node ID /// @@ -61,78 +83,30 @@ use bindings::tsk_size_t; /// assert_eq!(format!("{:?}", n), "NodeId(-1)"); /// ``` /// -#[repr(transparent)] -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] -pub struct NodeId(tsk_id_t); - -/// An individual ID -/// -/// This is an integer referring to a row of an [``IndividualTable``](crate::IndividualTable). -/// -/// The features for this type follow the same pattern as for [``NodeId``] -#[repr(transparent)] -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] -pub struct IndividualId(tsk_id_t); +pub use crate::sys::newtypes::NodeId; /// A population ID /// /// This is an integer referring to a row of an [``PopulationTable``](crate::PopulationTable). /// /// The features for this type follow the same pattern as for [``NodeId``] -#[repr(transparent)] -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] -pub struct PopulationId(tsk_id_t); - -/// A site ID -/// -/// This is an integer referring to a row of an [``SiteTable``](crate::SiteTable). -/// -/// The features for this type follow the same pattern as for [``NodeId``] -#[repr(transparent)] -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] -pub struct SiteId(tsk_id_t); +pub use crate::sys::newtypes::PopulationId; -/// A mutation ID -/// -/// This is an integer referring to a row of an [``MutationTable``](crate::MutationTable). -/// -/// The features for this type follow the same pattern as for [``NodeId``] -#[repr(transparent)] -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] -pub struct MutationId(tsk_id_t); - -/// A migration ID +/// A provenance ID /// -/// This is an integer referring to a row of an [``MigrationTable``](crate::MigrationTable). +/// This is an integer referring to a row of a [``provenance::ProvenanceTable``]. /// /// The features for this type follow the same pattern as for [``NodeId``] -#[repr(transparent)] -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] -pub struct MigrationId(tsk_id_t); +#[cfg(feature = "provenance")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "provenance")))] +pub use crate::sys::newtypes::ProvenanceId; -/// An edge ID +/// A site ID /// -/// This is an integer referring to a row of an [``EdgeTable``](crate::EdgeTable). +/// This is an integer referring to a row of an [``SiteTable``](crate::SiteTable). /// /// The features for this type follow the same pattern as for [``NodeId``] -#[repr(transparent)] -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] -pub struct EdgeId(tsk_id_t); - -impl_id_traits!(NodeId); -impl_id_traits!(IndividualId); -impl_id_traits!(PopulationId); -impl_id_traits!(SiteId); -impl_id_traits!(MutationId); -impl_id_traits!(MigrationId); -impl_id_traits!(EdgeId); - -impl_size_type_comparisons_for_row_ids!(NodeId); -impl_size_type_comparisons_for_row_ids!(EdgeId); -impl_size_type_comparisons_for_row_ids!(SiteId); -impl_size_type_comparisons_for_row_ids!(MutationId); -impl_size_type_comparisons_for_row_ids!(PopulationId); -impl_size_type_comparisons_for_row_ids!(MigrationId); +pub use crate::sys::newtypes::SiteId; /// Wraps `tsk_size_t` /// @@ -156,125 +130,28 @@ impl_size_type_comparisons_for_row_ids!(MigrationId); /// ``` /// /// #[repr(transparent)] -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] -pub struct SizeType(tsk_size_t); - -impl SizeType { - /// Convenience function to convert to usize. - /// - /// Works via [`TryFrom`]. - /// - /// # Returns - /// - /// * `None` if the underlying value is negative. - /// * `Some(usize)` otherwise. - pub fn to_usize(&self) -> Option { - (*self).try_into().ok() - } - - /// Convenience function to convert to usize. - /// Implemented via `as`. - /// Negative values with therefore wrap. - pub fn as_usize(&self) -> usize { - self.0 as usize - } -} - -impl std::fmt::Display for SizeType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl From for SizeType { - fn from(value: tsk_size_t) -> Self { - Self(value) - } -} - -impl From for tsk_size_t { - fn from(value: SizeType) -> Self { - value.0 - } -} - -// SizeType is u64, so converstion -// can fail on systems with smaller pointer widths. -impl TryFrom for usize { - type Error = TskitError; - - fn try_from(value: SizeType) -> Result { - match usize::try_from(value.0) { - Ok(x) => Ok(x), - Err(_) => Err(TskitError::RangeError(format!( - "could not convert {} to usize", - value - ))), - } - } -} - -impl TryFrom for SizeType { - type Error = TskitError; +pub use crate::sys::newtypes::SizeType; - fn try_from(value: usize) -> Result { - match tsk_size_t::try_from(value) { - Ok(x) => Ok(Self(x)), - Err(_) => Err(TskitError::RangeError(format!( - "could not convert usize {} to SizeType", - value - ))), - } - } -} - -impl TryFrom for SizeType { - type Error = crate::TskitError; - - fn try_from(value: tsk_id_t) -> Result { - match tsk_size_t::try_from(value) { - Ok(v) => Ok(Self(v)), - Err(_) => Err(crate::TskitError::RangeError( - stringify!(value.0).to_string(), - )), - } - } -} - -impl TryFrom for tsk_id_t { - type Error = crate::TskitError; - - fn try_from(value: SizeType) -> Result { - match tsk_id_t::try_from(value.0) { - Ok(v) => Ok(v), - Err(_) => Err(TskitError::RangeError(stringify!(value.0).to_string())), - } - } -} - -impl PartialEq for tsk_size_t { - fn eq(&self, other: &SizeType) -> bool { - *self == other.0 - } -} - -impl PartialEq for SizeType { - fn eq(&self, other: &tsk_size_t) -> bool { - self.0 == *other - } -} - -impl PartialOrd for SizeType { - fn partial_cmp(&self, other: &tsk_size_t) -> Option { - self.0.partial_cmp(other) - } -} +/// A newtype for the concept of "genomic position". +/// A `Position` can represent either a locus or a +/// distance between loci. +/// +/// Wraps [`f64`]. +/// +/// For examples, see [`Time`]. +/// +/// This type can be multiplied and divided by [`Time`]. +pub use crate::sys::newtypes::Position; -impl PartialOrd for tsk_size_t { - fn partial_cmp(&self, other: &SizeType) -> Option { - self.partial_cmp(&other.0) - } -} +/// A newtype for the concept of location. +/// A `Location` may represent a location or the +/// output of arithmetic involving locations. +/// +/// Wraps [`f64`]. +/// +/// For examples, see [`Time`]. +/// +pub use crate::sys::newtypes::Location; /// A newtype for the concept of time. /// A `Time` value can represent either a point in time @@ -310,88 +187,4 @@ impl PartialOrd for tsk_size_t { /// /// A `Time` can be multiplied and divided by a [`Position`] /// -#[repr(transparent)] -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] -pub struct Time(f64); - -/// A newtype for the concept of "genomic position". -/// A `Position` can represent either a locus or a -/// distance between loci. -/// -/// Wraps [`f64`]. -/// -/// For examples, see [`Time`]. -/// -/// This type can be multiplied and divided by [`Time`]. -#[repr(transparent)] -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] -pub struct Position(f64); - -/// A newtype for the concept of location. -/// A `Location` may represent a location or the -/// output of arithmetic involving locations. -/// -/// Wraps [`f64`]. -/// -/// For examples, see [`Time`]. -/// -#[repr(transparent)] -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] -pub struct Location(f64); - -impl_f64_newtypes!(Time); -impl_f64_newtypes!(Position); -impl_f64_newtypes!(Location); - -// It is natural to be able to * and / times and positions -impl_time_position_arithmetic!(Time, Position); -impl_time_position_arithmetic!(Position, Time); - -#[cfg(feature = "provenance")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "provenance")))] -/// A provenance ID -/// -/// This is an integer referring to a row of a [``provenance::ProvenanceTable``]. -/// -/// The features for this type follow the same pattern as for [``NodeId``] -#[repr(transparent)] -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] -pub struct ProvenanceId(tsk_id_t); - -#[cfg(feature = "provenance")] -impl_id_traits!(ProvenanceId); - -#[test] -fn test_f64_newtype_Display() { - let x = Position::from(1.0); - let mut output = String::new(); - std::fmt::write(&mut output, format_args!("{}", x)) - .expect("Error occurred while trying to write in String"); - assert_eq!(output, "1".to_string()); - let x = Time::from(1.0); - let mut output = String::new(); - std::fmt::write(&mut output, format_args!("{}", x)) - .expect("Error occurred while trying to write in String"); - assert_eq!(output, "1".to_string()); - let x = Location::from(1.0); - let mut output = String::new(); - std::fmt::write(&mut output, format_args!("{}", x)) - .expect("Error occurred while trying to write in String"); - assert_eq!(output, "1".to_string()); -} - -#[test] -fn test_usize_to_size_type() { - let x = usize::MAX; - let s = SizeType::try_from(x).ok(); - - #[cfg(target_pointer_width = "64")] - assert_eq!(s, Some(bindings::tsk_size_t::MAX.into())); - - #[cfg(target_pointer_width = "32")] - assert_eq!(s, Some((usize::MAX as bindings::tsk_size_t).into())); - - let x = usize::MIN; - let s = SizeType::try_from(x).ok(); - assert_eq!(s, Some(0.into())); -} +pub use crate::sys::newtypes::Time; diff --git a/src/sys/macros.rs b/src/sys/macros.rs index 3b45b5a5..473a8cc7 100644 --- a/src/sys/macros.rs +++ b/src/sys/macros.rs @@ -9,3 +9,271 @@ macro_rules! impl_tskteardown { } }; } + +macro_rules! impl_id_traits { + ($idtype: ty) => { + impl $idtype { + /// NULL value for this type. + pub const NULL: $idtype = Self(super::TSK_NULL); + + /// Return `true` is `self == Self::NULL` + pub fn is_null(&self) -> bool { + *self == Self::NULL + } + + /// Convenience function to convert to usize. + /// + /// Works via [`TryFrom`]. + /// + /// # Returns + /// + /// * `None` if the underlying value is negative. + /// * `Some(usize)` otherwise. + pub fn to_usize(&self) -> Option { + usize::try_from(*self).ok() + } + + /// Convenience function to convert to usize. + /// Implemented via `as`. + /// Negative values with therefore wrap. + pub fn as_usize(&self) -> usize { + self.0 as usize + } + } + + impl std::fmt::Display for $idtype { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match *self == Self::NULL { + false => write!(f, "{}", self.0), + true => write!(f, "NULL"), + } + } + } + + impl From for $idtype { + fn from(value: super::bindings::tsk_id_t) -> Self { + Self(value) + } + } + + impl TryFrom<$idtype> for usize { + type Error = $crate::TskitError; + fn try_from(value: $idtype) -> Result { + match value.0.try_into() { + Ok(value) => Ok(value), + Err(_) => Err(crate::TskitError::RangeError(format!( + "could not convert {:?} to usize", + value + ))), + } + } + } + + impl From<$idtype> for super::bindings::tsk_id_t { + fn from(value: $idtype) -> Self { + value.0 + } + } + + impl TryFrom<$idtype> for SizeType { + type Error = $crate::TskitError; + + fn try_from(value: $idtype) -> Result { + SizeType::try_from(value.0) + } + } + + impl PartialEq for $idtype { + fn eq(&self, other: &super::bindings::tsk_id_t) -> bool { + self.0 == *other + } + } + + impl PartialEq<$idtype> for super::bindings::tsk_id_t { + fn eq(&self, other: &$idtype) -> bool { + *self == other.0 + } + } + + impl PartialOrd for $idtype { + fn partial_cmp(&self, other: &super::bindings::tsk_id_t) -> Option { + self.0.partial_cmp(other) + } + } + + impl PartialOrd<$idtype> for super::bindings::tsk_id_t { + fn partial_cmp(&self, other: &$idtype) -> Option { + self.partial_cmp(&other.0) + } + } + + impl Default for $idtype { + fn default() -> Self { + Self::NULL + } + } + }; +} + +macro_rules! impl_size_type_comparisons_for_row_ids { + ($idtype: ty) => { + impl PartialEq<$idtype> for SizeType { + fn eq(&self, other: &$idtype) -> bool { + self.0 == other.0 as super::bindings::tsk_size_t + } + } + + impl PartialEq for $idtype { + fn eq(&self, other: &SizeType) -> bool { + (self.0 as super::bindings::tsk_size_t) == other.0 + } + } + + impl PartialOrd<$idtype> for SizeType { + fn partial_cmp(&self, other: &$idtype) -> Option { + self.0 + .partial_cmp(&(other.0 as super::bindings::tsk_size_t)) + } + } + + impl PartialOrd for $idtype { + fn partial_cmp(&self, other: &SizeType) -> Option { + (self.0 as super::bindings::tsk_size_t).partial_cmp(&other.0) + } + } + }; +} + +macro_rules! impl_f64_newtypes { + ($type: ty) => { + impl std::fmt::Display for $type { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.0) + } + } + + impl PartialEq for $type { + fn eq(&self, other: &f64) -> bool { + self.0.eq(other) + } + } + + impl PartialEq<$type> for f64 { + fn eq(&self, other: &$type) -> bool { + self.eq(&other.0) + } + } + + impl PartialOrd for $type { + fn partial_cmp(&self, other: &f64) -> Option { + self.0.partial_cmp(other) + } + } + + impl PartialOrd<$type> for f64 { + fn partial_cmp(&self, other: &$type) -> Option { + self.partial_cmp(&other.0) + } + } + + impl From for $type { + fn from(value: f64) -> Self { + Self(value) + } + } + + impl From<$type> for f64 { + fn from(value: $type) -> Self { + value.0 + } + } + + impl std::ops::Sub for $type { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } + } + + impl std::ops::SubAssign for $type { + fn sub_assign(&mut self, rhs: Self) { + self.0 -= rhs.0 + } + } + + impl std::ops::Add for $type { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self(self.0 + rhs.0) + } + } + + impl std::ops::AddAssign for $type { + fn add_assign(&mut self, rhs: Self) { + self.0 += rhs.0 + } + } + + impl std::ops::Mul for $type { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0 * rhs.0) + } + } + + impl std::ops::MulAssign for $type { + fn mul_assign(&mut self, rhs: Self) { + self.0.mul_assign(&rhs.0) + } + } + + impl std::ops::Div for $type { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0 / rhs.0) + } + } + + impl std::ops::DivAssign for $type { + fn div_assign(&mut self, rhs: Self) { + self.0.div_assign(&rhs.0) + } + } + }; +} + +macro_rules! impl_time_position_arithmetic { + ($lhs: ty, $rhs:ty) => { + impl std::ops::Mul<$rhs> for $lhs { + type Output = $lhs; + + fn mul(self, other: $rhs) -> Self { + Self(self.0.mul(&other.0)) + } + } + + impl std::ops::MulAssign<$rhs> for $lhs { + fn mul_assign(&mut self, other: $rhs) { + self.0.mul_assign(&other.0) + } + } + + impl std::ops::Div<$rhs> for $lhs { + type Output = $lhs; + + fn div(self, other: $rhs) -> Self { + Self(self.0.div(&other.0)) + } + } + + impl std::ops::DivAssign<$rhs> for $lhs { + fn div_assign(&mut self, other: $rhs) { + self.0.div_assign(&other.0) + } + } + }; +} diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 8c9d99b5..03fddd3d 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -12,6 +12,7 @@ pub mod flags; mod individual_table; mod migration_table; mod mutation_table; +pub mod newtypes; mod node_table; mod population_table; #[cfg(feature = "provenance")] diff --git a/src/sys/newtypes.rs b/src/sys/newtypes.rs new file mode 100644 index 00000000..18cd67c5 --- /dev/null +++ b/src/sys/newtypes.rs @@ -0,0 +1,231 @@ +use crate::sys::bindings; +use crate::TskitError; + +use bindings::tsk_id_t; +use bindings::tsk_size_t; + +#[repr(transparent)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] +pub struct NodeId(tsk_id_t); + +#[repr(transparent)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] +pub struct IndividualId(tsk_id_t); + +#[repr(transparent)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] +pub struct PopulationId(tsk_id_t); + +#[repr(transparent)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] +pub struct SiteId(tsk_id_t); + +#[repr(transparent)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] +pub struct MutationId(tsk_id_t); + +#[repr(transparent)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] +pub struct MigrationId(tsk_id_t); + +#[repr(transparent)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] +pub struct EdgeId(tsk_id_t); + +impl_id_traits!(NodeId); +impl_id_traits!(IndividualId); +impl_id_traits!(PopulationId); +impl_id_traits!(SiteId); +impl_id_traits!(MutationId); +impl_id_traits!(MigrationId); +impl_id_traits!(EdgeId); + +impl_size_type_comparisons_for_row_ids!(NodeId); +impl_size_type_comparisons_for_row_ids!(EdgeId); +impl_size_type_comparisons_for_row_ids!(SiteId); +impl_size_type_comparisons_for_row_ids!(MutationId); +impl_size_type_comparisons_for_row_ids!(PopulationId); +impl_size_type_comparisons_for_row_ids!(MigrationId); + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] +pub struct SizeType(tsk_size_t); + +impl SizeType { + /// Convenience function to convert to usize. + /// + /// Works via [`TryFrom`]. + /// + /// # Returns + /// + /// * `None` if the underlying value is negative. + /// * `Some(usize)` otherwise. + pub fn to_usize(&self) -> Option { + (*self).try_into().ok() + } + + /// Convenience function to convert to usize. + /// Implemented via `as`. + /// Negative values with therefore wrap. + pub fn as_usize(&self) -> usize { + self.0 as usize + } +} + +impl std::fmt::Display for SizeType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From for SizeType { + fn from(value: tsk_size_t) -> Self { + Self(value) + } +} + +impl From for tsk_size_t { + fn from(value: SizeType) -> Self { + value.0 + } +} + +// SizeType is u64, so converstion +// can fail on systems with smaller pointer widths. +impl TryFrom for usize { + type Error = TskitError; + + fn try_from(value: SizeType) -> Result { + match usize::try_from(value.0) { + Ok(x) => Ok(x), + Err(_) => Err(TskitError::RangeError(format!( + "could not convert {} to usize", + value + ))), + } + } +} + +impl TryFrom for SizeType { + type Error = TskitError; + + fn try_from(value: usize) -> Result { + match tsk_size_t::try_from(value) { + Ok(x) => Ok(Self(x)), + Err(_) => Err(TskitError::RangeError(format!( + "could not convert usize {} to SizeType", + value + ))), + } + } +} + +impl TryFrom for SizeType { + type Error = crate::TskitError; + + fn try_from(value: tsk_id_t) -> Result { + match tsk_size_t::try_from(value) { + Ok(v) => Ok(Self(v)), + Err(_) => Err(crate::TskitError::RangeError( + stringify!(value.0).to_string(), + )), + } + } +} + +impl TryFrom for tsk_id_t { + type Error = crate::TskitError; + + fn try_from(value: SizeType) -> Result { + match tsk_id_t::try_from(value.0) { + Ok(v) => Ok(v), + Err(_) => Err(TskitError::RangeError(stringify!(value.0).to_string())), + } + } +} + +impl PartialEq for tsk_size_t { + fn eq(&self, other: &SizeType) -> bool { + *self == other.0 + } +} + +impl PartialEq for SizeType { + fn eq(&self, other: &tsk_size_t) -> bool { + self.0 == *other + } +} + +impl PartialOrd for SizeType { + fn partial_cmp(&self, other: &tsk_size_t) -> Option { + self.0.partial_cmp(other) + } +} + +impl PartialOrd for tsk_size_t { + fn partial_cmp(&self, other: &SizeType) -> Option { + self.partial_cmp(&other.0) + } +} + +#[repr(transparent)] +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] +pub struct Time(f64); + +#[repr(transparent)] +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] +pub struct Position(f64); + +#[repr(transparent)] +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] +pub struct Location(f64); + +impl_f64_newtypes!(Time); +impl_f64_newtypes!(Position); +impl_f64_newtypes!(Location); + +// It is natural to be able to * and / times and positions +impl_time_position_arithmetic!(Time, Position); +impl_time_position_arithmetic!(Position, Time); + +#[cfg(feature = "provenance")] +#[repr(transparent)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)] +pub struct ProvenanceId(tsk_id_t); + +#[cfg(feature = "provenance")] +impl_id_traits!(ProvenanceId); + +#[test] +fn test_f64_newtype_Display() { + let x = Position::from(1.0); + let mut output = String::new(); + std::fmt::write(&mut output, format_args!("{}", x)) + .expect("Error occurred while trying to write in String"); + assert_eq!(output, "1".to_string()); + let x = Time::from(1.0); + let mut output = String::new(); + std::fmt::write(&mut output, format_args!("{}", x)) + .expect("Error occurred while trying to write in String"); + assert_eq!(output, "1".to_string()); + let x = Location::from(1.0); + let mut output = String::new(); + std::fmt::write(&mut output, format_args!("{}", x)) + .expect("Error occurred while trying to write in String"); + assert_eq!(output, "1".to_string()); +} + +#[test] +fn test_usize_to_size_type() { + let x = usize::MAX; + let s = SizeType::try_from(x).ok(); + + #[cfg(target_pointer_width = "64")] + assert_eq!(s, Some(bindings::tsk_size_t::MAX.into())); + + #[cfg(target_pointer_width = "32")] + assert_eq!(s, Some((usize::MAX as bindings::tsk_size_t).into())); + + let x = usize::MIN; + let s = SizeType::try_from(x).ok(); + assert_eq!(s, Some(0.into())); +}