Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(torii): configutation file for all torii cli options #2646

Merged
merged 8 commits into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
27 changes: 27 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions bin/torii/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ torii-grpc = { workspace = true, features = [ "server" ] }
torii-relay.workspace = true
torii-server.workspace = true
tower.workspace = true
toml.workspace = true

tower-http.workspace = true
tracing-subscriber.workspace = true
tracing.workspace = true
url.workspace = true
webbrowser = "0.8"
tempfile.workspace = true
clap_config = "0.1.1"
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

🛠️ Refactor suggestion

Consider using clap directly instead of clap_config, sensei!

The verification reveals:

  1. clap_config is only used in one place (bin/torii/src/main.rs) for a simple skip attribute
  2. The package is relatively new (last updated July 2024) with low adoption (~1.8K total downloads)
  3. The codebase already extensively uses clap directly, as seen in multiple files

The functionality can be replaced with clap's built-in attributes, making the dependency unnecessary:

  • Replace #[clap_config(skip)] with #[arg(skip)]
🔗 Analysis chain

Consider workspace version management and package stability, sensei!

The clap_config dependency has a few concerns:

  1. It's not using workspace version management (.workspace = true) like other dependencies
  2. Version 0.1.1 is quite early and might be unstable
  3. Being a small community package, we should verify its maintenance status

Let's check the package's health:

Consider:

  1. Moving the version to workspace-level management
  2. Evaluating more mature alternatives or waiting for a stable release
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify clap_config package health
# Get package info from crates.io API
curl -s https://crates.io/api/v1/crates/clap_config | jq -r '.crate | {max_version, updated_at, downloads, recent_downloads}'

Length of output: 257


Script:

#!/bin/bash
# Check if clap_config is used in workspace dependencies
rg -U "clap_config.*workspace\s*=\s*true" Cargo.toml

# Check workspace root Cargo.toml for any clap_config version specification
rg "clap_config.*version" Cargo.toml

# Find all usages of clap_config in the codebase
rg "clap_config" -A 2 -B 2

# Check for alternative crates that might be used
rg "clap::" -A 2 -B 2

Length of output: 15413


[dev-dependencies]
camino.workspace = true
Expand Down
75 changes: 24 additions & 51 deletions bin/torii/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
//! for more info.

use std::cmp;
use std::collections::VecDeque;
use std::net::SocketAddr;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;

use anyhow::Context;
use clap::{ArgAction, Parser};
use clap::{ArgAction, CommandFactory, FromArgMatches, Parser};
use clap_config::ClapConfig;
use dojo_metrics::exporters::prometheus::PrometheusRecorder;
use dojo_utils::parse::{parse_socket_address, parse_url};
use dojo_world::contracts::world::WorldContractReader;
Expand All @@ -39,7 +39,7 @@
use torii_core::processors::store_transaction::StoreTransactionProcessor;
use torii_core::simple_broker::SimpleBroker;
use torii_core::sql::Sql;
use torii_core::types::{Contract, ContractType, Model, ToriiConfig};
use torii_core::types::{Contract, ContractType, Model};
use torii_server::proxy::Proxy;
use tracing::{error, info};
use tracing_subscriber::{fmt, EnvFilter};
Expand All @@ -48,12 +48,12 @@
pub(crate) const LOG_TARGET: &str = "torii::cli";

/// Dojo World Indexer
#[derive(Parser, Debug)]
#[derive(ClapConfig, Parser, Debug)]

Check warning on line 51 in bin/torii/src/main.rs

View check run for this annotation

Codecov / codecov/patch

bin/torii/src/main.rs#L51

Added line #L51 was not covered by tests
#[command(name = "torii", author, version, about, long_about = None)]
struct Args {
/// The world to index
#[arg(short, long = "world", env = "DOJO_WORLD_ADDRESS")]
world_address: Option<Felt>,
world_address: Felt,

Check warning on line 56 in bin/torii/src/main.rs

View check run for this annotation

Codecov / codecov/patch

bin/torii/src/main.rs#L56

Added line #L56 was not covered by tests

/// The sequencer rpc endpoint to index.
#[arg(long, value_name = "URL", default_value = ":5050", value_parser = parse_url)]
Expand Down Expand Up @@ -139,33 +139,25 @@
index_raw_events: bool,

/// ERC contract addresses to index
#[arg(long, value_parser = parse_erc_contracts)]
#[arg(conflicts_with = "config")]
contracts: Option<std::vec::Vec<Contract>>,
#[arg(long, value_parser = parse_erc_contracts, default_value = "")]
contracts: Vec<Contract>,

Check warning on line 143 in bin/torii/src/main.rs

View check run for this annotation

Codecov / codecov/patch

bin/torii/src/main.rs#L143

Added line #L143 was not covered by tests

/// Configuration file
#[arg(long)]
#[clap_config(skip)]
config: Option<PathBuf>,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let args = Args::parse();

let mut config = if let Some(path) = args.config {
ToriiConfig::load_from_path(&path)?
let matches = <Args as CommandFactory>::command().get_matches();
let args = if let Some(path) = matches.get_one::<PathBuf>("config") {
let config: ArgsConfig = toml::from_str(&std::fs::read_to_string(path)?)?;
Args::from_merged(matches, Some(config))

Check warning on line 156 in bin/torii/src/main.rs

View check run for this annotation

Codecov / codecov/patch

bin/torii/src/main.rs#L153-L156

Added lines #L153 - L156 were not covered by tests
} else {
let mut config = ToriiConfig::default();

if let Some(contracts) = args.contracts {
config.contracts = VecDeque::from(contracts);
}

config
Args::from_arg_matches(&matches)?

Check warning on line 158 in bin/torii/src/main.rs

View check run for this annotation

Codecov / codecov/patch

bin/torii/src/main.rs#L158

Added line #L158 was not covered by tests
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ohayo sensei! Let's enhance the config file handling!

The config file handling could be more robust:

  1. Add file existence check
  2. Provide more specific error context for TOML parsing

Consider this improvement:

-    let args = if let Some(path) = matches.get_one::<PathBuf>("config") {
-        let config: ArgsConfig = toml::from_str(&std::fs::read_to_string(path)?)?;
-        Args::from_merged(matches, Some(config))
+    let args = if let Some(path) = matches.get_one::<PathBuf>("config") {
+        if !path.exists() {
+            anyhow::bail!("Config file not found: {}", path.display());
+        }
+        let content = std::fs::read_to_string(path)
+            .with_context(|| format!("Failed to read config file: {}", path.display()))?;
+        let config: ArgsConfig = toml::from_str(&content)
+            .with_context(|| format!("Failed to parse TOML config at: {}", path.display()))?;
+        Args::from_merged(matches, Some(config))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let matches = <Args as CommandFactory>::command().get_matches();
let args = if let Some(path) = matches.get_one::<PathBuf>("config") {
let config: ArgsConfig = toml::from_str(&std::fs::read_to_string(path)?)?;
Args::from_merged(matches, Some(config))
} else {
let mut config = ToriiConfig::default();
if let Some(contracts) = args.contracts {
config.contracts = VecDeque::from(contracts);
}
config
Args::from_arg_matches(&matches)?
let matches = <Args as CommandFactory>::command().get_matches();
let args = if let Some(path) = matches.get_one::<PathBuf>("config") {
if !path.exists() {
anyhow::bail!("Config file not found: {}", path.display());
}
let content = std::fs::read_to_string(path)
.with_context(|| format!("Failed to read config file: {}", path.display()))?;
let config: ArgsConfig = toml::from_str(&content)
.with_context(|| format!("Failed to parse TOML config at: {}", path.display()))?;
Args::from_merged(matches, Some(config))
} else {
Args::from_arg_matches(&matches)?

};

let world_address = verify_single_world_address(args.world_address, &mut config)?;

let filter_layer = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info,hyper_reverse_proxy=off"));

Expand Down Expand Up @@ -208,10 +200,9 @@
let provider: Arc<_> = JsonRpcClient::new(HttpTransport::new(args.rpc)).into();

// Get world address
let world = WorldContractReader::new(world_address, provider.clone());
let world = WorldContractReader::new(args.world_address, provider.clone());

Check warning on line 203 in bin/torii/src/main.rs

View check run for this annotation

Codecov / codecov/patch

bin/torii/src/main.rs#L203

Added line #L203 was not covered by tests

let contracts =
config.contracts.iter().map(|contract| (contract.address, contract.r#type)).collect();
let contracts = args.contracts.iter().map(|contract| (contract.address, contract.r#type)).collect();

Check warning on line 205 in bin/torii/src/main.rs

View check run for this annotation

Codecov / codecov/patch

bin/torii/src/main.rs#L205

Added line #L205 was not covered by tests

let (mut executor, sender) = Executor::new(pool.clone(), shutdown_tx.clone()).await?;
tokio::spawn(async move {
Expand Down Expand Up @@ -256,7 +247,7 @@

let shutdown_rx = shutdown_tx.subscribe();
let (grpc_addr, grpc_server) =
torii_grpc::server::new(shutdown_rx, &pool, block_rx, world_address, Arc::clone(&provider))
torii_grpc::server::new(shutdown_rx, &pool, block_rx, args.world_address, Arc::clone(&provider))

Check warning on line 250 in bin/torii/src/main.rs

View check run for this annotation

Codecov / codecov/patch

bin/torii/src/main.rs#L250

Added line #L250 was not covered by tests
.await?;

let mut libp2p_relay_server = torii_relay::server::Relay::new(
Expand Down Expand Up @@ -321,26 +312,6 @@
Ok(())
}

// Verifies that the world address is defined at most once
// and returns the world address
fn verify_single_world_address(
world_address: Option<Felt>,
config: &mut ToriiConfig,
) -> anyhow::Result<Felt> {
let world_from_config =
config.contracts.iter().find(|c| c.r#type == ContractType::WORLD).map(|c| c.address);

match (world_address, world_from_config) {
(Some(_), Some(_)) => Err(anyhow::anyhow!("World address specified multiple times")),
(Some(addr), _) => {
config.contracts.push_front(Contract { address: addr, r#type: ContractType::WORLD });
Ok(addr)
}
(_, Some(addr)) => Ok(addr),
(None, None) => Err(anyhow::anyhow!("World address not specified")),
}
}

async fn spawn_rebuilding_graphql_server(
shutdown_tx: Sender<()>,
pool: Arc<SqlitePool>,
Expand Down Expand Up @@ -369,18 +340,20 @@
// - erc_type:address:start_block
// - address:start_block (erc_type defaults to ERC20)
fn parse_erc_contracts(s: &str) -> anyhow::Result<Vec<Contract>> {
if s.is_empty() {
return Ok(Vec::new());
}

Check warning on line 346 in bin/torii/src/main.rs

View check run for this annotation

Codecov / codecov/patch

bin/torii/src/main.rs#L343-L346

Added lines #L343 - L346 were not covered by tests
let parts: Vec<&str> = s.split(',').collect();
let mut contracts = Vec::new();
for part in parts {
match part.split(':').collect::<Vec<&str>>().as_slice() {
[r#type, address] => {
let r#type = r#type.parse::<ContractType>()?;
let address = Felt::from_str(address)
.with_context(|| format!("Expected address, found {}", address))?;
contracts.push(Contract { address, r#type });
}
[address] => {
let r#type = ContractType::WORLD;
if r#type == ContractType::WORLD {
return Err(anyhow::anyhow!("World address cannot be specified as an ERC contract"));
}

Check warning on line 355 in bin/torii/src/main.rs

View check run for this annotation

Codecov / codecov/patch

bin/torii/src/main.rs#L353-L355

Added lines #L353 - L355 were not covered by tests

let address = Felt::from_str(address)
.with_context(|| format!("Expected address, found {}", address))?;
contracts.push(Contract { address, r#type });
Expand Down
21 changes: 2 additions & 19 deletions crates/torii/core/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use core::fmt;
use std::collections::VecDeque;
use std::path::PathBuf;
use std::str::FromStr;

use chrono::{DateTime, Utc};
Expand Down Expand Up @@ -123,28 +121,13 @@
pub executed_at: DateTime<Utc>,
pub created_at: DateTime<Utc>,
}

#[derive(Default, Deserialize, Debug, Clone)]
pub struct ToriiConfig {
/// contract addresses to index
pub contracts: VecDeque<Contract>,
}

impl ToriiConfig {
pub fn load_from_path(path: &PathBuf) -> Result<Self, anyhow::Error> {
let config = std::fs::read_to_string(path)?;
let config: Self = toml::from_str(&config)?;
Ok(config)
}
}

#[derive(Deserialize, Debug, Clone, Copy)]
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]

Check warning on line 124 in crates/torii/core/src/types.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/core/src/types.rs#L124

Added line #L124 was not covered by tests
pub struct Contract {
pub address: Felt,
pub r#type: ContractType,
}

#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]

Check warning on line 130 in crates/torii/core/src/types.rs

View check run for this annotation

Codecov / codecov/patch

crates/torii/core/src/types.rs#L130

Added line #L130 was not covered by tests
pub enum ContractType {
WORLD,
ERC20,
Expand Down
Loading