#![doc = include_str!("../README.md")]
use std::any::{Any, TypeId};
use std::fmt::{self, Debug, Formatter};
use std::mem;
pub use into_index::{At, AtMut, IntoIndex};
#[derive(Default)]
pub struct UniVec(Option<Box<dyn UniVecOps>>);
#[macro_export]
macro_rules! univec {
() => ( $crate::UniVec::new() );
($elem:expr; $n:expr) => ( $crate::UniVec::from(vec![$elem; $n]) );
($($x:expr),+ $(,)?) => { $crate::UniVec::from(vec![$($x),+]) };
}
impl UniVec {
#[must_use]
pub const fn new() -> Self {
Self(None)
}
#[must_use]
pub fn with_capacity<T: 'static>(capacity: usize) -> Self {
Self(Some(Box::new(Vec::<T>::with_capacity(capacity))))
}
pub fn try_get<T: 'static>(&self, index: impl IntoIndex) -> Result<Option<&T>, TypeError> {
if let Ok(index) = index.try_into_index() {
Ok(self.try_as_vec::<T>()?.and_then(|s| s.get(index)))
} else {
Ok(None)
}
}
#[must_use]
pub fn get<T: 'static>(&self, index: impl IntoIndex) -> Option<&T> {
self.as_vec::<T>().get(index.try_into_index().ok()?)
}
pub fn try_get_mut<T: 'static>(
&mut self,
index: impl IntoIndex,
) -> Result<Option<&mut T>, TypeError> {
if let Ok(index) = index.try_into_index() {
Ok(self.try_as_vec_mut::<T>()?.and_then(|s| s.get_mut(index)))
} else {
Ok(None)
}
}
#[must_use]
pub fn get_mut<T: 'static>(&mut self, index: impl IntoIndex) -> Option<&mut T> {
self.as_vec_mut::<T>().get_mut(index.try_into_index().ok()?)
}
pub fn ensure<T: 'static>(&mut self, capacity: usize) -> Result<(), TypeError> {
if self.0.is_none() {
self.0 = Some(Box::new(Vec::<T>::with_capacity(capacity)));
} else if !self.type_check::<T>() {
return Err(TypeError);
}
self.reserve(capacity);
Ok(())
}
pub fn push<T: 'static>(&mut self, value: T) -> Result<(), TypeErrorValue<T>> {
if self.ensure::<T>(1).is_ok() {
self.as_vec_mut::<T>().push(value);
Ok(())
} else {
Err(TypeErrorValue(value))
}
}
#[must_use]
pub fn pop_any(&mut self) -> Option<Box<dyn Any>> {
self.0.as_mut().and_then(|vec| vec.pop_box())
}
#[allow(clippy::missing_panics_doc)]
pub fn pop<T: 'static>(&mut self) -> Result<Option<T>, TypeError> {
Ok(self.try_as_vec_mut::<T>()?.and_then(Vec::pop))
}
pub fn drop_last(&mut self) -> bool {
self.0.as_mut().map_or(false, |vec| vec.drop_last())
}
pub fn insert<T: 'static>(
&mut self,
index: impl IntoIndex,
value: T,
) -> Result<(), TypeErrorValue<T>> {
if self.ensure::<T>(1).is_ok() {
self.as_vec_mut::<T>().insert(index.into_index(), value);
Ok(())
} else {
Err(TypeErrorValue(value))
}
}
pub fn remove<T: 'static>(&mut self, index: impl IntoIndex) -> Result<T, TypeError> {
Ok(self
.try_as_vec_mut::<T>()?
.map(|v| v.remove(index.into_index()))
.expect("index out of bounds"))
}
#[must_use]
pub fn len(&self) -> usize {
self.0.as_ref().map_or(0, |vec| vec.len())
}
#[must_use]
#[inline]
pub fn is_empty(&self) -> bool {
self.0.as_ref().map_or(true, |vec| vec.len() == 0)
}
#[must_use]
pub fn capacity(&self) -> usize {
self.0.as_ref().map_or(0, |vec| vec.capacity())
}
pub fn reserve(&mut self, additional: usize) {
if let Some(v) = self.0.as_mut() {
v.reserve(additional);
};
}
pub fn replace<T: 'static>(
&mut self,
index: impl IntoIndex,
value: T,
) -> Result<T, TypeErrorValue<T>> {
if self.type_check::<T>() {
Ok(mem::replace(
self.get_mut::<T>(index.into_index())
.expect("index out of bounds"),
value,
))
} else {
Err(TypeErrorValue(value))
}
}
pub fn try_set_last<T: 'static>(&mut self, value: T) -> Result<Option<T>, TypeErrorValue<T>> {
Ok(if self.is_empty() {
self.push(value)?;
None
} else {
Some(self.replace(self.len() - 1, value)?)
})
}
pub fn set_last<T: 'static>(&mut self, value: T) -> Option<T> {
self.try_set_last(value)
.unwrap_or_else(|_| panic!("This UniVec does not store the requested type"))
}
#[inline]
fn type_check<T: 'static>(&self) -> bool {
self.0
.as_ref()
.map_or(true, |vec| vec.inner_type_id() == TypeId::of::<T>())
}
#[must_use]
pub fn associated_type(&self) -> Option<TypeId> {
self.0.as_ref().map(|vec| vec.inner_type_id())
}
#[must_use]
pub fn associated_type_name(&self) -> Option<&'static str> {
self.0.as_ref().map(|vec| vec.inner_type_name())
}
pub fn take<T: 'static>(&mut self) -> Result<Option<Vec<T>>, TypeError> {
if self.is_empty() {
Ok(None)
} else if self.type_check::<T>() {
unsafe {
let vec = self.0.take().unwrap_unchecked();
#[allow(clippy::cast_ptr_alignment)]
Ok(Some(*Box::from_raw(Box::into_raw(vec).cast::<Vec<T>>())))
}
} else {
Err(TypeError)
}
}
pub fn into_inner<T: 'static>(mut self) -> Result<Option<Vec<T>>, TypeErrorIntoInner> {
if self.is_empty() {
Ok(None)
} else if self.type_check::<T>() {
unsafe {
let vec = self.0.take().unwrap_unchecked();
#[allow(clippy::cast_ptr_alignment)]
Ok(Some(*Box::from_raw(Box::into_raw(vec).cast::<Vec<T>>())))
}
} else {
Err(TypeErrorIntoInner(self))
}
}
pub fn try_as_vec<T: 'static>(&self) -> Result<Option<&Vec<T>>, TypeError> {
self.0
.as_ref()
.map(|b| b.as_any().downcast_ref::<Vec<T>>().ok_or(TypeError))
.transpose()
}
#[must_use]
pub fn as_vec<T: 'static>(&self) -> &Vec<T> {
self.0
.as_ref()
.expect("This Univec has no type associated")
.as_any()
.downcast_ref::<Vec<T>>()
.expect("This UniVec does not store the requested type")
}
pub fn try_as_vec_mut<T: 'static>(&mut self) -> Result<Option<&mut Vec<T>>, TypeError> {
self.0
.as_mut()
.map(|b| b.as_any_mut().downcast_mut::<Vec<T>>().ok_or(TypeError))
.transpose()
}
#[must_use]
pub fn as_vec_mut<T: 'static>(&mut self) -> &mut Vec<T> {
let _ = self.ensure::<T>(0);
self.0
.as_mut()
.expect("This Univec has no type associated")
.as_any_mut()
.downcast_mut::<Vec<T>>()
.expect("This UniVec does not store the requested type")
}
#[inline]
pub fn try_as_slice<T: 'static>(&self) -> Result<Option<&[T]>, TypeError> {
Ok(self.try_as_vec::<T>()?.map(Vec::as_slice))
}
#[inline]
#[must_use]
pub fn as_slice<T: 'static>(&self) -> &[T] {
self.as_vec::<T>().as_slice()
}
#[inline]
pub fn try_as_mut_slice<T: 'static>(&mut self) -> Result<Option<&mut [T]>, TypeError> {
Ok(self.try_as_vec_mut::<T>()?.map(Vec::as_mut_slice))
}
#[inline]
#[must_use]
pub fn as_mut_slice<T: 'static>(&mut self) -> &mut [T] {
self.as_vec_mut::<T>().as_mut_slice()
}
}
impl Debug for UniVec {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.0 {
Some(ref vec) => write!(f, "UniVec(Some(Vec<{}>))", vec.inner_type_name()),
None => write!(f, "UniVec(None)"),
}
}
}
trait UniVecOps: Any {
fn pop_box(&mut self) -> Option<Box<dyn Any>>;
fn drop_last(&mut self) -> bool;
fn len(&self) -> usize;
fn capacity(&self) -> usize;
fn reserve(&mut self, additional: usize);
fn inner_type_id(&self) -> TypeId;
fn inner_type_name(&self) -> &'static str;
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
impl<T> UniVecOps for Vec<T>
where
T: 'static,
{
fn pop_box(&mut self) -> Option<Box<dyn Any>> {
let value = self.pop()?;
Some(Box::new(value))
}
#[inline]
fn drop_last(&mut self) -> bool {
self.pop().is_some()
}
#[inline]
fn len(&self) -> usize {
self.len()
}
#[inline]
fn capacity(&self) -> usize {
self.capacity()
}
#[inline]
fn reserve(&mut self, additional: usize) {
self.reserve(additional);
}
#[inline]
fn inner_type_id(&self) -> TypeId {
TypeId::of::<T>()
}
fn inner_type_name(&self) -> &'static str {
std::any::type_name::<T>()
}
#[inline]
fn as_any(&self) -> &dyn Any {
self
}
#[inline]
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl<T> From<Vec<T>> for UniVec
where
T: 'static,
{
#[inline]
fn from(vec: Vec<T>) -> Self {
Self(Some(Box::new(vec)))
}
}
impl<T> AsRef<[T]> for UniVec
where
T: 'static,
{
#[inline]
fn as_ref(&self) -> &[T] {
self.as_slice()
}
}
impl<T> AsMut<[T]> for UniVec
where
T: 'static,
{
#[inline]
fn as_mut(&mut self) -> &mut [T] {
self.as_mut_slice()
}
}
#[derive(thiserror::Error, Debug, PartialEq)]
#[error("This UniVec does not store the requested type")]
pub struct TypeError;
#[derive(thiserror::Error, PartialEq)]
#[error("This UniVec does not store the requested type")]
pub struct TypeErrorValue<T>(
pub T,
);
impl<T> Debug for TypeErrorValue<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "TypeErrorValue<{}>", std::any::type_name::<T>())
}
}
#[derive(thiserror::Error, Debug)]
#[error("This UniVec does not store the requested type")]
pub struct TypeErrorIntoInner(
pub UniVec,
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_type_check() {
let mut univec = UniVec::new();
assert!(univec.type_check::<i64>());
univec.push(42_i32).unwrap();
assert!(univec.type_check::<i32>());
assert!(!univec.type_check::<i64>());
}
}