repr_rs/
repr.rs

1use std::cell::UnsafeCell;
2use std::fmt::{Debug, Display};
3use std::hash::{Hash, Hasher};
4use std::ops::{Deref, DerefMut};
5
6/// Wraps a value and ensures that an invariant is maintained while allowing that value to be
7/// mutated. The invariant is checked after every mutation.
8/// See [`crate::CacheableRepr`] for a version of this struct that supports caching.
9pub struct Repr<T: Debug, I: Fn(&T) -> bool> {
10	pub(crate) inner: UnsafeCell<T>,
11	invariant: I,
12	violation_message: &'static str,
13}
14impl<T: Debug, I: Fn(&T) -> bool> Repr<T, I> {
15	/// Creates a new representation invariant with the given value and invariant function.
16	/// ```rust
17	/// use repr_rs::Repr;
18	/// #[derive(Debug)]
19	/// struct MinMax { min: i32, max: i32 }
20	/// Repr::new(
21	///   MinMax { min: 1, max: 5 },
22	///   |mm| mm.min < mm.max,
23	/// );
24	/// ```
25	pub const fn new(inner: T, invariant: I) -> Self {
26		Self {
27			inner: UnsafeCell::new(inner),
28			invariant,
29			violation_message: "Invariant violated",
30		}
31	}
32	/// Creates a new representation invariant with the given value, invariant function, and violation message.
33	/// ```rust
34	/// use repr_rs::Repr;
35	/// #[derive(Debug)]
36	/// struct MinMax { min: i32, max: i32 }
37	/// Repr::with_msg(
38	///   MinMax { min: 1, max: 5 },
39	///   |mm| mm.min < mm.max,
40	///   "min must be less than max",
41	/// );
42	/// ```
43	pub const fn with_msg(inner: T, invariant: I, violation_message: &'static str) -> Self {
44		Self {
45			inner: UnsafeCell::new(inner),
46			invariant,
47			violation_message,
48		}
49	}
50	/// Borrows a read-only view of the value in the representation invariant.
51	/// ```rust
52	/// use repr_rs::Repr;
53	/// #[derive(Debug)]
54	/// struct MinMax { min: i32, max: i32 }
55	/// let repr = Repr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
56	/// let view = repr.read();
57	/// assert_eq!(1, view.min);
58	/// assert_eq!(5, view.max);
59	/// ```
60	#[inline]
61	pub fn read(&self) -> &T {
62		// Safety: borrowing rules ensure that T is valid, and because this is an immutable borrow
63		// of the Repr, no mutable borrows can take place.
64		unsafe { &*self.inner.get() }
65	}
66	/// Borrows a mutable view of the value in the representation invariant.
67	/// ```rust
68	/// use repr_rs::Repr;
69	/// #[derive(Debug)]
70	/// struct MinMax { min: i32, max: i32 }
71	/// let mut repr = Repr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
72	/// {
73	///   let view = repr.read();
74	///   assert_eq!(1, view.min);
75	///   assert_eq!(5, view.max);
76	/// }
77	/// repr.write().min = 4;
78	/// let view = repr.read();
79	/// assert_eq!(4, view.min);
80	/// assert_eq!(5, view.max);
81	/// ```
82	///
83	/// Rust's borrowing rules prevent the read-only view being held while a mutation occurs. For
84	/// example, this won't compile:
85	/// ```compile_fail
86	/// use repr_rs::Repr;
87	/// #[derive(Debug)]
88	/// struct MinMax { min: i32, max: i32 }
89	/// let mut repr = Repr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
90	/// let view = repr.read();
91	/// assert_eq!(1, view.min);
92	/// assert_eq!(5, view.max);
93	/// // error[E0502]: cannot borrow `repr` as mutable because it is also borrowed as immutable
94	/// repr.write().min = 4;
95	/// assert_eq!(4, view.min);
96	/// assert_eq!(5, view.max);
97	/// ```
98	#[inline]
99	pub fn write(&mut self) -> ReprMutator<T, I> {
100		// Can be `const` when `const_mut_refs` is stabilised.
101		ReprMutator {
102			repr: self,
103		}
104	}
105	/// Consumes the representation invariant and returns the inner value.
106	/// ```rust
107	/// use repr_rs::Repr;
108	/// #[derive(Debug)]
109	/// struct MinMax { min: i32, max: i32 }
110	/// let repr = Repr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
111	/// let inner = repr.into_inner();
112	/// assert_eq!(1, inner.min);
113	/// ```
114	#[inline]
115	pub fn into_inner(self) -> T {
116		self.inner.into_inner()
117	}
118	pub(crate) fn check(&mut self) {
119		let data = self.inner.get_mut();
120		assert!((self.invariant)(data), "{}\nState was: {:?}", self.violation_message, data);
121		// In debug mode
122		for _ in 0..10 {
123			debug_assert!((self.invariant)(data), "Invariants should be deterministic! The invariant function for this Repr is not deterministic.");
124		}
125	}
126}
127
128/// # Safety
129/// This is safe because we can only mutate the inner value through the ReprMutator, which can only
130/// be created by borrowing the Repr mutably. The only other potential issue could be if the
131/// invariant function was not thread safe, which is why we require it to be [Sync].
132unsafe impl<T: Debug + Sync, I: Fn(&T) -> bool + Sync> Sync for Repr<T, I> {}
133/// # Safety
134/// We exclusively own the repr here, so we can safely  implement Send for this type.
135unsafe impl<T: Debug + Send, I: Fn(&T) -> bool + Send> Send for Repr<T, I> {}
136
137impl<T: Debug, I: Fn(&T) -> bool> AsRef<T> for Repr<T, I> {
138	#[inline]
139	fn as_ref(&self) -> &T {
140		self.read()
141	}
142}
143
144impl<T: Debug + Clone, I: Fn(&T) -> bool + Clone> Clone for Repr<T, I> {
145	fn clone(&self) -> Self {
146		let inner = self.read().clone();
147		Self::with_msg(inner, self.invariant.clone(), self.violation_message)
148	}
149}
150impl<T: Debug + Hash, I: Fn(&T) -> bool> Hash for Repr<T, I> {
151	fn hash<H: Hasher>(&self, state: &mut H) {
152		self.read().hash(state);
153	}
154}
155impl<T: Debug + PartialEq, I: Fn(&T) -> bool> PartialEq for Repr<T, I> {
156	fn eq(&self, other: &Self) -> bool {
157		self.read() == other.read()
158	}
159}
160impl<T: Debug + Eq, I: Fn(&T) -> bool> Eq for Repr<T, I> {}
161
162impl<T: Debug, I: Fn(&T) -> bool> Debug for Repr<T, I> {
163	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164		write!(f, "Repr({:?})", self.read())
165	}
166}
167impl <T: Debug + Display, I: Fn(&T) -> bool> Display for Repr<T, I> {
168	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169		write!(f, "{}", self.read())
170	}
171}
172
173#[repr(transparent)]
174pub struct ReprMutator<'a, T: Debug, I: Fn(&T) -> bool> {
175	// inner: &'a mut T,
176	repr: &'a mut Repr<T, I>,
177}
178impl<'a, T: Debug, I: Fn(&T) -> bool> Deref for ReprMutator<'a, T, I> {
179	type Target = T;
180	fn deref(&self) -> &Self::Target {
181		// Safety: borrowing rules ensure that T is valid, and because ReprMutate mutably borrows
182		// the Repr, no mutable borrows of the inner can take place if we borrow it as imm here.
183		unsafe { &*self.repr.inner.get() }
184	}
185}
186impl<'a, T: Debug, I: Fn(&T) -> bool> DerefMut for ReprMutator<'a, T, I> {
187	fn deref_mut(&mut self) -> &mut Self::Target {
188		self.repr.inner.get_mut()
189	}
190}
191impl<T: Debug, I: Fn(&T) -> bool> Drop for ReprMutator<'_, T, I> {
192	fn drop(&mut self) {
193		self.repr.check();
194	}
195}
196
197// For Deref/DerefMut we need to make sure that it hashes, orders, and has equality with the
198// same semantics as the reference we give
199impl<'a, T: Debug + Hash> Hash for ReprMutator<'a, T, fn(&T) -> bool> {
200	fn hash<H: Hasher>(&self, state: &mut H) {
201		self.deref().hash(state);
202	}
203}
204impl<'a, T: Debug + PartialEq> PartialEq for ReprMutator<'a, T, fn(&T) -> bool> {
205	fn eq(&self, other: &Self) -> bool {
206		self.deref() == other.deref()
207	}
208}
209impl<'a, T: Debug + Eq> Eq for ReprMutator<'a, T, fn(&T) -> bool> {}
210impl<'a, T: Debug + PartialOrd> PartialOrd for ReprMutator<'a, T, fn(&T) -> bool> {
211	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
212		self.deref().partial_cmp(other.deref())
213	}
214}
215impl<'a, T: Debug + Ord> Ord for ReprMutator<'a, T, fn(&T) -> bool> {
216	fn cmp(&self, other: &Self) -> std::cmp::Ordering {
217		self.deref().cmp(other.deref())
218	}
219}