use crate::utils::{truncate_name, DEFAULT_NAME_MAX_LEN};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub enum As2relSortOrder {
#[default]
ConnectedDesc,
Asn2Asc,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub enum As2relOutputFormat {
#[default]
Markdown,
Pretty,
Json,
}
#[derive(Debug, Clone, Serialize, Deserialize, tabled::Tabled)]
pub struct As2relSearchResult {
pub asn1: u32,
pub asn2: u32,
#[tabled(skip)]
pub asn2_name: Option<String>,
pub connected: String,
#[tabled(skip)]
pub connected_pct: f32,
pub peer: String,
pub as1_upstream: String,
pub as2_upstream: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, tabled::Tabled)]
pub struct As2relSearchResultWithName {
pub asn1: u32,
pub asn2: u32,
pub asn2_name: String,
pub connected: String,
pub peer: String,
pub as1_upstream: String,
pub as2_upstream: String,
}
impl As2relSearchResult {
pub fn with_name(self, truncate: bool) -> As2relSearchResultWithName {
let name = self.asn2_name.unwrap_or_default();
let display_name = if truncate {
truncate_name(&name, DEFAULT_NAME_MAX_LEN)
} else {
name
};
As2relSearchResultWithName {
asn1: self.asn1,
asn2: self.asn2,
asn2_name: display_name,
connected: self.connected,
peer: self.peer,
as1_upstream: self.as1_upstream,
as2_upstream: self.as2_upstream,
}
}
pub fn from_aggregated(
asn1: u32,
asn2: u32,
asn2_name: Option<String>,
connected_count: u32,
as1_upstream_count: u32,
as2_upstream_count: u32,
max_peers: u32,
) -> Self {
let format_pct = |count: u32| -> String {
if max_peers > 0 {
format!("{:.1}%", (count as f32 / max_peers as f32) * 100.0)
} else {
"0.0%".to_string()
}
};
let connected_pct = if max_peers > 0 {
(connected_count as f32 / max_peers as f32) * 100.0
} else {
0.0
};
let peer_count = connected_count
.saturating_sub(as1_upstream_count)
.saturating_sub(as2_upstream_count);
Self {
asn1,
asn2,
asn2_name,
connected: format!("{:.1}%", connected_pct),
connected_pct,
peer: format_pct(peer_count),
as1_upstream: format_pct(as1_upstream_count),
as2_upstream: format_pct(as2_upstream_count),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct As2relDataMeta {
pub file_url: String,
pub last_updated: u64,
pub max_peers_count: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct As2relUpdateProgress {
pub stage: As2relUpdateStage,
pub current: usize,
pub total: Option<usize>,
pub message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum As2relUpdateStage {
Downloading,
Parsing,
Inserting,
Complete,
Error,
}
impl As2relUpdateProgress {
pub fn downloading(url: &str) -> Self {
Self {
stage: As2relUpdateStage::Downloading,
current: 0,
total: None,
message: format!("Downloading from {}...", url),
}
}
pub fn parsing() -> Self {
Self {
stage: As2relUpdateStage::Parsing,
current: 0,
total: None,
message: "Parsing data...".to_string(),
}
}
pub fn inserting(current: usize, total: usize) -> Self {
Self {
stage: As2relUpdateStage::Inserting,
current,
total: Some(total),
message: format!("Inserting records ({}/{})", current, total),
}
}
pub fn complete(entry_count: usize) -> Self {
Self {
stage: As2relUpdateStage::Complete,
current: entry_count,
total: Some(entry_count),
message: format!("Complete: {} relationship entries", entry_count),
}
}
pub fn error(message: &str) -> Self {
Self {
stage: As2relUpdateStage::Error,
current: 0,
total: None,
message: message.to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_search_result_from_aggregated() {
let result = As2relSearchResult::from_aggregated(
65000,
65001,
Some("Test Org".to_string()),
100, 30, 20, 200, );
assert_eq!(result.asn1, 65000);
assert_eq!(result.asn2, 65001);
assert_eq!(result.asn2_name, Some("Test Org".to_string()));
assert_eq!(result.connected, "50.0%"); assert_eq!(result.peer, "25.0%"); assert_eq!(result.as1_upstream, "15.0%"); assert_eq!(result.as2_upstream, "10.0%"); }
#[test]
fn test_search_result_with_name() {
let result = As2relSearchResult {
asn1: 65000,
asn2: 65001,
asn2_name: Some("Test Org".to_string()),
connected: "50.0%".to_string(),
connected_pct: 50.0,
peer: "50.0%".to_string(),
as1_upstream: "30.0%".to_string(),
as2_upstream: "20.0%".to_string(),
};
let with_name = result.with_name(false);
assert_eq!(with_name.asn2_name, "Test Org");
}
#[test]
fn test_update_progress() {
let prog = As2relUpdateProgress::downloading("https://example.com/data.json");
assert_eq!(prog.stage, As2relUpdateStage::Downloading);
let prog = As2relUpdateProgress::complete(1000);
assert_eq!(prog.stage, As2relUpdateStage::Complete);
assert_eq!(prog.current, 1000);
}
}