facet_types/value.rs
1use bitflags::bitflags;
2use facet_opaque::{Opaque, OpaqueConst, OpaqueUninit};
3use std::cmp::Ordering;
4
5use crate::Shape;
6
7//======== Type Information ========
8
9/// A function that formats the name of a type.
10///
11/// This helps avoid allocations, and it takes options.
12pub type TypeNameFn = fn(f: &mut std::fmt::Formatter, opts: TypeNameOpts) -> std::fmt::Result;
13
14/// Options for formatting the name of a type
15#[non_exhaustive]
16#[derive(Clone, Copy)]
17pub struct TypeNameOpts {
18 /// as long as this is > 0, keep formatting the type parameters
19 /// when it reaches 0, format type parameters as `...`
20 /// if negative, all type parameters are formatted
21 pub recurse_ttl: isize,
22}
23
24impl Default for TypeNameOpts {
25 fn default() -> Self {
26 Self { recurse_ttl: -1 }
27 }
28}
29
30impl TypeNameOpts {
31 /// Create a new `NameOpts` for which none of the type parameters are formatted
32 pub fn none() -> Self {
33 Self { recurse_ttl: 0 }
34 }
35
36 /// Create a new `NameOpts` for which only the direct children are formatted
37 pub fn one() -> Self {
38 Self { recurse_ttl: 1 }
39 }
40
41 /// Create a new `NameOpts` for which all type parameters are formatted
42 pub fn infinite() -> Self {
43 Self { recurse_ttl: -1 }
44 }
45
46 /// Decrease the `recurse_ttl` — if it's != 0, returns options to pass when
47 /// formatting children type parameters.
48 ///
49 /// If this returns `None` and you have type parameters, you should render a
50 /// `…` (unicode ellipsis) character instead of your list of types.
51 ///
52 /// See the implementation for `Vec` for examples.
53 pub fn for_children(&self) -> Option<Self> {
54 match self.recurse_ttl.cmp(&0) {
55 Ordering::Greater => Some(Self {
56 recurse_ttl: self.recurse_ttl - 1,
57 }),
58 Ordering::Less => Some(Self {
59 recurse_ttl: self.recurse_ttl,
60 }),
61 Ordering::Equal => None,
62 }
63 }
64}
65
66//======== Memory Management ========
67
68/// Function to drop a value
69///
70/// # Safety
71///
72/// The `value` parameter must point to aligned, initialized memory of the correct type.
73pub type DropInPlaceFn = for<'mem> unsafe fn(value: Opaque<'mem>);
74
75/// Generates a [`DropInPlaceFn`] for a concrete type
76pub const fn drop_in_place_fn_for<T>() -> Option<DropInPlaceFn> {
77 Some(|value: Opaque<'_>| unsafe {
78 value.drop_in_place::<T>();
79 })
80}
81
82/// Function to clone a value into another already-allocated value
83///
84/// # Safety
85///
86/// The `source` parameter must point to aligned, initialized memory of the correct type.
87/// The `target` parameter has the correct layout and alignment, but points to
88/// uninitialized memory. The function returns the same pointer wrapped in an [`Opaque`].
89pub type CloneIntoFn = for<'src, 'dst> unsafe fn(
90 source: OpaqueConst<'src>,
91 target: OpaqueUninit<'dst>,
92) -> Opaque<'dst>;
93
94/// Generates a [`CloneInPlaceFn`] for a concrete type
95pub const fn clone_into_fn_for<T: Clone>() -> Option<CloneIntoFn> {
96 Some(|source: OpaqueConst<'_>, target: OpaqueUninit<'_>| unsafe {
97 let source_val = source.as_ref::<T>();
98 target.write(source_val.clone())
99 })
100}
101
102/// Function to set a value to its default in-place
103///
104/// # Safety
105///
106/// The `target` parameter has the correct layout and alignment, but points to
107/// uninitialized memory. The function returns the same pointer wrapped in an [`Opaque`].
108pub type DefaultInPlaceFn = for<'mem> unsafe fn(target: OpaqueUninit<'mem>) -> Opaque<'mem>;
109
110/// Generates a [`DefaultInPlaceFn`] for a concrete type
111pub const fn default_in_place_fn_for<T: Default>() -> Option<DefaultInPlaceFn> {
112 Some(|target: OpaqueUninit<'_>| unsafe { target.write(T::default()) })
113}
114
115//======== Conversion ========
116
117/// Function to parse a value from a string.
118///
119/// If both [`DisplayFn`] and [`ParseFn`] are set, we should be able to round-trip the value.
120///
121/// # Safety
122///
123/// The `target` parameter has the correct layout and alignment, but points to
124/// uninitialized memory. If this function succeeds, it should return `Ok` with the
125/// same pointer wrapped in an [`Opaque`]. If parsing fails, it returns `Err` with an error.
126pub type ParseFn =
127 for<'mem> unsafe fn(s: &str, target: OpaqueUninit<'mem>) -> Result<Opaque<'mem>, ParseError>;
128
129/// Generates a [`ParseFn`] for a concrete type
130pub const fn parse_fn_for<T: std::str::FromStr>() -> Option<ParseFn> {
131 Some(|s: &str, target: OpaqueUninit<'_>| unsafe {
132 match s.parse::<T>() {
133 Ok(value) => Ok(target.write(value)),
134 Err(_) => Err(ParseError::Generic("failed to parse string")),
135 }
136 })
137}
138
139/// Error returned by [`ParseFn`]
140#[non_exhaustive]
141#[derive(Debug)]
142pub enum ParseError {
143 /// Generic error message
144 Generic(&'static str),
145}
146
147impl std::fmt::Display for ParseError {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 match self {
150 ParseError::Generic(msg) => write!(f, "Parse failed: {}", msg),
151 }
152 }
153}
154
155impl std::error::Error for ParseError {}
156
157/// Function to try converting from another type
158///
159/// # Safety
160///
161/// The `target` parameter has the correct layout and alignment, but points to
162/// uninitialized memory. If this function succeeds, it should return `Ok` with the
163/// same pointer wrapped in an [`Opaque`]. If conversion fails, it returns `Err` with an error.
164pub type TryFromFn = for<'src, 'mem> unsafe fn(
165 source: OpaqueConst<'src>,
166 target: OpaqueUninit<'mem>,
167) -> Result<Opaque<'mem>, TryFromError>;
168
169/// Error type for TryFrom conversion failures
170#[non_exhaustive]
171#[derive(Debug)]
172pub enum TryFromError {
173 /// Generic conversion error
174 Generic(&'static str),
175 /// The target shape doesn't implement conversion from any source shape (no try_from in vtable)
176 Unimplemented(&'static Shape),
177 /// The target shape has a conversion implementation, but it doesn't support converting from this specific source shape
178 Incompatible {
179 /// The source shape that we tried to convert from
180 source: &'static Shape,
181 /// The target shape that we tried to convert to
182 target: &'static Shape,
183 },
184}
185
186impl std::fmt::Display for TryFromError {
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 match self {
189 TryFromError::Generic(msg) => write!(f, "Conversion failed: {}", msg),
190 TryFromError::Unimplemented(shape) => write!(
191 f,
192 "Conversion failed: Shape {} doesn't implement any conversions (no try_from function)",
193 shape
194 ),
195 TryFromError::Incompatible { source, target } => write!(
196 f,
197 "Conversion failed: Cannot convert from shape {} to shape {}",
198 source, target
199 ),
200 }
201 }
202}
203
204impl std::error::Error for TryFromError {}
205
206//======== Comparison ========
207
208/// Function to check if two values are partially equal
209///
210/// # Safety
211///
212/// Both `left` and `right` parameters must point to aligned, initialized memory of the correct type.
213pub type PartialEqFn = for<'l, 'r> unsafe fn(left: OpaqueConst<'l>, right: OpaqueConst<'r>) -> bool;
214
215/// Generates a [`PartialEqFn`] for a concrete type
216pub const fn partial_eq_fn_for<T: PartialEq>() -> Option<PartialEqFn> {
217 Some(|left: OpaqueConst<'_>, right: OpaqueConst<'_>| -> bool {
218 let left_val = unsafe { left.as_ref::<T>() };
219 let right_val = unsafe { right.as_ref::<T>() };
220 left_val == right_val
221 })
222}
223
224/// Function to compare two values and return their ordering if comparable
225///
226/// # Safety
227///
228/// Both `left` and `right` parameters must point to aligned, initialized memory of the correct type.
229pub type PartialOrdFn =
230 for<'l, 'r> unsafe fn(left: OpaqueConst<'l>, right: OpaqueConst<'r>) -> Option<Ordering>;
231
232/// Generates a [`PartialOrdFn`] for a concrete type
233pub const fn partial_ord_fn_for<T: PartialOrd>() -> Option<PartialOrdFn> {
234 Some(
235 |left: OpaqueConst<'_>, right: OpaqueConst<'_>| -> Option<Ordering> {
236 let left_val = unsafe { left.as_ref::<T>() };
237 let right_val = unsafe { right.as_ref::<T>() };
238 left_val.partial_cmp(right_val)
239 },
240 )
241}
242
243/// Function to compare two values and return their ordering
244///
245/// # Safety
246///
247/// Both `left` and `right` parameters must point to aligned, initialized memory of the correct type.
248pub type CmpFn = for<'l, 'r> unsafe fn(left: OpaqueConst<'l>, right: OpaqueConst<'r>) -> Ordering;
249
250/// Generates a [`CmpFn`] for a concrete type
251pub const fn cmp_fn_for<T: Ord>() -> Option<CmpFn> {
252 Some(
253 |left: OpaqueConst<'_>, right: OpaqueConst<'_>| -> Ordering {
254 let left_val = unsafe { left.as_ref::<T>() };
255 let right_val = unsafe { right.as_ref::<T>() };
256 left_val.cmp(right_val)
257 },
258 )
259}
260
261//======== Hashing ========
262
263/// Function to hash a value
264///
265/// # Safety
266///
267/// The `value` parameter must point to aligned, initialized memory of the correct type.
268/// The hasher pointer must be a valid pointer to a Hasher trait object.
269pub type HashFn = for<'mem> unsafe fn(
270 value: OpaqueConst<'mem>,
271 hasher_this: Opaque<'mem>,
272 hasher_write_fn: HasherWriteFn,
273);
274
275/// Generates a [`HashFn`] for a concrete type
276pub const fn hash_fn_for<T: std::hash::Hash>() -> Option<HashFn> {
277 Some(
278 |value: OpaqueConst<'_>, hasher_this: Opaque<'_>, hasher_write_fn: HasherWriteFn| unsafe {
279 let val = value.as_ref::<T>();
280 val.hash(&mut HasherProxy::new(hasher_this, hasher_write_fn));
281 },
282 )
283}
284
285/// Function to write bytes to a hasher
286///
287/// # Safety
288///
289/// The `hasher_self` parameter must be a valid pointer to a hasher
290pub type HasherWriteFn = for<'mem> unsafe fn(hasher_self: Opaque<'mem>, bytes: &[u8]);
291
292/// Provides an implementation of [`std::hash::Hasher`] for a given hasher pointer and write function
293///
294/// See [`HashFn`] for more details on the parameters.
295///
296/// Example usage (for a type that already implements `Hasher`)
297///
298/// ```rust,ignore
299/// hash: Some(|value, hasher_self, hasher_write_fn| unsafe {
300/// value
301/// .as_ref::<Self>()
302/// .hash(&mut HasherProxy::new(hasher_self, hasher_write_fn));
303/// }),
304/// ```
305pub struct HasherProxy<'a> {
306 hasher_this: Opaque<'a>,
307 hasher_write_fn: HasherWriteFn,
308}
309
310impl<'a> HasherProxy<'a> {
311 /// Create a new `HasherProxy` from a hasher pointer and a write function
312 ///
313 /// # Safety
314 ///
315 /// The `hasher_this` parameter must be a valid pointer to a Hasher trait object.
316 /// The `hasher_write_fn` parameter must be a valid function pointer.
317 pub unsafe fn new(hasher_this: Opaque<'a>, hasher_write_fn: HasherWriteFn) -> Self {
318 Self {
319 hasher_this,
320 hasher_write_fn,
321 }
322 }
323}
324
325impl std::hash::Hasher for HasherProxy<'_> {
326 fn finish(&self) -> u64 {
327 unimplemented!("finish is not needed for this implementation")
328 }
329 fn write(&mut self, bytes: &[u8]) {
330 unsafe { (self.hasher_write_fn)(self.hasher_this, bytes) }
331 }
332}
333
334//======== Marker Traits ========
335
336bitflags! {
337 /// Bitflags for common marker traits that a type may implement
338 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
339 pub struct MarkerTraits: u8 {
340 /// Indicates that the type implements the [`Eq`] marker trait
341 const EQ = 1 << 0;
342 /// Indicates that the type implements the [`Send`] marker trait
343 const SEND = 1 << 1;
344 /// Indicates that the type implements the [`Sync`] marker trait
345 const SYNC = 1 << 2;
346 /// Indicates that the type implements the [`Copy`] marker trait
347 const COPY = 1 << 3;
348 }
349}
350
351//======== Display and Debug ========
352
353/// Function to format a value for display
354///
355/// If both [`DisplayFn`] and [`ParseFn`] are set, we should be able to round-trip the value.
356///
357/// # Safety
358///
359/// The `value` parameter must point to aligned, initialized memory of the correct type.
360pub type DisplayFn =
361 for<'mem> unsafe fn(value: OpaqueConst<'mem>, f: &mut std::fmt::Formatter) -> std::fmt::Result;
362
363/// Generates a [`DisplayFn`] for a concrete type
364pub const fn display_fn_for<T: std::fmt::Display>() -> Option<DisplayFn> {
365 Some(
366 |value: OpaqueConst<'_>, f: &mut std::fmt::Formatter| -> std::fmt::Result {
367 let val = unsafe { value.as_ref::<T>() };
368 write!(f, "{val}")
369 },
370 )
371}
372
373/// Function to format a value for debug.
374/// If this returns None, the shape did not implement Debug.
375///
376/// # Safety
377///
378/// The `value` parameter must point to aligned, initialized memory of the correct type.
379pub type DebugFn =
380 for<'mem> unsafe fn(value: OpaqueConst<'mem>, f: &mut std::fmt::Formatter) -> std::fmt::Result;
381
382/// Generates a [`DebugFn`] for a concrete type
383pub const fn debug_fn_for<T: std::fmt::Debug>() -> Option<DebugFn> {
384 Some(
385 |value: OpaqueConst<'_>, f: &mut std::fmt::Formatter| -> std::fmt::Result {
386 let val = unsafe { value.as_ref::<T>() };
387 write!(f, "{val:?}")
388 },
389 )
390}
391
392/// VTable for common operations that can be performed on any shape
393#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
394pub struct ValueVTable {
395 /// cf. [`TypeNameFn`]
396 pub type_name: TypeNameFn,
397
398 /// cf. [`DisplayFn`]
399 pub display: Option<DisplayFn>,
400
401 /// cf. [`DebugFn`]
402 pub debug: Option<DebugFn>,
403
404 /// cf. [`DefaultInPlaceFn`]
405 pub default_in_place: Option<DefaultInPlaceFn>,
406
407 /// cf. [`CloneInPlaceFn`]
408 pub clone_into: Option<CloneIntoFn>,
409
410 /// Marker traits implemented by the type
411 // FIXME: move out of vtable, it's not really... functions.
412 // Belongs in Shape directly.
413 pub marker_traits: MarkerTraits,
414
415 /// cf. [`PartialEqFn`] for equality comparison
416 pub eq: Option<PartialEqFn>,
417
418 /// cf. [`PartialOrdFn`] for partial ordering comparison
419 pub partial_ord: Option<PartialOrdFn>,
420
421 /// cf. [`CmpFn`] for total ordering
422 pub ord: Option<CmpFn>,
423
424 /// cf. [`HashFn`]
425 pub hash: Option<HashFn>,
426
427 /// cf. [`DropInPlaceFn`] — if None, drops without side-effects
428 pub drop_in_place: Option<DropInPlaceFn>,
429
430 /// cf. [`ParseFn`]
431 pub parse: Option<ParseFn>,
432
433 /// cf. [`TryFromFn`]
434 pub try_from: Option<TryFromFn>,
435}
436
437impl ValueVTable {
438 /// Check if the type implements the [`Eq`] marker trait
439 pub fn is_eq(&self) -> bool {
440 self.marker_traits.contains(MarkerTraits::EQ)
441 }
442
443 /// Check if the type implements the [`Send`] marker trait
444 pub fn is_send(&self) -> bool {
445 self.marker_traits.contains(MarkerTraits::SEND)
446 }
447
448 /// Check if the type implements the [`Sync`] marker trait
449 pub fn is_sync(&self) -> bool {
450 self.marker_traits.contains(MarkerTraits::SYNC)
451 }
452
453 /// Check if the type implements the [`Copy`] marker trait
454 pub fn is_copy(&self) -> bool {
455 self.marker_traits.contains(MarkerTraits::COPY)
456 }
457}