From 0085e81e240022a03df44e9f34468fc6737331c7 Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Tue, 11 Jun 2024 11:25:07 -0700 Subject: [PATCH 1/6] Extract alias logic into its own implementation. --- .../src/commands/contract/deploy/wasm.rs | 57 ++-------- .../src/commands/contract/invoke.rs | 53 ++------- cmd/soroban-cli/src/commands/contract/mod.rs | 106 +++++++++++++++++- 3 files changed, 122 insertions(+), 94 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index ff56c0e50..a5f58d437 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -1,9 +1,6 @@ +use std::array::TryFromSliceError; use std::fmt::Debug; -use std::fs::{self, create_dir_all}; -use std::io::Write; use std::num::ParseIntError; -use std::path::PathBuf; -use std::{array::TryFromSliceError, fs::OpenOptions}; use clap::{arg, command, Parser}; use rand::Rng; @@ -18,7 +15,7 @@ use soroban_env_host::{ HostError, }; -use crate::commands::contract::AliasData; +use crate::commands::contract::{AliasData, AliasDataError}; use crate::commands::{ config::data, contract::{self, id::wasm::get_contract_id}, @@ -108,16 +105,12 @@ pub enum Error { Network(#[from] network::Error), #[error(transparent)] Wasm(#[from] wasm::Error), - #[error("cannot access config dir for alias file")] - CannotAccessConfigDir, #[error( "alias must be 1-30 chars long, and have only letters, numbers, underscores and dashes" )] InvalidAliasFormat { alias: String }, #[error(transparent)] - JsonSerialization(#[from] serde_json::Error), - #[error(transparent)] - Io(#[from] std::io::Error), + AliasData(#[from] AliasDataError), } impl Cmd { @@ -128,7 +121,13 @@ impl Cmd { match res { TxnEnvelopeResult::TxnEnvelope(tx) => println!("{}", tx.to_xdr_base64(Limits::none())?), TxnEnvelopeResult::Res(contract) => { - self.save_contract_id(&contract)?; + AliasData::save_contract_id( + &self.config.config_dir()?, + &contract, + self.alias.as_ref(), + &self.config.get_network()?.network_passphrase, + )?; + println!("{contract}"); } } @@ -149,42 +148,6 @@ impl Cmd { None => Ok(()), } } - - fn alias_path_for(&self, alias: &str) -> Result { - let config_dir = self.config.config_dir()?; - let file_name = format!("{alias}.json"); - - Ok(config_dir.join("contract-ids").join(file_name)) - } - - fn save_contract_id(&self, contract: &String) -> Result<(), Error> { - let Some(alias) = &self.alias else { - return Ok(()); - }; - - let file_path = self.alias_path_for(alias)?; - let dir = file_path.parent().ok_or(Error::CannotAccessConfigDir)?; - - create_dir_all(dir).map_err(|_| Error::CannotAccessConfigDir)?; - - let content = fs::read_to_string(&file_path).unwrap_or_default(); - let mut data: AliasData = serde_json::from_str(&content).unwrap_or_default(); - - let mut to_file = OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .open(file_path)?; - - data.ids.insert( - self.config.get_network()?.network_passphrase, - contract.into(), - ); - - let content = serde_json::to_string(&data)?; - - Ok(to_file.write_all(content.as_bytes())?) - } } #[async_trait::async_trait] diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 712c73c51..e41374cc0 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -25,13 +25,12 @@ use soroban_env_host::xdr::{ AccountEntry, AccountEntryExt, AccountId, DiagnosticEvent, Thresholds, }; use soroban_spec::read::FromWasmError; -use stellar_strkey::DecodeError; use super::super::{ config::{self, locator}, events, }; -use super::AliasData; +use super::{AliasData, AliasDataError}; use crate::commands::txn_result::{TxnEnvelopeResult, TxnResult}; use crate::commands::NetworkRunnable; use crate::get_spec::{self, get_remote_contract_spec}; @@ -98,8 +97,6 @@ pub enum Error { filepath: std::path::PathBuf, error: events::Error, }, - #[error("cannot parse contract ID {0}: {1}")] - CannotParseContractId(String, DecodeError), #[error("function {0} was not found in the contract")] FunctionNotFoundInContractSpec(String), #[error("parsing contract spec: {0}")] @@ -153,12 +150,8 @@ pub enum Error { Network(#[from] network::Error), #[error(transparent)] GetSpecError(#[from] get_spec::Error), - #[error("unable to read alias file")] - UnableToReadAliasFile, - #[error("alias file not found")] - NoAliasFileFound, #[error(transparent)] - JsonDeserialization(#[from] serde_json::Error), + AliasData(#[from] AliasDataError), } impl From for Error { @@ -310,42 +303,6 @@ impl Cmd { } } -impl Cmd { - fn contract_id(&self) -> Result<[u8; 32], Error> { - let contract_id: String = match self.load_contract_id() { - Ok(Some(id)) => id.to_string(), - _ => self.contract_id.clone(), - }; - - soroban_spec_tools::utils::contract_id_from_str(&contract_id) - .map_err(|e| Error::CannotParseContractId(contract_id.clone(), e)) - } - - fn alias_path(&self) -> Result { - let config_dir = self.config.config_dir()?; - let file_name = format!("{}.json", self.contract_id); - - Ok(config_dir.join("contract-ids").join(file_name)) - } - - fn load_contract_id(&self) -> Result, Error> { - let network = &self.config.get_network()?.network_passphrase; - let file_path = self.alias_path()?; - - if !file_path.exists() { - return Ok(None); - } - - let content = fs::read_to_string(file_path)?; - let data: AliasData = serde_json::from_str(&content)?; - - match data.ids.get(network) { - Some(id) => Ok(Some(id.into())), - _ => Ok(None), - } - } -} - #[async_trait::async_trait] impl NetworkRunnable for Cmd { type Error = Error; @@ -359,7 +316,11 @@ impl NetworkRunnable for Cmd { let unwrap_config = config.unwrap_or(&self.config); let network = unwrap_config.get_network()?; tracing::trace!(?network); - let contract_id = self.contract_id()?; + let contract_id = AliasData::load_contract_id_or_default( + &self.contract_id, + &self.config.config_dir()?, + &network.network_passphrase, + )?; let spec_entries = self.spec_entries()?; if let Some(spec_entries) = &spec_entries { // For testing wasm arg parsing diff --git a/cmd/soroban-cli/src/commands/contract/mod.rs b/cmd/soroban-cli/src/commands/contract/mod.rs index 9da55714c..cb302a410 100644 --- a/cmd/soroban-cli/src/commands/contract/mod.rs +++ b/cmd/soroban-cli/src/commands/contract/mod.rs @@ -13,9 +13,15 @@ pub mod optimize; pub mod read; pub mod restore; -use std::collections::HashMap; +use std::{ + collections::HashMap, + fs::{self, create_dir_all, OpenOptions}, + io::Write, + path::{Path, PathBuf}, +}; use serde::{Deserialize, Serialize}; +use stellar_strkey::DecodeError; use crate::commands::global; @@ -174,3 +180,101 @@ pub enum SpecOutput { pub struct AliasData { ids: HashMap, } + +#[derive(thiserror::Error, Debug)] +pub enum AliasDataError { + #[error(transparent)] + Json(#[from] serde_json::Error), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error("cannot access config dir for alias file")] + CannotAccessConfigDir, + #[error("cannot parse contract ID {0}: {1}")] + CannotParseContractId(String, DecodeError), +} + +impl AliasData { + pub fn load(config_dir: &Path, alias: Option<&str>) -> Result, AliasDataError> { + let Some(alias) = alias else { + return Ok(None); + }; + + let path = Self::alias_path(config_dir, alias); + + if !path.exists() { + return Ok(None); + } + + let content = fs::read_to_string(path)?; + let data: Self = serde_json::from_str(&content).unwrap_or_default(); + + Ok(Some(data)) + } + + pub fn alias_path(config_dir: &Path, alias: &str) -> PathBuf { + let file_name = format!("{alias}.json"); + config_dir.join("contract-ids").join(file_name) + } + + pub fn save_contract_id( + config_dir: &Path, + contract_id: &str, + alias: Option<&String>, + network_passphrase: &str, + ) -> Result<(), AliasDataError> { + let Some(alias) = alias else { + return Ok(()); + }; + + let path = Self::alias_path(config_dir, alias); + let dir = path.parent().ok_or(AliasDataError::CannotAccessConfigDir)?; + + create_dir_all(dir).map_err(|_| AliasDataError::CannotAccessConfigDir)?; + + let content = fs::read_to_string(&path).unwrap_or_default(); + let mut data: Self = serde_json::from_str(&content).unwrap_or_default(); + + let mut to_file = OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open(path)?; + + data.ids + .insert(network_passphrase.into(), contract_id.into()); + + let content = serde_json::to_string(&data)?; + + Ok(to_file.write_all(content.as_bytes())?) + } + + pub fn get_contract_id( + alias: &str, + config_dir: &Path, + network_passphrase: &str, + ) -> Result, AliasDataError> { + let alias_data = Self::load(config_dir, Some(alias))?; + let Some(alias_data) = alias_data else { + return Ok(None); + }; + + match alias_data.ids.get(network_passphrase) { + Some(id) => Ok(Some(id.into())), + _ => Ok(None), + } + } + + pub fn load_contract_id_or_default( + alias: &str, + config_dir: &Path, + network_passphrase: &str, + ) -> Result<[u8; 32], AliasDataError> { + let contract_id = match Self::get_contract_id(alias, config_dir, network_passphrase)? { + None => alias.to_string(), + Some(id) => id, + }; + + soroban_spec_tools::utils::contract_id_from_str(&contract_id) + .map_err(|e| AliasDataError::CannotParseContractId(contract_id.clone(), e)) + } +} From 16a70ba11d2d563c48730047831ac3180d950ace Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Wed, 12 Jun 2024 10:16:24 -0700 Subject: [PATCH 2/6] Apply pr feedback. --- cmd/soroban-cli/src/commands/config/alias.rs | 106 ++++++++++++++++ cmd/soroban-cli/src/commands/config/mod.rs | 1 + .../src/commands/contract/deploy/wasm.rs | 7 +- .../src/commands/contract/invoke.rs | 6 +- cmd/soroban-cli/src/commands/contract/mod.rs | 113 ------------------ 5 files changed, 113 insertions(+), 120 deletions(-) create mode 100644 cmd/soroban-cli/src/commands/config/alias.rs diff --git a/cmd/soroban-cli/src/commands/config/alias.rs b/cmd/soroban-cli/src/commands/config/alias.rs new file mode 100644 index 000000000..13ba2f2ee --- /dev/null +++ b/cmd/soroban-cli/src/commands/config/alias.rs @@ -0,0 +1,106 @@ +use std::{ + collections::HashMap, + fs::{self, create_dir_all, OpenOptions}, + io::Write, + path::{Path, PathBuf}, +}; + +use serde::{Deserialize, Serialize}; +use stellar_strkey::DecodeError; + +#[derive(Serialize, Deserialize, Default)] +pub struct Data { + ids: HashMap, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Json(#[from] serde_json::Error), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error("cannot access config dir for alias file")] + CannotAccessConfigDir, + #[error("cannot parse contract ID {0}: {1}")] + CannotParseContractId(String, DecodeError), +} + +impl Data { + pub fn load(config_dir: &Path, alias: Option<&str>) -> Result, Error> { + let Some(alias) = alias else { + return Ok(None); + }; + + let path = Self::alias_path(config_dir, alias); + + if !path.exists() { + return Ok(None); + } + + let content = fs::read_to_string(path)?; + let data: Self = serde_json::from_str(&content).unwrap_or_default(); + + Ok(Some(data)) + } + + pub fn alias_path(config_dir: &Path, alias: &str) -> PathBuf { + let file_name = format!("{alias}.json"); + config_dir.join("contract-ids").join(file_name) + } + + pub fn save_contract_id( + config_dir: &Path, + contract_id: &str, + alias: Option<&String>, + network_passphrase: &str, + ) -> Result<(), Error> { + let Some(alias) = alias else { + return Ok(()); + }; + + let path = Self::alias_path(config_dir, alias); + let dir = path.parent().ok_or(Error::CannotAccessConfigDir)?; + + create_dir_all(dir).map_err(|_| Error::CannotAccessConfigDir)?; + + let content = fs::read_to_string(&path).unwrap_or_default(); + let mut data: Self = serde_json::from_str(&content).unwrap_or_default(); + + let mut to_file = OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open(path)?; + + data.ids + .insert(network_passphrase.into(), contract_id.into()); + + let content = serde_json::to_string(&data)?; + + Ok(to_file.write_all(content.as_bytes())?) + } + + pub fn get_contract_id( + alias: &str, + config_dir: &Path, + network_passphrase: &str, + ) -> Result, Error> { + let Some(alias_data) = Self::load(config_dir, Some(alias))? else { + return Ok(None); + }; + + Ok(alias_data.ids.get(network_passphrase).cloned()) + } + + pub fn load_contract_id_or_default( + alias: &str, + config_dir: &Path, + network_passphrase: &str, + ) -> Result<[u8; 32], Error> { + let contract_id = Self::get_contract_id(alias, config_dir, network_passphrase)? + .unwrap_or_else(|| alias.to_string()); + + soroban_spec_tools::utils::contract_id_from_str(&contract_id) + .map_err(|e| Error::CannotParseContractId(contract_id.clone(), e)) + } +} diff --git a/cmd/soroban-cli/src/commands/config/mod.rs b/cmd/soroban-cli/src/commands/config/mod.rs index e80aea73c..dcf76aabc 100644 --- a/cmd/soroban-cli/src/commands/config/mod.rs +++ b/cmd/soroban-cli/src/commands/config/mod.rs @@ -9,6 +9,7 @@ use self::{network::Network, secret::Secret}; use super::{keys, network}; +pub mod alias; pub mod data; pub mod locator; pub mod secret; diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index a5f58d437..e3888c71c 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -15,9 +15,8 @@ use soroban_env_host::{ HostError, }; -use crate::commands::contract::{AliasData, AliasDataError}; use crate::commands::{ - config::data, + config::{alias, data}, contract::{self, id::wasm::get_contract_id}, global, network, txn_result::{TxnEnvelopeResult, TxnResult}, @@ -110,7 +109,7 @@ pub enum Error { )] InvalidAliasFormat { alias: String }, #[error(transparent)] - AliasData(#[from] AliasDataError), + Alias(#[from] alias::Error), } impl Cmd { @@ -121,7 +120,7 @@ impl Cmd { match res { TxnEnvelopeResult::TxnEnvelope(tx) => println!("{}", tx.to_xdr_base64(Limits::none())?), TxnEnvelopeResult::Res(contract) => { - AliasData::save_contract_id( + alias::Data::save_contract_id( &self.config.config_dir()?, &contract, self.alias.as_ref(), diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index e41374cc0..579c46129 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -30,7 +30,7 @@ use super::super::{ config::{self, locator}, events, }; -use super::{AliasData, AliasDataError}; +use crate::commands::config::alias; use crate::commands::txn_result::{TxnEnvelopeResult, TxnResult}; use crate::commands::NetworkRunnable; use crate::get_spec::{self, get_remote_contract_spec}; @@ -151,7 +151,7 @@ pub enum Error { #[error(transparent)] GetSpecError(#[from] get_spec::Error), #[error(transparent)] - AliasData(#[from] AliasDataError), + Alias(#[from] alias::Error), } impl From for Error { @@ -316,7 +316,7 @@ impl NetworkRunnable for Cmd { let unwrap_config = config.unwrap_or(&self.config); let network = unwrap_config.get_network()?; tracing::trace!(?network); - let contract_id = AliasData::load_contract_id_or_default( + let contract_id = alias::Data::load_contract_id_or_default( &self.contract_id, &self.config.config_dir()?, &network.network_passphrase, diff --git a/cmd/soroban-cli/src/commands/contract/mod.rs b/cmd/soroban-cli/src/commands/contract/mod.rs index cb302a410..66541a546 100644 --- a/cmd/soroban-cli/src/commands/contract/mod.rs +++ b/cmd/soroban-cli/src/commands/contract/mod.rs @@ -13,16 +13,6 @@ pub mod optimize; pub mod read; pub mod restore; -use std::{ - collections::HashMap, - fs::{self, create_dir_all, OpenOptions}, - io::Write, - path::{Path, PathBuf}, -}; - -use serde::{Deserialize, Serialize}; -use stellar_strkey::DecodeError; - use crate::commands::global; #[derive(Debug, clap::Subcommand)] @@ -175,106 +165,3 @@ pub enum SpecOutput { /// Pretty print of contract spec entries Docs, } - -#[derive(Serialize, Deserialize, Default)] -pub struct AliasData { - ids: HashMap, -} - -#[derive(thiserror::Error, Debug)] -pub enum AliasDataError { - #[error(transparent)] - Json(#[from] serde_json::Error), - #[error(transparent)] - Io(#[from] std::io::Error), - #[error("cannot access config dir for alias file")] - CannotAccessConfigDir, - #[error("cannot parse contract ID {0}: {1}")] - CannotParseContractId(String, DecodeError), -} - -impl AliasData { - pub fn load(config_dir: &Path, alias: Option<&str>) -> Result, AliasDataError> { - let Some(alias) = alias else { - return Ok(None); - }; - - let path = Self::alias_path(config_dir, alias); - - if !path.exists() { - return Ok(None); - } - - let content = fs::read_to_string(path)?; - let data: Self = serde_json::from_str(&content).unwrap_or_default(); - - Ok(Some(data)) - } - - pub fn alias_path(config_dir: &Path, alias: &str) -> PathBuf { - let file_name = format!("{alias}.json"); - config_dir.join("contract-ids").join(file_name) - } - - pub fn save_contract_id( - config_dir: &Path, - contract_id: &str, - alias: Option<&String>, - network_passphrase: &str, - ) -> Result<(), AliasDataError> { - let Some(alias) = alias else { - return Ok(()); - }; - - let path = Self::alias_path(config_dir, alias); - let dir = path.parent().ok_or(AliasDataError::CannotAccessConfigDir)?; - - create_dir_all(dir).map_err(|_| AliasDataError::CannotAccessConfigDir)?; - - let content = fs::read_to_string(&path).unwrap_or_default(); - let mut data: Self = serde_json::from_str(&content).unwrap_or_default(); - - let mut to_file = OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .open(path)?; - - data.ids - .insert(network_passphrase.into(), contract_id.into()); - - let content = serde_json::to_string(&data)?; - - Ok(to_file.write_all(content.as_bytes())?) - } - - pub fn get_contract_id( - alias: &str, - config_dir: &Path, - network_passphrase: &str, - ) -> Result, AliasDataError> { - let alias_data = Self::load(config_dir, Some(alias))?; - let Some(alias_data) = alias_data else { - return Ok(None); - }; - - match alias_data.ids.get(network_passphrase) { - Some(id) => Ok(Some(id.into())), - _ => Ok(None), - } - } - - pub fn load_contract_id_or_default( - alias: &str, - config_dir: &Path, - network_passphrase: &str, - ) -> Result<[u8; 32], AliasDataError> { - let contract_id = match Self::get_contract_id(alias, config_dir, network_passphrase)? { - None => alias.to_string(), - Some(id) => id, - }; - - soroban_spec_tools::utils::contract_id_from_str(&contract_id) - .map_err(|e| AliasDataError::CannotParseContractId(contract_id.clone(), e)) - } -} From cf0d8e84fa42c713e134ae3e354f998ee0281361 Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Wed, 12 Jun 2024 10:33:00 -0700 Subject: [PATCH 3/6] Name the argument explicitly. --- cmd/soroban-cli/src/commands/config/alias.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/soroban-cli/src/commands/config/alias.rs b/cmd/soroban-cli/src/commands/config/alias.rs index 13ba2f2ee..5de4d912e 100644 --- a/cmd/soroban-cli/src/commands/config/alias.rs +++ b/cmd/soroban-cli/src/commands/config/alias.rs @@ -93,12 +93,13 @@ impl Data { } pub fn load_contract_id_or_default( - alias: &str, + alias_or_contract_id: &str, config_dir: &Path, network_passphrase: &str, ) -> Result<[u8; 32], Error> { - let contract_id = Self::get_contract_id(alias, config_dir, network_passphrase)? - .unwrap_or_else(|| alias.to_string()); + let contract_id = + Self::get_contract_id(alias_or_contract_id, config_dir, network_passphrase)? + .unwrap_or_else(|| alias_or_contract_id.to_string()); soroban_spec_tools::utils::contract_id_from_str(&contract_id) .map_err(|e| Error::CannotParseContractId(contract_id.clone(), e)) From b2edc3ad911ef07d026d71d75b3a2bcb9fe6a1cd Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Thu, 13 Jun 2024 11:24:39 -0700 Subject: [PATCH 4/6] Do not use an Option on Data::load(). --- cmd/soroban-cli/src/commands/config/alias.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cmd/soroban-cli/src/commands/config/alias.rs b/cmd/soroban-cli/src/commands/config/alias.rs index 5de4d912e..b09677248 100644 --- a/cmd/soroban-cli/src/commands/config/alias.rs +++ b/cmd/soroban-cli/src/commands/config/alias.rs @@ -26,11 +26,7 @@ pub enum Error { } impl Data { - pub fn load(config_dir: &Path, alias: Option<&str>) -> Result, Error> { - let Some(alias) = alias else { - return Ok(None); - }; - + pub fn load(config_dir: &Path, alias: &str) -> Result, Error> { let path = Self::alias_path(config_dir, alias); if !path.exists() { @@ -85,7 +81,7 @@ impl Data { config_dir: &Path, network_passphrase: &str, ) -> Result, Error> { - let Some(alias_data) = Self::load(config_dir, Some(alias))? else { + let Some(alias_data) = Self::load(config_dir, alias)? else { return Ok(None); }; From 5d3da77b349d0dcf9e86cae07be872fb1a0ab718 Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Thu, 13 Jun 2024 11:46:12 -0700 Subject: [PATCH 5/6] Move functions to config instead. --- cmd/soroban-cli/src/commands/config/alias.rs | 50 ++++++++++--------- .../src/commands/contract/deploy/wasm.rs | 8 +-- .../src/commands/contract/invoke.rs | 8 ++- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/cmd/soroban-cli/src/commands/config/alias.rs b/cmd/soroban-cli/src/commands/config/alias.rs index b09677248..f2436bc72 100644 --- a/cmd/soroban-cli/src/commands/config/alias.rs +++ b/cmd/soroban-cli/src/commands/config/alias.rs @@ -2,12 +2,16 @@ use std::{ collections::HashMap, fs::{self, create_dir_all, OpenOptions}, io::Write, - path::{Path, PathBuf}, + path::PathBuf, }; use serde::{Deserialize, Serialize}; use stellar_strkey::DecodeError; +use crate::commands::config; + +use super::Args; + #[derive(Serialize, Deserialize, Default)] pub struct Data { ids: HashMap, @@ -23,44 +27,42 @@ pub enum Error { CannotAccessConfigDir, #[error("cannot parse contract ID {0}: {1}")] CannotParseContractId(String, DecodeError), + #[error(transparent)] + Config(#[from] config::Error), } -impl Data { - pub fn load(config_dir: &Path, alias: &str) -> Result, Error> { - let path = Self::alias_path(config_dir, alias); +impl Args { + pub fn load(&self, alias: &str) -> Result, Error> { + let path = self.alias_path(alias)?; if !path.exists() { return Ok(None); } let content = fs::read_to_string(path)?; - let data: Self = serde_json::from_str(&content).unwrap_or_default(); + let data: Data = serde_json::from_str(&content).unwrap_or_default(); Ok(Some(data)) } - pub fn alias_path(config_dir: &Path, alias: &str) -> PathBuf { + fn alias_path(&self, alias: &str) -> Result { let file_name = format!("{alias}.json"); - config_dir.join("contract-ids").join(file_name) + let config_dir = self.config_dir()?; + Ok(config_dir.join("contract-ids").join(file_name)) } - pub fn save_contract_id( - config_dir: &Path, - contract_id: &str, - alias: Option<&String>, - network_passphrase: &str, - ) -> Result<(), Error> { + pub fn save_contract_id(&self, contract_id: &str, alias: Option<&String>) -> Result<(), Error> { let Some(alias) = alias else { return Ok(()); }; - let path = Self::alias_path(config_dir, alias); + let path = self.alias_path(alias)?; let dir = path.parent().ok_or(Error::CannotAccessConfigDir)?; create_dir_all(dir).map_err(|_| Error::CannotAccessConfigDir)?; let content = fs::read_to_string(&path).unwrap_or_default(); - let mut data: Self = serde_json::from_str(&content).unwrap_or_default(); + let mut data: Data = serde_json::from_str(&content).unwrap_or_default(); let mut to_file = OpenOptions::new() .create(true) @@ -68,8 +70,10 @@ impl Data { .write(true) .open(path)?; + let network = self.get_network()?; + data.ids - .insert(network_passphrase.into(), contract_id.into()); + .insert(network.network_passphrase, contract_id.into()); let content = serde_json::to_string(&data)?; @@ -77,25 +81,25 @@ impl Data { } pub fn get_contract_id( + &self, alias: &str, - config_dir: &Path, network_passphrase: &str, ) -> Result, Error> { - let Some(alias_data) = Self::load(config_dir, alias)? else { + let Some(alias_data) = self.load(alias)? else { return Ok(None); }; Ok(alias_data.ids.get(network_passphrase).cloned()) } - pub fn load_contract_id_or_default( + pub fn resolve_contract_id( + &self, alias_or_contract_id: &str, - config_dir: &Path, network_passphrase: &str, ) -> Result<[u8; 32], Error> { - let contract_id = - Self::get_contract_id(alias_or_contract_id, config_dir, network_passphrase)? - .unwrap_or_else(|| alias_or_contract_id.to_string()); + let contract_id = self + .get_contract_id(alias_or_contract_id, network_passphrase)? + .unwrap_or_else(|| alias_or_contract_id.to_string()); soroban_spec_tools::utils::contract_id_from_str(&contract_id) .map_err(|e| Error::CannotParseContractId(contract_id.clone(), e)) diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index e3888c71c..989a594a6 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -120,12 +120,8 @@ impl Cmd { match res { TxnEnvelopeResult::TxnEnvelope(tx) => println!("{}", tx.to_xdr_base64(Limits::none())?), TxnEnvelopeResult::Res(contract) => { - alias::Data::save_contract_id( - &self.config.config_dir()?, - &contract, - self.alias.as_ref(), - &self.config.get_network()?.network_passphrase, - )?; + self.config + .save_contract_id(&contract, self.alias.as_ref())?; println!("{contract}"); } diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 579c46129..99163f064 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -316,11 +316,9 @@ impl NetworkRunnable for Cmd { let unwrap_config = config.unwrap_or(&self.config); let network = unwrap_config.get_network()?; tracing::trace!(?network); - let contract_id = alias::Data::load_contract_id_or_default( - &self.contract_id, - &self.config.config_dir()?, - &network.network_passphrase, - )?; + let contract_id = self + .config + .resolve_contract_id(&self.contract_id, &network.network_passphrase)?; let spec_entries = self.spec_entries()?; if let Some(spec_entries) = &spec_entries { // For testing wasm arg parsing From 134846769b2ba27dbbf951bb11bb45f410789868 Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Fri, 14 Jun 2024 09:38:33 -0700 Subject: [PATCH 6/6] Remove guard from function and bring it to the caller. --- cmd/soroban-cli/src/commands/config/alias.rs | 8 ++------ cmd/soroban-cli/src/commands/contract/deploy/wasm.rs | 5 +++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/cmd/soroban-cli/src/commands/config/alias.rs b/cmd/soroban-cli/src/commands/config/alias.rs index f2436bc72..ca17c411e 100644 --- a/cmd/soroban-cli/src/commands/config/alias.rs +++ b/cmd/soroban-cli/src/commands/config/alias.rs @@ -32,7 +32,7 @@ pub enum Error { } impl Args { - pub fn load(&self, alias: &str) -> Result, Error> { + fn load(&self, alias: &str) -> Result, Error> { let path = self.alias_path(alias)?; if !path.exists() { @@ -51,11 +51,7 @@ impl Args { Ok(config_dir.join("contract-ids").join(file_name)) } - pub fn save_contract_id(&self, contract_id: &str, alias: Option<&String>) -> Result<(), Error> { - let Some(alias) = alias else { - return Ok(()); - }; - + pub fn save_contract_id(&self, contract_id: &str, alias: &str) -> Result<(), Error> { let path = self.alias_path(alias)?; let dir = path.parent().ok_or(Error::CannotAccessConfigDir)?; diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 989a594a6..2f85268b4 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -120,8 +120,9 @@ impl Cmd { match res { TxnEnvelopeResult::TxnEnvelope(tx) => println!("{}", tx.to_xdr_base64(Limits::none())?), TxnEnvelopeResult::Res(contract) => { - self.config - .save_contract_id(&contract, self.alias.as_ref())?; + if let Some(alias) = self.alias.clone() { + self.config.save_contract_id(&contract, &alias)?; + } println!("{contract}"); }