use serde::{Deserialize, Serialize};
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};

static UNIQUE_ID: AtomicUsize = AtomicUsize::new(0);

/// A id unique within a process or within a single file.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(into = "()")]
#[serde(from = "()")]
pub struct UniqueId(usize);

impl UniqueId {
    pub fn new() -> UniqueId {
        let unique_id = UNIQUE_ID.fetch_add(1, Relaxed);
        UniqueId(unique_id)
    }
}

impl Default for UniqueId {
    fn default() -> Self {
        Self::new()
    }
}

impl From<()> for UniqueId {
    fn from(_: ()) -> Self {
        UniqueId::new()
    }
}

impl From<UniqueId> for () {
    fn from(_: UniqueId) -> Self {}
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn cloning() {
        let unique_id = UniqueId::new();
        #[allow(clippy::clone_on_copy)]
        let cloned = unique_id.clone();
        // Clones (and copies) are indeed equal to themselves.
        assert_eq!(unique_id, cloned);
    }

    #[test]
    fn serialization() {
        let unique_id = UniqueId::new();
        let serialized = bincode::serialize(&unique_id).unwrap();
        let deserialized: UniqueId = bincode::deserialize(&serialized).unwrap();

        // They should NOT be equal.
        // Serialized unique ids are different from the ones that we directly created.
        assert_ne!(unique_id, deserialized);
    }
}
