use crate::mesh_error::MeshSieveError;
use std::{convert::TryFrom, fmt, num::NonZeroU64};
#[derive(
Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
)]
#[repr(transparent)]
pub struct PointId(NonZeroU64);
impl PointId {
#[inline]
pub fn new(raw: u64) -> Result<Self, MeshSieveError> {
NonZeroU64::new(raw)
.map(PointId)
.ok_or(MeshSieveError::InvalidPointId)
}
#[inline]
pub fn get(&self) -> u64 {
self.0.get()
}
#[inline]
pub unsafe fn new_unchecked(raw: u64) -> Self {
PointId(unsafe { NonZeroU64::new_unchecked(raw) })
}
}
impl TryFrom<u64> for PointId {
type Error = MeshSieveError;
#[inline]
fn try_from(raw: u64) -> Result<Self, MeshSieveError> {
PointId::new(raw)
}
}
impl TryFrom<usize> for PointId {
type Error = MeshSieveError;
#[inline]
fn try_from(raw: usize) -> Result<Self, MeshSieveError> {
let raw64 = u64::try_from(raw).map_err(|_| MeshSieveError::InvalidPointId)?;
PointId::new(raw64)
}
}
impl From<PointId> for u64 {
#[inline]
fn from(p: PointId) -> Self {
p.get()
}
}
impl From<PointId> for NonZeroU64 {
#[inline]
fn from(p: PointId) -> Self {
p.0
}
}
impl From<NonZeroU64> for PointId {
#[inline]
fn from(nz: NonZeroU64) -> Self {
PointId(nz)
}
}
impl fmt::Debug for PointId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("PointId").field(&self.get()).finish()
}
}
impl fmt::Display for PointId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.get())
}
}
#[cfg(feature = "mpi-support")]
unsafe impl mpi::datatype::Equivalence for PointId {
type Out = <u64 as mpi::datatype::Equivalence>::Out;
fn equivalent_datatype() -> Self::Out {
u64::equivalent_datatype()
}
}
#[cfg(test)]
mod layout_tests {
use super::*;
use static_assertions::assert_eq_size;
assert_eq_size!(PointId, u64);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_zero_returns_error() {
assert!(PointId::new(0).is_err());
}
#[test]
fn new_and_get() {
let p = PointId::new(42).unwrap();
assert_eq!(p.get(), 42);
}
#[test]
fn try_from_u64_success() -> Result<(), MeshSieveError> {
let p = PointId::try_from(42u64)?;
assert_eq!(p.get(), 42);
Ok(())
}
#[test]
fn try_from_u64_zero_fails() {
assert_eq!(
PointId::try_from(0u64).unwrap_err(),
MeshSieveError::InvalidPointId
);
}
#[test]
fn try_from_usize_success() -> Result<(), MeshSieveError> {
let p = PointId::try_from(123usize)?;
assert_eq!(p.get(), 123);
Ok(())
}
#[test]
fn try_from_usize_zero_fails() {
assert_eq!(
PointId::try_from(0usize).unwrap_err(),
MeshSieveError::InvalidPointId
);
}
#[test]
fn debug_and_display() {
let p = PointId::new(7).unwrap();
assert_eq!(format!("{:?}", p), "PointId(7)");
assert_eq!(format!("{}", p), "7");
}
#[test]
fn ordering_and_hash() {
let a = PointId::new(1).unwrap();
let b = PointId::new(2).unwrap();
assert!(a < b);
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(a);
set.insert(b);
assert_eq!(set.len(), 2);
}
#[test]
fn roundtrip_conversions() {
use std::num::NonZeroU64;
let p = PointId::new(1).unwrap();
let raw: u64 = p.into();
assert_eq!(raw, 1);
let nz = NonZeroU64::new(123).unwrap();
let p2: PointId = nz.into();
assert_eq!(u64::from(p2), 123);
let nz2: NonZeroU64 = p2.into();
assert_eq!(nz2.get(), 123);
}
}
#[cfg(test)]
mod serde_tests {
use super::*;
#[test]
fn json_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
let p = PointId::new(123)?;
let s = serde_json::to_string(&p)?;
let p2: PointId = serde_json::from_str(&s)?;
assert_eq!(p2, p);
Ok(())
}
#[test]
fn bincode_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
let p = PointId::new(456)?;
let bytes = bincode::serialize(&p)?;
let p2: PointId = bincode::deserialize(&bytes)?;
assert_eq!(p2, p);
Ok(())
}
}
#[cfg(test)]
mod abi_tests {
use super::*;
use static_assertions::{assert_eq_align, assert_eq_size};
#[test]
fn alignment_matches_u64() {
assert_eq_align!(PointId, u64);
}
#[test]
fn size_matches_u64() {
assert_eq_size!(PointId, u64);
}
}
#[cfg(all(test, feature = "mpi-support"))]
mod mpi_tests {
use super::*;
use mpi::datatype::Equivalence;
#[test]
fn pointid_mpi_equivalence_type_is_u64() {
fn _assert_equiv<T: Equivalence>() {}
_assert_equiv::<PointId>();
}
}
#[cfg(test)]
mod copy_clone_eq_tests {
use super::*;
#[test]
fn copy_and_clone() {
let p = PointId::new(5).unwrap();
let q = p;
let r = p.clone();
assert_eq!(p, q);
assert_eq!(p, r);
}
#[test]
fn eq_and_neq() {
let p = PointId::new(8).unwrap();
let q = PointId::new(8).unwrap();
let r = PointId::new(9).unwrap();
assert_eq!(p, p);
assert_eq!(p, q);
assert_ne!(p, r);
}
}
#[cfg(test)]
mod edge_case_tests {
use super::*;
#[test]
fn max_value() {
let p = PointId::new(u64::MAX).unwrap();
assert_eq!(p.get(), u64::MAX);
}
}