use std::cmp::{Ordering, PartialEq, PartialOrd};
use std::collections::HashMap;
use std::convert::TryInto;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::num::NonZeroU32;
use std::sync::Mutex;
pub use urid_derive::*;
pub type Uri = ::std::ffi::CStr;
pub type UriBuf = ::std::ffi::CString;
pub unsafe trait UriBound {
const URI: &'static [u8];
fn uri() -> &'static Uri {
unsafe { Uri::from_bytes_with_nul_unchecked(Self::URI) }
}
}
#[repr(transparent)]
pub struct URID<T = ()>(NonZeroU32, PhantomData<T>)
where
T: ?Sized;
pub trait URIDCollection: Sized {
fn from_map<M: Map + ?Sized>(map: &M) -> Option<Self>;
}
impl URID<()> {
#[inline]
pub fn new(raw_urid: u32) -> Option<Self> {
NonZeroU32::new(raw_urid).map(|inner| Self(inner, PhantomData))
}
}
impl<T: ?Sized> URID<T> {
pub unsafe fn new_unchecked(raw_urid: u32) -> Self {
Self(NonZeroU32::new_unchecked(raw_urid), PhantomData)
}
pub fn get(self) -> u32 {
self.0.get()
}
pub fn into_general(self) -> URID<()> {
unsafe { URID::new_unchecked(self.get()) }
}
}
impl<T: UriBound + ?Sized> URIDCollection for URID<T> {
fn from_map<M: Map + ?Sized>(map: &M) -> Option<Self> {
map.map_type()
}
}
impl<T: ?Sized> fmt::Debug for URID<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T: ?Sized> Clone for URID<T> {
fn clone(&self) -> Self {
Self(self.0, PhantomData)
}
}
impl<T: ?Sized> Copy for URID<T> {}
impl<T1: ?Sized, T2: ?Sized> PartialEq<URID<T1>> for URID<T2> {
fn eq(&self, other: &URID<T1>) -> bool {
self.0 == other.0
}
}
impl<T: ?Sized> PartialEq<u32> for URID<T> {
fn eq(&self, other: &u32) -> bool {
self.get() == *other
}
}
impl<T: ?Sized> PartialEq<URID<T>> for u32 {
fn eq(&self, other: &URID<T>) -> bool {
*self == other.get()
}
}
impl<T: ?Sized> Eq for URID<T> {}
impl<T1: ?Sized, T2: ?Sized> PartialOrd<URID<T1>> for URID<T2> {
fn partial_cmp(&self, other: &URID<T1>) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<T: ?Sized> PartialOrd<u32> for URID<T> {
fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
self.get().partial_cmp(other)
}
}
impl<T: ?Sized> PartialOrd<URID<T>> for u32 {
fn partial_cmp(&self, other: &URID<T>) -> Option<Ordering> {
self.partial_cmp(&other.get())
}
}
impl<T: ?Sized> Ord for URID<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl<T: ?Sized> Hash for URID<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
impl std::convert::TryFrom<u32> for URID {
type Error = ();
fn try_from(value: u32) -> Result<URID, ()> {
if value == 0 {
Err(())
} else {
Ok(unsafe { URID::new_unchecked(value) })
}
}
}
#[cfg(test)]
mod tests {
use crate::URID;
#[test]
fn test_urid_size() {
use std::mem::size_of;
let size = size_of::<u32>();
assert_eq!(size, size_of::<URID>());
assert_eq!(size, size_of::<Option<URID>>());
}
}
pub trait Map {
fn map_uri(&self, uri: &Uri) -> Option<URID>;
fn map_str(&self, uri: &str) -> Option<URID> {
if !uri.is_ascii() {
return None;
}
let mut bytes: Vec<u8> = uri.as_bytes().to_owned();
bytes.push(0);
self.map_uri(Uri::from_bytes_with_nul(bytes.as_ref()).ok()?)
}
fn map_type<T: UriBound + ?Sized>(&self) -> Option<URID<T>> {
self.map_uri(T::uri())
.map(|urid| unsafe { URID::new_unchecked(urid.get()) })
}
fn populate_collection<T: URIDCollection>(&self) -> Option<T> {
T::from_map(self)
}
}
pub trait Unmap {
fn unmap<T: ?Sized>(&self, urid: URID<T>) -> Option<&Uri>;
}
#[derive(Default)]
pub struct HashURIDMapper(Mutex<HashMap<UriBuf, URID>>);
impl Map for HashURIDMapper {
fn map_uri(&self, uri: &Uri) -> Option<URID<()>> {
let mut map = self.0.lock().ok()?; match map.get(uri) {
Some(urid) => Some(*urid),
None => {
let map_length: u32 = map.len().try_into().ok()?; let next_urid = map_length.checked_add(1)?;
let next_urid = unsafe { URID::new_unchecked(next_urid) };
map.insert(uri.into(), next_urid);
Some(next_urid)
}
}
}
}
impl Unmap for HashURIDMapper {
fn unmap<T: ?Sized>(&self, urid: URID<T>) -> Option<&Uri> {
let map = self.0.lock().ok()?;
for (uri, contained_urid) in map.iter() {
if *contained_urid == urid {
return Some(unsafe {
let bytes = uri.as_bytes_with_nul();
Uri::from_bytes_with_nul_unchecked(std::slice::from_raw_parts(
bytes.as_ptr(),
bytes.len(),
))
});
}
}
None
}
}
impl HashURIDMapper {
pub fn new() -> Self {
Default::default()
}
}