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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! Module ID.

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

use crate::alloc::Box;

/// 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::builder()) a module. It is displayed
/// in error messages (using `Display::fmt`). `ModuleId` is also associated with some types
/// (e.g., [`InterpretedFn`] and [`CodeInModule`]), 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
/// [`CodeInModule`]: crate::error::CodeInModule
pub trait ModuleId: Any + fmt::Display + Send + Sync {
    /// Clones this module ID and boxes the result. It is expected that the output will have
    /// the same specific type as the original module ID. This operation is generally expected
    /// to be quite cheap.
    fn clone_boxed(&self) -> Box<dyn ModuleId>;
}

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 {
    fn clone_boxed(&self) -> Box<dyn ModuleId> {
        Box::new(*self)
    }
}

/// 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 {
    fn clone_boxed(&self) -> Box<dyn ModuleId> {
        Box::new(*self)
    }
}

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::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 {
    fn clone_boxed(&self) -> Box<dyn ModuleId> {
        Box::new(*self)
    }
}