miden_client_cli/
config.rs1use core::fmt::Debug;
2use std::fmt::Display;
3use std::path::PathBuf;
4use std::str::FromStr;
5
6use figment::value::{Dict, Map};
7use figment::{Metadata, Profile, Provider};
8use miden_client::note_transport::NOTE_TRANSPORT_DEFAULT_ENDPOINT;
9use miden_client::rpc::Endpoint;
10use serde::{Deserialize, Serialize};
11
12use crate::errors::CliError;
13
14pub const MIDEN_DIR: &str = ".miden";
15pub const TOKEN_SYMBOL_MAP_FILENAME: &str = "token_symbol_map.toml";
16pub const DEFAULT_PACKAGES_DIR: &str = "packages";
17pub const STORE_FILENAME: &str = "store.sqlite3";
18pub const KEYSTORE_DIRECTORY: &str = "keystore";
19
20pub fn get_global_miden_dir() -> Result<PathBuf, std::io::Error> {
22 dirs::home_dir()
23 .ok_or_else(|| {
24 std::io::Error::new(std::io::ErrorKind::NotFound, "Could not determine home directory")
25 })
26 .map(|home| home.join(MIDEN_DIR))
27}
28
29pub fn get_local_miden_dir() -> Result<PathBuf, std::io::Error> {
31 std::env::current_dir().map(|cwd| cwd.join(MIDEN_DIR))
32}
33
34#[derive(Debug, Deserialize, Serialize)]
38pub struct CliConfig {
39 pub rpc: RpcConfig,
41 pub store_filepath: PathBuf,
43 pub secret_keys_directory: PathBuf,
45 pub token_symbol_map_filepath: PathBuf,
47 pub remote_prover_endpoint: Option<CliEndpoint>,
49 pub package_directory: PathBuf,
51 pub max_block_number_delta: Option<u32>,
54 pub note_transport: Option<NoteTransportConfig>,
56}
57
58impl Provider for CliConfig {
60 fn metadata(&self) -> Metadata {
61 Metadata::named("CLI Config")
62 }
63
64 fn data(&self) -> Result<Map<Profile, Dict>, figment::Error> {
65 figment::providers::Serialized::defaults(CliConfig::default()).data()
66 }
67
68 fn profile(&self) -> Option<Profile> {
69 None
71 }
72}
73
74impl Default for CliConfig {
75 fn default() -> Self {
76 Self {
79 rpc: RpcConfig::default(),
80 store_filepath: PathBuf::from(STORE_FILENAME),
81 secret_keys_directory: PathBuf::from(KEYSTORE_DIRECTORY),
82 token_symbol_map_filepath: PathBuf::from(TOKEN_SYMBOL_MAP_FILENAME),
83 remote_prover_endpoint: None,
84 package_directory: PathBuf::from(DEFAULT_PACKAGES_DIR),
85 max_block_number_delta: None,
86 note_transport: None,
87 }
88 }
89}
90
91#[derive(Debug, Deserialize, Serialize)]
96pub struct RpcConfig {
97 pub endpoint: CliEndpoint,
99 pub timeout_ms: u64,
101}
102
103impl Default for RpcConfig {
104 fn default() -> Self {
105 Self {
106 endpoint: Endpoint::testnet().into(),
107 timeout_ms: 10000,
108 }
109 }
110}
111
112#[derive(Debug, Deserialize, Serialize)]
117pub struct NoteTransportConfig {
118 pub endpoint: String,
120 pub timeout_ms: u64,
122}
123
124impl Default for NoteTransportConfig {
125 fn default() -> Self {
126 Self {
127 endpoint: NOTE_TRANSPORT_DEFAULT_ENDPOINT.to_string(),
128 timeout_ms: 10000,
129 }
130 }
131}
132
133#[derive(Clone, Debug)]
137pub struct CliEndpoint(pub Endpoint);
138
139impl Display for CliEndpoint {
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141 write!(f, "{}", self.0)
142 }
143}
144
145impl TryFrom<&str> for CliEndpoint {
146 type Error = String;
147
148 fn try_from(endpoint: &str) -> Result<Self, Self::Error> {
149 let endpoint = Endpoint::try_from(endpoint).map_err(|err| err.clone())?;
150 Ok(Self(endpoint))
151 }
152}
153
154impl From<Endpoint> for CliEndpoint {
155 fn from(endpoint: Endpoint) -> Self {
156 Self(endpoint)
157 }
158}
159
160impl TryFrom<Network> for CliEndpoint {
161 type Error = CliError;
162
163 fn try_from(value: Network) -> Result<Self, Self::Error> {
164 Ok(Self(Endpoint::try_from(value.to_rpc_endpoint().as_str()).map_err(|err| {
165 CliError::Parse(err.into(), "Failed to parse RPC endpoint".to_string())
166 })?))
167 }
168}
169
170impl From<CliEndpoint> for Endpoint {
171 fn from(endpoint: CliEndpoint) -> Self {
172 endpoint.0
173 }
174}
175
176impl From<&CliEndpoint> for Endpoint {
177 fn from(endpoint: &CliEndpoint) -> Self {
178 endpoint.0.clone()
179 }
180}
181
182impl Serialize for CliEndpoint {
183 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
184 where
185 S: serde::Serializer,
186 {
187 serializer.serialize_str(&self.to_string())
188 }
189}
190
191impl<'de> Deserialize<'de> for CliEndpoint {
192 fn deserialize<D>(deserializer: D) -> Result<CliEndpoint, D::Error>
193 where
194 D: serde::Deserializer<'de>,
195 {
196 let endpoint = String::deserialize(deserializer)?;
197 CliEndpoint::try_from(endpoint.as_str()).map_err(serde::de::Error::custom)
198 }
199}
200
201#[derive(Debug, Clone, Deserialize, Serialize)]
207pub enum Network {
208 Custom(String),
209 Devnet,
210 Localhost,
211 Testnet,
212}
213
214impl FromStr for Network {
215 type Err = String;
216
217 fn from_str(s: &str) -> Result<Self, Self::Err> {
218 match s.to_lowercase().as_str() {
219 "devnet" => Ok(Network::Devnet),
220 "localhost" => Ok(Network::Localhost),
221 "testnet" => Ok(Network::Testnet),
222 custom => Ok(Network::Custom(custom.to_string())),
223 }
224 }
225}
226
227impl Network {
228 #[allow(dead_code)]
230 pub fn to_rpc_endpoint(&self) -> String {
231 match self {
232 Network::Custom(custom) => custom.clone(),
233 Network::Devnet => Endpoint::devnet().to_string(),
234 Network::Localhost => Endpoint::default().to_string(),
235 Network::Testnet => Endpoint::testnet().to_string(),
236 }
237 }
238}