use std::fmt;
use std::hash::Hash;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct FileId(u32);
impl FileId {
pub const INVALID: FileId = FileId(u32::MAX);
#[inline]
#[must_use]
pub const fn new(index: u32) -> Self {
Self(index)
}
#[inline]
#[must_use]
pub const fn index(self) -> u32 {
self.0
}
#[inline]
#[must_use]
pub const fn as_usize(self) -> usize {
self.0 as usize
}
#[inline]
#[must_use]
pub const fn is_invalid(self) -> bool {
self.0 == u32::MAX
}
#[inline]
#[must_use]
pub const fn is_valid(self) -> bool {
self.0 != u32::MAX
}
}
impl fmt::Debug for FileId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_invalid() {
write!(f, "FileId(INVALID)")
} else {
write!(f, "FileId({})", self.0)
}
}
}
impl fmt::Display for FileId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_invalid() {
write!(f, "INVALID")
} else {
write!(f, "file:{}", self.0)
}
}
}
impl Default for FileId {
#[inline]
fn default() -> Self {
Self::INVALID
}
}
impl From<u32> for FileId {
#[inline]
fn from(index: u32) -> Self {
Self(index)
}
}
impl From<usize> for FileId {
#[inline]
fn from(index: usize) -> Self {
Self(u32::try_from(index).unwrap_or(u32::MAX))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_file_id_creation() {
let id = FileId::new(42);
assert_eq!(id.index(), 42);
assert_eq!(id.as_usize(), 42);
assert!(!id.is_invalid());
assert!(id.is_valid());
}
#[test]
fn test_file_id_invalid_sentinel() {
assert!(FileId::INVALID.is_invalid());
assert!(!FileId::INVALID.is_valid());
assert_eq!(FileId::INVALID.index(), u32::MAX);
}
#[test]
fn test_file_id_default() {
let default_id: FileId = FileId::default();
assert_eq!(default_id, FileId::INVALID);
}
#[test]
fn test_file_id_equality() {
let id1 = FileId::new(5);
let id2 = FileId::new(5);
let id3 = FileId::new(6);
assert_eq!(id1, id2);
assert_ne!(id1, id3);
}
#[test]
fn test_file_id_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(FileId::new(1));
set.insert(FileId::new(2));
set.insert(FileId::new(3));
assert!(set.contains(&FileId::new(1)));
assert!(!set.contains(&FileId::new(4)));
assert_eq!(set.len(), 3);
}
#[test]
fn test_file_id_from() {
let from_u32: FileId = 42u32.into();
assert_eq!(from_u32.index(), 42);
let from_usize: FileId = 42usize.into();
assert_eq!(from_usize.index(), 42);
}
#[test]
fn test_debug_display_format() {
let id = FileId::new(42);
assert_eq!(format!("{id:?}"), "FileId(42)");
assert_eq!(format!("{id}"), "file:42");
assert_eq!(format!("{:?}", FileId::INVALID), "FileId(INVALID)");
assert_eq!(format!("{}", FileId::INVALID), "INVALID");
}
#[test]
fn test_serde_roundtrip() {
let original = FileId::new(123);
let json = serde_json::to_string(&original).unwrap();
let deserialized: FileId = serde_json::from_str(&json).unwrap();
assert_eq!(original, deserialized);
let bytes = postcard::to_allocvec(&original).unwrap();
let deserialized: FileId = postcard::from_bytes(&bytes).unwrap();
assert_eq!(original, deserialized);
}
#[test]
fn test_size_of_file_id() {
assert_eq!(std::mem::size_of::<FileId>(), 4);
}
#[test]
#[allow(clippy::clone_on_copy)] fn test_copy_clone() {
let id = FileId::new(10);
let copied = id;
let cloned = id.clone();
assert_eq!(id, copied);
assert_eq!(id, cloned);
}
}