arithmetic_eval/executable/module_id.rs
1//! Module ID.
2
3use core::{
4 any::{Any, TypeId},
5 fmt,
6};
7
8use crate::alloc::Box;
9
10/// Identifier of an [`ExecutableModule`](crate::ExecutableModule). This is usually a "small" type,
11/// such as an integer or a string.
12///
13/// The ID is provided when [creating](crate::ExecutableModule::builder()) a module. It is displayed
14/// in error messages (using `Display::fmt`). `ModuleId` is also associated with some types
15/// (e.g., [`InterpretedFn`] and [`CodeInModule`]), which allows to obtain module info.
16/// This can be particularly useful for outputting rich error information.
17///
18/// A `ModuleId` can be downcast to a specific type, similarly to [`Any`].
19///
20/// [`InterpretedFn`]: crate::InterpretedFn
21/// [`CodeInModule`]: crate::error::CodeInModule
22pub trait ModuleId: Any + fmt::Display + Send + Sync {
23 /// Clones this module ID and boxes the result. It is expected that the output will have
24 /// the same specific type as the original module ID. This operation is generally expected
25 /// to be quite cheap.
26 fn clone_boxed(&self) -> Box<dyn ModuleId>;
27}
28
29impl dyn ModuleId {
30 /// Returns `true` if the boxed type is the same as `T`.
31 ///
32 /// This method is effectively a carbon copy of [`<dyn Any>::is`]. Such a copy is necessary
33 /// because `&dyn ModuleId` cannot be converted to `&dyn Any`, despite `ModuleId` having `Any`
34 /// as a super-trait.
35 ///
36 /// [`<dyn Any>::is`]: https://doc.rust-lang.org/std/any/trait.Any.html#method.is
37 #[inline]
38 pub fn is<T: ModuleId>(&self) -> bool {
39 let t = TypeId::of::<T>();
40 let concrete = self.type_id();
41 t == concrete
42 }
43
44 /// Returns a reference to the boxed value if it is of type `T`, or `None` if it isn't.
45 ///
46 /// This method is effectively a carbon copy of [`<dyn Any>::downcast_ref`]. Such a copy
47 /// is necessary because `&dyn ModuleId` cannot be converted to `&dyn Any`, despite `ModuleId`
48 /// having `Any` as a super-trait.
49 ///
50 /// [`<dyn Any>::downcast_ref`]: https://doc.rust-lang.org/std/any/trait.Any.html#method.downcast_ref
51 pub fn downcast_ref<T: ModuleId>(&self) -> Option<&T> {
52 if self.is::<T>() {
53 // SAFETY: Same code as for `<dyn Any>::downcast_ref()`.
54 unsafe { Some(&*(self as *const dyn ModuleId).cast::<T>()) }
55 } else {
56 None
57 }
58 }
59}
60
61impl fmt::Debug for dyn ModuleId {
62 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
63 write!(formatter, "ModuleId({})", self)
64 }
65}
66
67impl ModuleId for &'static str {
68 fn clone_boxed(&self) -> Box<dyn ModuleId> {
69 Box::new(*self)
70 }
71}
72
73/// Module identifier that has a single possible value, which is displayed as `*`.
74///
75/// This type is a `ModuleId`-compatible replacement of `()`; `()` does not implement `Display`
76/// and thus cannot implement `ModuleId` directly.
77#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
78pub struct WildcardId;
79
80impl ModuleId for WildcardId {
81 fn clone_boxed(&self) -> Box<dyn ModuleId> {
82 Box::new(*self)
83 }
84}
85
86impl fmt::Display for WildcardId {
87 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
88 formatter.write_str("*")
89 }
90}
91
92/// Indexed module ID containing a prefix part (e.g., `snippet`).
93///
94/// The ID is `Display`ed as `{prefix} #{index + 1}`:
95///
96/// ```
97/// # use arithmetic_eval::IndexedId;
98/// let module_id = IndexedId::new("snippet", 4);
99/// assert_eq!(module_id.to_string(), "snippet #5");
100/// ```
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
102pub struct IndexedId {
103 /// Prefix that can identify the nature of the module, such as `snippet`.
104 pub prefix: &'static str,
105 /// 0-based index of the module.
106 pub index: usize,
107}
108
109impl IndexedId {
110 /// Creates a new ID instance.
111 pub const fn new(prefix: &'static str, index: usize) -> Self {
112 Self { prefix, index }
113 }
114}
115
116impl fmt::Display for IndexedId {
117 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
118 write!(formatter, "{} #{}", self.prefix, self.index + 1)
119 }
120}
121
122impl ModuleId for IndexedId {
123 fn clone_boxed(&self) -> Box<dyn ModuleId> {
124 Box::new(*self)
125 }
126}