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