Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
11 changes: 11 additions & 0 deletions Cargo.lock

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

6 changes: 5 additions & 1 deletion cli/src/ad4m.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ struct ClapApp {
/// Provide admin credential to gain all capabilities
#[arg(short, long)]
admin_credential: Option<String>,

/// Data directory to read executor-port from (default: ~/.ad4m)
#[arg(long)]
data_path: Option<String>,
Comment on lines +77 to +79
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

log still ignores the selected instance path.

--data-path now selects which executor directory to inspect for executor-port, but Domain::Log still reads executor_data_path().join("ad4m.log") at Lines 163-169. ad4m --data-path ~/.ad4m-dev log will therefore still show the default instance's log.

Also applies to: 120-125

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/ad4m.rs` around lines 77 - 79, The log command ignores the
CLI-provided data_path and always reads executor_data_path(); update the
Domain::Log handling to use the parsed Option<String> data_path (the same source
used for executor-port) instead of calling executor_data_path() directly —
locate the Domain::Log match arm and replace its use of
executor_data_path().join("ad4m.log") with a path derived from the provided
data_path (falling back to executor_data_path() only when data_path is None);
ensure the same change is applied to the other occurrence mentioned (around the
other 120-125 block) so both log reads honor the --data-path option.

}

#[derive(Debug, Subcommand)]
Expand Down Expand Up @@ -117,7 +121,7 @@ async fn get_ad4m_client(args: &ClapApp) -> Result<Ad4mClient> {
let executor_url = if let Some(custom_url) = args.executor_url.clone() {
custom_url
} else {
crate::startup::get_executor_url()?
crate::startup::get_executor_url(args.data_path.as_deref())?
};

let cap_token = if let Some(admin_credential) = &args.admin_credential {
Expand Down
85 changes: 79 additions & 6 deletions cli/src/ad4m_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ enum Domain {
/// Useful for test harnesses that need targeted process cleanup.
#[arg(long)]
pid_file: Option<String>,
/// Run in development mode (data: ~/.ad4m-dev, network: devnet, log: debug)
#[arg(long, action)]
dev: bool,
/// Network mode: mainnet (default), devnet, or local
#[arg(long)]
network_mode: Option<String>,
},
RunLocalHcServices {},
}
Expand Down Expand Up @@ -169,19 +175,19 @@ async fn main() -> Result<()> {
};

if let Domain::Run {
app_data_path,
network_bootstrap_seed,
mut app_data_path,
mut network_bootstrap_seed,
language_language_only,
run_dapp_server,
gql_port,
hc_admin_port,
hc_app_port,
hc_use_bootstrap,
mut hc_use_bootstrap,
hc_use_local_proxy,
hc_use_mdns,
mut hc_use_mdns,
hc_use_proxy,
hc_proxy_url,
hc_bootstrap_url,
mut hc_proxy_url,
mut hc_bootstrap_url,
Comment on lines +185 to +190
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Local mode doesn't fully force proxy discovery off.

NetworkMode::Local only disables bootstrap and clears URLs. If the caller also passes --hc-use-proxy or --hc-use-local-proxy, those values still flow into Ad4mConfig, so this mode is no longer guaranteed to be mDNS-only.

💡 Suggested change
@@
-        hc_use_local_proxy,
+        mut hc_use_local_proxy,
@@
-        hc_use_proxy,
+        mut hc_use_proxy,
@@
             NetworkMode::Local => {
                 hc_use_mdns = Some(true);
                 hc_use_bootstrap = Some(false);
+                hc_use_proxy = Some(false);
+                hc_use_local_proxy = Some(false);
                 hc_proxy_url = Some(String::new());
                 hc_bootstrap_url = Some(String::new());
             }

Also applies to: 237-243

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/ad4m_executor.rs` around lines 185 - 190, NetworkMode::Local
currently only clears bootstrap URLs but doesn't override inbound flags, so
hc_use_proxy and hc_use_local_proxy can still be set when constructing
Ad4mConfig; update the logic that handles NetworkMode::Local (the branch setting
hc_use_bootstrap, hc_use_local_proxy, hc_use_mdns, hc_use_proxy, hc_proxy_url,
hc_bootstrap_url and the similar block later around lines 237-243) to explicitly
force proxy discovery off and mDNS on: set hc_use_proxy = false,
hc_use_local_proxy = false, hc_use_mdns = true and clear hc_proxy_url and
hc_bootstrap_url before building Ad4mConfig so Local mode is guaranteed to be
mDNS-only regardless of incoming CLI flags.

hc_relay_url,
connect_holochain,
admin_credential,
Expand All @@ -194,8 +200,75 @@ async fn main() -> Result<()> {
enable_mcp,
mcp_port,
pid_file,
dev,
network_mode,
} = args.domain
{
use rust_executor::config::NetworkMode;

// Refuse --dev in production builds
if rust_executor::globals::IS_PRODUCTION_BUILD && dev {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why? I hardly use dev builds because of the 100GBs for cargo artefacts and no GPU accelleration. So I would want to use this feature with a release build.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ah just read below that this does not equal release built, but there is a special production feature flag... ok.

eprintln!("ERROR: Cannot use --dev with production builds");
std::process::exit(1);
}

// Parse network mode
let network_mode: NetworkMode = if let Some(ref mode_str) = network_mode {
mode_str.parse().unwrap_or_else(|_| {
eprintln!(
"ERROR: Invalid network mode '{}'. Use: mainnet, devnet, local",
mode_str
);
std::process::exit(1);
})
} else if dev {
NetworkMode::Devnet
} else {
NetworkMode::Mainnet
};

// Apply --dev defaults
if dev && app_data_path.is_none() {
let home = dirs::home_dir().expect("Could not get home directory");
app_data_path = Some(home.join(".ad4m-dev").to_string_lossy().into_owned());
}
Comment on lines +231 to +234
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

--network-mode devnet still falls back to the production data directory.

These defaults are keyed off dev, not the parsed network_mode. Running with --network-mode devnet but without --dev leaves app_data_path unset, so Lines 249-252 still fall back to ~/.ad4m and reuse the production conductor state instead of the isolated dev directory.

💡 Suggested change
-        // Apply --dev defaults
-        if dev && app_data_path.is_none() {
+        // Apply dev/devnet defaults
+        if app_data_path.is_none() && (dev || network_mode == NetworkMode::Devnet) {
             let home = dirs::home_dir().expect("Could not get home directory");
             app_data_path = Some(home.join(".ad4m-dev").to_string_lossy().into_owned());
         }

Also applies to: 249-252

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/ad4m_executor.rs` around lines 231 - 234, The code sets the dev data
directory based on the boolean flag dev, so when a user passes --network-mode
devnet (parsed into network_mode) but not --dev the app_data_path stays unset
and falls back to production; update the logic that assigns app_data_path to
also check network_mode for the dev/devnet value (in the same scope where
app_data_path, dev and network_mode are defined) so that either dev == true or
network_mode == "devnet" (or the equivalent enum/variant) will set app_data_path
to ~/.ad4m-dev; apply the same change where the later fallback logic (the block
handling app_data_path defaults) currently uses dev so it uses network_mode as
well to avoid reusing the production conductor state.


// Apply network mode to config
match network_mode {
NetworkMode::Local => {
hc_use_mdns = Some(true);
hc_use_bootstrap = Some(false);
hc_proxy_url = Some(String::new());
hc_bootstrap_url = Some(String::new());
}
_ => {}
};

// For devnet mode, write devnet seed
if network_mode == NetworkMode::Devnet && network_bootstrap_seed.is_none() {
let data_path = app_data_path.clone().unwrap_or_else(|| {
let home = dirs::home_dir().expect("Could not get home directory");
home.join(".ad4m").to_string_lossy().into_owned()
});
std::fs::create_dir_all(&data_path).ok();
let seed_path = std::path::PathBuf::from(&data_path).join("devnet_seed.seed");
std::fs::write(&seed_path, rust_executor::globals::DEVNET_JSON)
.expect("Could not write devnet seed file");
Comment on lines +253 to +256
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Don't discard create_dir_all failures here.

create_dir_all(...).ok() hides the actual filesystem error and turns it into a later write panic. Failing fast on directory creation will make devnet seed setup failures much easier to diagnose.

💡 Suggested change
-            std::fs::create_dir_all(&data_path).ok();
+            std::fs::create_dir_all(&data_path)
+                .expect("Could not create data directory for devnet seed");
📝 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
std::fs::create_dir_all(&data_path).ok();
let seed_path = std::path::PathBuf::from(&data_path).join("devnet_seed.seed");
std::fs::write(&seed_path, rust_executor::globals::DEVNET_JSON)
.expect("Could not write devnet seed file");
std::fs::create_dir_all(&data_path)
.expect("Could not create data directory for devnet seed");
let seed_path = std::path::PathBuf::from(&data_path).join("devnet_seed.seed");
std::fs::write(&seed_path, rust_executor::globals::DEVNET_JSON)
.expect("Could not write devnet seed file");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/ad4m_executor.rs` around lines 253 - 256, The call to
std::fs::create_dir_all(&data_path).ok() silently ignores errors causing the
later std::fs::write(&seed_path, ...) to panic without the real cause; change
this to check/propagate the error (e.g., use
std::fs::create_dir_all(&data_path).expect("Could not create data directory") or
propagate with ? and return a Result) so failures creating data_path are
reported immediately; keep the subsequent construction of seed_path and the
write of rust_executor::globals::DEVNET_JSON unchanged but ensure you reference
and handle/create errors for create_dir_all on data_path before attempting to
write seed_path.

network_bootstrap_seed = Some(seed_path.to_string_lossy().into_owned());
}

if dev {
eprintln!(
"⚠️ DEVELOPMENT MODE — data: {} | network: {}",
app_data_path.as_deref().unwrap_or("~/.ad4m-dev"),
network_mode
);
if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "debug");
}
} else if !rust_executor::globals::IS_PRODUCTION_BUILD {
eprintln!("[dev build] Use --dev for development mode defaults");
}
let tls = if tls_cert_file.is_some() && tls_key_file.is_some() {
Some(TlsConfig {
cert_file_path: tls_cert_file.unwrap(),
Expand Down
77 changes: 76 additions & 1 deletion cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use ad4m_client::*;
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
use rust_executor::Ad4mConfig;
use rust_executor::config::NetworkMode;
use startup::executor_data_path;

/// AD4M command line interface.
Expand Down Expand Up @@ -78,6 +79,10 @@ struct ClapApp {
/// Provide admin credential to gain all capabilities
#[arg(short, long)]
admin_credential: Option<String>,

/// Data directory to read executor-port from (default: ~/.ad4m)
#[arg(long)]
data_path: Option<String>,
}

#[derive(Debug, Subcommand)]
Expand Down Expand Up @@ -166,6 +171,12 @@ enum Domain {
/// Write the executor PID to this file on startup (removed on clean shutdown).
#[arg(long)]
pid_file: Option<String>,
/// Run in development mode (data: ~/.ad4m-dev, network: devnet, log: debug)
#[arg(long, action)]
dev: bool,
/// Network mode: mainnet (default), devnet, or local
#[arg(long)]
network_mode: Option<String>,
},
RunLocalHcServices {},
Eve {
Expand All @@ -178,7 +189,7 @@ async fn get_ad4m_client(args: &ClapApp) -> Result<Ad4mClient> {
let executor_url = if let Some(custom_url) = args.executor_url.clone() {
custom_url
} else {
crate::startup::get_executor_url()?
crate::startup::get_executor_url(args.data_path.as_deref())?
};

let cap_token = if let Some(admin_credential) = &args.admin_credential {
Expand Down Expand Up @@ -247,8 +258,70 @@ async fn main() -> Result<()> {
enable_mcp,
mcp_port,
pid_file,
dev,
network_mode,
} = args.domain
{
// Validate production build constraints
if rust_executor::globals::IS_PRODUCTION_BUILD && dev {
eprintln!("ERROR: Cannot use --dev with production builds");
std::process::exit(1);
}

// Parse network mode
let network_mode: NetworkMode = if let Some(ref mode_str) = network_mode {
mode_str.parse().unwrap_or_else(|e: String| {
eprintln!("ERROR: {}", e);
std::process::exit(1);
})
} else if dev {
NetworkMode::Devnet
} else {
NetworkMode::Mainnet
};

// Apply --dev defaults
let app_data_path = if dev && app_data_path.is_none() {
let home = dirs::home_dir().expect("Could not get home directory");
Some(home.join(".ad4m-dev").to_string_lossy().into_owned())
} else {
app_data_path
};

// Apply network mode to config
let (hc_use_mdns, hc_use_bootstrap, hc_use_proxy, hc_proxy_url, hc_bootstrap_url) = match network_mode {
NetworkMode::Local => {
(Some(true), Some(false), Some(false), Some(String::new()), Some(String::new()))
}
_ => (hc_use_mdns, hc_use_bootstrap, hc_use_proxy, hc_proxy_url, hc_bootstrap_url),
};

// For devnet mode, write devnet seed instead of mainnet
let network_bootstrap_seed = if network_mode == NetworkMode::Devnet && network_bootstrap_seed.is_none() {
// Will be handled in config.prepare() — write devnet seed file
let data_path = app_data_path.clone().unwrap_or_else(|| {
let home = dirs::home_dir().expect("Could not get home directory");
home.join(".ad4m").to_string_lossy().into_owned()
});
std::fs::create_dir_all(&data_path).ok();
let seed_path = std::path::PathBuf::from(&data_path).join("devnet_seed.seed");
std::fs::write(&seed_path, rust_executor::globals::DEVNET_JSON)
.expect("Could not write devnet seed file");
Some(seed_path.to_string_lossy().into_owned())
} else {
network_bootstrap_seed
};
Comment on lines +283 to +313
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

--network-mode devnet still falls back to the mainnet state directory.

When dev is false, Lines 284-289 leave app_data_path as None. Because Ad4mConfig does not carry network_mode (rust-executor/src/config.rs:99-130), the spawned executor still uses its normal default data dir, and Lines 302-305 also place the generated seed under ~/.ad4m. ad4m run --network-mode devnet therefore shares mainnet state unless the caller also adds --dev or --app-data-path, which breaks the isolation this flag is supposed to provide.

💡 Suggested fix
-        let app_data_path = if dev && app_data_path.is_none() {
+        let app_data_path = if app_data_path.is_none()
+            && (dev || network_mode == NetworkMode::Devnet)
+        {
             let home = dirs::home_dir().expect("Could not get home directory");
             Some(home.join(".ad4m-dev").to_string_lossy().into_owned())
         } else {
             app_data_path
         };
@@
-            let data_path = app_data_path.clone().unwrap_or_else(|| {
-                let home = dirs::home_dir().expect("Could not get home directory");
-                home.join(".ad4m").to_string_lossy().into_owned()
-            });
+            let data_path = app_data_path
+                .clone()
+                .expect("devnet should always resolve to an isolated data path");

Please add a regression test for run --network-mode devnet without --dev.

📝 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
// Apply --dev defaults
let app_data_path = if dev && app_data_path.is_none() {
let home = dirs::home_dir().expect("Could not get home directory");
Some(home.join(".ad4m-dev").to_string_lossy().into_owned())
} else {
app_data_path
};
// Apply network mode to config
let (hc_use_mdns, hc_use_bootstrap, hc_use_proxy, hc_proxy_url, hc_bootstrap_url) = match network_mode {
NetworkMode::Local => {
(Some(true), Some(false), Some(false), Some(String::new()), Some(String::new()))
}
_ => (hc_use_mdns, hc_use_bootstrap, hc_use_proxy, hc_proxy_url, hc_bootstrap_url),
};
// For devnet mode, write devnet seed instead of mainnet
let network_bootstrap_seed = if network_mode == NetworkMode::Devnet && network_bootstrap_seed.is_none() {
// Will be handled in config.prepare() — write devnet seed file
let data_path = app_data_path.clone().unwrap_or_else(|| {
let home = dirs::home_dir().expect("Could not get home directory");
home.join(".ad4m").to_string_lossy().into_owned()
});
std::fs::create_dir_all(&data_path).ok();
let seed_path = std::path::PathBuf::from(&data_path).join("devnet_seed.seed");
std::fs::write(&seed_path, rust_executor::globals::DEVNET_JSON)
.expect("Could not write devnet seed file");
Some(seed_path.to_string_lossy().into_owned())
} else {
network_bootstrap_seed
};
// Apply --dev defaults
let app_data_path = if app_data_path.is_none()
&& (dev || network_mode == NetworkMode::Devnet)
{
let home = dirs::home_dir().expect("Could not get home directory");
Some(home.join(".ad4m-dev").to_string_lossy().into_owned())
} else {
app_data_path
};
// Apply network mode to config
let (hc_use_mdns, hc_use_bootstrap, hc_use_proxy, hc_proxy_url, hc_bootstrap_url) = match network_mode {
NetworkMode::Local => {
(Some(true), Some(false), Some(false), Some(String::new()), Some(String::new()))
}
_ => (hc_use_mdns, hc_use_bootstrap, hc_use_proxy, hc_proxy_url, hc_bootstrap_url),
};
// For devnet mode, write devnet seed instead of mainnet
let network_bootstrap_seed = if network_mode == NetworkMode::Devnet && network_bootstrap_seed.is_none() {
// Will be handled in config.prepare() — write devnet seed file
let data_path = app_data_path
.clone()
.expect("devnet should always resolve to an isolated data path");
std::fs::create_dir_all(&data_path).ok();
let seed_path = std::path::PathBuf::from(&data_path).join("devnet_seed.seed");
std::fs::write(&seed_path, rust_executor::globals::DEVNET_JSON)
.expect("Could not write devnet seed file");
Some(seed_path.to_string_lossy().into_owned())
} else {
network_bootstrap_seed
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/main.rs` around lines 283 - 313, The CLI still leaves app_data_path
as None when --network-mode devnet is used without --dev, causing the executor
to use the mainnet directory; fix by treating NetworkMode::Devnet like dev for
default data dir: if network_mode == NetworkMode::Devnet &&
app_data_path.is_none() set app_data_path to the dev path (e.g., ~/.ad4m-dev)
before writing the devnet seed and before constructing Ad4mConfig/spawning the
executor (update the logic around the app_data_path assignment and where
network_bootstrap_seed is computed to use that value). Also add a regression
test exercising `run --network-mode devnet` without `--dev` to assert the
created data dir and seed file are under the devnet-specific directory.
Reference symbols: app_data_path, network_mode, NetworkMode::Devnet,
network_bootstrap_seed, and Ad4mConfig/config.prepare().


if dev {
eprintln!("⚠️ DEVELOPMENT MODE — data: {} | network: {}",
app_data_path.as_deref().unwrap_or("~/.ad4m-dev"),
network_mode);
// Set RUST_LOG to debug if not already set
if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "debug");
}
}

let _ = tokio::spawn(async move {
rust_executor::run(Ad4mConfig {
app_data_path,
Expand Down Expand Up @@ -348,6 +421,8 @@ async fn main() -> Result<()> {
enable_mcp: _,
mcp_port: _,
pid_file: _,
dev: _,
network_mode: _,
} => unreachable!(),
Domain::RunLocalHcServices {} => unreachable!(),
Domain::Eve { command: _ } => unreachable!(),
Expand Down
20 changes: 13 additions & 7 deletions cli/src/startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,19 @@ pub fn data_path() -> Result<PathBuf> {

pub fn executor_data_path() -> PathBuf {
let home_dir = dirs::home_dir().expect("Could not get home directory");

home_dir.join(".ad4m")
}

pub fn get_executor_port() -> Result<u16> {
let executor_data_path = executor_data_path();
let file_path = executor_data_path.join("executor-port");
pub fn get_executor_port_from_path(data_path: &std::path::Path) -> Result<u16> {
let file_path = data_path.join("executor-port");
let executor_port = std::fs::read_to_string(file_path.clone()).with_context(|| {
format!(
"Could not get executor port file `{}`!\nIs AD4M executor running?",
file_path.display()
)
});
match executor_port {
Ok(port) => Ok(port.parse::<u16>().unwrap()),
Ok(port) => Ok(port.trim().parse::<u16>().unwrap()),
Err(err) => {
println!("{}", err);
println!("Attempting to connect on default port 12000...\n");
Expand All @@ -41,8 +39,16 @@ pub fn get_executor_port() -> Result<u16> {
}
}

pub fn get_executor_url() -> Result<String> {
let port = get_executor_port()?;
pub fn get_executor_port() -> Result<u16> {
get_executor_port_from_path(&executor_data_path())
}

pub fn get_executor_url(data_path: Option<&str>) -> Result<String> {
let port = if let Some(dp) = data_path {
get_executor_port_from_path(std::path::Path::new(dp))?
} else {
get_executor_port()?
};
Ok(format!("http://127.0.0.1:{}/graphql", port))
}

Expand Down
2 changes: 2 additions & 0 deletions rust-executor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ default = []
metal = ["kalosm/metal"]
cuda = ["kalosm/cuda"]
generate_snapshot = [] # Feature flag for snapshot generation mode
production = [] # Production build: disables --dev flag, defaults to mainnet


# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand All @@ -46,6 +47,7 @@ deno_snapshots = {version = "0.19.0", git = "https://github.com/coasys/deno.git"
sys_traits = "=0.1.9"
# deno_runtime = {version = "0.162.0", path = "../../deno/runtime"}
tokio = { version = "1.25.0", features = ["full"] }
fs2 = "0.4"
url = "2.3.1"
urlencoding = "2.1"
percent-encoding = "2"
Expand Down
34 changes: 34 additions & 0 deletions rust-executor/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,40 @@ use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::sync::{Arc, Mutex};

/// Network mode for the AD4M executor
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum NetworkMode {
Mainnet,
Devnet,
Local,
}

impl std::fmt::Display for NetworkMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NetworkMode::Mainnet => write!(f, "mainnet"),
NetworkMode::Devnet => write!(f, "devnet"),
NetworkMode::Local => write!(f, "local"),
}
}
}

impl std::str::FromStr for NetworkMode {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"mainnet" => Ok(NetworkMode::Mainnet),
"devnet" => Ok(NetworkMode::Devnet),
"local" => Ok(NetworkMode::Local),
_ => Err(format!(
"Invalid network mode: '{}'. Must be mainnet, devnet, or local",
s
)),
}
}
}

lazy_static::lazy_static! {
/// Global SMTP configuration for sending emails
pub static ref SMTP_CONFIG: Arc<Mutex<Option<SmtpConfig>>> = Arc::new(Mutex::new(None));
Expand Down
Loading