use chrono::{DateTime, Local};
use pnet::datalink::NetworkInterface;
use pnet::util::MacAddr;
use ratatui::text::Line;
use serde::{
de::{self, Deserializer, Visitor},
Deserialize, Serialize,
};
use std::{fmt, net::Ipv4Addr};
use crate::{
components::{packetdump::ArpPacketData, wifi_scan::WifiInfo},
enums::{ExportData, PacketTypeEnum, PacketsInfoTypesEnum, TabsEnum},
mode::Mode,
};
#[derive(Debug, Clone, PartialEq)]
pub enum Action {
Tick,
Render,
Resize(u16, u16),
Suspend,
Resume,
Quit,
Refresh,
Error(String),
Help,
Up,
Down,
Left,
Right,
Tab,
TabChange(TabsEnum),
GraphToggle,
DumpToggle,
InterfaceSwitch,
ScanCidr,
ActiveInterface(NetworkInterface),
ArpRecieve(ArpPacketData),
Scan(Vec<WifiInfo>),
AppModeChange(Mode),
ModeChange(Mode),
PingIp(String),
ArpRetry(String),
HostnameUpdate(String, String),
CountIp,
CidrError,
PacketDump(DateTime<Local>, PacketsInfoTypesEnum, PacketTypeEnum),
PortScan(usize, u16),
PortScanDone(usize),
Clear,
Export,
ExportData(ExportData),
}
impl<'de> Deserialize<'de> for Action {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct ActionVisitor;
impl<'de> Visitor<'de> for ActionVisitor {
type Value = Action;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid string representation of Action")
}
fn visit_str<E>(self, value: &str) -> Result<Action, E>
where
E: de::Error,
{
match value {
"InputMode" => Ok(Action::ModeChange(Mode::Input)),
"NormalMode" => Ok(Action::ModeChange(Mode::Normal)),
"Graph" => Ok(Action::GraphToggle),
"Dump" => Ok(Action::DumpToggle),
"Interface" => Ok(Action::InterfaceSwitch),
"Scan" => Ok(Action::ScanCidr),
"Clear" => Ok(Action::Clear),
"Up" => Ok(Action::Up),
"Down" => Ok(Action::Down),
"Left" => Ok(Action::Left),
"Right" => Ok(Action::Right),
"Tab" => Ok(Action::Tab),
"Export" => Ok(Action::Export),
"JumpDiscovery" => Ok(Action::TabChange(TabsEnum::Discovery)),
"JumpPackets" => Ok(Action::TabChange(TabsEnum::Packets)),
"JumpPorts" => Ok(Action::TabChange(TabsEnum::Ports)),
"JumpSniffer" => Ok(Action::TabChange(TabsEnum::Traffic)),
"Tick" => Ok(Action::Tick),
"Render" => Ok(Action::Render),
"Suspend" => Ok(Action::Suspend),
"Resume" => Ok(Action::Resume),
"Quit" => Ok(Action::Quit),
"Refresh" => Ok(Action::Refresh),
"Help" => Ok(Action::Help),
data if data.starts_with("Error(") => {
let error_msg = data.trim_start_matches("Error(").trim_end_matches(')');
Ok(Action::Error(error_msg.to_string()))
}
data if data.starts_with("HostnameUpdate(") => {
let inner = data.trim_start_matches("HostnameUpdate(").trim_end_matches(')');
let parts: Vec<&str> = inner.splitn(2, ",").collect();
if parts.len() == 2 {
Ok(Action::HostnameUpdate(
parts[0].trim().to_string(),
parts[1].trim().to_string(),
))
} else {
Err(E::custom(format!("Invalid HostnameUpdate format: {}", value)))
}
}
data if data.starts_with("Resize(") => {
let parts: Vec<&str> = data
.trim_start_matches("Resize(")
.trim_end_matches(')')
.split(',')
.collect();
if parts.len() == 2 {
let width: u16 = parts[0].trim().parse().map_err(E::custom)?;
let height: u16 = parts[1].trim().parse().map_err(E::custom)?;
Ok(Action::Resize(width, height))
} else {
Err(E::custom(format!("Invalid Resize format: {}", value)))
}
}
_ => Err(E::custom(format!("Unknown Action variant: {}", value))),
}
}
}
deserializer.deserialize_str(ActionVisitor)
}
}