Skip to main content

wiwi_chain/
chain.rs

1#[must_use = "a chain takes ownership of itself, performs the action, then returns itself again"]
2pub struct Chain<T> {
3	inner: T
4}
5
6impl<T> Chain<T> {
7	#[inline]
8	pub const fn from_inner(inner: T) -> Self {
9		Self { inner }
10	}
11
12	#[inline]
13	pub fn into_inner(self) -> T {
14		self.inner
15	}
16
17	#[inline]
18	pub const fn as_inner(&self) -> &T {
19		&self.inner
20	}
21
22	#[inline]
23	pub const fn as_inner_mut(&mut self) -> &mut T {
24		&mut self.inner
25	}
26
27	#[inline]
28	pub const fn as_inner_chain(&self) -> Chain<&T> {
29		Chain::from_inner(self.as_inner())
30	}
31
32	#[inline]
33	pub const fn as_inner_mut_chain(&mut self) -> Chain<&mut T> {
34		Chain::from_inner(self.as_inner_mut())
35	}
36
37	#[inline]
38	pub fn with_inner<F>(self, f: F) -> Self
39	where
40		F: FnOnce(&T)
41	{
42		f(self.as_inner());
43		self
44	}
45
46	#[inline]
47	pub fn with_inner_mut<F>(mut self, f: F) -> Self
48	where
49		F: FnOnce(&mut T)
50	{
51		f(self.as_inner_mut());
52		self
53	}
54}
55
56pub trait ChainInner: Sized {
57	#[inline]
58	fn from_chain(chain: Chain<Self>) -> Self {
59		chain.into_inner()
60	}
61
62	#[inline]
63	fn into_chain(self) -> Chain<Self> {
64		Chain::from_inner(self)
65	}
66}
67
68impl<T> ChainInner for T {}
69
70pub(crate) trait ChainInnerType {
71	type Inner;
72}
73
74impl<T> ChainInnerType for Chain<T> {
75	type Inner = T;
76}
77
78/// # Safety
79///
80/// By using this trait, implementors of functions are promising to call
81/// [`write`](Output::write), so that when the function returns, there is a
82/// value written to the output. For example, users can pass a reference to
83/// [`MaybeUninit`](core::mem::MaybeUninit) and rely on the fact that it got
84/// initialised to safely call [`assume_init`](core::mem::MaybeUninit::assume_init).
85///
86/// idk how to enforce the above properly using unsafe etc.
87pub unsafe trait Output<T>: Sized + private::Sealed<T> {
88	/// Stores a value
89	fn write(self, item: T);
90}
91
92// SAFETY: we write once to `self`
93unsafe impl<T> Output<T> for &mut T {
94	#[expect(
95		clippy::inline_always,
96		reason = "same as MaybeUninit::write"
97	)]
98	#[inline(always)]
99	fn write(self, item: T) {
100		*self = item;
101	}
102}
103impl<T> private::Sealed<T> for &mut T {}
104
105// SAFETY: we write once to `self`
106unsafe impl<T> Output<T> for &mut Option<T> {
107	#[expect(
108		clippy::inline_always,
109		reason = "same as MaybeUninit::write"
110	)]
111	#[inline(always)]
112	fn write(self, item: T) {
113		*self = Some(item);
114	}
115}
116impl<T> private::Sealed<T> for &mut Option<T> {}
117
118// SAFETY: we write once to `self`
119unsafe impl<T> Output<T> for &mut core::mem::MaybeUninit<T> {
120	#[expect(
121		clippy::inline_always,
122		reason = "same as MaybeUninit::write"
123	)]
124	#[inline(always)]
125	fn write(self, item: T) {
126		self.write(item);
127	}
128}
129impl<T> private::Sealed<T> for &mut core::mem::MaybeUninit<T> {}
130
131/// Tool for helping to debug [`Output`] trait usage in debug mode (if `out` is
132/// not written to, the function will panic only in debug mode)
133///
134/// This function should optimise out to a no-op in release mode.
135#[expect(
136	clippy::inline_always,
137	reason = "should be no-op"
138)]
139#[inline(always)]
140pub fn out_dbg<T>(out: impl Output<T>) -> impl Output<T> {
141	#[cfg(debug_assertions)]
142	let out = OutputDebug {
143		inner: out,
144		__marker: std::marker::PhantomData
145	};
146
147	out
148}
149
150#[cfg(debug_assertions)]
151#[repr(transparent)]
152struct OutputDebug<T, O>
153where
154	O: Output<T>
155{
156	inner: O,
157	__marker: core::marker::PhantomData<fn(T)>
158}
159
160// SAFETY: we write once to `self`
161#[cfg(debug_assertions)]
162unsafe impl<T, O> Output<T> for OutputDebug<T, O>
163where
164	O: Output<T>
165{
166	#[inline]
167	fn write(self, item: T) {
168		let this = core::mem::ManuallyDrop::new(self);
169
170		// SAFETY: ManuallyDrop above prevents double drops
171		let inner = unsafe { core::ptr::read(&raw const this.inner) };
172
173		inner.write(item);
174	}
175}
176
177#[cfg(debug_assertions)]
178impl<T, O> private::Sealed<T> for OutputDebug<T, O>
179where
180	O: Output<T>
181{}
182
183#[cfg(debug_assertions)]
184impl<T, O> Drop for OutputDebug<T, O>
185where
186	O: Output<T>
187{
188	#[inline]
189	fn drop(&mut self) {
190		// writing to this slot will prevent this panic from being called (via ManuallyDrop)
191		panic!("`write` not called on created instance of `Output` (this is a bug)")
192	}
193}
194
195mod private {
196	pub trait Sealed<T> {}
197}