#![allow(dead_code)]
use crate::{
commands::wallet::CliState,
errors::CliError,
utils_core::{
create_table, print_error, print_info, print_section_header, print_success, print_warning,
prompt_select, prompt_yes_no, status_indicator, with_loading,
},
};
use clap::{Args, Subcommand};
use comfy_table::{Cell, Color};
use neo3::neo_clients::{APITrait, HttpProvider, RpcClient};
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct NetworkConfig {
pub name: String,
pub rpc_url: String,
pub network_type: String,
pub chain_id: u32,
pub is_default: bool,
}
#[derive(Args, Debug)]
pub struct NetworkArgs {
#[command(subcommand)]
pub command: NetworkCommands,
}
#[derive(Subcommand, Debug)]
pub enum NetworkCommands {
#[command(about = "Connect to a Neo network")]
Connect {
#[arg(short, long, help = "Network name or custom RPC URL")]
network: Option<String>,
},
#[command(about = "Show current network status and information")]
Status,
#[command(about = "List all configured networks")]
List,
#[command(about = "Add a new network configuration")]
Add {
#[arg(long, help = "Name for the network")]
name: String,
#[arg(short, long, help = "RPC endpoint URL")]
url: String,
#[arg(
short,
long,
default_value = "custom",
help = "Network type (mainnet, testnet, custom)"
)]
network_type: String,
#[arg(short, long, help = "Chain ID for the network")]
chain_id: Option<u32>,
},
#[command(about = "Remove a network configuration")]
Remove {
#[arg(short, long, help = "Name of the network to remove")]
name: String,
},
#[command(about = "Show connected peers")]
Peers,
#[command(about = "Get latest block information")]
Block {
#[arg(long, help = "Specific block height")]
height: Option<u32>,
#[arg(short = 'i', long, help = "Specific block index")]
index: Option<u32>,
},
#[command(about = "Test connectivity to the network")]
Ping {
#[arg(short, long, help = "Network name to test")]
network: Option<String>,
},
}
pub async fn handle_network_command(
args: NetworkArgs,
state: &mut CliState,
) -> Result<(), CliError> {
match args.command {
NetworkCommands::Connect { network } => handle_connect_network(network, state).await,
NetworkCommands::Status => handle_network_status(state).await,
NetworkCommands::List => handle_list_networks(state).await,
NetworkCommands::Add { name, url, network_type, chain_id } => {
handle_add_network(name, url, network_type, chain_id, state).await
},
NetworkCommands::Remove { name } => handle_remove_network(name, state).await,
NetworkCommands::Peers => handle_show_peers(state).await,
NetworkCommands::Block { height, index } => {
handle_show_block(height.or(index), state).await
},
NetworkCommands::Ping { network } => handle_ping_network(network, state).await,
}
}
async fn handle_connect_network(
network: Option<String>,
state: &mut CliState,
) -> Result<(), CliError> {
print_section_header("Connecting to Network");
let target_network = if let Some(network_name) = network {
if network_name.starts_with("http") {
NetworkConfig {
name: "Custom".to_string(),
rpc_url: network_name,
network_type: "custom".to_string(),
chain_id: 0,
is_default: false,
}
} else {
state
.networks
.iter()
.find(|n| n.name.to_lowercase().contains(&network_name.to_lowercase()))
.cloned()
.ok_or_else(|| CliError::Network(format!("Network '{}' not found", network_name)))?
}
} else {
let network_names: Vec<&str> = state.networks.iter().map(|n| n.name.as_str()).collect();
let selection =
prompt_select("Select a network:", &network_names).map_err(|e| CliError::Io(e))?;
state.networks[selection].clone()
};
let client = with_loading("Testing connection...", async {
let url =
Url::parse(&target_network.rpc_url).map_err(|e| format!("Invalid RPC URL: {}", e))?;
let provider = HttpProvider::new(url).unwrap();
Ok::<_, String>(RpcClient::new(provider))
})
.await
.map_err(|e| CliError::Network(e))?;
match client.get_block_count().await {
Ok(block_count) => {
state.current_network = Some(target_network.clone());
state.rpc_client = Some(client);
let mut table = create_table();
table.add_row(vec![
Cell::new("Network").fg(Color::Cyan),
Cell::new(&target_network.name).fg(Color::Green),
]);
table.add_row(vec![
Cell::new("RPC URL").fg(Color::Cyan),
Cell::new(&target_network.rpc_url).fg(Color::Green),
]);
table.add_row(vec![
Cell::new("Type").fg(Color::Cyan),
Cell::new(&target_network.network_type).fg(Color::Green),
]);
table.add_row(vec![
Cell::new("Block Height").fg(Color::Cyan),
Cell::new(block_count.to_string()).fg(Color::Green),
]);
table.add_row(vec![
Cell::new("Status").fg(Color::Cyan),
Cell::new(format!("{} Connected", status_indicator("success"))).fg(Color::Green),
]);
println!("{table}");
print_success("🌐 Successfully connected to network!");
},
Err(e) => {
return Err(CliError::Network(format!("Failed to connect: {}", e)));
},
}
Ok(())
}
async fn handle_network_status(state: &CliState) -> Result<(), CliError> {
print_section_header("Network Status");
let network = state
.current_network
.as_ref()
.ok_or_else(|| CliError::Network("No network connected".to_string()))?;
let client = state
.rpc_client
.as_ref()
.ok_or_else(|| CliError::Network("No RPC client available".to_string()))?;
let (block_count, version) = with_loading("Fetching network information...", async {
let block_count = client.get_block_count().await.unwrap_or(0);
let version = client
.get_version()
.await
.map(|v| format!("{:?}", v))
.unwrap_or_else(|_| "Unknown".to_string());
(block_count, version)
})
.await;
let mut table = create_table();
table.add_row(vec![
Cell::new("Network Name").fg(Color::Cyan),
Cell::new(&network.name).fg(Color::Green),
]);
table.add_row(vec![
Cell::new("RPC Endpoint").fg(Color::Cyan),
Cell::new(&network.rpc_url).fg(Color::Blue),
]);
table.add_row(vec![
Cell::new("Network Type").fg(Color::Cyan),
Cell::new(&network.network_type).fg(Color::Yellow),
]);
table.add_row(vec![
Cell::new("Chain ID").fg(Color::Cyan),
Cell::new(network.chain_id.to_string()).fg(Color::Magenta),
]);
table.add_row(vec![
Cell::new("Block Height").fg(Color::Cyan),
Cell::new(block_count.to_string()).fg(Color::Green),
]);
table.add_row(vec![
Cell::new("Node Version").fg(Color::Cyan),
Cell::new(version).fg(Color::Blue),
]);
table.add_row(vec![
Cell::new("Connection").fg(Color::Cyan),
Cell::new(format!("{} Active", status_indicator("success"))).fg(Color::Green),
]);
println!("{table}");
Ok(())
}
async fn handle_list_networks(state: &CliState) -> Result<(), CliError> {
print_section_header("Available Networks");
if state.networks.is_empty() {
print_warning("No networks configured");
return Ok(());
}
let mut table = create_table();
table.set_header(vec![
Cell::new("Name").fg(Color::Cyan),
Cell::new("Type").fg(Color::Cyan),
Cell::new("RPC URL").fg(Color::Cyan),
Cell::new("Status").fg(Color::Cyan),
]);
for network in &state.networks {
let is_current =
state.current_network.as_ref().map(|n| n.name == network.name).unwrap_or(false);
table.add_row(vec![
Cell::new(&network.name).fg(if is_current { Color::Green } else { Color::White }),
Cell::new(&network.network_type).fg(Color::Yellow),
Cell::new(&network.rpc_url).fg(Color::Blue),
Cell::new(if is_current {
format!("{} Current", status_indicator("success"))
} else if network.is_default {
format!("{} Default", status_indicator("info"))
} else {
format!("{} Available", status_indicator("info"))
})
.fg(if is_current { Color::Green } else { Color::White }),
]);
}
println!("{table}");
print_info(&format!("Total networks: {}", state.networks.len()));
Ok(())
}
async fn handle_add_network(
name: String,
url: String,
network_type: String,
chain_id: Option<u32>,
state: &mut CliState,
) -> Result<(), CliError> {
print_section_header("Adding Network");
if state.networks.iter().any(|n| n.name == name) {
return Err(CliError::Network(format!("Network '{}' already exists", name)));
}
let client = with_loading("Testing network connection...", async {
let url = Url::parse(&url).map_err(|e| format!("Invalid RPC URL: {}", e))?;
let provider = HttpProvider::new(url).unwrap();
Ok::<_, String>(RpcClient::new(provider))
})
.await
.map_err(|e| CliError::Network(e))?;
let actual_chain_id = match client.get_version().await {
Ok(_version) => {
chain_id.unwrap_or(0)
},
Err(e) => {
let proceed =
prompt_yes_no(&format!("Failed to connect to network ({}). Add anyway?", e))
.map_err(|e| CliError::Io(e))?;
if !proceed {
print_warning("Network addition cancelled");
return Ok(());
}
chain_id.unwrap_or(0)
},
};
let new_network = NetworkConfig {
name: name.clone(),
rpc_url: url,
network_type,
chain_id: actual_chain_id,
is_default: false,
};
state.networks.push(new_network);
let mut table = create_table();
table.add_row(vec![Cell::new("Name").fg(Color::Cyan), Cell::new(&name).fg(Color::Green)]);
table.add_row(vec![
Cell::new("Status").fg(Color::Cyan),
Cell::new(format!("{} Added Successfully", status_indicator("success"))).fg(Color::Green),
]);
println!("{table}");
print_success("✅ Network added successfully!");
Ok(())
}
async fn handle_remove_network(name: String, state: &mut CliState) -> Result<(), CliError> {
print_section_header("Removing Network");
let network_index = state
.networks
.iter()
.position(|n| n.name == name)
.ok_or_else(|| CliError::Network(format!("Network '{}' not found", name)))?;
let _network = &state.networks[network_index];
if state.current_network.as_ref().map(|n| &n.name) == Some(&name) {
print_warning("Cannot remove the currently connected network");
return Ok(());
}
let confirm =
prompt_yes_no(&format!("Remove network '{}'?", name)).map_err(|e| CliError::Io(e))?;
if !confirm {
print_warning("Network removal cancelled");
return Ok(());
}
state.networks.remove(network_index);
print_success(&format!("🗑️ Network '{}' removed successfully", name));
Ok(())
}
async fn handle_show_peers(_state: &CliState) -> Result<(), CliError> {
Err(CliError::NotImplemented(
"Network peers query requires comprehensive network topology integration. \
Professional implementation includes:\n\n\
1. Advanced RPC getpeers method implementation\n\
2. Complete peer connection status and health monitoring\n\
3. Professional geographical location and latency tracking\n\
4. Comprehensive connection quality and version compatibility\n\
5. Advanced network topology visualization\n\n\
For peer information, check the Neo network explorer or node status directly."
.to_string(),
))
}
async fn handle_show_block(_height: Option<u32>, _state: &CliState) -> Result<(), CliError> {
Err(CliError::NotImplemented(
"Block information query requires comprehensive blockchain integration. \
Professional implementation includes:\n\n\
1. Advanced RPC getblock method with detailed parsing\n\
2. Complete transaction list and witness data formatting\n\
3. Professional block validation and merkle root verification\n\
4. Comprehensive historical block navigation and search\n\
5. Advanced performance optimization for large blocks\n\n\
For block information, use blockchain explorers or direct RPC calls."
.to_string(),
))
}
async fn handle_ping_network(_network: Option<String>, _state: &CliState) -> Result<(), CliError> {
Err(CliError::NotImplemented(
"Network connectivity testing requires comprehensive network analysis integration. \
Professional implementation includes:\n\n\
1. Advanced multi-endpoint latency measurement\n\
2. Complete connection stability and timeout handling\n\
3. Professional bandwidth and throughput testing\n\
4. Comprehensive network health scoring and recommendations\n\
5. Advanced continuous monitoring and alerting\n\n\
For network testing, use external monitoring tools or manual RPC calls."
.to_string(),
))
}
async fn connect_to_network(network_index: usize, state: &mut CliState) -> Result<(), CliError> {
let target_network = &state.networks[network_index];
print_info(&format!("Connecting to {} network...", target_network.name));
let url = Url::parse(&target_network.rpc_url)
.map_err(|e| CliError::Network(format!("Invalid RPC URL: {}", e)))?;
let provider = HttpProvider::new(url).unwrap();
let client = RpcClient::new(provider);
match client.get_block_count().await {
Ok(block_count) => {
print_success(&format!(
"Connected to {} (block: {})",
target_network.name, block_count
));
state.rpc_client = Some(client);
state.current_network = Some(target_network.clone());
},
Err(e) => {
print_error(&format!("Failed to connect: {}", e));
return Err(CliError::Network(format!("Connection failed: {}", e)));
},
}
Ok(())
}
async fn test_network_connection(url: String) -> Result<(), CliError> {
print_info(&format!("Testing connection to {}...", url));
let parsed_url =
Url::parse(&url).map_err(|e| CliError::Network(format!("Invalid RPC URL: {}", e)))?;
let provider = HttpProvider::new(parsed_url).unwrap();
let client = RpcClient::new(provider);
match client.get_version().await {
Ok(version) => {
print_success(&format!("✅ Connection successful"));
println!(" Version: {:?}", version);
},
Err(e) => {
print_error(&format!("❌ Connection failed: {}", e));
return Err(CliError::Network(format!("Connection test failed: {}", e)));
},
}
Ok(())
}