Skip to content

Commit

Permalink
feat(migrate): read config (#147)
Browse files Browse the repository at this point in the history
* feat(migrate): read config

* refactor: modularize migrate
  • Loading branch information
tarrencev authored Mar 29, 2023
1 parent 2144ef8 commit cd5c011
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 99 deletions.
200 changes: 132 additions & 68 deletions crates/dojo-cli/src/migrate.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,82 @@
use std::env::current_dir;
use std::env::{self, current_dir};
use std::fmt::Display;
use std::fs;
use std::path::PathBuf;

use anyhow::Context;
use cairo_lang_starknet::casm_contract_class::CasmContractClass;
use camino::Utf8PathBuf;
use clap::Args;
use comfy_table::Table;
use dojo_project::WorldConfig;
use scarb::core::Config;
use scarb::ops;
use scarb::ui::Verbosity;
use starknet::core::types::contract::CompiledClass;
use starknet::core::types::FieldElement;

#[derive(Args)]
pub struct MigrateArgs {
#[clap(help = "Source directory")]
path: Option<PathBuf>,
path: Option<Utf8PathBuf>,

#[clap(short, long, help = "Perform a dry run and outputs the plan to be executed")]
plan: bool,
}

pub async fn run(args: MigrateArgs) -> anyhow::Result<()> {
let source_dir = match args.path {
Some(path) => {
if path.is_absolute() {
path
} else {
let mut current_path = current_dir().unwrap();
current_path.push(path);
Utf8PathBuf::from_path_buf(current_path).unwrap()
}
}
None => Utf8PathBuf::from_path_buf(current_dir().unwrap()).unwrap(),
};

let world = World::from_path(source_dir.clone())?;

println!("{world}");

Ok(())
}

struct World {
address: Option<FieldElement>,
modules: Modules,
}

impl World {
fn from_path(source_dir: Utf8PathBuf) -> anyhow::Result<World> {
let manifest_path = source_dir.join("Scarb.toml");
let config = Config::builder(manifest_path)
.ui_verbosity(Verbosity::Verbose)
.log_filter_directive(env::var_os("SCARB_LOG"))
.build()
.unwrap();
let ws = ops::read_workspace(config.manifest_path(), &config).unwrap_or_else(|err| {
eprintln!("error: {}", err);
std::process::exit(1);
});
let world_config =
WorldConfig::from_workspace(&ws).unwrap_or_else(|_| WorldConfig::default());

let modules = Modules::from_path(source_dir.clone())?;

Ok(World { address: world_config.address, modules })
}
}

impl Display for World {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "World Address: 0x{:x}", self.address.unwrap())?;
writeln!(f, "{}", self.modules)
}
}

struct Module {
name: String,
hash: FieldElement,
Expand All @@ -29,74 +88,79 @@ struct Modules {
systems: Vec<Module>,
}

pub async fn run(args: MigrateArgs) -> anyhow::Result<()> {
let source_dir = match args.path {
Some(path) => path,
None => current_dir().unwrap(),
};

let mut modules = Modules { contracts: vec![], components: vec![], systems: vec![] };

// Read the directory
let entries = fs::read_dir(source_dir.join("target/release")).unwrap_or_else(|error| {
panic!("Problem reading source directory: {:?}", error);
});

for entry in entries.flatten() {
let file_name = entry.file_name();
let file_name_str = file_name.to_string_lossy();
if !file_name_str.ends_with(".json") {
continue;
impl Modules {
fn from_path(source_dir: Utf8PathBuf) -> anyhow::Result<Modules> {
let mut modules = Modules { contracts: vec![], components: vec![], systems: vec![] };

// Read the directory
let entries = fs::read_dir(source_dir.join("target/release")).unwrap_or_else(|error| {
panic!("Problem reading source directory: {:?}", error);
});

for entry in entries.flatten() {
let file_name = entry.file_name();
let file_name_str = file_name.to_string_lossy();
if !file_name_str.ends_with(".json") {
continue;
}

let name = file_name_str.split('_').last().unwrap().to_string();
let contract_class = serde_json::from_reader(fs::File::open(entry.path()).unwrap())
.unwrap_or_else(|error| {
panic!("Problem parsing {} artifact: {:?}", file_name_str, error);
});

let casm_contract = CasmContractClass::from_contract_class(contract_class, true)
.with_context(|| "Compilation failed.")?;
let res = serde_json::to_string_pretty(&casm_contract)
.with_context(|| "Casm contract Serialization failed.")?;

let compiled_class: CompiledClass =
serde_json::from_str(res.as_str()).unwrap_or_else(|error| {
panic!("Problem parsing {} artifact: {:?}", file_name_str, error);
});

let hash = compiled_class
.class_hash()
.with_context(|| "Casm contract Serialization failed.")?;

if name.ends_with("Component.json") {
modules.components.push(Module {
name: name.strip_suffix("Component.json").unwrap().to_string(),
hash,
});
} else if name.ends_with("System.json") {
modules.systems.push(Module {
name: name.strip_suffix("System.json").unwrap().to_string(),
hash,
});
} else {
modules
.contracts
.push(Module { name: name.strip_suffix(".json").unwrap().to_string(), hash });
};
}

let name = file_name_str.split('_').last().unwrap().to_string();
let contract_class = serde_json::from_reader(fs::File::open(entry.path()).unwrap())
.unwrap_or_else(|error| {
panic!("Problem parsing {} artifact: {:?}", file_name_str, error);
});

let casm_contract = CasmContractClass::from_contract_class(contract_class, true)
.with_context(|| "Compilation failed.")?;
let res = serde_json::to_string_pretty(&casm_contract)
.with_context(|| "Casm contract Serialization failed.")?;

let compiled_class: CompiledClass =
serde_json::from_str(res.as_str()).unwrap_or_else(|error| {
panic!("Problem parsing {} artifact: {:?}", file_name_str, error);
});

let hash =
compiled_class.class_hash().with_context(|| "Casm contract Serialization failed.")?;

if name.ends_with("Component.json") {
modules.components.push(Module {
name: name.strip_suffix("Component.json").unwrap().to_string(),
hash,
});
} else if name.ends_with("System.json") {
modules
.systems
.push(Module { name: name.strip_suffix("System.json").unwrap().to_string(), hash });
} else {
modules
.contracts
.push(Module { name: name.strip_suffix(".json").unwrap().to_string(), hash });
};
Ok(modules)
}
}

let mut components_table = Table::new();
components_table.set_header(vec!["Component", "Class Hash"]);
for component in modules.components {
components_table.add_row(vec![component.name, format!("0x{:x} ", component.hash)]);
}
println!("{components_table}\n");
impl Display for Modules {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut components_table = Table::new();
components_table.set_header(vec!["Component", "Class Hash"]);
for component in &self.components {
components_table
.add_row(vec![component.name.clone(), format!("0x{:x} ", component.hash)]);
}
writeln!(f, "{}", components_table)?;

let mut systems_table = Table::new();
systems_table.set_header(vec!["System", "Class Hash"]);
for system in modules.systems {
systems_table.add_row(vec![system.name, format!("0x{:x} ", system.hash)]);
}
println!("{systems_table}");
let mut systems_table = Table::new();
systems_table.set_header(vec!["System", "Class Hash"]);
for system in &self.systems {
systems_table.add_row(vec![system.name.clone(), format!("0x{:x} ", system.hash)]);
}
writeln!(f, "{}", systems_table)?;

Ok(())
Ok(())
}
}
23 changes: 1 addition & 22 deletions crates/dojo-lang/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ use cairo_lang_semantic::patcher::{PatchBuilder, RewriteNode};
use cairo_lang_semantic::plugin::DynPluginAuxData;
use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::{ast, Terminal, TypedSyntaxNode};
use dojo_project::WorldConfig;
use smol_str::SmolStr;

use crate::plugin::{get_contract_address, DojoAuxData};
use crate::plugin::DojoAuxData;

#[cfg(test)]
#[path = "component_test.rs"]
Expand Down Expand Up @@ -205,23 +204,3 @@ pub fn find_components(db: &dyn SemanticGroup, crate_ids: &[CrateId]) -> Vec<Com
}
components
}

pub fn compute_component_id(
db: &dyn SyntaxGroup,
path: ast::ExprPath,
world_config: WorldConfig,
) -> String {
// Component name to felt
let component_name_raw = path.as_syntax_node().get_text(db);
let mut component_name_parts: Vec<&str> = component_name_raw.split("::").collect();
let component_name = component_name_parts.pop().unwrap();

format!(
"{:#x}",
get_contract_address(
component_name,
world_config.initializer_class_hash.unwrap_or_default(),
world_config.address.unwrap_or_default(),
)
)
}
9 changes: 0 additions & 9 deletions crates/dojo-project/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ pub enum DeserializationError {
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct WorldConfig {
pub address: Option<FieldElement>,
pub initializer_class_hash: Option<FieldElement>,
}

pub struct DeploymentConfig {
Expand Down Expand Up @@ -47,14 +46,6 @@ impl WorldConfig {
world_config.address = Some(world_address);
}
}

if let Some(initializer_class_hash) = dojo_metadata.get("initializer_class_hash") {
if let Some(initializer_class_hash) = initializer_class_hash.as_str() {
let initializer_class_hash = FieldElement::from_hex_be(initializer_class_hash)
.map_err(|_| DeserializationError::ParsingFieldElement)?;
world_config.initializer_class_hash = Some(initializer_class_hash);
}
}
}

Ok(world_config)
Expand Down

0 comments on commit cd5c011

Please sign in to comment.