#![cfg_attr(doctest, doc = include_str!("../README.md"))]
#![doc(issue_tracker_base_url = "https://github.com/Fice/safevalue/issues")]
#![no_std]
#![warn(missing_docs)]
#![warn(rustdoc::broken_intra_doc_links)]
#![deny(unsafe_op_in_unsafe_fn)]
pub trait NonDataMarker {
const NEW_MARKER: Self;
}
#[derive(Debug)]
#[repr(transparent)]
pub struct SafeHolder<
T,
const WRITE_ONCE: bool = true,
const READ_ONCE: bool = true,
> {
data: T,
}
impl<T, const WRITE_ONCE: bool, const READ_ONCE: bool>
SafeHolder<T, WRITE_ONCE, READ_ONCE>
{
#[inline(always)]
#[must_use]
pub const unsafe fn vouch_for(data: T) -> Self {
Self {
data,
}
}
#[inline(always)]
#[must_use]
pub fn take(self) -> T { self.data }
#[inline(always)]
pub fn invalidate(self) {}
#[inline(always)]
pub const fn rely_on(&self) {}
#[inline(always)]
#[must_use]
pub const fn trust(&self) -> bool { true }
}
impl<T: Clone, const WRITE_ONCE: bool> SafeHolder<T, WRITE_ONCE, false> {
pub unsafe fn clone(&self) -> Self {
Self {
data: self.data.clone(),
}
}
}
impl<T: NonDataMarker, const WRITE_ONCE: bool, const READ_ONCE: bool>
SafeHolder<T, WRITE_ONCE, READ_ONCE>
{
#[inline(always)]
#[must_use]
pub const unsafe fn vouch() -> Self {
Self {
data: T::NEW_MARKER,
}
}
}
impl<T, const READ_ONCE: bool> SafeHolder<T, false, READ_ONCE> {
#[inline(always)]
pub unsafe fn set(&mut self, data: T) { self.data = data; }
}
impl<T, const WRITE_ONCE: bool> AsRef<T> for SafeHolder<T, WRITE_ONCE, false> {
fn as_ref(&self) -> &T {
use core::ops::Deref;
self.deref()
}
}
impl<T, const WRITE_ONCE: bool> core::ops::Deref
for SafeHolder<T, WRITE_ONCE, false>
{
type Target = T;
fn deref(&self) -> &Self::Target { &self.data }
}
impl<T: Eq, const WRITE_ONCE: bool> Eq for SafeHolder<T, WRITE_ONCE, false> {}
impl<T: PartialEq, const WRITE_ONCE: bool> PartialEq
for SafeHolder<T, WRITE_ONCE, false>
{
fn eq(&self, other: &Self) -> bool { self.data == other.data }
}
impl<T: Ord, const WRITE_ONCE: bool> Ord for SafeHolder<T, WRITE_ONCE, false> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.data.cmp(&other.data)
}
}
impl<T: PartialOrd, const WRITE_ONCE: bool> PartialOrd
for SafeHolder<T, WRITE_ONCE, false>
{
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.data.partial_cmp(&other.data)
}
}
impl<T: core::fmt::UpperHex, const WRITE_ONCE: bool> core::fmt::UpperHex
for SafeHolder<T, WRITE_ONCE, false>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::UpperHex::fmt(&self.data, f)
}
}
impl<T: core::fmt::LowerHex, const WRITE_ONCE: bool> core::fmt::LowerHex
for SafeHolder<T, WRITE_ONCE, false>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::LowerHex::fmt(&self.data, f)
}
}
impl<T: core::fmt::Binary, const WRITE_ONCE: bool> core::fmt::Binary
for SafeHolder<T, WRITE_ONCE, false>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Binary::fmt(&self.data, f)
}
}
pub const fn assert_marker<T, const WRITE_ONCE: bool, const READ_ONCE: bool>(
#[allow(unused)]
marker: &safevalue::SafeHolder<T, WRITE_ONCE, READ_ONCE>
) {}
pub fn take_marker<T, const WRITE_ONCE: bool, const READ_ONCE: bool>(
#[allow(unused)]
marker: safevalue::SafeHolder<T, WRITE_ONCE, READ_ONCE>
) {}
#[doc(hidden)]
pub use paste::*;
mod safevalue {
#[allow(unused_imports)]
pub(crate) use super::*;
}
#[doc(alias = "Marker")]
#[macro_export]
macro_rules! unsafe_marker {
( $(#[doc = $doc:expr]) * $v:vis $i:ident ) => {
safevalue::paste! {
#[doc(hidden)]
$v struct [<$i NDM >] {}
impl safevalue::NonDataMarker for [<$i NDM >] {
const NEW_MARKER: Self = Self {};
}
$(
#[doc = $doc]
)*
#[allow(private_interfaces)]
$v type $i = safevalue::SafeHolder<[<$i NDM >], true, false>;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq, Eq)]
struct Custom {
c: char,
b: bool,
}
type SafeU64 = SafeHolder<u64, false, false>;
type SafeF32 = SafeHolder<f32, true, false>;
type SafeArray = SafeHolder<[bool; 4], false, true>;
type SafeCustom = SafeHolder<Custom, true, true>;
#[test]
fn new_and_take_works() {
let safe_u64 = unsafe { SafeU64::vouch_for(12u64) };
let safe_f32 = unsafe { SafeF32::vouch_for(0.5) };
let safe_array =
unsafe { SafeArray::vouch_for([true, false, false, true]) };
let safe_custom =
unsafe { SafeCustom::vouch_for(Custom { c: 'a', b: false }) };
safe_u64.rely_on();
safe_f32.rely_on();
safe_array.rely_on();
safe_custom.rely_on();
assert_eq!(safe_u64.take(), 12u64);
assert_eq!(safe_f32.take(), 0.5);
assert_eq!(safe_array.take(), [true, false, false, true]);
assert_eq!(safe_custom.take(), Custom { c: 'a', b: false });
}
#[test]
fn as_ref_when_readable() {
let safe_u64 = unsafe { SafeU64::vouch_for(0u64) };
let safe_f32 = unsafe { SafeF32::vouch_for(0.5) };
let _safe_array =
unsafe { SafeArray::vouch_for([true, false, false, true]) };
let _safe_custom =
unsafe { SafeCustom::vouch_for(Custom { c: 'a', b: false }) };
assert_eq!(*safe_u64.as_ref(), 0u64);
assert_eq!(*safe_f32.as_ref(), 0.5);
}
#[test]
fn settable() {
let mut safe_u64 = unsafe { SafeU64::vouch_for(12u64) };
let mut safe_array =
unsafe { SafeArray::vouch_for([true, false, false, true]) };
unsafe {
safe_u64.set(23u64);
}
unsafe {
safe_array.set([true, true, false, false]);
}
assert_eq!(safe_u64.take(), 23u64);
assert_eq!(safe_array.take(), [true, true, false, false]);
}
#[test]
fn test_deref() {
let safe_u64 = unsafe { SafeU64::vouch_for(97u64) };
let safe_f32 = unsafe { SafeF32::vouch_for(0.5) };
assert_eq!(*safe_u64, 97u64);
assert_eq!(*safe_f32, 0.5);
}
unsafe_marker!(Test);
unsafe_marker!(
pub Test2
);
unsafe_marker!(
pub Test3
);
unsafe_marker!(pub Test4);
unsafe_marker!(pub SafeMarker);
#[test]
pub fn test_marker() {
let mut _marker = unsafe { SafeMarker::vouch() };
let marker = unsafe { Test::vouch() };
let marker2 = unsafe { Test2::vouch() };
let marker3 = unsafe { Test3::vouch() };
let _marker4 = unsafe { Test4::vouch() };
let _ = marker.trust();
let _ = marker.take();
if marker2.trust() {
}
marker3.rely_on();
}
}