Skip to content

Commit

Permalink
add tuple management
Browse files Browse the repository at this point in the history
  • Loading branch information
remybar committed Feb 20, 2025
1 parent 50b8d8b commit e754f86
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 32 deletions.
5 changes: 1 addition & 4 deletions crates/dojo/core/src/storage/dojo_store.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,7 @@ impl DojoStore_array<T, +Drop<T>, +Serde<T>, +DojoStore<T>> of DojoStore<Array<T
deserialize_array_helper(ref values, arr, length)
}
}
// TODO RBA: specific implementation for tuples.

/// Specific implementation of DojoStore for Span<T>,
/// TODO RBA: Specific implementation of DojoStore for Span<T>,
/// to call DojoStore for span items instead of Serde directly.
//impl DojoStore_span<T, +Drop<T>, +Serde<T>, +DojoStore<T>> of DojoStore<Span<T>> {
// fn serialize(self: @Span<T>, ref serialized: Array<felt252>) {
Expand All @@ -113,4 +111,3 @@ impl DojoStore_array<T, +Drop<T>, +Serde<T>, +DojoStore<T>> of DojoStore<Array<T
// }
//}


210 changes: 182 additions & 28 deletions crates/dojo/lang/src/derive_macros/introspect/dojo_store.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,128 @@
use cairo_lang_syntax::node::ast::{ItemEnum, ItemStruct, OptionTypeClause};
use cairo_lang_syntax::node::ast::{
Expr, ExprListParenthesized, ItemEnum, ItemStruct, OptionTypeClause,
};
use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode};

use crate::attribute_macros::element::{deserialize_member_ty, serialize_member_ty};

/// Destructure a tuple member into a string representing the destructured tuple,
/// and the index of the last element.
///
/// For example: (u8, u8, (u8, (u8, u8))) should give (e1,e2,(e3,(e4,e5,))) and 5.
fn destructure_tuple_member(
db: &dyn SyntaxGroup,
expr: &ExprListParenthesized,
start: usize,
) -> (String, usize) {
if expr.expressions(db).elements(db).is_empty() {
return ("()".to_string(), 0);
}

let elements = expr
.expressions(db)
.elements(db)
.iter()
.enumerate()
.map(|(index, element)| {
let current = start + index;
match element {
Expr::Tuple(expr) => destructure_tuple_member(db, expr, current),
_ => (format!("e{},", current), current),
}
})
.collect::<Vec<_>>();

(
format!("({})", elements.iter().map(|(str, _)| str.clone()).collect::<Vec<_>>().join("")),
elements.last().unwrap().1,
)
}

/// Generate the list of tuple element deserialization.
///
/// For example: (u8, u16, (u32, (u64, u128))) should give:
/// let e1 = dojo::storage::DojoStore::<u8>::deserialize(ref values)?;
/// let e2 = dojo::storage::DojoStore::<u16>::deserialize(ref values)?;
/// let e3 = dojo::storage::DojoStore::<u32>::deserialize(ref values)?;
/// let e4 = dojo::storage::DojoStore::<u64>::deserialize(ref values)?;
/// let e5 = dojo::storage::DojoStore::<u128>::deserialize(ref values)?;
fn deserialize_tuple_list(
db: &dyn SyntaxGroup,
expr: &ExprListParenthesized,
start: usize,
) -> Vec<String> {
expr.expressions(db)
.elements(db)
.iter()
.enumerate()
.flat_map(|(index, element)| {
let current = start + index;
match element {
Expr::Tuple(expr) => deserialize_tuple_list(db, expr, current),
Expr::Path(p) => {
let ty = p.as_syntax_node().get_text(db);
vec![format!(
"let e{} = dojo::storage::DojoStore::<{ty}>::deserialize(ref values)?;",
current
)]
}
// TODO RBA: handle Expr::FixedSizeArray
_ => {
unimplemented!(
"Tuple: Expr '{}' not supported inside tuples",
element.as_syntax_node().get_text(db)
)
}
}
})
.collect::<Vec<_>>()
}

/// Generate the list of tuple element serialization.
fn serialize_tuple_list(last: usize) -> Vec<String> {
(1..last + 1)
.map(|index| format!("dojo::storage::DojoStore::serialize(e{index}, ref serialized);"))
.collect::<Vec<_>>()
}

/// Serialize a tuple member by destructure it and then call DojoStore::serialize for
/// every tuple items.
fn serialize_tuple_member(
db: &dyn SyntaxGroup,
member_name: &String,
expr: &ExprListParenthesized,
) -> String {
if expr.expressions(db).elements(db).is_empty() {
return "".to_string();
}

let (tuple_repr, last) = destructure_tuple_member(db, expr, 1);
let serialized_tuple_items = serialize_tuple_list(last).join("\n");

format!(
"let {tuple_repr} = self.{member_name};
{serialized_tuple_items}
"
)
}

/// Deserialize a tuple member by deserialize every tuple items and then rebuild
/// the original tuple.
fn deserialize_tuple_member(
db: &dyn SyntaxGroup,
member_name: &String,
expr: &ExprListParenthesized,
) -> String {
let (tuple_repr, _) = destructure_tuple_member(db, expr, 1);
let deserialized_tuple_items = deserialize_tuple_list(db, expr, 1).join("\n");

format!(
"{deserialized_tuple_items}
let {member_name} = {tuple_repr};"
)
}

pub fn build_struct_dojo_store(
db: &dyn SyntaxGroup,
name: &String,
Expand All @@ -18,8 +137,17 @@ pub fn build_struct_dojo_store(
let member_ty =
member.type_clause(db).ty(db).as_syntax_node().get_text(db).trim().to_string();

serialized_members.push(serialize_member_ty(&member_name, true, false));
deserialized_members.push(deserialize_member_ty(&member_name, &member_ty, false));
match member.type_clause(db).ty(db) {
Expr::Tuple(tuple) => {
serialized_members.push(serialize_tuple_member(db, &member_name, &tuple));
deserialized_members.push(deserialize_tuple_member(db, &member_name, &tuple));
}
_ => {
serialized_members.push(serialize_member_ty(&member_name, true, false));
deserialized_members.push(deserialize_member_ty(&member_name, &member_ty, false));
}
}

member_names.push(member_name);
}

Expand Down Expand Up @@ -51,33 +179,59 @@ pub fn build_enum_dojo_store(db: &dyn SyntaxGroup, name: &String, enum_ast: &Ite
let full_variant_name = format!("{name}::{variant_name}");
let variant_index = index + 1;

let serialized_variant = match variant.type_clause(db) {
OptionTypeClause::TypeClause(_) => {
format!(
"{full_variant_name}(d) => {{
serialized.append({variant_index});
dojo::storage::DojoStore::serialize(d, ref serialized);
}},"
)
}
OptionTypeClause::Empty(_) => {
format!("{full_variant_name} => {{ serialized.append({variant_index}); }},")
}
};
let (serialized_variant, deserialized_variant) = match variant.type_clause(db) {
OptionTypeClause::TypeClause(ty) => match ty.ty(db) {
Expr::Tuple(expr) => {
let (tuple_repr, last) = destructure_tuple_member(db, &expr, 1);
let serialized_tuple_items = serialize_tuple_list(last).join("\n");
let deserialized_tuple_items = deserialize_tuple_list(db, &expr, 1).join("\n");

let deserialized_variant = match variant.type_clause(db) {
OptionTypeClause::TypeClause(ty) => {
let ty = ty.ty(db).as_syntax_node().get_text(db).trim().to_string();
format!(
"{variant_index} => {{
let variant_data = dojo::storage::DojoStore::<{ty}>::deserialize(ref \
values)?;
Option::Some({full_variant_name}(variant_data))
}},",
)
}
let serialized = format!(
"{full_variant_name}(d) => {{
serialized.append({variant_index});
let {tuple_repr} = d;
{serialized_tuple_items}
}},"
);

let deserialized = format!(
"{variant_index} => {{
{deserialized_tuple_items}
let variant_data = {tuple_repr};
Option::Some({full_variant_name}(variant_data))
}},",
);

(serialized, deserialized)
}
_ => {
let ty = ty.ty(db).as_syntax_node().get_text(db).trim().to_string();

let serialized = format!(
"{full_variant_name}(d) => {{
serialized.append({variant_index});
dojo::storage::DojoStore::serialize(d, ref serialized);
}},"
);

let deserialized = format!(
"{variant_index} => {{
let variant_data = \
dojo::storage::DojoStore::<{ty}>::deserialize(ref values)?;
Option::Some({full_variant_name}(variant_data))
}},",
);

(serialized, deserialized)
}
},
OptionTypeClause::Empty(_) => {
format!("{variant_index} => Option::Some({full_variant_name}),",)
let serialized =
format!("{full_variant_name} => {{ serialized.append({variant_index}); }},");
let deserialized =
format!("{variant_index} => Option::Some({full_variant_name}),",);

(serialized, deserialized)
}
};

Expand Down
6 changes: 6 additions & 0 deletions crates/dojo/lang/src/derive_macros/introspect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode};
use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;

use crate::debug_expand;

mod dojo_store;
mod layout;
mod size;
Expand Down Expand Up @@ -41,6 +43,8 @@ pub fn handle_introspect_struct(
let (gen_types, gen_impls) = build_generic_types_and_impls(db, struct_ast.generic_params(db));
let dojo_store = dojo_store::build_struct_dojo_store(db, &struct_name, &struct_ast);

debug_expand(&format!("DOJO_STORE STRUCT::{struct_name}"), &dojo_store);

generate_introspect(
&struct_name,
&struct_size,
Expand Down Expand Up @@ -90,6 +94,8 @@ pub fn handle_introspect_enum(
let ty = ty::build_enum_ty(db, &enum_name, &enum_ast);
let dojo_store = dojo_store::build_enum_dojo_store(db, &enum_name, &enum_ast);

debug_expand(&format!("DOJO_STORE ENUM::{enum_name}"), &dojo_store);

generate_introspect(&enum_name, &enum_size, &gen_types, gen_impls, &layout, &ty, &dojo_store)
}

Expand Down

0 comments on commit e754f86

Please sign in to comment.