rnp 0.1.146

A simple layer 4 ping tool for cloud.
Documentation
use crate::{PingClientFactory, PingResultDto, PingResultProcessor, PortRangeList};
use std::fmt;
use std::fmt::Debug;
use std::net::{IpAddr, SocketAddr};
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use std::{path::PathBuf, time::Duration};

pub const RNP_NAME: &str = "Rnp";
pub const RNP_SERVER_NAME: &str = "Rnp Server";
pub const RNP_AUTHOR: &str = "r12f (r12f.com, github.com/r12f)";
pub const RNP_ABOUT: &str = "A simple layer 4 ping tool for cloud.";

#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum RnpSupportedProtocol {
    TCP,
    QUIC,
    External(String),
}

impl FromStr for RnpSupportedProtocol {
    type Err = String;

    fn from_str(input: &str) -> Result<RnpSupportedProtocol, Self::Err> {
        match input.to_uppercase().as_str() {
            "TCP" => Ok(RnpSupportedProtocol::TCP),
            "QUIC" => Ok(RnpSupportedProtocol::QUIC),
            _ => Err(String::from("Invalid protocol")),
        }
    }
}

impl fmt::Display for RnpSupportedProtocol {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let protocol = match self {
            RnpSupportedProtocol::TCP => "TCP",
            RnpSupportedProtocol::QUIC => "QUIC",
            RnpSupportedProtocol::External(p) => &p,
        };

        write!(f, "{}", protocol)
    }
}

pub struct RnpPingRunnerConfig {
    pub worker_config: PingWorkerConfig,
    pub worker_scheduler_config: PingWorkerSchedulerConfig,
    pub result_processor_config: PingResultProcessorConfig,
    pub external_ping_client_factory: Option<PingClientFactory>,
    pub extra_ping_result_processors: Vec<Box<dyn PingResultProcessor + Send + Sync>>,
}

impl Debug for RnpPingRunnerConfig {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("RnpPingRunnerConfig")
            .field("worker_config", &self.worker_config)
            .field("worker_scheduler_config", &self.worker_scheduler_config)
            .field("result_processor_config", &self.result_processor_config)
            .field(
                "external_ping_client_factory",
                &if self.external_ping_client_factory.is_some() { "Some(PingClientFactory)".to_string() } else { "None".to_string() },
            )
            .field("extra_ping_result_processors", &self.extra_ping_result_processors.iter().map(|p| p.name()).collect::<Vec<&'static str>>())
            .finish()
    }
}

impl PartialEq for RnpPingRunnerConfig {
    fn eq(&self, other: &RnpPingRunnerConfig) -> bool {
        if self.worker_config != other.worker_config {
            return false;
        }
        if self.worker_scheduler_config != other.worker_scheduler_config {
            return false;
        }
        if self.result_processor_config != other.result_processor_config {
            return false;
        }
        if self.external_ping_client_factory.is_some() != other.external_ping_client_factory.is_some() {
            return false;
        }
        let matching_processor_count =
            self.extra_ping_result_processors.iter().zip(other.extra_ping_result_processors.iter()).filter(|&(a, b)| a.name() == b.name()).count();
        return matching_processor_count == self.extra_ping_result_processors.len()
            && matching_processor_count == other.extra_ping_result_processors.len();
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct PingWorkerConfig {
    pub protocol: RnpSupportedProtocol,
    pub target: SocketAddr,
    pub source_ip: IpAddr,
    pub ping_interval: Duration,
    pub ping_client_config: PingClientConfig,
}

#[derive(Debug, Clone, PartialEq)]
pub struct PingClientConfig {
    pub wait_timeout: Duration,
    pub time_to_live: Option<u32>,
    pub check_disconnect: bool,
    pub wait_before_disconnect: Duration,
    pub disconnect_timeout: Duration,
    pub server_name: Option<String>,
    pub log_tls_key: bool,
    pub alpn_protocol: Option<String>,
    pub use_timer_rtt: bool,
}

#[derive(Debug, Clone, PartialEq)]
pub struct PingWorkerSchedulerConfig {
    pub source_ports: PortRangeList,
    pub ping_count: Option<u32>,
    pub warmup_count: u32,
    pub parallel_ping_count: u32,
}

#[derive(Debug, Clone, PartialEq)]
pub struct PingResultProcessorCommonConfig {
    pub quiet_level: i32,
}

pub const RNP_QUIET_LEVEL_NONE: i32 = 0;
pub const RNP_QUIET_LEVEL_NO_PING_RESULT: i32 = 1;
pub const RNP_QUIET_LEVEL_NO_PING_SUMMARY: i32 = 2;
pub const RNP_QUIET_LEVEL_NO_OUTPUT: i32 = 3;

#[derive(Debug, Clone)]
pub struct PingResultProcessorConfig {
    pub common_config: PingResultProcessorCommonConfig,
    pub exit_on_fail: bool,
    pub exit_failure_reason: Option<Arc<Mutex<Option<PingResultDto>>>>,
    pub csv_log_path: Option<PathBuf>,
    pub json_log_path: Option<PathBuf>,
    pub text_log_path: Option<PathBuf>,
    pub show_result_scatter: bool,
    pub show_latency_scatter: bool,
    pub latency_buckets: Option<Vec<f64>>,
}

impl PartialEq for PingResultProcessorConfig {
    fn eq(&self, other: &PingResultProcessorConfig) -> bool {
        if self.common_config != other.common_config {
            return false;
        }
        if self.exit_on_fail != other.exit_on_fail {
            return false;
        }
        if self.exit_failure_reason.is_some() != other.exit_failure_reason.is_some() {
            return false;
        }
        if self.csv_log_path != other.csv_log_path {
            return false;
        }
        if self.json_log_path != other.json_log_path {
            return false;
        }
        if self.text_log_path != other.text_log_path {
            return false;
        }
        if self.show_result_scatter != other.show_result_scatter {
            return false;
        }
        if self.show_latency_scatter != other.show_latency_scatter {
            return false;
        }
        if self.latency_buckets != other.latency_buckets {
            return false;
        }
        return true;
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct RnpStubServerConfig {
    pub protocol: RnpSupportedProtocol,
    pub server_address: SocketAddr,
    pub report_interval: Duration,
    pub close_on_accept: bool,
    pub write_chunk_size: usize,
    pub write_count_limit: u32,
    pub sleep_before_write: Duration,
    pub wait_before_disconnect: Duration,
}