Skip to content

Commit

Permalink
Move alias supporting code to locator. (#1401)
Browse files Browse the repository at this point in the history
  • Loading branch information
fnando authored Jun 24, 2024
1 parent 85ec1ee commit 257c236
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 105 deletions.
99 changes: 2 additions & 97 deletions cmd/soroban-cli/src/commands/config/alias.rs
Original file line number Diff line number Diff line change
@@ -1,103 +1,8 @@
use std::{
collections::HashMap,
fs::{self, create_dir_all, OpenOptions},
io::Write,
path::PathBuf,
};
use std::collections::HashMap;

use serde::{Deserialize, Serialize};
use stellar_strkey::DecodeError;

use crate::commands::config;

use super::Args;

#[derive(Serialize, Deserialize, Default)]
pub struct Data {
ids: HashMap<String, String>,
}

#[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),
#[error(transparent)]
Config(#[from] config::Error),
}

impl Args {
fn load(&self, alias: &str) -> Result<Option<Data>, Error> {
let path = self.alias_path(alias)?;

if !path.exists() {
return Ok(None);
}

let content = fs::read_to_string(path)?;
let data: Data = serde_json::from_str(&content).unwrap_or_default();

Ok(Some(data))
}

fn alias_path(&self, alias: &str) -> Result<PathBuf, Error> {
let file_name = format!("{alias}.json");
let config_dir = self.config_dir()?;
Ok(config_dir.join("contract-ids").join(file_name))
}

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)?;

create_dir_all(dir).map_err(|_| Error::CannotAccessConfigDir)?;

let content = fs::read_to_string(&path).unwrap_or_default();
let mut data: Data = serde_json::from_str(&content).unwrap_or_default();

let mut to_file = OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(path)?;

let network = self.get_network()?;

data.ids
.insert(network.network_passphrase, contract_id.into());

let content = serde_json::to_string(&data)?;

Ok(to_file.write_all(content.as_bytes())?)
}

pub fn get_contract_id(
&self,
alias: &str,
network_passphrase: &str,
) -> Result<Option<String>, Error> {
let Some(alias_data) = self.load(alias)? else {
return Ok(None);
};

Ok(alias_data.ids.get(network_passphrase).cloned())
}

pub fn resolve_contract_id(
&self,
alias_or_contract_id: &str,
network_passphrase: &str,
) -> Result<[u8; 32], Error> {
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))
}
pub ids: HashMap<String, String>,
}
85 changes: 83 additions & 2 deletions cmd/soroban-cli/src/commands/config/locator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ use serde::de::DeserializeOwned;
use std::{
ffi::OsStr,
fmt::Display,
fs, io,
fs::{self, create_dir_all, OpenOptions},
io,
io::Write,
path::{Path, PathBuf},
str::FromStr,
};
use stellar_strkey::DecodeError;

use crate::{utils::find_config_dir, Pwd};

use super::{network::Network, secret::Secret};
use super::{alias, network::Network, secret::Secret};

#[derive(thiserror::Error, Debug)]
pub enum Error {
Expand Down Expand Up @@ -56,6 +59,12 @@ pub enum Error {
String(#[from] std::string::FromUtf8Error),
#[error(transparent)]
Secret(#[from] crate::commands::config::secret::Error),
#[error(transparent)]
Json(#[from] serde_json::Error),
#[error("cannot access config dir for alias file")]
CannotAccessConfigDir,
#[error("cannot parse contract ID {0}: {1}")]
CannotParseContractId(String, DecodeError),
}

#[derive(Debug, clap::Args, Default, Clone)]
Expand Down Expand Up @@ -211,6 +220,78 @@ impl Args {
pub fn remove_network(&self, name: &str) -> Result<(), Error> {
KeyType::Network.remove(name, &self.config_dir()?)
}

fn load_contract_from_alias(&self, alias: &str) -> Result<Option<alias::Data>, Error> {
let path = self.alias_path(alias)?;

if !path.exists() {
return Ok(None);
}

let content = fs::read_to_string(path)?;
let data: alias::Data = serde_json::from_str(&content).unwrap_or_default();

Ok(Some(data))
}

fn alias_path(&self, alias: &str) -> Result<PathBuf, Error> {
let file_name = format!("{alias}.json");
let config_dir = self.config_dir()?;
Ok(config_dir.join("contract-ids").join(file_name))
}

pub fn save_contract_id(
&self,
network_passphrase: &str,
contract_id: &str,
alias: &str,
) -> Result<(), Error> {
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: alias::Data = 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(
&self,
alias: &str,
network_passphrase: &str,
) -> Result<Option<String>, Error> {
let Some(alias_data) = self.load_contract_from_alias(alias)? else {
return Ok(None);
};

Ok(alias_data.ids.get(network_passphrase).cloned())
}

pub fn resolve_contract_id(
&self,
alias_or_contract_id: &str,
network_passphrase: &str,
) -> Result<[u8; 32], Error> {
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))
}
}

fn ensure_directory(dir: PathBuf) -> Result<PathBuf, Error> {
Expand Down
12 changes: 9 additions & 3 deletions cmd/soroban-cli/src/commands/contract/deploy/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use soroban_env_host::{
};

use crate::commands::{
config::{alias, data},
config::{data, locator},
contract::{self, id::wasm::get_contract_id},
global, network,
txn_result::{TxnEnvelopeResult, TxnResult},
Expand Down Expand Up @@ -109,7 +109,7 @@ pub enum Error {
)]
InvalidAliasFormat { alias: String },
#[error(transparent)]
Alias(#[from] alias::Error),
Locator(#[from] locator::Error),
}

impl Cmd {
Expand All @@ -120,8 +120,14 @@ impl Cmd {
match res {
TxnEnvelopeResult::TxnEnvelope(tx) => println!("{}", tx.to_xdr_base64(Limits::none())?),
TxnEnvelopeResult::Res(contract) => {
let network = self.config.get_network()?;

if let Some(alias) = self.alias.clone() {
self.config.save_contract_id(&contract, &alias)?;
self.config.locator.save_contract_id(
&network.network_passphrase,
&contract,
&alias,
)?;
}

println!("{contract}");
Expand Down
4 changes: 1 addition & 3 deletions cmd/soroban-cli/src/commands/contract/invoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ use super::super::{
config::{self, locator},
events,
};
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};
Expand Down Expand Up @@ -147,8 +146,6 @@ pub enum Error {
Network(#[from] network::Error),
#[error(transparent)]
GetSpecError(#[from] get_spec::Error),
#[error(transparent)]
Alias(#[from] alias::Error),
}

impl From<Infallible> for Error {
Expand Down Expand Up @@ -315,6 +312,7 @@ impl NetworkRunnable for Cmd {
tracing::trace!(?network);
let contract_id = self
.config
.locator
.resolve_contract_id(&self.contract_id, &network.network_passphrase)?;
let spec_entries = self.spec_entries()?;
if let Some(spec_entries) = &spec_entries {
Expand Down

0 comments on commit 257c236

Please sign in to comment.