arithmetic_eval/exec/
module_id.rs

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