1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//! Module ID.

use core::{
    any::{Any, TypeId},
    fmt,
};

/// Identifier of an [`ExecutableModule`](crate::ExecutableModule). This is usually a "small" type,
/// such as an integer or a string.
///
/// The ID is provided when [creating](crate::ExecutableModule::new()) a module. It is displayed
/// in error messages (using `Display::fmt`). `ModuleId` is also associated with some types
/// (e.g., [`InterpretedFn`] and [`LocationInModule`]), which allows to obtain module info.
/// This can be particularly useful for outputting rich error information.
///
/// A `ModuleId` can be downcast to a specific type, similarly to [`Any`].
///
/// [`InterpretedFn`]: crate::InterpretedFn
/// [`LocationInModule`]: crate::error::LocationInModule
pub trait ModuleId: Any + fmt::Display + Send + Sync {}

impl dyn ModuleId {
    /// Returns `true` if the boxed type is the same as `T`.
    ///
    /// This method is effectively a carbon copy of [`<dyn Any>::is`]. Such a copy is necessary
    /// because `&dyn ModuleId` cannot be converted to `&dyn Any`, despite `ModuleId` having `Any`
    /// as a super-trait.
    ///
    /// [`<dyn Any>::is`]: https://doc.rust-lang.org/std/any/trait.Any.html#method.is
    #[inline]
    pub fn is<T: ModuleId>(&self) -> bool {
        let t = TypeId::of::<T>();
        let concrete = self.type_id();
        t == concrete
    }

    /// Returns a reference to the boxed value if it is of type `T`, or `None` if it isn't.
    ///
    /// This method is effectively a carbon copy of [`<dyn Any>::downcast_ref`]. Such a copy
    /// is necessary because `&dyn ModuleId` cannot be converted to `&dyn Any`, despite `ModuleId`
    /// having `Any` as a super-trait.
    ///
    /// [`<dyn Any>::downcast_ref`]: https://doc.rust-lang.org/std/any/trait.Any.html#method.downcast_ref
    pub fn downcast_ref<T: ModuleId>(&self) -> Option<&T> {
        if self.is::<T>() {
            // SAFETY: Same code as for `<dyn Any>::downcast_ref()`.
            unsafe { Some(&*(self as *const dyn ModuleId).cast::<T>()) }
        } else {
            None
        }
    }
}

impl fmt::Debug for dyn ModuleId {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(formatter, "ModuleId({self})")
    }
}

impl ModuleId for &'static str {}

/// Module identifier that has a single possible value, which is displayed as `*`.
///
/// This type is a `ModuleId`-compatible replacement of `()`; `()` does not implement `Display`
/// and thus cannot implement `ModuleId` directly.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WildcardId;

impl ModuleId for WildcardId {}

impl fmt::Display for WildcardId {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("*")
    }
}

/// Indexed module ID containing a prefix part (e.g., `snippet`).
///
/// The ID is `Display`ed as `{prefix} #{index + 1}`:
///
/// ```
/// # use arithmetic_eval::exec::IndexedId;
/// let module_id = IndexedId::new("snippet", 4);
/// assert_eq!(module_id.to_string(), "snippet #5");
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct IndexedId {
    /// Prefix that can identify the nature of the module, such as `snippet`.
    pub prefix: &'static str,
    /// 0-based index of the module.
    pub index: usize,
}

impl IndexedId {
    /// Creates a new ID instance.
    pub const fn new(prefix: &'static str, index: usize) -> Self {
        Self { prefix, index }
    }
}

impl fmt::Display for IndexedId {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(formatter, "{} #{}", self.prefix, self.index + 1)
    }
}

impl ModuleId for IndexedId {}