cairo_lang_utils/
lib.rs

1//! Cairo utilities.
2
3#![cfg_attr(not(feature = "std"), no_std)]
4
5#[cfg(not(feature = "std"))]
6extern crate alloc;
7
8#[cfg(not(feature = "std"))]
9use alloc::boxed::Box;
10use core::fmt;
11
12/// Re-exporting the [`smol_str`] crate so that downstream projects can always use the same
13/// instance the compiler does.
14pub use ::smol_str;
15
16pub mod bigint;
17pub mod byte_array;
18pub mod casts;
19pub mod collection_arithmetics;
20pub mod extract_matches;
21#[cfg(feature = "std")]
22pub mod graph_algos;
23pub mod iterators;
24#[cfg(feature = "env_logger")]
25pub mod logging;
26pub mod ordered_hash_map;
27pub mod ordered_hash_set;
28pub mod unordered_hash_map;
29pub mod unordered_hash_set;
30
31/// Similar to From / TryFrom, but returns an option.
32pub trait OptionFrom<T>
33where
34    Self: Sized,
35{
36    fn option_from(other: T) -> Option<Self>;
37}
38
39pub fn write_comma_separated<Iter: IntoIterator<Item = V>, V: core::fmt::Display>(
40    f: &mut fmt::Formatter<'_>,
41    values: Iter,
42) -> fmt::Result {
43    let mut iter = values.into_iter();
44    if let Some(value) = iter.next() {
45        write!(f, "{value}")?;
46    }
47    for value in iter {
48        write!(f, ", {value}")?;
49    }
50    Ok(())
51}
52
53/// Helper operations on `Option<T>`.
54pub trait OptionHelper {
55    fn on_none<F: FnOnce()>(self, f: F) -> Self;
56}
57impl<T> OptionHelper for Option<T> {
58    fn on_none<F: FnOnce()>(self, f: F) -> Self {
59        if self.is_none() {
60            f();
61        }
62        self
63    }
64}
65
66/// Borrows a mutable reference as Box for the lifespan of this function.
67///
68/// Runs the given closure with the boxed value as a parameter.
69/// The closure is expected to return a boxed value, whose changes will be reflected on the mutable
70/// reference.
71/// Example:
72/// ```
73/// use cairo_lang_utils::borrow_as_box;
74/// let mut x = 5;
75/// borrow_as_box(&mut x, |mut x: Box<usize>| {
76///     *x += 1;
77///     ((), x)
78/// });
79/// assert_eq!(x, 6);
80/// ```
81pub fn borrow_as_box<T: Default, R, F: FnOnce(Box<T>) -> (R, Box<T>)>(ptr: &mut T, f: F) -> R {
82    // TODO(spapini): Consider replacing take with something the leaves the memory dangling, instead
83    // of filling with default().
84    let (res, boxed) = f(Box::new(core::mem::take(ptr)));
85    *ptr = *boxed;
86    res
87}
88
89/// A trait for the `lookup_intern` method for short IDs (returning the long ID).
90pub trait LookupIntern<'a, DynDbGroup: ?Sized, LongId> {
91    fn lookup_intern(&self, db: &(impl Upcast<DynDbGroup> + ?Sized)) -> LongId;
92}
93pub trait Intern<'a, DynDbGroup: ?Sized, ShortId> {
94    fn intern(self, db: &(impl Upcast<DynDbGroup> + ?Sized)) -> ShortId;
95}
96
97// Defines a short id struct for use with salsa interning.
98// Interning is the process of representing a value as an id in a table.
99// We usually denote the value type as "long id", and the id type as "short id" or just "id".
100// Example:
101//   A function's long id may be the module in which it is defined and its name. The function is
102//   assigned a sequential integer (salsa::InternId) which will be its short id. Salsa will hold a
103//   table to translate between the two representations. Note that a long id of an entity will
104//   usually include the short id of the entity's parent.
105#[macro_export]
106macro_rules! define_short_id {
107    ($short_id:ident, $long_id:path, $db:ident, $lookup:ident, $intern:ident) => {
108        #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
109        pub struct $short_id(salsa::InternId);
110        impl<'a> cairo_lang_utils::LookupIntern<'a, dyn $db + 'a, $long_id> for $short_id {
111            fn lookup_intern(
112                &self,
113                db: &(impl cairo_lang_utils::Upcast<dyn $db + 'a> + ?Sized),
114            ) -> $long_id {
115                $db::$lookup(db.upcast(), *self)
116            }
117        }
118        impl<'a> cairo_lang_utils::Intern<'a, dyn $db + 'a, $short_id> for $long_id {
119            fn intern(
120                self,
121                db: &(impl cairo_lang_utils::Upcast<dyn $db + 'a> + ?Sized),
122            ) -> $short_id {
123                $db::$intern(db.upcast(), self)
124            }
125        }
126        impl salsa::InternKey for $short_id {
127            fn from_intern_id(salsa_id: salsa::InternId) -> Self {
128                Self(salsa_id)
129            }
130
131            fn as_intern_id(&self) -> salsa::InternId {
132                self.0
133            }
134        }
135        // Impl transparent DebugWithDb.
136        impl<T: ?Sized + cairo_lang_utils::Upcast<dyn $db + 'static>>
137            cairo_lang_debug::DebugWithDb<T> for $short_id
138        {
139            fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &T) -> std::fmt::Result {
140                use core::fmt::Debug;
141
142                use cairo_lang_debug::helper::Fallback;
143                let db = db.upcast();
144                cairo_lang_debug::helper::HelperDebug::<$long_id, dyn $db>::helper_debug(
145                    &db.$lookup(*self),
146                    db,
147                )
148                .fmt(f)
149            }
150        }
151    };
152}
153
154pub trait Upcast<T: ?Sized> {
155    fn upcast(&self) -> &T;
156}
157
158impl<T: ?Sized> Upcast<T> for T {
159    fn upcast(&self) -> &T {
160        self
161    }
162}
163
164/// Returns `Some(())` if the condition is true, otherwise `None`.
165///
166/// Useful in functions returning `None` on some condition:
167/// `require(condition)?;`
168/// And for functions returning `Err` on some condition:
169/// `require(condition).ok_or_else(|| create_err())?;`
170#[must_use = "This function is only relevant to create a possible return."]
171pub fn require(condition: bool) -> Option<()> {
172    condition.then_some(())
173}