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