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 {}