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}