From a93a2afe455e775c646e819f44900d37b451c3e4 Mon Sep 17 00:00:00 2001 From: paulobressan Date: Wed, 14 May 2025 19:22:02 -0300 Subject: [PATCH 1/3] feat: updated config to support chain selection and more chains --- src/commands/bindgen.rs | 6 +- src/commands/devnet/mod.rs | 2 +- src/commands/init.rs | 31 +++- src/config.rs | 325 +++++++++++++++++++++++++------------ 4 files changed, 248 insertions(+), 116 deletions(-) diff --git a/src/commands/bindgen.rs b/src/commands/bindgen.rs index 2424003..c8f0e38 100644 --- a/src/commands/bindgen.rs +++ b/src/commands/bindgen.rs @@ -277,12 +277,10 @@ pub fn run(_args: Args, config: &Config) -> miette::Result<()> { std::fs::create_dir_all(&bindgen.output_dir).into_diagnostic()?; let profile = config - .profiles - .clone() + .devnet() .unwrap_or_default() - .devnet .trp - .unwrap_or_else(|| TrpConfig::from(KnownChain::CardanoDevnet)); + .unwrap_or_else(|| TrpConfig::from(KnownChain::Devnet)); let job = Job { name: config.protocol.name.clone(), diff --git a/src/commands/devnet/mod.rs b/src/commands/devnet/mod.rs index f7aecd0..fdba330 100644 --- a/src/commands/devnet/mod.rs +++ b/src/commands/devnet/mod.rs @@ -31,7 +31,7 @@ fn get_home_path() -> miette::Result { } fn handle_devnet(home_path: &PathBuf, config: &Config) -> miette::Result { - let profile = config.profiles.clone().unwrap_or_default().devnet; + let profile = config.devnet().unwrap_or_default(); let value = serde_json::to_vec(&profile.wallets).into_diagnostic()?; let mut hasher = Sha256::new(); diff --git a/src/commands/init.rs b/src/commands/init.rs index 1668ecd..24e2d85 100644 --- a/src/commands/init.rs +++ b/src/commands/init.rs @@ -1,13 +1,12 @@ use std::path::PathBuf; -use crate::config::{BindingsConfig, Config, ProfilesConfig, ProtocolConfig}; +use crate::config::{BindingsConfig, Config, KnownChain, ProfileConfig, ProtocolConfig}; use clap::Args as ClapArgs; -use inquire::{Confirm, MultiSelect, Text}; +use inquire::{Confirm, MultiSelect, Select, Text}; use miette::IntoDiagnostic; // Include template files at compile time const TEMPLATE_MAIN_TX3: &str = include_str!("templates/main.tx3.tpl"); - const DEFAULT_PROJECT_NAME: &str = "my-project"; fn infer_project_name() -> String { @@ -74,6 +73,17 @@ pub fn run(_args: Args, config: Option<&Config>) -> miette::Result<()> { .prompt() .into_diagnostic()?; + let available_chains = KnownChain::all(); + + let chain = Select::new("Chain:", available_chains.clone()) + .prompt() + .unwrap_or_default(); + + let network = KnownChain::network(&chain); + let network = MultiSelect::new("Network:", network.to_vec()) + .prompt() + .unwrap_or_default(); + let generate_bindings = MultiSelect::new( "Generate bindings for:", vec!["Typescript", "Rust", "Go", "Python"], @@ -81,6 +91,19 @@ pub fn run(_args: Args, config: Option<&Config>) -> miette::Result<()> { .prompt() .unwrap_or_default(); + let mut profiles = vec![KnownChain::Devnet.into()]; + if !network.is_empty() { + let profiles_selected = network + .iter() + .map(|n| format!("{chain}{n}").parse()) + .collect::, _>>()? + .into_iter() + .map(ProfileConfig::from) + .collect::>(); + + profiles.extend(profiles_selected) + } + let config = Config { protocol: ProtocolConfig { name: protocol_name, @@ -96,7 +119,7 @@ pub fn run(_args: Args, config: Option<&Config>) -> miette::Result<()> { plugin: binding.to_string().to_lowercase(), }) .collect(), - profiles: ProfilesConfig::default().into(), + profiles: Some(profiles), registry: None, }; diff --git a/src/config.rs b/src/config.rs index cadfe4f..bb7828f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,15 +1,52 @@ -use miette::IntoDiagnostic as _; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, path::PathBuf}; +use miette::{IntoDiagnostic as _, bail}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Visitor}; +use std::{ + collections::HashMap, + fmt::{self, Display}, + path::PathBuf, + str::FromStr, +}; + +const CARDANO_PREVIEW_PUBLIC_TRP_KEY: &str = "trp1ffyf88ugcyg6j6n3yuh"; +const CARDANO_PREPROD_PUBLIC_TRP_KEY: &str = "trp1mtg35n2n9lv7yauanfa"; +const CARDANO_MAINNET_PUBLIC_TRP_KEY: &str = "trp1lrnhzcax5064cgxsaup"; + +const CARDANO_PREVIEW_PUBLIC_U5C_KEY: &str = "trpjodqbmjblunzpbikpcrl"; +const CARDANO_PREPROD_PUBLIC_U5C_KEY: &str = "trpjodqbmjblunzpbikpcrl"; +const CARDANO_MAINNET_PUBLIC_U5C_KEY: &str = "trpjodqbmjblunzpbikpcrl"; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Config { pub protocol: ProtocolConfig, pub registry: Option, - pub profiles: Option, + pub profiles: Option>, pub bindings: Vec, } +impl Config { + pub fn load(path: &PathBuf) -> miette::Result { + let contents = std::fs::read_to_string(path).into_diagnostic()?; + let config = toml::from_str(&contents).into_diagnostic()?; + Ok(config) + } + + pub fn save(&self, path: &PathBuf) -> miette::Result<()> { + let contents = toml::to_string_pretty(self).into_diagnostic()?; + std::fs::write(path, contents).into_diagnostic()?; + Ok(()) + } + + pub fn devnet(&self) -> Option { + if let Some(profiles) = &self.profiles { + return profiles + .iter() + .find(|p| p.chain.eq(&KnownChain::Devnet)) + .cloned(); + } + None + } +} + #[derive(Debug, Serialize, Deserialize, Clone)] pub struct ProtocolConfig { pub name: String, @@ -32,23 +69,16 @@ impl Default for RegistryConfig { } } -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ProfilesConfig { - pub devnet: ProfileConfig, - pub preview: Option, - pub preprod: Option, - pub mainnet: Option, -} +#[derive(Debug, Serialize, Deserialize, Default, Clone)] +pub struct ProfileConfig { + pub chain: KnownChain, + pub env_file: Option, -impl Default for ProfilesConfig { - fn default() -> Self { - Self { - devnet: KnownChain::CardanoDevnet.into(), - preview: Some(KnownChain::CardanoPreview.into()), - preprod: Some(KnownChain::CardanoPreprod.into()), - mainnet: Some(KnownChain::CardanoMainnet.into()), - } - } + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub wallets: Vec, + + pub trp: Option, + pub u5c: Option, } impl From for ProfileConfig { @@ -57,7 +87,7 @@ impl From for ProfileConfig { chain: chain.clone(), env_file: None, wallets: match chain { - KnownChain::CardanoDevnet => vec![ + KnownChain::Devnet => vec![ WalletConfig { name: "alice".to_string(), random_key: true, @@ -77,26 +107,6 @@ impl From for ProfileConfig { } } -const PUBLIC_PREVIEW_TRP_KEY: &str = "trp1ffyf88ugcyg6j6n3yuh"; -const PUBLIC_PREPROD_TRP_KEY: &str = "trp1mtg35n2n9lv7yauanfa"; -const PUBLIC_MAINNET_TRP_KEY: &str = "trp1lrnhzcax5064cgxsaup"; - -const PUBLIC_PREVIEW_U5C_KEY: &str = "trpjodqbmjblunzpbikpcrl"; -const PUBLIC_PREPROD_U5C_KEY: &str = "trpjodqbmjblunzpbikpcrl"; -const PUBLIC_MAINNET_U5C_KEY: &str = "trpjodqbmjblunzpbikpcrl"; - -#[derive(Debug, Serialize, Deserialize, Default, Clone)] -pub struct ProfileConfig { - pub chain: KnownChain, - pub env_file: Option, - - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub wallets: Vec, - - pub trp: Option, - pub u5c: Option, -} - #[derive(Debug, Serialize, Deserialize, Clone)] pub struct WalletConfig { pub name: String, @@ -104,17 +114,128 @@ pub struct WalletConfig { pub initial_balance: u64, } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub enum CardanoNetwork { + Mainnet, + Preprod, + Preview, +} +impl CardanoNetwork { + pub fn all() -> Vec { + vec![ + CardanoNetwork::Mainnet, + CardanoNetwork::Preprod, + CardanoNetwork::Preview, + ] + .into_iter() + .map(|c| c.to_string()) + .collect() + } +} +impl Display for CardanoNetwork { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let name = match self { + CardanoNetwork::Mainnet => "Mainnet", + CardanoNetwork::Preprod => "Preprod", + CardanoNetwork::Preview => "Preview", + }; + write!(f, "{name}") + } +} +impl FromStr for CardanoNetwork { + type Err = miette::Error; + + fn from_str(s: &str) -> Result { + match s { + "Mainnet" => Ok(Self::Mainnet), + "Preprod" => Ok(Self::Preprod), + "Preview" => Ok(Self::Preview), + _ => bail!("invalid cardano network"), + } + } +} + +#[derive(Debug, Clone, PartialEq, Default)] pub enum KnownChain { - CardanoMainnet, - CardanoPreview, - CardanoPreprod, - CardanoDevnet, + #[default] + Devnet, + Cardano(CardanoNetwork), } +impl KnownChain { + pub fn all() -> Vec { + vec!["Cardano".into()] + } -impl Default for KnownChain { - fn default() -> Self { - Self::CardanoDevnet + pub fn network(chain: &str) -> Vec { + match chain { + "Cardano" => CardanoNetwork::all(), + _ => Vec::new(), + } + } +} + +impl Display for KnownChain { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let name = match self { + Self::Devnet => "Devnet", + Self::Cardano(_) => "Cardano", + }; + write!(f, "{name}") + } +} + +impl FromStr for KnownChain { + type Err = miette::Error; + + fn from_str(s: &str) -> Result { + match s { + "Devnet" => Ok(Self::Devnet), + "CardanoMainnet" => Ok(Self::Cardano(CardanoNetwork::Mainnet)), + "CardanoPreview" => Ok(Self::Cardano(CardanoNetwork::Preview)), + "CardanoPreprod" => Ok(Self::Cardano(CardanoNetwork::Preprod)), + _ => bail!(format!("Unknown chain: {}", s)), + } + } +} + +impl Serialize for KnownChain { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s = match self { + KnownChain::Devnet => self.to_string(), + KnownChain::Cardano(c) => format!("{self}{c}"), + }; + serializer.serialize_str(&s) + } +} + +impl<'de> Deserialize<'de> for KnownChain { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct KnownChainVisitor; + + impl<'de> Visitor<'de> for KnownChainVisitor { + type Value = KnownChain; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string like 'Devnet' or 'CardanoPreprod'") + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, + { + v.as_str() + .parse::() + .map_err(|err| E::custom(err.to_string())) + } + } + + deserializer.deserialize_string(KnownChainVisitor) } } @@ -127,31 +248,33 @@ pub struct TrpConfig { impl From for TrpConfig { fn from(chain: KnownChain) -> Self { match chain { - KnownChain::CardanoMainnet => Self { - url: "https://cardano-mainnet.trp-m1.demeter.run".to_string(), - headers: HashMap::from([( - "dmtr-api-key".to_string(), - PUBLIC_MAINNET_TRP_KEY.to_string(), - )]), - }, - KnownChain::CardanoPreview => Self { - url: "https://cardano-preview.trp-m1.demeter.run".to_string(), - headers: HashMap::from([( - "dmtr-api-key".to_string(), - PUBLIC_PREVIEW_TRP_KEY.to_string(), - )]), - }, - KnownChain::CardanoPreprod => Self { - url: "https://cardano-preprod.trp-m1.demeter.run".to_string(), - headers: HashMap::from([( - "dmtr-api-key".to_string(), - PUBLIC_PREPROD_TRP_KEY.to_string(), - )]), - }, - KnownChain::CardanoDevnet => Self { + KnownChain::Devnet => Self { url: "http://localhost:3000/trp".to_string(), headers: HashMap::new(), }, + KnownChain::Cardano(chain) => match chain { + CardanoNetwork::Mainnet => Self { + url: "https://cardano-mainnet.trp-m1.demeter.run".to_string(), + headers: HashMap::from([( + "dmtr-api-key".to_string(), + CARDANO_MAINNET_PUBLIC_TRP_KEY.to_string(), + )]), + }, + CardanoNetwork::Preview => Self { + url: "https://cardano-preview.trp-m1.demeter.run".to_string(), + headers: HashMap::from([( + "dmtr-api-key".to_string(), + CARDANO_PREVIEW_PUBLIC_TRP_KEY.to_string(), + )]), + }, + CardanoNetwork::Preprod => Self { + url: "https://cardano-preprod.trp-m1.demeter.run".to_string(), + headers: HashMap::from([( + "dmtr-api-key".to_string(), + CARDANO_PREPROD_PUBLIC_TRP_KEY.to_string(), + )]), + }, + }, } } } @@ -165,31 +288,33 @@ pub struct U5cConfig { impl From for U5cConfig { fn from(chain: KnownChain) -> Self { match chain { - KnownChain::CardanoMainnet => Self { - url: "https://mainnet.utxorpc-v0.demeter.run".to_string(), - headers: HashMap::from([( - "dmtr-api-key".to_string(), - PUBLIC_MAINNET_U5C_KEY.to_string(), - )]), - }, - KnownChain::CardanoPreview => Self { - url: "https://preview.utxorpc-v0.demeter.run".to_string(), - headers: HashMap::from([( - "dmtr-api-key".to_string(), - PUBLIC_PREVIEW_U5C_KEY.to_string(), - )]), - }, - KnownChain::CardanoPreprod => Self { - url: "https://preprod.utxorpc-v0.demeter.run".to_string(), - headers: HashMap::from([( - "dmtr-api-key".to_string(), - PUBLIC_PREPROD_U5C_KEY.to_string(), - )]), - }, - KnownChain::CardanoDevnet => Self { + KnownChain::Devnet => Self { url: "http://localhost:3000/u5c".to_string(), headers: HashMap::new(), }, + KnownChain::Cardano(chain) => match chain { + CardanoNetwork::Mainnet => Self { + url: "https://mainnet.utxorpc-v0.demeter.run".to_string(), + headers: HashMap::from([( + "dmtr-api-key".to_string(), + CARDANO_MAINNET_PUBLIC_U5C_KEY.to_string(), + )]), + }, + CardanoNetwork::Preview => Self { + url: "https://preview.utxorpc-v0.demeter.run".to_string(), + headers: HashMap::from([( + "dmtr-api-key".to_string(), + CARDANO_PREVIEW_PUBLIC_U5C_KEY.to_string(), + )]), + }, + CardanoNetwork::Preprod => Self { + url: "https://preprod.utxorpc-v0.demeter.run".to_string(), + headers: HashMap::from([( + "dmtr-api-key".to_string(), + CARDANO_PREPROD_PUBLIC_U5C_KEY.to_string(), + )]), + }, + }, } } } @@ -199,17 +324,3 @@ pub struct BindingsConfig { pub plugin: String, pub output_dir: PathBuf, } - -impl Config { - pub fn load(path: &PathBuf) -> miette::Result { - let contents = std::fs::read_to_string(path).into_diagnostic()?; - let config = toml::from_str(&contents).into_diagnostic()?; - Ok(config) - } - - pub fn save(&self, path: &PathBuf) -> miette::Result<()> { - let contents = toml::to_string_pretty(self).into_diagnostic()?; - std::fs::write(path, contents).into_diagnostic()?; - Ok(()) - } -} From 9d85389b3a3603ba9db714b0a81d056d9660534d Mon Sep 17 00:00:00 2001 From: paulobressan Date: Wed, 14 May 2025 19:35:09 -0300 Subject: [PATCH 2/3] chore: adjusted config lint --- src/config.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index bb7828f..1372426 100644 --- a/src/config.rs +++ b/src/config.rs @@ -218,13 +218,21 @@ impl<'de> Deserialize<'de> for KnownChain { { struct KnownChainVisitor; - impl<'de> Visitor<'de> for KnownChainVisitor { + impl Visitor<'_> for KnownChainVisitor { type Value = KnownChain; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a string like 'Devnet' or 'CardanoPreprod'") } + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + v.parse::() + .map_err(|err| E::custom(err.to_string())) + } + fn visit_string(self, v: String) -> Result where E: serde::de::Error, From 0500042ceb8b668af084d6011125b162c0703fa7 Mon Sep 17 00:00:00 2001 From: paulobressan Date: Tue, 20 May 2025 18:57:22 -0300 Subject: [PATCH 3/3] feat: updated config to use hashmap of profiles --- src/commands/init.rs | 17 ++++++++++------- src/config.rs | 7 ++++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/commands/init.rs b/src/commands/init.rs index a78bab2..35c62a9 100644 --- a/src/commands/init.rs +++ b/src/commands/init.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{collections::HashMap, path::PathBuf}; use crate::config::{BindingsConfig, Config, KnownChain, ProfileConfig, ProtocolConfig}; use clap::Args as ClapArgs; @@ -93,15 +93,18 @@ pub fn run(_args: Args, config: Option<&Config>) -> miette::Result<()> { .prompt() .unwrap_or_default(); - let mut profiles = vec![KnownChain::Devnet.into()]; + let mut profiles: HashMap = HashMap::new(); + profiles.insert(KnownChain::Devnet.to_string(), KnownChain::Devnet.into()); + if !network.is_empty() { let profiles_selected = network .iter() - .map(|n| format!("{chain}{n}").parse()) - .collect::, _>>()? - .into_iter() - .map(ProfileConfig::from) - .collect::>(); + .map(|n| { + let key = format!("{chain}{n}"); + let value = key.parse::()?.into(); + Ok((key, value)) + }) + .collect::, miette::Error>>()?; profiles.extend(profiles_selected) } diff --git a/src/config.rs b/src/config.rs index 1372426..1f03a48 100644 --- a/src/config.rs +++ b/src/config.rs @@ -19,7 +19,7 @@ const CARDANO_MAINNET_PUBLIC_U5C_KEY: &str = "trpjodqbmjblunzpbikpcrl"; pub struct Config { pub protocol: ProtocolConfig, pub registry: Option, - pub profiles: Option>, + pub profiles: Option>, pub bindings: Vec, } @@ -40,8 +40,8 @@ impl Config { if let Some(profiles) = &self.profiles { return profiles .iter() - .find(|p| p.chain.eq(&KnownChain::Devnet)) - .cloned(); + .find(|(_, p)| p.chain.eq(&KnownChain::Devnet)) + .map(|(_, p)| p.clone()); } None } @@ -190,6 +190,7 @@ impl FromStr for KnownChain { fn from_str(s: &str) -> Result { match s { "Devnet" => Ok(Self::Devnet), + "CardanoDevnet" => Ok(Self::Devnet), "CardanoMainnet" => Ok(Self::Cardano(CardanoNetwork::Mainnet)), "CardanoPreview" => Ok(Self::Cardano(CardanoNetwork::Preview)), "CardanoPreprod" => Ok(Self::Cardano(CardanoNetwork::Preprod)),