Skip to main content

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/// Re-exporting the [`smol_str`] crate so that downstream projects can always use the same
9/// instance the compiler does.
10pub use ::smol_str;
11
12pub mod bigint;
13pub mod byte_array;
14pub mod casts;
15pub mod collection_arithmetics;
16pub mod deque;
17pub mod extract_matches;
18#[cfg(feature = "std")]
19pub mod graph_algos;
20#[cfg(feature = "std")]
21pub mod heap_size;
22pub mod iterators;
23#[cfg(feature = "tracing")]
24pub mod logging;
25pub mod ordered_hash_map;
26pub mod ordered_hash_set;
27#[cfg(feature = "std")]
28pub mod small_ordered_map;
29pub mod unordered_hash_map;
30pub mod unordered_hash_set;
31
32#[cfg(feature = "std")]
33pub use heap_size::HeapSize;
34
35/// Similar to From / TryFrom, but returns an option.
36pub trait OptionFrom<T>: Sized {
37    fn option_from(other: T) -> Option<Self>;
38}
39
40/// Helper operations on `Option<T>`.
41pub trait OptionHelper {
42    fn on_none<F: FnOnce()>(self, f: F) -> Self;
43}
44impl<T> OptionHelper for Option<T> {
45    fn on_none<F: FnOnce()>(self, f: F) -> Self {
46        if self.is_none() {
47            f();
48        }
49        self
50    }
51}
52
53/// Traits requesting the for a dynamic clone for a salsa database.
54#[cfg(feature = "std")]
55pub trait CloneableDatabase: salsa::Database + Send {
56    /// Returns a Box of the cloned database.
57    fn dyn_clone(&self) -> Box<dyn CloneableDatabase>;
58}
59
60/// Implements Clone for `Box<dyn CloneableDatabase>`.
61#[cfg(feature = "std")]
62impl Clone for Box<dyn CloneableDatabase> {
63    fn clone(&self) -> Self {
64        self.dyn_clone()
65    }
66}
67
68#[cfg(feature = "std")]
69pub trait Intern<'db, Target> {
70    fn intern(self, db: &'db dyn salsa::Database) -> Target;
71}
72
73/// TODO(eytan-starkware): Remove this macro entirely and rely on `salsa::interned`.
74// Defines a short id struct for use with salsa interning.
75// Interning is the process of representing a value as an id in a table.
76// We usually denote the value type as "long id", and the id type as "short id" or just "id".
77// Example:
78//   A function's long id may be the module in which it is defined and its name. The function is
79//   assigned a sequential integer (salsa::InternId) which will be its short id. Salsa will hold a
80//   table to translate between the two representations. Note that a long id of an entity will
81//   usually include the short id of the entity's parent.
82#[macro_export]
83macro_rules! define_short_id {
84    ($short_id:ident, $long_id:path) => {
85        // 1. Modern interned struct.
86        #[cairo_lang_proc_macros::interned(revisions = usize::MAX)]
87        pub struct $short_id<'db> {
88            #[returns(ref)]
89            pub long: $long_id,
90        }
91
92        impl<'db> std::fmt::Debug for $short_id<'db> {
93            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94                write!(f, "{}({:x})", stringify!($short_id), self.as_intern_id().index())
95            }
96        }
97
98        impl<'db> cairo_lang_utils::Intern<'db, $short_id<'db>> for $long_id {
99            fn intern(self, db: &'db dyn salsa::Database) -> $short_id<'db> {
100                $short_id::new(db, self)
101            }
102        }
103
104        impl<'db> $short_id<'db> {
105            pub fn from_intern_id(intern_id: salsa::Id) -> Self {
106                use salsa::plumbing::FromId;
107                Self::from_id(intern_id)
108            }
109
110            pub fn as_intern_id(self) -> salsa::Id {
111                use salsa::plumbing::AsId;
112                self.as_id()
113            }
114        }
115
116        // 4. DebugWithDb is identical to the old macro.
117        impl<'db> cairo_lang_debug::DebugWithDb<'db> for $short_id<'db> {
118            type Db = dyn salsa::Database;
119            fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
120                use core::fmt::Debug;
121
122                use cairo_lang_debug::helper::{Fallback, HelperDebug};
123
124                HelperDebug::<$long_id, dyn salsa::Database>::helper_debug(self.long(db), db).fmt(f)
125            }
126        }
127
128        // 5. HeapSize implementation - short ids are just wrappers around salsa::Id (no heap
129        //    allocation).
130        impl<'db> cairo_lang_utils::HeapSize for $short_id<'db> {
131            fn heap_size(&self) -> usize {
132                0
133            }
134        }
135    };
136}
137
138/// Performs the given `action` and then calls `finally`, returning the action's result.
139/// Useful for restoring states post running of an action.
140pub fn with_finally<Ctx, R>(
141    ctx: &mut Ctx,
142    action: impl FnOnce(&mut Ctx) -> R,
143    finally: impl FnOnce(&mut Ctx),
144) -> R {
145    let result = action(ctx);
146    finally(ctx);
147    result
148}
149
150/// Returns `Some(())` if the condition is true, otherwise `None`.
151///
152/// Useful in functions returning `None` on some condition:
153/// `require(condition)?;`
154/// And for functions returning `Err` on some condition:
155/// `require(condition).ok_or_else(|| create_err())?;`
156#[must_use = "This function is only relevant to create a possible return."]
157pub fn require(condition: bool) -> Option<()> {
158    condition.then_some(())
159}