use crossterm::event::{KeyCode, KeyEvent};
use seer_core::RecordType;
use crate::tui::action::FetchReq;
use crate::tui::panes::PaneOutcome;
const RESOLVERS: &[&str] = &["8.8.8.8", "1.1.1.1", "9.9.9.9", "208.67.222.222"];
pub struct CompareState {
pub a_idx: usize,
pub b_idx: usize,
pub a: String,
pub b: String,
}
impl Default for CompareState {
fn default() -> Self {
Self {
a_idx: 0,
b_idx: 1,
a: RESOLVERS[0].to_string(),
b: RESOLVERS[1].to_string(),
}
}
}
impl CompareState {
fn build_fetch(&self, domain: &str) -> PaneOutcome {
PaneOutcome::Fetch(FetchReq::Compare {
domain: domain.to_string(),
record_type: RecordType::A,
a: self.a.clone(),
b: self.b.clone(),
})
}
pub fn handle_key(&mut self, key: KeyEvent, domain: Option<&str>) -> Option<PaneOutcome> {
let domain = domain?;
match key.code {
KeyCode::Char('a') => {
self.a_idx = (self.a_idx + 1) % RESOLVERS.len();
self.a = RESOLVERS[self.a_idx].to_string();
Some(self.build_fetch(domain))
}
KeyCode::Char('b') => {
self.b_idx = (self.b_idx + 1) % RESOLVERS.len();
self.b = RESOLVERS[self.b_idx].to_string();
Some(self.build_fetch(domain))
}
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crossterm::event::KeyModifiers;
fn press(code: KeyCode) -> KeyEvent {
KeyEvent::new(code, KeyModifiers::NONE)
}
#[test]
fn default_state_has_google_and_cloudflare() {
let s = CompareState::default();
assert_eq!(s.a, "8.8.8.8");
assert_eq!(s.b, "1.1.1.1");
}
#[test]
fn b_cycles_and_returns_fetch_compare() {
let mut state = CompareState::default();
let outcome = state.handle_key(press(KeyCode::Char('b')), Some("x.com"));
assert_eq!(state.b, "9.9.9.9");
assert!(
matches!(
outcome,
Some(PaneOutcome::Fetch(FetchReq::Compare {
ref b, ..
})) if b == "9.9.9.9"
),
"expected Fetch(Compare{{b:9.9.9.9}}), got {outcome:?}",
);
}
#[test]
fn a_cycles_and_returns_fetch_compare() {
let mut state = CompareState::default();
let outcome = state.handle_key(press(KeyCode::Char('a')), Some("x.com"));
assert_eq!(state.a, "1.1.1.1");
assert!(matches!(
outcome,
Some(PaneOutcome::Fetch(FetchReq::Compare { .. }))
));
}
#[test]
fn without_domain_returns_none() {
let mut state = CompareState::default();
assert!(state.handle_key(press(KeyCode::Char('b')), None).is_none());
}
#[test]
fn unowned_keys_return_none() {
let mut state = CompareState::default();
assert!(state
.handle_key(press(KeyCode::Esc), Some("x.com"))
.is_none());
assert!(state
.handle_key(press(KeyCode::Tab), Some("x.com"))
.is_none());
assert!(state
.handle_key(press(KeyCode::Char('j')), Some("x.com"))
.is_none());
assert!(state
.handle_key(press(KeyCode::Char('l')), Some("x.com"))
.is_none());
}
#[test]
fn resolvers_wrap_around() {
let mut state = CompareState::default();
for _ in 0..RESOLVERS.len() {
state.handle_key(press(KeyCode::Char('a')), Some("x.com"));
}
assert_eq!(state.a_idx, 0);
assert_eq!(state.a, RESOLVERS[0]);
}
}