Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion crates/parser/src/abi/parser.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use starknet::core::types::contract::{AbiEntry, AbiEvent, SierraClass, TypedAbiEvent};
use std::collections::HashMap;

use crate::tokens::{Array, Composite, CompositeType, CoreBasic, Function, Token};
use crate::tokens::{
extract_type_path_with_depth, Array, Composite, CompositeType, CoreBasic, Function, Token,
};
use crate::{CainomeResult, Error};

#[derive(Debug, Clone, PartialEq, Default)]
Expand Down Expand Up @@ -71,8 +73,38 @@ impl AbiParser {
Self::collect_entry_token(entry, &mut token_candidates)?;
}

// In theory this is not enough, as after deepening collisions are still possible
// Although possible in theory should not be a problem in practice.
Comment on lines +76 to +77

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment could be re-written to be more instructive on why this loop is done.

Loops through the tokens to identify naming collisions at the type name level.

let mut token_name_occurence: HashMap<String, usize> = HashMap::new();
for (_, tokens) in &token_candidates {
for token in tokens {
let name = token.type_name();
let val = token_name_occurence.entry(name).or_insert(0);
*val += 1;
}
}

let tokens = Self::filter_struct_enum_tokens(token_candidates);

let mut additional_aliases: HashMap<String, String> = HashMap::new();
for (_, t) in &tokens {
let path = t.type_path();
// Crotch to handle cases when type with the same typename are used
// in same contract. For example:
// enum Event {
// Event1(namespace1::Event),
// Event2(namespace2::Event)
// }
// When name that occures several times is spotted we register a type alias
// (only if there is none). Will apply those later.
Comment on lines +92 to +99

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, you should also provide the output of your example.

You could actually refactor this logic into a function (including the one looping on the tokens to get the name occurence count).

Like so, if the proposition of making this depth dynamic sounds good to you, we could avoid this extra loop when the user doesn't want cainome to auto-resolve the conflicts.

You could like so provide the example + the detail of the behavior in the function documentation. 👍

if token_name_occurence.get(&t.type_name()) > Some(&1)
&& !type_aliases.contains_key(&path)
{
let alias = extract_type_path_with_depth(&path, 1);
additional_aliases.insert(path.into(), alias);
}
}

let mut structs = vec![];
let mut enums = vec![];
// This is not memory efficient, but
Expand All @@ -86,6 +118,11 @@ impl AbiParser {
t.apply_alias(type_path, alias);
}

// NOTE: it's important that user defined aliases were applied first

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting note, but you didn't explain why. :)

for (type_path, alias) in &additional_aliases {
t.apply_alias(&type_path, &alias);
}

if let Token::Composite(ref c) = t {
all_composites.insert(c.type_path_no_generic(), c.clone());

Expand Down
4 changes: 3 additions & 1 deletion crates/parser/src/tokens/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ use std::collections::HashMap;

pub use array::Array;
pub use basic::CoreBasic;
pub use composite::{Composite, CompositeInner, CompositeInnerKind, CompositeType};
pub use composite::{
extract_type_path_with_depth, Composite, CompositeInner, CompositeInnerKind, CompositeType,
};
pub use function::{Function, FunctionOutputKind, StateMutability};
pub use non_zero::NonZero;
pub use option::Option;
Expand Down
28 changes: 27 additions & 1 deletion crates/rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ use crate::expand::{CairoContract, CairoEnum, CairoEnumEvent, CairoFunction, Cai
pub struct ContractBindings {
/// Name of the contract.
pub name: String,

/// Name of the contract.
pub imports: Vec<String>,

/// Tokenized ABI written to a `[TokenStream2]`.
pub tokens: TokenStream2,
}
Expand All @@ -32,8 +36,16 @@ impl ContractBindings {
///
/// * `file` - The path to the file to write the bindings to.
pub fn write_to_file(&self, file: &str) -> io::Result<()> {
let imports = self
.imports
.iter()
.map(|s| format!("use {};", s))
.collect::<Vec<String>>()
.join("\n");

let content = format!(
"// ****\n// Auto-generated by cainome do not edit.\n// ****\n\n#![allow(clippy::all)]\n#![allow(warnings)]\n\n{}",
"// ****\n// Auto-generated by cainome do not edit.\n// ****\n\n#![allow(clippy::all)]\n#![allow(warnings)]\n\n{}\n\n{}",
imports,
self
);
fs::write(file, content)
Expand Down Expand Up @@ -79,6 +91,8 @@ pub struct Abigen {
/// let the user specify the implementation of the types. If a type is generic, the generic arguments
/// are not part of the compared name.
pub type_skips: Vec<String>,
/// Imports to added to the generated files. (For serde for example)
pub imports: Vec<String>,
}

impl Abigen {
Expand All @@ -98,6 +112,7 @@ impl Abigen {
derives: vec![],
contract_derives: vec![],
type_skips: vec![],
imports: vec![],
}
}

Expand All @@ -111,6 +126,16 @@ impl Abigen {
self
}

/// Sets Imports to be added to the generated files.
///
/// # Arguments
///
/// * `imports` - Imports needed for derive macros or other purposes.
pub fn with_imports(mut self, imports: Vec<String>) -> Self {
self.imports = imports;
self
}

/// Sets the execution version to be used.
///
/// # Arguments
Expand Down Expand Up @@ -167,6 +192,7 @@ impl Abigen {

Ok(ContractBindings {
name: self.contract_name.clone(),
imports: self.imports.clone(),
tokens: expanded,
})
}
Expand Down
1 change: 1 addition & 0 deletions src/bin/cli/plugins/builtins/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ impl BuiltinPlugin for RustPlugin {
for contract in &input.contracts {
// The contract name contains the fully qualified path of the cairo module.
// For now, let's only take the latest part of this path.
//
// TODO: if a project has several contracts with the same name under different
// namespaces, we should provide a solution to solve those conflicts.
let contract_name = contract
Expand Down