use crate::tui::action::LensData;
pub fn matches(text: &str, filter: &str) -> bool {
filter.is_empty() || text.to_lowercase().contains(&filter.to_lowercase())
}
pub fn is_filterable(lens_key: &str) -> bool {
matches!(lens_key, "subdomains" | "history" | "propagation")
}
fn history_label(e: &seer_core::HistoryEntry) -> String {
let source = if e.result.is_rdap() {
"RDAP"
} else if e.result.is_whois() {
"WHOIS"
} else {
"-"
};
format!(
"{} {} {} {}",
e.timestamp.format("%Y-%m-%d %H:%M"),
e.domain,
source,
e.result.registrar().unwrap_or_default(),
)
}
pub fn apply(data: &LensData, filter: &str) -> Option<LensData> {
if filter.is_empty() {
return None;
}
match data {
LensData::Subdomains(s) => {
let mut r = (**s).clone();
r.subdomains.retain(|host| matches(host, filter));
r.count = r.subdomains.len();
Some(LensData::Subdomains(Box::new(r)))
}
LensData::History(entries) => {
let filtered: Vec<_> = entries
.iter()
.filter(|e| matches(&history_label(e), filter))
.cloned()
.collect();
Some(LensData::History(filtered))
}
LensData::Prop(p) => {
let mut r = (**p).clone();
r.results.retain(|sr| {
let answer = sr
.records
.first()
.map(|rec| rec.format_short())
.unwrap_or_default();
let label = format!(
"{} {} {} {}",
sr.server.ip, sr.server.provider, sr.server.location, answer
);
matches(&label, filter)
});
Some(LensData::Prop(Box::new(r)))
}
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn matches_is_case_insensitive_and_empty_matches_all() {
assert!(matches("API.example.com", "api"));
assert!(matches("anything", ""));
assert!(!matches("host.example.com", "zzz"));
}
#[test]
fn is_filterable_covers_the_table_lenses() {
assert!(is_filterable("subdomains"));
assert!(is_filterable("history"));
assert!(is_filterable("propagation"));
assert!(!is_filterable("whois"));
}
#[test]
fn apply_filters_subdomains_and_updates_count() {
let result = seer_core::SubdomainResult {
domain: "example.com".into(),
subdomains: vec![
"api.example.com".into(),
"mail.example.com".into(),
"api-staging.example.com".into(),
],
source: "crt.sh".into(),
count: 3,
};
let data = LensData::Subdomains(Box::new(result));
let filtered = apply(&data, "api").expect("filter applies");
let LensData::Subdomains(s) = filtered else {
panic!("wrong variant");
};
assert_eq!(s.subdomains.len(), 2);
assert_eq!(s.count, 2);
assert!(s.subdomains.iter().all(|h| h.contains("api")));
}
#[test]
fn apply_returns_none_for_empty_filter_or_unfilterable_lens() {
let result = seer_core::SubdomainResult {
domain: "example.com".into(),
subdomains: vec!["a.example.com".into()],
source: "crt.sh".into(),
count: 1,
};
let data = LensData::Subdomains(Box::new(result));
assert!(apply(&data, "").is_none());
}
}