unify overview page reports and create favorite enums to include services and programs (WIP)

This commit is contained in:
GyulyVGC 2026-03-11 19:41:05 +01:00
parent ca5f349e39
commit 7b905baf6a
16 changed files with 600 additions and 420 deletions

View file

@ -1,9 +1,9 @@
use crate::countries::country_utils::get_computer_tooltip;
use crate::countries::country_utils::{get_computer_tooltip, get_flag_tooltip};
use crate::countries::flags_pictures::FLAGS_HEIGHT_BIG;
use crate::gui::components::header::get_button_settings;
use crate::gui::components::tab::get_pages_tabs;
use crate::gui::components::types::my_modal::MyModal;
use crate::gui::pages::overview_page::{get_bars, host_bar, simple_bar};
use crate::gui::pages::overview_page::{get_bars, item_bar};
use crate::gui::pages::types::settings_page::SettingsPage;
use crate::gui::styles::container::ContainerType;
use crate::gui::styles::rule::RuleType;
@ -198,13 +198,15 @@ fn favorite_notification_log<'a>(
data_repr: DataRepr,
language: Language,
) -> Container<'a, Message, StyleType> {
let host_bar = host_bar(
logged_notification.host.to_entry_string(),
logged_notification.host.country,
&logged_notification.data_info_host,
let host = &logged_notification.host;
let data_info_host = logged_notification.data_info_host;
let icon = get_flag_tooltip(host.country, &data_info_host, language, false);
let host_bar = item_bar(
icon,
host.to_entry_string(),
&data_info_host.data_info_fav.data_info,
data_repr,
first_entry_data_info,
language,
);
let content = Row::new()
@ -242,15 +244,15 @@ fn blacklisted_notification_log<'a>(
data_repr: DataRepr,
language: Language,
) -> Container<'a, Message, StyleType> {
let blacklisted_bar = host_bar(
logged_notification
.host
.to_blacklist_string(logged_notification.ip),
logged_notification.host.country,
&logged_notification.data_info_host,
let host = &logged_notification.host;
let data_info_host = logged_notification.data_info_host;
let icon = get_flag_tooltip(host.country, &data_info_host, language, false);
let blacklisted_bar = item_bar(
icon,
host.to_blacklist_string(logged_notification.ip),
&data_info_host.data_info_fav.data_info,
data_repr,
first_entry_data_info,
language,
);
let content = Row::new()
@ -427,15 +429,16 @@ fn data_notification_extra<'a>(
.first()
.unwrap_or(&(Host::default(), DataInfoHost::default()))
.1
.data_info_fav
.data_info;
for (host, data_info_host) in &logged_notification.hosts {
let host_bar = host_bar(
let icon = get_flag_tooltip(host.country, &data_info_host, language, false);
let host_bar = item_bar(
icon,
host.to_entry_string(),
host.country,
data_info_host,
&data_info_host.data_info_fav.data_info,
logged_notification.data_repr,
first_data_info,
language,
);
hosts_col = hosts_col.push(host_bar);
}
@ -447,7 +450,7 @@ fn data_notification_extra<'a>(
.unwrap_or(&(Service::default(), DataInfo::default()))
.1;
for (service, data_info) in &logged_notification.services {
let service_bar = simple_bar(
let service_bar = item_bar(
None::<Element<Message, StyleType>>,
service.to_string(),
data_info,

View file

@ -4,8 +4,6 @@
//! and overall statistics about the traffic.
use crate::chart::types::donut_chart::donut_chart;
use crate::countries::country_utils::get_flag_tooltip;
use crate::countries::types::country::Country;
use crate::gui::components::ellipsized_text::EllipsizedText;
use crate::gui::components::tab::get_pages_tabs;
use crate::gui::pages::initial_page::get_addresses_row;
@ -16,32 +14,21 @@ use crate::gui::styles::rule::RuleType;
use crate::gui::styles::scrollbar::ScrollbarType;
use crate::gui::styles::style_constants::{FONT_SIZE_FOOTER, FONT_SIZE_TITLE, TOOLTIP_DELAY};
use crate::gui::styles::text::TextType;
use crate::gui::types::conf::Conf;
use crate::gui::types::favorite::Favorite;
use crate::gui::types::filters::Filters;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::networking::types::capture_context::CaptureSource;
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::Host;
use crate::networking::types::program_lookup::ProgramLookup;
use crate::report::get_report_entries::{
get_host_entries, get_program_entries, get_service_entries,
};
use crate::report::types::search_parameters::SearchParameters;
use crate::report::types::sort_type::SortType;
use crate::translations::translations::{
active_filters_translation, incoming_translation, none_translation, outgoing_translation,
traffic_rate_translation,
};
use crate::translations::translations_2::{
data_representation_translation, dropped_translation, host_translation,
only_top_30_items_translation,
data_representation_translation, dropped_translation, only_top_30_items_translation,
};
use crate::translations::translations_3::service_translation;
use crate::translations::translations_5::program_translation;
use crate::utils::types::icon::Icon;
use crate::{Language, RunningPage, StyleType};
use iced::Length::Fill;
use iced::alignment::{Horizontal, Vertical};
@ -89,64 +76,55 @@ pub fn overview_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
Container::new(Column::new().push(tab_and_body.push(body))).height(Length::Fill)
}
fn row_report<'a>(sniffer: &Sniffer) -> Row<'a, Message, StyleType> {
let col_host = col_host(sniffer);
let col_service = col_service(sniffer);
let container_program = sniffer.program_lookup.as_ref().map(|program_lookup| {
Container::new(col_program(&sniffer.conf, program_lookup))
.width(Length::FillPortion(1))
.height(Length::Fill)
.padding(Padding::new(10.0).top(0).bottom(5))
.class(ContainerType::BorderedRound)
});
fn row_report(sniffer: &Sniffer) -> Row<Message, StyleType> {
let col_host = col_favorite_item(sniffer, Favorite::Host);
let col_service = col_favorite_item(sniffer, Favorite::Service);
let col_program = col_favorite_item(sniffer, Favorite::Program);
Row::new()
.spacing(10)
.push(
Container::new(col_host)
.width(Length::FillPortion(2))
.height(Length::Fill)
.padding(Padding::new(10.0).top(0).bottom(5))
.class(ContainerType::BorderedRound),
)
.push(
Container::new(col_service)
.width(Length::FillPortion(1))
.height(Length::Fill)
.padding(Padding::new(10.0).top(0).bottom(5))
.class(ContainerType::BorderedRound),
)
.push(container_program)
.push(col_host)
.push(col_service)
.push(col_program)
}
fn col_host<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
fn col_favorite_item(
sniffer: &Sniffer,
favorite: Favorite,
) -> impl Into<Element<Message, StyleType>> {
let Settings { language, .. } = sniffer.conf.settings;
let data_repr = sniffer.conf.data_repr;
let program_lookup = sniffer.program_lookup.as_ref();
let mut scroll_host = Column::new()
if program_lookup.is_none() && favorite == Favorite::Program {
return None::<Element<Message, StyleType>>;
}
let mut scroll_item = Column::new()
.padding(Padding::ZERO.right(11.0))
.align_x(Alignment::Center);
let entries = get_host_entries(
let entries = favorite.get_entries(
&sniffer.info_traffic,
program_lookup,
data_repr,
sniffer.conf.host_sort_type,
);
let first_entry_data_info = entries
.iter()
.map(|(_, d)| d.data_info)
.map(|fi| fi.data_info())
.max_by(|d1, d2| d1.compare(d2, SortType::Ascending, data_repr))
.unwrap_or_default();
for (host, data_info_host) in &entries {
let star_button = get_star_button(data_info_host.is_favorite, host.clone());
for fi in &entries {
let star_button = fi.star_button();
let host_bar = host_bar(
host.to_entry_string(),
host.country,
data_info_host,
let icon = fi.icon(language, false, program_lookup);
let host_bar = item_bar(
icon,
fi.to_entry_string(),
&fi.data_info(),
data_repr,
first_entry_data_info,
language,
);
let content = Row::new()
@ -155,213 +133,184 @@ fn col_host<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
.push(star_button)
.push(host_bar);
scroll_host = scroll_host.push(
scroll_item = scroll_item.push(
button(content)
.padding(Padding::new(5.0).right(10))
.on_press(Message::Search(SearchParameters::new_host_search(host)))
.on_press(Message::Search(fi.new_entry_search()))
.class(ButtonType::Neutral),
);
}
if entries.len() >= 30 {
scroll_host = scroll_host
scroll_item = scroll_item
.push(Space::new().height(25))
.push(Text::new(only_top_30_items_translation(language)).align_x(Alignment::Center));
}
Column::new()
let col = Column::new()
.push(
Row::new()
.height(45)
.align_y(Alignment::Center)
.push(
Text::new(host_translation(language))
Text::new(favorite.title(language))
.class(TextType::Title)
.size(FONT_SIZE_TITLE),
)
.push(Space::new().width(Length::Fill))
.push(sort_arrows(
sniffer.conf.host_sort_type,
Message::HostSortSelection,
)),
.push(favorite.sort_arrows(&sniffer.conf)),
)
.push(
Scrollable::with_direction(
scroll_host,
scroll_item,
Direction::Vertical(ScrollbarType::properties()),
)
.width(Length::Fill),
)
}
fn col_service<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
let Settings { language, .. } = sniffer.conf.settings;
let data_repr = sniffer.conf.data_repr;
let mut scroll_service = Column::new()
.padding(Padding::ZERO.right(11.0))
.align_x(Alignment::Center);
let entries = get_service_entries(
&sniffer.info_traffic,
data_repr,
sniffer.conf.service_sort_type,
);
let first_entry_data_info = entries
.iter()
.map(|&(_, d)| d)
.max_by(|d1, d2| d1.compare(d2, SortType::Ascending, data_repr))
.unwrap_or_default();
for (service, data_info) in &entries {
let content = simple_bar(
None::<Element<Message, StyleType>>,
service.to_string(),
data_info,
data_repr,
first_entry_data_info,
);
scroll_service = scroll_service.push(
button(content)
.padding(Padding::new(5.0).right(15).left(10))
.on_press(Message::Search(SearchParameters::new_service_search(
service,
)))
.class(ButtonType::Neutral),
);
}
if entries.len() >= 30 {
scroll_service = scroll_service
.push(Space::new().height(25))
.push(Text::new(only_top_30_items_translation(language)).align_x(Alignment::Center));
}
Column::new()
.push(
Row::new()
.height(45)
.align_y(Alignment::Center)
.push(
Text::new(service_translation(language))
.class(TextType::Title)
.size(FONT_SIZE_TITLE),
)
.push(Space::new().width(Length::Fill))
.push(sort_arrows(
sniffer.conf.service_sort_type,
Message::ServiceSortSelection,
)),
)
.push(
Scrollable::with_direction(
scroll_service,
Direction::Vertical(ScrollbarType::properties()),
)
.width(Length::Fill),
)
Some(
Container::new(col)
.width(favorite.fill_portion())
.height(Length::Fill)
.padding(Padding::new(10.0).top(0).bottom(5))
.class(ContainerType::BorderedRound)
.into(),
)
}
fn col_program<'a>(conf: &Conf, program_lookup: &ProgramLookup) -> Column<'a, Message, StyleType> {
let Settings { language, .. } = conf.settings;
let data_repr = conf.data_repr;
// fn col_service<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
// let Settings { language, .. } = sniffer.conf.settings;
// let data_repr = sniffer.conf.data_repr;
//
// let mut scroll_service = Column::new()
// .padding(Padding::ZERO.right(11.0))
// .align_x(Alignment::Center);
// let entries = get_service_entries(
// &sniffer.info_traffic,
// data_repr,
// sniffer.conf.service_sort_type,
// );
// let first_entry_data_info = entries
// .iter()
// .map(|&(_, d)| d)
// .max_by(|d1, d2| d1.compare(d2, SortType::Ascending, data_repr))
// .unwrap_or_default();
//
// for (service, data_info) in &entries {
// let content = item_bar(
// None::<Element<Message, StyleType>>,
// service.to_string(),
// data_info,
// data_repr,
// first_entry_data_info,
// );
//
// scroll_service = scroll_service.push(
// button(content)
// .padding(Padding::new(5.0).right(15).left(10))
// .on_press(Message::Search(SearchParameters::new_service_search(
// service,
// )))
// .class(ButtonType::Neutral),
// );
// }
//
// if entries.len() >= 30 {
// scroll_service = scroll_service
// .push(Space::new().height(25))
// .push(Text::new(only_top_30_items_translation(language)).align_x(Alignment::Center));
// }
//
// Column::new()
// .push(
// Row::new()
// .height(45)
// .align_y(Alignment::Center)
// .push(
// Text::new(service_translation(language))
// .class(TextType::Title)
// .size(FONT_SIZE_TITLE),
// )
// .push(Space::new().width(Length::Fill))
// .push(sort_arrows(
// sniffer.conf.service_sort_type,
// Message::ServiceSortSelection,
// )),
// )
// .push(
// Scrollable::with_direction(
// scroll_service,
// Direction::Vertical(ScrollbarType::properties()),
// )
// .width(Length::Fill),
// )
// }
let mut scroll_program = Column::new()
.padding(Padding::ZERO.right(11.0))
.align_x(Alignment::Center);
let entries = get_program_entries(program_lookup, data_repr, conf.program_sort_type);
let first_entry_data_info = entries
.iter()
.map(|&(_, d)| d)
.max_by(|d1, d2| d1.compare(d2, SortType::Ascending, data_repr))
.unwrap_or_default();
// fn col_program<'a>(conf: &Conf, program_lookup: &ProgramLookup) -> Column<'a, Message, StyleType> {
// let Settings { language, .. } = conf.settings;
// let data_repr = conf.data_repr;
//
// let mut scroll_program = Column::new()
// .padding(Padding::ZERO.right(11.0))
// .align_x(Alignment::Center);
// let entries = get_program_entries(program_lookup, data_repr, conf.program_sort_type);
// let first_entry_data_info = entries
// .iter()
// .map(|&(_, d)| d)
// .max_by(|d1, d2| d1.compare(d2, SortType::Ascending, data_repr))
// .unwrap_or_default();
//
// for (program, data_info) in &entries {
// let content = item_bar(
// program_lookup.picon_tooltip(program.icon_key(), program.path()),
// program.to_string(),
// data_info,
// data_repr,
// first_entry_data_info,
// );
//
// scroll_program = scroll_program.push(
// button(content)
// .padding(Padding::new(5.0).right(15).left(10))
// .on_press(Message::Search(SearchParameters::new_program_search(
// program,
// )))
// .class(ButtonType::Neutral),
// );
// }
//
// if entries.len() >= 30 {
// scroll_program = scroll_program
// .push(Space::new().height(25))
// .push(Text::new(only_top_30_items_translation(language)).align_x(Alignment::Center));
// }
//
// Column::new()
// .push(
// Row::new()
// .height(45)
// .align_y(Alignment::Center)
// .push(
// Text::new(program_translation(language))
// .class(TextType::Title)
// .size(FONT_SIZE_TITLE),
// )
// .push(Space::new().width(Length::Fill))
// .push(sort_arrows(
// conf.program_sort_type,
// Message::ProgramSortSelection,
// )),
// )
// .push(
// Scrollable::with_direction(
// scroll_program,
// Direction::Vertical(ScrollbarType::properties()),
// )
// .width(Length::Fill),
// )
// }
for (program, data_info) in &entries {
let content = simple_bar(
program_lookup.picon_tooltip(program.icon_key(), program.path()),
program.to_string(),
data_info,
data_repr,
first_entry_data_info,
);
scroll_program = scroll_program.push(
button(content)
.padding(Padding::new(5.0).right(15).left(10))
.on_press(Message::Search(SearchParameters::new_program_search(
program,
)))
.class(ButtonType::Neutral),
);
}
if entries.len() >= 30 {
scroll_program = scroll_program
.push(Space::new().height(25))
.push(Text::new(only_top_30_items_translation(language)).align_x(Alignment::Center));
}
Column::new()
.push(
Row::new()
.height(45)
.align_y(Alignment::Center)
.push(
Text::new(program_translation(language))
.class(TextType::Title)
.size(FONT_SIZE_TITLE),
)
.push(Space::new().width(Length::Fill))
.push(sort_arrows(
conf.program_sort_type,
Message::ProgramSortSelection,
)),
)
.push(
Scrollable::with_direction(
scroll_program,
Direction::Vertical(ScrollbarType::properties()),
)
.width(Length::Fill),
)
}
pub fn host_bar<'a>(
item: String,
country: Country,
data_info_host: &DataInfoHost,
data_repr: DataRepr,
first_entry_data_info: DataInfo,
language: Language,
) -> Row<'a, Message, StyleType> {
Row::new()
.height(32)
.align_y(Alignment::Center)
.spacing(5)
.push(get_flag_tooltip(country, data_info_host, language, false))
.push(
Column::new()
.spacing(2)
.push(
Row::new()
.push(
EllipsizedText::new(item)
.wrapping(Wrapping::Glyph)
.width(Length::Fill),
)
.push(Text::new(data_repr.formatted_string(
data_info_host.data_info.tot_data(data_repr),
))),
)
.push(get_bars(
data_repr,
&first_entry_data_info,
&data_info_host.data_info,
)),
)
}
pub fn simple_bar<'a>(
pub fn item_bar<'a>(
icon: impl Into<Element<'a, Message, StyleType>>,
item: String,
data_info: &DataInfo,
@ -387,7 +336,7 @@ pub fn simple_bar<'a>(
data_repr.formatted_string(data_info.tot_data(data_repr)),
)),
)
.push(get_bars(data_repr, &first_entry_data_info, data_info)),
.push(get_bars(data_repr, &first_entry_data_info, &data_info)),
)
}
@ -693,26 +642,6 @@ pub fn get_bars<'a>(
})
}
fn get_star_button<'a>(is_favorite: bool, host: Host) -> Button<'a, Message, StyleType> {
let (icon, class) = if is_favorite {
(Icon::StarFull, ButtonType::Starred)
} else {
(Icon::StarEmpty, ButtonType::NotStarred)
};
button(
icon.to_text()
.size(16)
.align_x(Alignment::Center)
.align_y(Alignment::Center),
)
.padding(0)
.height(25)
.width(25)
.class(class)
.on_press(Message::AddOrRemoveFavorite(host, !is_favorite))
}
fn get_info_tooltip(tooltip_content: Element<Message, StyleType>) -> Tooltip<Message, StyleType> {
Tooltip::new(
Container::new(
@ -732,24 +661,6 @@ fn get_info_tooltip(tooltip_content: Element<Message, StyleType>) -> Tooltip<Mes
.delay(TOOLTIP_DELAY)
}
fn sort_arrows<'a>(
active_sort_type: SortType,
message: fn(SortType) -> Message,
) -> Container<'a, Message, StyleType> {
Container::new(
button(
active_sort_type
.icon()
.align_x(Alignment::Center)
.align_y(Alignment::Center),
)
.class(active_sort_type.button_type())
.on_press(message(active_sort_type.next_sort())),
)
.width(50.0)
.align_x(Alignment::Center)
}
#[cfg(test)]
mod tests {
use crate::gui::pages::overview_page::{MIN_BARS_LENGTH, get_bars_length};

View file

@ -9,11 +9,11 @@ use crate::gui::sniffer::Sniffer;
use crate::gui::styles::rule::RuleType;
use crate::gui::styles::style_constants::FONT_SIZE_FOOTER;
use crate::gui::styles::types::style_type::StyleType;
use crate::gui::types::favorite::{Favorite, FavoriteItem};
use crate::gui::types::message::Message;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::ThumbnailHost;
use crate::networking::types::info_traffic::InfoTraffic;
use crate::report::get_report_entries::{get_host_entries, get_service_entries};
use crate::report::types::sort_type::SortType;
use crate::translations::types::language::Language;
use crate::utils::formatted_strings::clip_text;
@ -92,10 +92,14 @@ fn host_col<'a>(
.padding([0, 5])
.spacing(3)
.width(Length::FillPortion(2));
let hosts = get_host_entries(info_traffic, data_repr, sort_type);
let hosts = Favorite::Host.get_entries(info_traffic, None, data_repr, sort_type);
let mut thumbnail_hosts = Vec::new();
for (host, data_info_host) in &hosts {
for fi in &hosts {
let FavoriteItem::Host((host, data_info_host)) = fi else {
continue;
};
let thumbnail_host = ThumbnailHost::from_host(host, MAX_CHARS_HOST);
let country = thumbnail_host.country;
let text = thumbnail_host.text.clone();
@ -128,9 +132,13 @@ fn service_col<'a>(
sort_type: SortType,
) -> Column<'a, Message, StyleType> {
let mut service_col = Column::new().padding([0, 5]).spacing(3).width(Length::Fill);
let services = get_service_entries(info_traffic, data_repr, sort_type);
let services = Favorite::Service.get_entries(info_traffic, None, data_repr, sort_type);
let n_entry = min(services.len(), MAX_ENTRIES);
for (service, _) in services.get(..n_entry).unwrap_or_default() {
for fi in services.get(..n_entry).unwrap_or_default() {
let FavoriteItem::Service((service, _)) = fi else {
continue;
};
service_col = service_col.push(
Text::new(clip_text(&service.to_string(), MAX_CHARS_SERVICE)).size(FONT_SIZE_FOOTER),
);

View file

@ -22,6 +22,7 @@ use crate::gui::styles::types::custom_palette::CustomPalette;
use crate::gui::styles::types::gradient_type::GradientType;
use crate::gui::styles::types::palette::Palette;
use crate::gui::types::conf::Conf;
use crate::gui::types::favorite::FavoriteKey;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::gui::types::timing_events::TimingEvents;
@ -96,8 +97,8 @@ pub struct Sniffer {
pub info_traffic: InfoTraffic,
/// Map of the resolved addresses with their full rDNS value and the corresponding host
pub addresses_resolved: HashMap<IpAddr, (String, Host)>,
/// Collection of the favorite hosts
pub favorite_hosts: HashSet<Host>,
/// Collection of the favorite hosts, services and programs
pub favorites: HashSet<FavoriteKey>,
/// Log of the displayed notifications, with the total number of notifications for this capture
pub logged_notifications: LoggedNotifications,
/// Reports if a newer release of the software is available on GitHub
@ -167,7 +168,7 @@ impl Sniffer {
preview_captures_rx: None,
info_traffic: InfoTraffic::default(),
addresses_resolved: HashMap::new(),
favorite_hosts: HashSet::new(),
favorites: HashSet::new(),
logged_notifications: LoggedNotifications::default(),
newer_release_available: None,
capture_source,
@ -302,7 +303,7 @@ impl Sniffer {
Message::Reset => return self.reset(),
Message::Style(style) => self.style(style),
Message::LoadStyle(path) => self.load_style(path),
Message::AddOrRemoveFavorite(host, add) => self.add_or_remove_favorite(&host, add),
Message::AddOrRemoveFavorite(fav, add) => self.add_or_remove_favorite(&fav, add),
Message::ShowModal(modal) => self.show_modal(modal),
Message::HideModal => self.hide_modal(),
Message::OpenSettings(settings_page) => self.open_settings(settings_page),
@ -905,7 +906,7 @@ impl Sniffer {
&mut self.logged_notifications,
&self.conf.settings.notifications,
&msg,
&self.favorite_hosts,
&self.favorites,
&self.capture_source,
&self.addresses_resolved,
);
@ -1050,7 +1051,7 @@ impl Sniffer {
self.current_capture_rx = (self.current_capture_rx.0 + 1, None);
self.info_traffic = InfoTraffic::default();
self.addresses_resolved = HashMap::new();
self.favorite_hosts = HashSet::new();
self.favorites = HashSet::new();
self.logged_notifications = LoggedNotifications::default();
self.pcap_error = None;
self.traffic_chart = TrafficChart::new(style, language, self.conf.data_repr);
@ -1101,16 +1102,31 @@ impl Sniffer {
}
}
fn add_or_remove_favorite(&mut self, host: &Host, add: bool) {
let info_traffic = &mut self.info_traffic;
fn add_or_remove_favorite(&mut self, fav: &FavoriteKey, add: bool) {
if add {
self.favorite_hosts.insert(host.clone());
self.favorites.insert(fav.clone());
} else {
self.favorite_hosts.remove(host);
}
if let Some(host_info) = info_traffic.hosts.get_mut(host) {
host_info.is_favorite = add;
self.favorites.remove(fav);
}
match fav {
FavoriteKey::Host(host) => {
if let Some(host_info) = self.info_traffic.hosts.get_mut(host) {
host_info.data_info_fav.is_favorite = add;
}
}
FavoriteKey::Service(service) => {
if let Some(service_info) = self.info_traffic.services.get_mut(service) {
service_info.is_favorite = add;
}
}
FavoriteKey::Program(program) => {
let Some(program_lookup) = &mut self.program_lookup else {
return;
};
program_lookup.edit_fav(program, add);
}
};
}
fn close_settings(&mut self) {
@ -1618,7 +1634,7 @@ mod tests {
},
false,
));
assert_eq!(sniffer.favorite_hosts, HashSet::new());
assert_eq!(sniffer.favorites, HashSet::new());
// remove 2
sniffer.update(Message::AddOrRemoveFavorite(
Host {
@ -1628,7 +1644,7 @@ mod tests {
},
false,
));
assert_eq!(sniffer.favorite_hosts, HashSet::new());
assert_eq!(sniffer.favorites, HashSet::new());
// add 2
sniffer.update(Message::AddOrRemoveFavorite(
Host {
@ -1639,7 +1655,7 @@ mod tests {
true,
));
assert_eq!(
sniffer.favorite_hosts,
sniffer.favorites,
HashSet::from([Host {
domain: "2.2".to_string(),
asn: Default::default(),
@ -1656,7 +1672,7 @@ mod tests {
false,
));
assert_eq!(
sniffer.favorite_hosts,
sniffer.favorites,
HashSet::from([Host {
domain: "2.2".to_string(),
asn: Default::default(),
@ -1673,7 +1689,7 @@ mod tests {
true,
));
assert_eq!(
sniffer.favorite_hosts,
sniffer.favorites,
HashSet::from([Host {
domain: "2.2".to_string(),
asn: Default::default(),
@ -1690,7 +1706,7 @@ mod tests {
true,
));
assert_eq!(
sniffer.favorite_hosts,
sniffer.favorites,
HashSet::from([
Host {
domain: "1.1".to_string(),
@ -1714,7 +1730,7 @@ mod tests {
true,
));
assert_eq!(
sniffer.favorite_hosts,
sniffer.favorites,
HashSet::from([
Host {
domain: "1.1".to_string(),
@ -1743,7 +1759,7 @@ mod tests {
false,
));
assert_eq!(
sniffer.favorite_hosts,
sniffer.favorites,
HashSet::from([
Host {
domain: "1.1".to_string(),
@ -1767,7 +1783,7 @@ mod tests {
false,
));
assert_eq!(
sniffer.favorite_hosts,
sniffer.favorites,
HashSet::from([Host {
domain: "1.1".to_string(),
asn: Default::default(),
@ -1783,7 +1799,7 @@ mod tests {
},
false,
));
assert_eq!(sniffer.favorite_hosts, HashSet::new());
assert_eq!(sniffer.favorites, HashSet::new());
}
#[test]

273
src/gui/types/favorite.rs Normal file
View file

@ -0,0 +1,273 @@
use crate::countries::country_utils::get_flag_tooltip;
use crate::gui::styles::button::ButtonType;
use crate::gui::styles::types::style_type::StyleType;
use crate::gui::types::conf::Conf;
use crate::gui::types::message::Message;
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_fav::DataInfoFav;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::Host;
use crate::networking::types::info_traffic::InfoTraffic;
use crate::networking::types::program::Program;
use crate::networking::types::program_lookup::ProgramLookup;
use crate::networking::types::service::Service;
use crate::report::types::search_parameters::SearchParameters;
use crate::report::types::sort_type::SortType;
use crate::translations::translations_2::host_translation;
use crate::translations::translations_3::service_translation;
use crate::translations::translations_5::program_translation;
use crate::translations::types::language::Language;
use crate::utils::types::icon::Icon;
use iced::widget::{Button, Container, Tooltip, button};
use iced::{Alignment, Element};
use std::cmp::min;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Favorite {
Host,
Service,
Program,
}
impl Favorite {
pub fn get_entries(
self,
info_traffic: &InfoTraffic,
program_lookup: Option<&ProgramLookup>,
data_repr: DataRepr,
sort_type: SortType,
) -> Vec<FavoriteItem> {
match self {
Favorite::Host => get_host_entries(info_traffic, data_repr, sort_type),
Favorite::Service => get_service_entries(info_traffic, data_repr, sort_type),
Favorite::Program => get_program_entries(program_lookup, data_repr, sort_type),
}
}
pub fn fill_portion(self) -> iced::Length {
match self {
Favorite::Host => iced::Length::FillPortion(2),
Favorite::Service | Favorite::Program => iced::Length::FillPortion(1),
}
}
pub fn title(self, language: Language) -> &'static str {
match self {
Favorite::Host => host_translation(language),
Favorite::Service => service_translation(language),
Favorite::Program => program_translation(language),
}
}
pub fn sort_arrows<'a>(self, conf: &Conf) -> Container<'a, Message, StyleType> {
let active_sort_type = match self {
Favorite::Host => conf.host_sort_type,
Favorite::Service => conf.service_sort_type,
Favorite::Program => conf.program_sort_type,
};
let message = match self {
Favorite::Host => Message::HostSortSelection,
Favorite::Service => Message::ServiceSortSelection,
Favorite::Program => Message::ProgramSortSelection,
};
Container::new(
button(
active_sort_type
.icon()
.align_x(Alignment::Center)
.align_y(Alignment::Center),
)
.class(active_sort_type.button_type())
.on_press(message(active_sort_type.next_sort())),
)
.width(50.0)
.align_x(Alignment::Center)
}
}
#[derive(Clone)]
pub enum FavoriteItem {
Host((Host, DataInfoHost)),
Service((Service, DataInfoFav)),
Program((Program, DataInfoFav)),
}
impl FavoriteItem {
pub fn data_info(&self) -> DataInfo {
match self {
FavoriteItem::Host((_, data_info_host)) => data_info_host.data_info_fav.data_info,
FavoriteItem::Service((_, data_info_fav)) => data_info_fav.data_info,
FavoriteItem::Program((_, data_info_fav)) => data_info_fav.data_info,
}
}
fn is_favorite(&self) -> bool {
match self {
FavoriteItem::Host((_, data_info_host)) => data_info_host.data_info_fav.is_favorite,
FavoriteItem::Service((_, data_info_fav)) => data_info_fav.is_favorite,
FavoriteItem::Program((_, data_info_fav)) => data_info_fav.is_favorite,
}
}
pub fn star_button<'a>(&self) -> Button<'a, Message, StyleType> {
let is_favorite = self.is_favorite();
let (icon, class) = if is_favorite {
(Icon::StarFull, ButtonType::Starred)
} else {
(Icon::StarEmpty, ButtonType::NotStarred)
};
button(
icon.to_text()
.size(16)
.align_x(Alignment::Center)
.align_y(Alignment::Center),
)
.padding(0)
.height(25)
.width(25)
.class(class)
.on_press(Message::AddOrRemoveFavorite(
self.clone().into(),
!is_favorite,
))
}
pub fn icon<'a>(
&self,
language: Language,
thumbnail: bool,
program_lookup: Option<&'a ProgramLookup>,
) -> impl Into<Element<'a, Message, StyleType>> {
match self {
FavoriteItem::Host((host, data_info_host)) => Some(get_flag_tooltip(
host.country,
data_info_host,
language,
thumbnail,
)),
FavoriteItem::Service(_) => None::<Tooltip<Message, StyleType>>,
FavoriteItem::Program((program, _)) => {
let Some(program_lookup) = program_lookup else {
return None::<Tooltip<Message, StyleType>>;
};
Some(program_lookup.picon_tooltip(program.icon_key(), program.path()))
}
}
}
pub fn to_entry_string(&self) -> String {
match self {
FavoriteItem::Host((host, _)) => host.to_entry_string(),
FavoriteItem::Service((service, _)) => service.to_string(),
FavoriteItem::Program((program, _)) => program.to_string(),
}
}
pub fn new_entry_search(&self) -> SearchParameters {
match self {
FavoriteItem::Host((host, _)) => SearchParameters::new_host_search(host),
FavoriteItem::Service((service, _)) => SearchParameters::new_service_search(service),
FavoriteItem::Program((program, _)) => SearchParameters::new_program_search(program),
}
}
}
#[derive(Clone, Hash, Eq, PartialEq, Debug)]
pub enum FavoriteKey {
Host(Host),
Service(Service),
Program(Program),
}
impl From<FavoriteItem> for FavoriteKey {
fn from(item: FavoriteItem) -> Self {
match item {
FavoriteItem::Host((h, _)) => FavoriteKey::Host(h),
FavoriteItem::Service((s, _)) => FavoriteKey::Service(s),
FavoriteItem::Program((p, _)) => FavoriteKey::Program(p),
}
}
}
// get entries helpers -----------------------------------------------------------------------------
fn get_host_entries(
info_traffic: &InfoTraffic,
data_repr: DataRepr,
sort_type: SortType,
) -> Vec<FavoriteItem> {
let mut sorted_vec: Vec<(&Host, &DataInfoHost)> = info_traffic.hosts.iter().collect();
sorted_vec.sort_by(|&(_, a), &(_, b)| {
a.data_info_fav
.data_info
.compare(&b.data_info_fav.data_info, sort_type, data_repr)
});
let n_entry = min(sorted_vec.len(), 30);
sorted_vec[0..n_entry]
.iter()
.map(|&(host, data_info_host)| {
FavoriteItem::Host((host.to_owned(), data_info_host.to_owned()))
})
.collect()
}
fn get_service_entries(
info_traffic: &InfoTraffic,
data_repr: DataRepr,
sort_type: SortType,
) -> Vec<FavoriteItem> {
let mut sorted_vec: Vec<(&Service, &DataInfoFav)> = info_traffic
.services
.iter()
.filter(|(service, _)| service != &&Service::NotApplicable)
.collect();
sorted_vec.sort_by(|&(_, a), &(_, b)| a.data_info.compare(&b.data_info, sort_type, data_repr));
let n_entry = min(sorted_vec.len(), 30);
sorted_vec[0..n_entry]
.iter()
.map(|&(service, data_info_fav)| FavoriteItem::Service((*service, *data_info_fav)))
.collect()
}
fn get_program_entries(
program_lookup: Option<&ProgramLookup>,
data_repr: DataRepr,
sort_type: SortType,
) -> Vec<FavoriteItem> {
let Some(program_lookup) = program_lookup else {
return Vec::new();
};
let mut sorted_vec: Vec<(&Program, &DataInfoFav)> = program_lookup
.programs()
.iter()
// Unknown may be inserted, and then all of its data could be reassigned to known programs
.filter(|(_, d)| d.data_info.tot_data(DataRepr::Packets) > 0)
.collect();
sorted_vec.sort_by(|&(p1, a), &(p2, b)| {
if sort_type == SortType::Neutral && a.data_info.is_within_same_second(&b.data_info) {
if p1.is_unknown() {
return std::cmp::Ordering::Greater;
} else if p2.is_unknown() {
return std::cmp::Ordering::Less;
}
}
a.data_info.compare(&b.data_info, sort_type, data_repr)
});
let n_entry = min(sorted_vec.len(), 30);
sorted_vec[0..n_entry]
.iter()
.map(|&(program, data_info)| FavoriteItem::Program((program.to_owned(), *data_info)))
.collect()
}

View file

@ -2,10 +2,11 @@ use crate::gui::components::types::my_modal::MyModal;
use crate::gui::pages::types::running_page::RunningPage;
use crate::gui::pages::types::settings_page::SettingsPage;
use crate::gui::styles::types::gradient_type::GradientType;
use crate::gui::types::favorite::FavoriteKey;
use crate::networking::traffic_preview::TrafficPreview;
use crate::networking::types::capture_context::CaptureSourcePicklist;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::{Host, HostMessage};
use crate::networking::types::host::HostMessage;
use crate::networking::types::info_traffic::InfoTraffic;
use crate::networking::types::ip_blacklist::IpBlacklist;
use crate::notifications::types::notifications::Notification;
@ -44,8 +45,8 @@ pub enum Message {
ServiceSortSelection(SortType),
/// Select program sort type to be displayed (overview page)
ProgramSortSelection(SortType),
/// Adds or removes the given host into/from the favorites
AddOrRemoveFavorite(Host, bool),
/// Adds or removes the given item into/from the favorites
AddOrRemoveFavorite(FavoriteKey, bool),
/// Open the supplied web page
OpenWebPage(WebPage),
/// Start sniffing packets

View file

@ -1,6 +1,7 @@
pub mod conf;
pub mod config_window;
pub mod export_pcap;
pub mod favorite;
pub mod filters;
pub mod message;
pub mod settings;

View file

@ -14,6 +14,7 @@ use crate::networking::types::arp_type::ArpType;
use crate::networking::types::bogon::is_bogon;
use crate::networking::types::capture_context::{CaptureContext, CaptureSource, CaptureType};
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_fav::DataInfoFav;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::host::{Host, HostMessage};
use crate::networking::types::icmp_type::IcmpType;
@ -243,6 +244,7 @@ pub fn parse_packets(
.entry(host)
.and_modify(|data_info_host| {
data_info_host
.data_info_fav
.data_info
.add_packet(exchanged_bytes, traffic_direction);
})
@ -260,11 +262,11 @@ pub fn parse_packets(
);
let is_bogon = is_bogon(&address_to_lookup);
DataInfoHost {
data_info: DataInfo::new_with_first_packet(
data_info_fav: DataInfo::new_with_first_packet(
exchanged_bytes,
traffic_direction,
),
is_favorite: false,
)
.into(),
is_loopback,
is_local,
is_bogon,
@ -278,11 +280,14 @@ pub fn parse_packets(
info_traffic_msg
.services
.entry(service)
.and_modify(|data_info| {
data_info.add_packet(exchanged_bytes, traffic_direction);
.and_modify(|data_info_fav| {
data_info_fav
.data_info
.add_packet(exchanged_bytes, traffic_direction);
})
.or_insert_with(|| {
DataInfo::new_with_first_packet(exchanged_bytes, traffic_direction)
.into()
});
// update dropped packets number
@ -399,8 +404,7 @@ fn reverse_dns_lookups(
};
let data_info_host = DataInfoHost {
data_info: DataInfo::default(),
is_favorite: false,
data_info_fav: DataInfoFav::default(),
is_local,
is_bogon,
is_loopback,
@ -455,7 +459,7 @@ impl AddressesResolutionState {
.remove(&address_to_lookup)
.unwrap_or_default();
// overwrite the host message with the collected data
host_msg.data_info_host.data_info = other_data;
host_msg.data_info_host.data_info_fav.data_info = other_data;
// insert the newly resolved host in the collection of resolved addresses
self.addresses_resolved
.insert(address_to_lookup, host_msg.host.clone());

View file

@ -0,0 +1,17 @@
use crate::networking::types::data_info::DataInfo;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Default)]
pub struct DataInfoFav {
pub data_info: DataInfo,
/// Determine if this item is one of the favorites
pub is_favorite: bool,
}
impl From<DataInfo> for DataInfoFav {
fn from(data_info: DataInfo) -> Self {
Self {
data_info,
is_favorite: false,
}
}
}

View file

@ -1,15 +1,13 @@
//! Module defining the `DataInfoHost` struct related to hosts.
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_fav::DataInfoFav;
use crate::networking::types::traffic_type::TrafficType;
/// Host-related information.
#[derive(Clone, Copy, Default, Debug, Eq, PartialEq, Hash)]
pub struct DataInfoHost {
/// Incoming and outgoing packets and bytes
pub data_info: DataInfo,
/// Determine if this host is one of the favorites
pub is_favorite: bool,
pub data_info_fav: DataInfoFav,
/// Determine if the connection is loopback (the "remote" is loopback)
pub is_loopback: bool,
/// Determine if the connection with this host is local
@ -22,7 +20,9 @@ pub struct DataInfoHost {
impl DataInfoHost {
pub fn refresh(&mut self, other: &Self) {
self.data_info.refresh(other.data_info);
self.data_info_fav
.data_info
.refresh(other.data_info_fav.data_info);
self.is_loopback = other.is_loopback;
self.is_local = other.is_local;
self.is_bogon = other.is_bogon;

View file

@ -2,6 +2,7 @@ use crate::Service;
use crate::networking::manage_packets::get_local_port;
use crate::networking::types::address_port_pair::AddressPortPair;
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_fav::DataInfoFav;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::Host;
@ -23,7 +24,7 @@ pub struct InfoTraffic {
/// Map of the traffic
pub map: HashMap<AddressPortPair, InfoAddressPortPair>,
/// Map of the upper layer services with their data info
pub services: HashMap<Service, DataInfo>,
pub services: HashMap<Service, DataInfoFav>,
/// Map of the hosts with their data info
pub hosts: HashMap<Host, DataInfoHost>,
}
@ -72,7 +73,7 @@ impl InfoTraffic {
for (key, value) in &msg.services {
self.services
.entry(*key)
.and_modify(|x| x.refresh(*value))
.and_modify(|x| x.data_info.refresh(value.data_info))
.or_insert(*value);
}

View file

@ -6,6 +6,7 @@ pub mod capture_context;
pub mod combobox_data_states;
pub mod config_device;
pub mod data_info;
pub mod data_info_fav;
pub mod data_info_host;
pub mod data_representation;
pub mod host;

View file

@ -5,6 +5,7 @@ use crate::gui::types::message::Message;
use crate::networking::manage_packets::get_local_port;
use crate::networking::types::address_port_pair::AddressPortPair;
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_fav::DataInfoFav;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
use crate::networking::types::program::Program;
@ -26,7 +27,7 @@ pub struct ProgramLookup {
icon_key_tx: Sender<String>,
picon_rx: Receiver<(String, IconHandle)>,
state: HashMap<(u16, Protocol), LookedUpProgram>,
programs: HashMap<Program, DataInfo>,
programs: HashMap<Program, DataInfoFav>,
picons: HashMap<String, IconHandle>,
}
@ -59,8 +60,8 @@ impl ProgramLookup {
let res = Program::from_proc(proc.as_ref());
self.programs
.entry(res.clone())
.and_modify(|d| d.refresh(new_data))
.or_insert(new_data);
.and_modify(|d| d.data_info.refresh(new_data))
.or_insert(new_data.into());
res
}
@ -159,8 +160,8 @@ impl ProgramLookup {
// assign to known
self.programs
.entry(program)
.and_modify(|d| d.refresh(reassigned_data))
.or_insert(reassigned_data);
.and_modify(|d| d.data_info.refresh(reassigned_data))
.or_insert(reassigned_data.into());
// remove from Unknown
// NOTE: subtracting reassigned_data from Unknown wouldn't correctly reassign final_instant,
// so let's just reiterate through all the Unknown connections
@ -174,7 +175,7 @@ impl ProgramLookup {
// or_insert not needed: Unknown is already in the map since reassigned data came from it
self.programs
.entry(Program::Unknown)
.and_modify(|d| *d = unknown_data);
.and_modify(|d| d.data_info = unknown_data);
}
}
@ -191,10 +192,16 @@ impl ProgramLookup {
}
}
pub fn programs(&self) -> &HashMap<Program, DataInfo> {
pub fn programs(&self) -> &HashMap<Program, DataInfoFav> {
&self.programs
}
pub fn edit_fav(&mut self, program: &Program, add: bool) {
if let Some(info) = self.programs.get_mut(program) {
info.is_favorite = add;
}
}
pub fn picon_tooltip<'a>(
&self,
icon_key: &str,

View file

@ -1,3 +1,4 @@
use crate::gui::types::favorite::FavoriteKey;
use crate::networking::manage_packets::get_address_to_lookup;
use crate::networking::types::capture_context::CaptureSource;
use crate::networking::types::data_info::DataInfo;
@ -27,7 +28,7 @@ pub fn notify_and_log(
logged_notifications: &mut LoggedNotifications,
notifications: &Notifications,
info_traffic_msg: &InfoTraffic,
favorites: &HashSet<Host>,
favorites: &HashSet<FavoriteKey>,
cs: &CaptureSource,
addresses_resolved: &HashMap<IpAddr, (String, Host)>,
) -> usize {
@ -114,13 +115,14 @@ pub fn notify_and_log(
.get(&host)
.copied()
.unwrap_or_default();
data_info_host.data_info = v.data_info();
data_info_host.data_info_fav.data_info = v.data_info();
blacklisted_last_interval
.entry(*address_to_lookup)
.and_modify(|(_, existing_data_info_host)| {
existing_data_info_host
.data_info_fav
.data_info
.refresh(data_info_host.data_info);
.refresh(data_info_host.data_info_fav.data_info);
})
.or_insert((host, data_info_host));
}
@ -164,8 +166,11 @@ fn hosts_list(info_traffic_msg: &InfoTraffic, data_repr: DataRepr) -> Vec<(Host,
.map(|(h, data)| (h.clone(), *data))
.collect();
hosts.sort_by(|(_, a), (_, b)| {
a.data_info
.compare(&b.data_info, SortType::Descending, data_repr)
a.data_info_fav.data_info.compare(
&b.data_info_fav.data_info,
SortType::Descending,
data_repr,
)
});
let n_entry = min(hosts.len(), 4);
hosts
@ -181,9 +186,9 @@ fn services_list(info_traffic_msg: &InfoTraffic, data_repr: DataRepr) -> Vec<(Se
.services
.iter()
.filter(|(service, _)| service != &&Service::NotApplicable)
.map(|(s, data)| (*s, *data))
.map(|(s, data_info_fav)| (*s, data_info_fav.data_info))
.collect();
services.sort_by(|(_, a), (_, b)| a.compare(b, SortType::Descending, data_repr));
services.sort_by(|(_, a), (_, b)| a.compare(&b, SortType::Descending, data_repr));
let n_entry = min(services.len(), 4);
services
.get(..n_entry)

View file

@ -80,8 +80,10 @@ impl LoggedNotification {
pub fn data_info(&self) -> DataInfo {
match self {
LoggedNotification::DataThresholdExceeded(d) => d.data_info,
LoggedNotification::FavoriteTransmitted(f) => f.data_info_host.data_info,
LoggedNotification::BlacklistedTransmitted(b) => b.data_info_host.data_info,
LoggedNotification::FavoriteTransmitted(f) => f.data_info_host.data_info_fav.data_info,
LoggedNotification::BlacklistedTransmitted(b) => {
b.data_info_host.data_info_fav.data_info
}
}
}
@ -144,7 +146,7 @@ impl FavoriteTransmitted {
"domain": self.host.domain,
"asn": self.host.asn.name,
},
"data": DataRepr::Bytes.formatted_string(self.data_info_host.data_info.tot_data(DataRepr::Bytes)),
"data": DataRepr::Bytes.formatted_string(self.data_info_host.data_info_fav.data_info.tot_data(DataRepr::Bytes)),
})
.to_string()
}
@ -170,7 +172,7 @@ impl BlacklistedTransmitted {
"domain": self.host.domain,
"asn": self.host.asn.name,
},
"data": DataRepr::Bytes.formatted_string(self.data_info_host.data_info.tot_data(DataRepr::Bytes)),
"data": DataRepr::Bytes.formatted_string(self.data_info_host.data_info_fav.data_info.tot_data(DataRepr::Bytes)),
})
.to_string()
}

View file

@ -1,16 +1,11 @@
use std::cmp::min;
use crate::Sniffer;
use crate::networking::manage_packets::get_address_to_lookup;
use crate::networking::types::address_port_pair::AddressPortPair;
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::Host;
use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
use crate::networking::types::program::Program;
use crate::networking::types::program_lookup::ProgramLookup;
use crate::report::types::sort_type::SortType;
use crate::{InfoTraffic, Service, Sniffer};
/// Return the elements that satisfy the search constraints and belong to the given page,
/// and the total number of elements which satisfy the search constraints,
@ -31,6 +26,7 @@ pub fn get_searched_entries(
.hosts
.get(&e.1)
.unwrap_or(&DataInfoHost::default())
.data_info_fav
.is_favorite
} else {
false
@ -62,69 +58,3 @@ pub fn get_searched_entries(
agglomerate,
)
}
pub fn get_host_entries(
info_traffic: &InfoTraffic,
data_repr: DataRepr,
sort_type: SortType,
) -> Vec<(Host, DataInfoHost)> {
let mut sorted_vec: Vec<(&Host, &DataInfoHost)> = info_traffic.hosts.iter().collect();
sorted_vec.sort_by(|&(_, a), &(_, b)| a.data_info.compare(&b.data_info, sort_type, data_repr));
let n_entry = min(sorted_vec.len(), 30);
sorted_vec[0..n_entry]
.iter()
.map(|&(host, data_info_host)| (host.to_owned(), data_info_host.to_owned()))
.collect()
}
pub fn get_service_entries(
info_traffic: &InfoTraffic,
data_repr: DataRepr,
sort_type: SortType,
) -> Vec<(Service, DataInfo)> {
let mut sorted_vec: Vec<(&Service, &DataInfo)> = info_traffic
.services
.iter()
.filter(|(service, _)| service != &&Service::NotApplicable)
.collect();
sorted_vec.sort_by(|&(_, a), &(_, b)| a.compare(b, sort_type, data_repr));
let n_entry = min(sorted_vec.len(), 30);
sorted_vec[0..n_entry]
.iter()
.map(|&(service, data_info)| (*service, *data_info))
.collect()
}
pub fn get_program_entries(
program_lookup: &ProgramLookup,
data_repr: DataRepr,
sort_type: SortType,
) -> Vec<(Program, DataInfo)> {
let mut sorted_vec: Vec<(&Program, &DataInfo)> = program_lookup
.programs()
.iter()
// Unknown may be inserted, and then all of its data could be reassigned to known programs
.filter(|(_, d)| d.tot_data(DataRepr::Packets) > 0)
.collect();
sorted_vec.sort_by(|&(p1, a), &(p2, b)| {
if sort_type == SortType::Neutral && a.is_within_same_second(b) {
if p1.is_unknown() {
return std::cmp::Ordering::Greater;
} else if p2.is_unknown() {
return std::cmp::Ordering::Less;
}
}
a.compare(b, sort_type, data_repr)
});
let n_entry = min(sorted_vec.len(), 30);
sorted_vec[0..n_entry]
.iter()
.map(|&(program, data_info)| (program.to_owned(), *data_info))
.collect()
}