Merge pull request #938 from GyulyVGC/configs

Improve configurations persistence across different runs of the app
This commit is contained in:
Giuliano Bellini
2025-08-30 00:10:36 +02:00
committed by GitHub
36 changed files with 746 additions and 874 deletions

View File

@@ -7,6 +7,7 @@ ## [UNRELEASED]
- Added _bits_ data representation ([#936](https://github.com/GyulyVGC/sniffnet/pull/936) — fixes [#506](https://github.com/GyulyVGC/sniffnet/issues/506))
- An AppImage of Sniffnet is now available ([#859](https://github.com/GyulyVGC/sniffnet/pull/859) — fixes [#900](https://github.com/GyulyVGC/sniffnet/issues/900))
- Added Dutch translation 🇳🇱 ([#854](https://github.com/GyulyVGC/sniffnet/pull/854))
- Improved configurations persistence across different runs of the app ([#938](https://github.com/GyulyVGC/sniffnet/pull/938) — fixes [#507](https://github.com/GyulyVGC/sniffnet/issues/507))
- The Windows Installer is now signed with a code signing certificate provided by the [SignPath Foundation](https://signpath.org/) ([#897](https://github.com/GyulyVGC/sniffnet/pull/897) — fixes [#894](https://github.com/GyulyVGC/sniffnet/issues/894))
- Updated some of the existing translations to v1.4:
- German ([#833](https://github.com/GyulyVGC/sniffnet/pull/833))

View File

@@ -1,12 +1,11 @@
use crate::SNIFFNET_LOWERCASE;
use crate::gui::types::conf::{CONF, Conf};
use crate::gui::types::message::Message;
use crate::networking::types::capture_context::CaptureSourcePicklist;
use crate::utils::formatted_strings::APP_VERSION;
use clap::Parser;
use iced::{Task, window};
use crate::CONFIGS;
use crate::Configs;
use crate::SNIFFNET_LOWERCASE;
use crate::gui::types::message::Message;
use crate::utils::formatted_strings::APP_VERSION;
#[derive(Parser, Debug)]
#[command(
name = SNIFFNET_LOWERCASE,
@@ -16,7 +15,7 @@
)]
struct Args {
/// Start sniffing packets from the supplied network adapter
#[arg(short, long, value_name = "NAME", default_missing_value = CONFIGS.device.device_name.as_str(), num_args = 0..=1)]
#[arg(short, long, value_name = "NAME", default_missing_value = CONF.device.device_name.as_str(), num_args = 0..=1)]
adapter: Option<String>,
#[cfg(all(windows, not(debug_assertions)))]
/// Show the logs (stdout and stderr) of the most recent application run
@@ -50,7 +49,7 @@ pub fn handle_cli_args() -> Task<Message> {
}
if args.restore_default {
if Configs::default().store().is_ok() {
if Conf::default().store().is_ok() {
println!("Restored default settings");
}
std::process::exit(0);
@@ -60,7 +59,12 @@ pub fn handle_cli_args() -> Task<Message> {
.map(Message::StartApp)
.chain(Task::done(Message::Periodic));
if let Some(adapter) = args.adapter {
// TODO: check if this works once #653 is fixed
// currently the link type and device name aren't displayed properly when starting from CLI
boot_task_chain = boot_task_chain
.chain(Task::done(Message::SetCaptureSource(
CaptureSourcePicklist::Device,
)))
.chain(Task::done(Message::DeviceSelection(adapter)))
.chain(Task::done(Message::Start));
}
@@ -72,21 +76,28 @@ pub fn handle_cli_args() -> Task<Message> {
mod tests {
use serial_test::serial;
use crate::configs::types::config_window::{PositionTuple, SizeTuple};
use crate::gui::pages::types::running_page::RunningPage;
use crate::gui::pages::types::settings_page::SettingsPage;
use crate::gui::styles::types::custom_palette::ExtraStyles;
use crate::gui::styles::types::gradient_type::GradientType;
use crate::gui::types::conf::Conf;
use crate::gui::types::config_window::{PositionTuple, SizeTuple};
use crate::gui::types::export_pcap::ExportPcap;
use crate::gui::types::filters::Filters;
use crate::gui::types::settings::Settings;
use crate::networking::types::capture_context::CaptureSourcePicklist;
use crate::networking::types::config_device::ConfigDevice;
use crate::notifications::types::notifications::Notifications;
use crate::{ConfigDevice, ConfigSettings, ConfigWindow, Language, Sniffer, StyleType};
use super::*;
use crate::report::types::sort_type::SortType;
use crate::{ConfigWindow, Language, Sniffer, StyleType};
#[test]
#[serial]
fn test_restore_default_configs() {
// initial configs stored are the default ones
assert_eq!(Configs::load(), Configs::default());
let modified_configs = Configs {
settings: ConfigSettings {
assert_eq!(Conf::load(), Conf::default());
let modified_conf = Conf {
settings: Settings {
color_gradient: GradientType::Wild,
language: Language::ZH,
scale_factor: 0.65,
@@ -111,19 +122,35 @@ fn test_restore_default_configs() {
size: SizeTuple(452.0, 870.0),
thumbnail_position: PositionTuple(20.0, 20.0),
},
capture_source_picklist: CaptureSourcePicklist::File,
report_sort_type: SortType::Ascending,
host_sort_type: SortType::Descending,
service_sort_type: SortType::Neutral,
filters: Filters {
bpf: "tcp".to_string(),
expanded: true,
},
import_pcap_path: "whole_day.pcapng".to_string(),
export_pcap: ExportPcap {
enabled: true,
file_name: "sniffnet.pcap".to_string(),
directory: "home".to_string(),
},
last_opened_setting: SettingsPage::General,
last_opened_page: RunningPage::Inspect,
};
// we want to be sure that modified config is different from defaults
assert_ne!(Configs::default(), modified_configs);
assert_ne!(Conf::default(), modified_conf);
//store modified configs
modified_configs.clone().store().unwrap();
modified_conf.clone().store().unwrap();
// assert they've been stored
assert_eq!(Configs::load(), modified_configs);
assert_eq!(Conf::load(), modified_conf);
// restore defaults
Configs::default().store().unwrap();
Conf::default().store().unwrap();
// assert that defaults are stored
assert_eq!(Configs::load(), Configs::default());
assert_eq!(Conf::load(), Conf::default());
// only needed because it will delete config files via its Drop implementation
Sniffer::new(Configs::default());
Sniffer::new(Conf::default());
}
}

View File

@@ -1 +0,0 @@
pub mod types;

View File

@@ -1,86 +0,0 @@
//! Module defining the `ConfigDevice` struct, which allows to save and reload
//! the application default configuration.
use crate::networking::types::my_device::MyDevice;
#[cfg(not(test))]
use crate::utils::error_logger::{ErrorLogger, Location};
#[cfg(not(test))]
use crate::{SNIFFNET_LOWERCASE, location};
use pcap::{Device, DeviceFlags};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub struct ConfigDevice {
pub device_name: String,
}
impl Default for ConfigDevice {
fn default() -> Self {
Self {
device_name: Device::lookup()
.unwrap_or(None)
.unwrap_or_else(|| Device {
name: String::new(),
desc: None,
addresses: vec![],
flags: DeviceFlags::empty(),
})
.name,
}
}
}
impl ConfigDevice {
const FILE_NAME: &'static str = "device";
#[cfg(not(test))]
pub fn load() -> Self {
if let Ok(device) = confy::load::<ConfigDevice>(SNIFFNET_LOWERCASE, Self::FILE_NAME) {
device
} else {
let _ = confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, ConfigDevice::default())
.log_err(location!());
ConfigDevice::default()
}
}
#[cfg(not(test))]
pub fn store(self) -> Result<(), confy::ConfyError> {
confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, self).log_err(location!())
}
pub fn to_my_device(&self) -> MyDevice {
for device in Device::list().unwrap_or_default() {
if device.name.eq(&self.device_name) {
return MyDevice::from_pcap_device(device);
}
}
let standard_device = Device::lookup().unwrap_or(None).unwrap_or_else(|| Device {
name: String::new(),
desc: None,
addresses: vec![],
flags: DeviceFlags::empty(),
});
MyDevice::from_pcap_device(standard_device)
}
}
#[cfg(test)]
mod tests {
use crate::ConfigDevice;
impl ConfigDevice {
pub fn test_path() -> String {
format!("{}/{}.toml", env!("CARGO_MANIFEST_DIR"), Self::FILE_NAME)
}
pub fn load() -> Self {
confy::load_path::<ConfigDevice>(ConfigDevice::test_path())
.unwrap_or_else(|_| ConfigDevice::default())
}
pub fn store(self) -> Result<(), confy::ConfyError> {
confy::store_path(ConfigDevice::test_path(), self)
}
}
}

View File

@@ -1,84 +0,0 @@
//! Module defining the `ConfigSettings` struct, which allows to save and reload
//! the application default configuration.
use serde::{Deserialize, Serialize};
use crate::gui::styles::types::gradient_type::GradientType;
use crate::notifications::types::notifications::Notifications;
#[cfg(not(test))]
use crate::utils::error_logger::{ErrorLogger, Location};
use crate::{Language, StyleType};
#[cfg(not(test))]
use crate::{SNIFFNET_LOWERCASE, location};
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub struct ConfigSettings {
pub color_gradient: GradientType,
pub language: Language,
pub scale_factor: f64,
pub mmdb_country: String,
pub mmdb_asn: String,
pub style_path: String,
pub notifications: Notifications,
// StyleType should be last in order to deserialize as a table properly
pub style: StyleType,
}
impl ConfigSettings {
const FILE_NAME: &'static str = "settings";
#[cfg(not(test))]
pub fn load() -> Self {
if let Ok(settings) = confy::load::<ConfigSettings>(SNIFFNET_LOWERCASE, Self::FILE_NAME) {
settings
} else {
let _ = confy::store(
SNIFFNET_LOWERCASE,
Self::FILE_NAME,
ConfigSettings::default(),
)
.log_err(location!());
ConfigSettings::default()
}
}
#[cfg(not(test))]
pub fn store(self) -> Result<(), confy::ConfyError> {
confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, self).log_err(location!())
}
}
impl Default for ConfigSettings {
fn default() -> Self {
ConfigSettings {
color_gradient: GradientType::default(),
language: Language::default(),
scale_factor: 1.0,
mmdb_country: String::new(),
mmdb_asn: String::new(),
style_path: String::new(),
notifications: Notifications::default(),
style: StyleType::default(),
}
}
}
#[cfg(test)]
mod tests {
use crate::ConfigSettings;
impl ConfigSettings {
pub fn test_path() -> String {
format!("{}/{}.toml", env!("CARGO_MANIFEST_DIR"), Self::FILE_NAME)
}
pub fn load() -> Self {
confy::load_path::<ConfigSettings>(ConfigSettings::test_path())
.unwrap_or_else(|_| ConfigSettings::default())
}
pub fn store(self) -> Result<(), confy::ConfyError> {
confy::store_path(ConfigSettings::test_path(), self)
}
}
}

View File

@@ -1,30 +0,0 @@
use crate::{ConfigDevice, ConfigSettings, ConfigWindow};
use confy::ConfyError;
pub static CONFIGS: std::sync::LazyLock<Configs> = std::sync::LazyLock::new(Configs::load);
#[derive(Default, Clone, PartialEq, Debug)]
pub struct Configs {
pub settings: ConfigSettings,
pub device: ConfigDevice,
pub window: ConfigWindow,
}
impl Configs {
/// This should only be used directly to load fresh configs;
/// use `CONFIGS` instead to access the initial instance
pub fn load() -> Self {
Configs {
settings: ConfigSettings::load(),
device: ConfigDevice::load(),
window: ConfigWindow::load(),
}
}
pub fn store(self) -> Result<(), ConfyError> {
self.settings.store()?;
self.device.store()?;
self.window.store()?;
Ok(())
}
}

View File

@@ -1,4 +0,0 @@
pub mod config_device;
pub mod config_settings;
pub mod config_window;
pub mod configs;

View File

@@ -5,15 +5,14 @@
use iced::widget::{Container, Row, Space, Text, Tooltip, button, horizontal_space};
use iced::{Alignment, Font, Length};
use crate::configs::types::config_settings::ConfigSettings;
use crate::gui::components::tab::notifications_badge;
use crate::gui::pages::types::running_page::RunningPage;
use crate::gui::pages::types::settings_page::SettingsPage;
use crate::gui::sniffer::Sniffer;
use crate::gui::styles::button::ButtonType;
use crate::gui::styles::container::ContainerType;
use crate::gui::styles::types::gradient_type::GradientType;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::translations::translations::{quit_analysis_translation, settings_translation};
use crate::translations::translations_3::thumbnail_mode_translation;
use crate::utils::types::icon::Icon;
@@ -21,12 +20,12 @@
pub fn header(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let thumbnail = sniffer.thumbnail;
let ConfigSettings {
let Settings {
style,
language,
color_gradient,
..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
if thumbnail {
@@ -41,8 +40,8 @@ pub fn header(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
);
}
let last_opened_setting = sniffer.last_opened_setting;
let is_running = sniffer.running_page.ne(&RunningPage::Init);
let last_opened_setting = sniffer.conf.last_opened_setting;
let is_running = sniffer.running_page.is_some();
let logo = Icon::Sniffnet
.to_text()

View File

@@ -8,6 +8,7 @@
use crate::gui::styles::text::TextType;
use crate::gui::styles::types::gradient_type::GradientType;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::gui::types::timing_events::TimingEvents;
use crate::networking::manage_packets::{
get_address_to_lookup, get_traffic_type, is_local_connection, is_my_address,
@@ -34,7 +35,7 @@
};
use crate::utils::formatted_strings::{get_formatted_timestamp, get_socket_address};
use crate::utils::types::icon::Icon;
use crate::{ConfigSettings, Language, Protocol, Sniffer, StyleType};
use crate::{Language, Protocol, Sniffer, StyleType};
use iced::alignment::Vertical;
use iced::widget::scrollable::Direction;
use iced::widget::tooltip::Position;
@@ -50,12 +51,12 @@ pub fn connection_details_page(
}
fn page_content<'a>(sniffer: &Sniffer, key: &AddressPortPair) -> Container<'a, Message, StyleType> {
let ConfigSettings {
let Settings {
style,
language,
color_gradient,
..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let data_repr = sniffer.traffic_chart.data_repr;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
@@ -299,9 +300,9 @@ fn get_local_tooltip<'a>(
address_to_lookup: &IpAddr,
key: &AddressPortPair,
) -> Tooltip<'a, Message, StyleType> {
let ConfigSettings {
let Settings {
style, language, ..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let local_address = if address_to_lookup.eq(&key.address1) {
&key.address2

View File

@@ -15,6 +15,7 @@
use crate::gui::types::export_pcap::ExportPcap;
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, CaptureSourcePicklist};
use crate::translations::translations::{
address_translation, addresses_translation, network_adapter_translation, start_translation,
@@ -27,7 +28,7 @@
use crate::utils::formatted_strings::get_path_termination_string;
use crate::utils::types::file_info::FileInfo;
use crate::utils::types::icon::Icon;
use crate::{ConfigSettings, Language, StyleType};
use crate::{Language, StyleType};
use iced::Length::FillPortion;
use iced::widget::scrollable::Direction;
use iced::widget::{
@@ -38,12 +39,12 @@
/// Computes the body of gui initial page
pub fn initial_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let ConfigSettings {
let Settings {
style,
language,
color_gradient,
..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
@@ -51,10 +52,10 @@ pub fn initial_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let col_checkboxes = Column::new()
.spacing(10)
.push(get_filters_group(&sniffer.filters, font, language))
.push(get_filters_group(&sniffer.conf.filters, font, language))
.push_maybe(get_export_pcap_group_maybe(
sniffer.capture_source_picklist,
&sniffer.export_pcap,
sniffer.conf.capture_source_picklist,
&sniffer.conf.export_pcap,
language,
font,
));
@@ -113,7 +114,7 @@ fn get_col_data_source(
font: Font,
language: Language,
) -> Column<'_, Message, StyleType> {
let current_option = if sniffer.capture_source_picklist == CaptureSourcePicklist::Device {
let current_option = if sniffer.conf.capture_source_picklist == CaptureSourcePicklist::Device {
network_adapter_translation(language)
} else {
capture_file_translation(language)
@@ -153,7 +154,7 @@ fn get_col_data_source(
.push(picklist),
);
match &sniffer.capture_source_picklist {
match &sniffer.conf.capture_source_picklist {
CaptureSourcePicklist::Device => {
col = col.push(get_col_adapter(sniffer, font, language));
}
@@ -162,7 +163,7 @@ fn get_col_data_source(
language,
font,
&sniffer.capture_source,
&sniffer.import_pcap_path,
&sniffer.conf.import_pcap_path,
));
}
}

View File

@@ -21,6 +21,7 @@
use crate::gui::styles::text::TextType;
use crate::gui::styles::text_input::TextInputType;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::networking::types::address_port_pair::AddressPortPair;
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_representation::DataRepr;
@@ -30,19 +31,20 @@
use crate::report::get_report_entries::get_searched_entries;
use crate::report::types::report_col::ReportCol;
use crate::report::types::search_parameters::{FilterInputType, SearchParameters};
use crate::report::types::sort_type::SortType;
use crate::translations::translations_2::{
administrative_entity_translation, country_translation, domain_name_translation,
no_search_results_translation, only_show_favorites_translation, showing_results_translation,
};
use crate::translations::translations_3::filter_by_host_translation;
use crate::utils::types::icon::Icon;
use crate::{ConfigSettings, Language, ReportSortType, RunningPage, Sniffer, StyleType};
use crate::{Language, RunningPage, Sniffer, StyleType};
/// Computes the body of gui inspect page
pub fn inspect_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let ConfigSettings {
let Settings {
style, language, ..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
@@ -74,7 +76,7 @@ pub fn inspect_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
language,
&sniffer.search,
font,
sniffer.report_sort_type,
sniffer.conf.report_sort_type,
sniffer.traffic_chart.data_repr,
))
.push(Space::with_height(4))
@@ -105,9 +107,9 @@ pub fn inspect_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
}
fn report<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
let ConfigSettings {
let Settings {
style, language, ..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let data_repr = sniffer.traffic_chart.data_repr;
let font = style.get_extension().font;
@@ -183,7 +185,7 @@ fn report_header_row(
language: Language,
search_params: &SearchParameters,
font: Font,
sort_type: ReportSortType,
sort_type: SortType,
data_repr: DataRepr,
) -> Row<'_, Message, StyleType> {
let mut ret_val = Row::new().padding([0, 2]).align_y(Alignment::Center);
@@ -269,7 +271,7 @@ fn title_report_col_display(
}
}
fn sort_arrows<'a>(active_sort_type: ReportSortType) -> Container<'a, Message, StyleType> {
fn sort_arrows<'a>(active_sort_type: SortType) -> Container<'a, Message, StyleType> {
Container::new(
button(
active_sort_type

View File

@@ -10,6 +10,7 @@
use crate::gui::styles::style_constants::FONT_SIZE_FOOTER;
use crate::gui::styles::text::TextType;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::data_representation::DataRepr;
@@ -26,7 +27,7 @@
threshold_translation,
};
use crate::utils::types::icon::Icon;
use crate::{ConfigSettings, Language, RunningPage, Sniffer, StyleType};
use crate::{Language, RunningPage, Sniffer, StyleType};
use iced::Length::FillPortion;
use iced::widget::scrollable::Direction;
use iced::widget::text::LineHeight;
@@ -38,12 +39,12 @@
/// Computes the body of gui notifications page
pub fn notifications_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let ConfigSettings {
let Settings {
style,
language,
notifications,
..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
@@ -282,9 +283,9 @@ fn get_button_clear_all<'a>(font: Font, language: Language) -> Tooltip<'a, Messa
}
fn logged_notifications<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
let ConfigSettings {
let Settings {
style, language, ..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let data_repr = sniffer.traffic_chart.data_repr;
let font = style.get_extension().font;
let mut ret_val = Column::new()

View File

@@ -17,6 +17,7 @@
use crate::gui::styles::types::palette_extension::PaletteExtension;
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;
@@ -37,7 +38,7 @@
use crate::translations::translations_3::{service_translation, unsupported_link_type_translation};
use crate::translations::translations_4::reading_from_pcap_translation;
use crate::utils::types::icon::Icon;
use crate::{ConfigSettings, Language, RunningPage, StyleType};
use crate::{Language, RunningPage, StyleType};
use iced::Length::{Fill, FillPortion};
use iced::alignment::{Horizontal, Vertical};
use iced::widget::scrollable::Direction;
@@ -52,66 +53,74 @@
/// Computes the body of gui overview page
pub fn overview_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let ConfigSettings {
let Settings {
style, language, ..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
let mut body = Column::new();
let mut tab_and_body = Column::new().height(Length::Fill);
let dots = &sniffer.dots_pulse.0;
// some packets are there!
let tabs = get_pages_tabs(
RunningPage::Overview,
font,
font_headers,
language,
sniffer.unread_notifications,
);
tab_and_body = tab_and_body.push(tabs);
if let Some(error) = sniffer.pcap_error.as_ref() {
// pcap threw an ERROR!
body = body_pcap_error(error, dots, language, font);
} else {
// NO pcap error detected
let tot_packets = sniffer
.info_traffic
.tot_data_info
.tot_data(DataRepr::Packets);
let container_chart = container_chart(sniffer, font);
if tot_packets == 0 {
// no packets observed at all
body = body_no_packets(&sniffer.capture_source, font, language, dots);
} else {
// some packets are there!
let tabs = get_pages_tabs(
RunningPage::Overview,
font,
font_headers,
language,
sniffer.unread_notifications,
);
tab_and_body = tab_and_body.push(tabs);
let container_info = col_info(sniffer);
let container_chart = container_chart(sniffer, font);
let container_report = row_report(sniffer);
let container_info = col_info(sniffer);
let container_report = row_report(sniffer);
body = body
.width(Length::Fill)
.padding(10)
body = body
.width(Length::Fill)
.padding(10)
.spacing(10)
.align_x(Alignment::Center)
.push(
Row::new()
.height(280)
.spacing(10)
.align_x(Alignment::Center)
.push(
Row::new()
.height(280)
.spacing(10)
.push(container_info)
.push(container_chart),
)
.push(container_report);
}
}
.push(container_info)
.push(container_chart),
)
.push(container_report);
Container::new(Column::new().push(tab_and_body.push(body))).height(Length::Fill)
}
pub fn waiting_page(sniffer: &Sniffer) -> Option<Container<'_, Message, StyleType>> {
let Settings {
style, language, ..
} = sniffer.conf.settings;
let font = style.get_extension().font;
let dots = &sniffer.dots_pulse.0;
let tot_packets = sniffer
.info_traffic
.tot_data_info
.tot_data(DataRepr::Packets);
let body = if let Some(error) = sniffer.pcap_error.as_ref() {
// pcap threw an ERROR!
body_pcap_error(error, dots, language, font)
} else if tot_packets == 0 {
// no packets observed at all
body_no_packets(&sniffer.capture_source, font, language, dots)
} else {
return None;
};
Some(Container::new(Column::new().push(body)).height(Length::Fill))
}
fn body_no_packets<'a>(
cs: &CaptureSource,
font: Font,
@@ -210,16 +219,20 @@ fn row_report<'a>(sniffer: &Sniffer) -> Row<'a, Message, StyleType> {
}
fn col_host<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
let ConfigSettings {
let Settings {
style, language, ..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let data_repr = sniffer.traffic_chart.data_repr;
let mut scroll_host = Column::new()
.padding(Padding::ZERO.right(11.0))
.align_x(Alignment::Center);
let entries = get_host_entries(&sniffer.info_traffic, data_repr, sniffer.host_sort_type);
let entries = get_host_entries(
&sniffer.info_traffic,
data_repr,
sniffer.conf.host_sort_type,
);
let first_entry_data_info = entries
.iter()
.map(|(_, d)| d.data_info)
@@ -273,7 +286,7 @@ fn col_host<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
)
.push(horizontal_space())
.push(sort_arrows(
sniffer.host_sort_type,
sniffer.conf.host_sort_type,
Message::HostSortSelection,
)),
)
@@ -287,16 +300,20 @@ fn col_host<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
}
fn col_service<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
let ConfigSettings {
let Settings {
style, language, ..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let data_repr = sniffer.traffic_chart.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.service_sort_type);
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)
@@ -337,7 +354,7 @@ fn col_service<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
)
.push(horizontal_space())
.push(sort_arrows(
sniffer.service_sort_type,
sniffer.conf.service_sort_type,
Message::ServiceSortSelection,
)),
)
@@ -430,12 +447,17 @@ pub fn service_bar<'a>(
}
fn col_info(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let ConfigSettings {
let Settings {
style, language, ..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let PaletteExtension { font, .. } = style.get_extension();
let col_device = col_device(language, font, &sniffer.capture_source, &sniffer.filters);
let col_device = col_device(
language,
font,
&sniffer.capture_source,
&sniffer.conf.filters,
);
let col_data_representation =
col_data_representation(language, font, sniffer.traffic_chart.data_repr);
@@ -469,7 +491,7 @@ fn col_info(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
}
fn container_chart(sniffer: &Sniffer, font: Font) -> Container<'_, Message, StyleType> {
let ConfigSettings { language, .. } = sniffer.configs.settings;
let Settings { language, .. } = sniffer.conf.settings;
let traffic_chart = &sniffer.traffic_chart;
Container::new(

View File

@@ -14,6 +14,7 @@
use crate::gui::styles::style_constants::FONT_SIZE_SUBTITLE;
use crate::gui::styles::text::TextType;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::mmdb::types::mmdb_reader::{MmdbReader, MmdbReaders};
use crate::translations::translations::language_translation;
use crate::translations::translations_2::country_translation;
@@ -25,15 +26,15 @@
use crate::utils::types::file_info::FileInfo;
use crate::utils::types::icon::Icon;
use crate::utils::types::web_page::WebPage;
use crate::{ConfigSettings, Language, RunningPage, Sniffer, StyleType};
use crate::{Language, Sniffer, StyleType};
pub fn settings_general_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let ConfigSettings {
let Settings {
style,
language,
color_gradient,
..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
@@ -57,15 +58,15 @@ pub fn settings_general_page(sniffer: &Sniffer) -> Container<'_, Message, StyleT
}
fn column_all_general_setting(sniffer: &Sniffer, font: Font) -> Column<'_, Message, StyleType> {
let ConfigSettings {
let Settings {
language,
scale_factor,
mmdb_country,
mmdb_asn,
..
} = sniffer.configs.settings.clone();
} = sniffer.conf.settings.clone();
let is_editable = sniffer.running_page.eq(&RunningPage::Init);
let is_editable = sniffer.running_page.is_none();
let mut column = Column::new()
.align_x(Alignment::Center)

View File

@@ -13,6 +13,7 @@
use crate::gui::styles::text::TextType;
use crate::gui::styles::types::gradient_type::GradientType;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::networking::types::data_representation::DataRepr;
use crate::notifications::types::notifications::{
DataNotification, FavoriteNotification, Notification,
@@ -25,16 +26,16 @@
use crate::translations::translations_2::data_representation_translation;
use crate::translations::translations_4::data_exceeded_translation;
use crate::utils::types::icon::Icon;
use crate::{ConfigSettings, Language, Sniffer, StyleType};
use crate::{Language, Sniffer, StyleType};
pub fn settings_notifications_page<'a>(sniffer: &Sniffer) -> Container<'a, Message, StyleType> {
let ConfigSettings {
let Settings {
style,
language,
color_gradient,
mut notifications,
..
} = sniffer.configs.settings;
} = sniffer.conf.settings;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;

View File

@@ -19,22 +19,23 @@
use crate::gui::styles::types::palette::Palette;
use crate::gui::styles::types::palette_extension::PaletteExtension;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::translations::translations::appearance_title_translation;
use crate::translations::translations_2::color_gradients_translation;
use crate::translations::translations_3::custom_style_translation;
use crate::utils::formatted_strings::get_path_termination_string;
use crate::utils::types::file_info::FileInfo;
use crate::utils::types::icon::Icon;
use crate::{ConfigSettings, Language, Sniffer, StyleType};
use crate::{Language, Sniffer, StyleType};
pub fn settings_style_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let ConfigSettings {
let Settings {
style,
language,
color_gradient,
style_path,
..
} = sniffer.configs.settings.clone();
} = sniffer.conf.settings.clone();
let PaletteExtension {
font, font_headers, ..
} = style.get_extension();

View File

@@ -5,12 +5,12 @@
use iced::{Alignment, Font, Length};
use crate::chart::types::donut_chart::donut_chart;
use crate::configs::types::config_settings::ConfigSettings;
use crate::countries::country_utils::get_flag_tooltip;
use crate::gui::sniffer::Sniffer;
use crate::gui::styles::style_constants::FONT_SIZE_FOOTER;
use crate::gui::styles::types::style_type::StyleType;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::{Host, ThumbnailHost};
use crate::networking::types::info_traffic::InfoTraffic;
@@ -24,7 +24,7 @@
/// Computes the body of the thumbnail view
pub fn thumbnail_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let ConfigSettings { style, .. } = sniffer.configs.settings;
let Settings { style, .. } = sniffer.conf.settings;
let font = style.get_extension().font;
let tot_packets = sniffer
@@ -75,14 +75,14 @@ pub fn thumbnail_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
info_traffic,
data_repr,
font,
sniffer.host_sort_type,
sniffer.conf.host_sort_type,
))
.push(Rule::vertical(10))
.push(service_col(
info_traffic,
data_repr,
font,
sniffer.service_sort_type,
sniffer.conf.service_sort_type,
));
let content = Column::new()

View File

@@ -3,13 +3,13 @@
use crate::translations::translations_2::inspect_translation;
use crate::utils::types::icon::Icon;
use crate::{Language, StyleType};
use serde::{Deserialize, Serialize};
/// This enum defines the current GUI page.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
/// This enum defines the current running page.
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, Default)]
pub enum RunningPage {
/// Initial page.
Init,
/// Overview page.
#[default]
Overview,
/// Inspect page.
Inspect,
@@ -29,7 +29,6 @@ pub fn get_tab_label(&self, language: Language) -> &str {
RunningPage::Overview => overview_translation(language),
RunningPage::Inspect => inspect_translation(language),
RunningPage::Notifications => notifications_translation(language),
RunningPage::Init => "",
}
}
@@ -38,7 +37,6 @@ pub fn next(self) -> Self {
RunningPage::Overview => RunningPage::Inspect,
RunningPage::Inspect => RunningPage::Notifications,
RunningPage::Notifications => RunningPage::Overview,
RunningPage::Init => RunningPage::Init,
}
}
@@ -47,7 +45,6 @@ pub fn previous(self) -> Self {
RunningPage::Overview => RunningPage::Notifications,
RunningPage::Inspect => RunningPage::Overview,
RunningPage::Notifications => RunningPage::Inspect,
RunningPage::Init => RunningPage::Init,
}
}
@@ -56,7 +53,6 @@ pub fn icon<'a>(self) -> iced::widget::Text<'a, StyleType> {
RunningPage::Overview => Icon::Overview,
RunningPage::Inspect => Icon::Inspect,
RunningPage::Notifications => Icon::Notification,
RunningPage::Init => Icon::Sniffnet,
}
.to_text()
}

View File

@@ -3,11 +3,13 @@
use crate::translations::translations_3::general_translation;
use crate::utils::types::icon::Icon;
use crate::{Language, StyleType};
use serde::{Deserialize, Serialize};
/// This enum defines the current settings page.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, Default)]
pub enum SettingsPage {
/// Settings Notifications page.
#[default]
Notifications,
/// Settings Appearance page.
Appearance,

View File

File diff suppressed because it is too large Load Diff

87
src/gui/types/conf.rs Normal file
View File

@@ -0,0 +1,87 @@
use crate::gui::pages::types::running_page::RunningPage;
use crate::gui::pages::types::settings_page::SettingsPage;
use crate::gui::types::config_window::ConfigWindow;
use crate::gui::types::export_pcap::ExportPcap;
use crate::gui::types::filters::Filters;
use crate::gui::types::settings::Settings;
use crate::networking::types::capture_context::CaptureSourcePicklist;
use crate::networking::types::config_device::ConfigDevice;
use crate::report::types::sort_type::SortType;
#[cfg(not(test))]
use crate::utils::error_logger::{ErrorLogger, Location};
#[cfg(not(test))]
use crate::{SNIFFNET_LOWERCASE, location};
#[cfg(not(test))]
use confy::ConfyError;
use serde::{Deserialize, Serialize};
pub static CONF: std::sync::LazyLock<Conf> = std::sync::LazyLock::new(Conf::load);
#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Debug)]
#[serde(default)]
pub struct Conf {
/// Parameters from settings pages
pub settings: Settings,
/// Last selected network device name
pub device: ConfigDevice,
/// Window configuration, such as size and position
pub window: ConfigWindow,
/// Capture source picklist, to select the source of the capture
pub capture_source_picklist: CaptureSourcePicklist,
/// BPF filter program to be applied to the capture
pub filters: Filters,
/// Report sort type (inspect page)
pub report_sort_type: SortType,
/// Host sort type (overview page)
pub host_sort_type: SortType,
/// Service sort type (overview page)
pub service_sort_type: SortType,
/// Remembers the last opened setting page
pub last_opened_setting: SettingsPage,
/// Remembers the last opened running page
pub last_opened_page: RunningPage,
/// Information about PCAP file export
pub export_pcap: ExportPcap,
/// Import path for PCAP file
pub import_pcap_path: String,
}
impl Conf {
const FILE_NAME: &'static str = "conf";
/// This should only be used directly to load fresh configurations;
/// use `CONF` instead to access the initial instance
#[cfg(not(test))]
pub fn load() -> Self {
if let Ok(conf) = confy::load::<Conf>(SNIFFNET_LOWERCASE, Self::FILE_NAME) {
conf
} else {
let _ = Conf::default().store();
Conf::default()
}
}
#[cfg(not(test))]
pub fn store(self) -> Result<(), ConfyError> {
confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, self).log_err(location!())
}
}
#[cfg(test)]
mod tests {
use crate::gui::types::conf::Conf;
impl Conf {
pub fn test_path() -> String {
format!("{}/{}.toml", env!("CARGO_MANIFEST_DIR"), Self::FILE_NAME)
}
pub fn load() -> Self {
confy::load_path::<Conf>(Conf::test_path()).unwrap_or_else(|_| Conf::default())
}
pub fn store(self) -> Result<(), confy::ConfyError> {
confy::store_path(Conf::test_path(), self)
}
}
}

View File

@@ -1,7 +1,3 @@
#[cfg(not(test))]
use crate::utils::error_logger::{ErrorLogger, Location};
#[cfg(not(test))]
use crate::{SNIFFNET_LOWERCASE, location};
use iced::window::Position;
use iced::{Point, Size};
use serde::{Deserialize, Serialize};
@@ -12,6 +8,7 @@
pub struct SizeTuple(pub f32, pub f32);
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Debug)]
#[serde(default)]
pub struct ConfigWindow {
pub position: PositionTuple,
pub size: SizeTuple,
@@ -30,23 +27,6 @@ impl ConfigWindow {
const MIN_SIZE_X: f32 = 100.0;
const MIN_SIZE_Y: f32 = 100.0;
const FILE_NAME: &'static str = "window";
#[cfg(not(test))]
pub fn load() -> Self {
if let Ok(window) = confy::load::<ConfigWindow>(SNIFFNET_LOWERCASE, Self::FILE_NAME) {
window
} else {
let _ = confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, ConfigWindow::default())
.log_err(location!());
ConfigWindow::default()
}
}
#[cfg(not(test))]
pub fn store(self) -> Result<(), confy::ConfyError> {
confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, self).log_err(location!())
}
pub fn thumbnail_size(factor: f64) -> SizeTuple {
Self::THUMBNAIL_SIZE.scale_and_check(factor)
}
@@ -142,23 +122,3 @@ fn scale_and_check(self, factor: f64) -> PositionTuple {
PositionTuple(x, y)
}
}
#[cfg(test)]
mod tests {
use crate::ConfigWindow;
impl ConfigWindow {
pub fn test_path() -> String {
format!("{}/{}.toml", env!("CARGO_MANIFEST_DIR"), Self::FILE_NAME)
}
pub fn load() -> Self {
confy::load_path::<ConfigWindow>(ConfigWindow::test_path())
.unwrap_or_else(|_| ConfigWindow::default())
}
pub fn store(self) -> Result<(), confy::ConfyError> {
confy::store_path(ConfigWindow::test_path(), self)
}
}
}

View File

@@ -1,9 +1,12 @@
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
#[serde(default)]
pub struct ExportPcap {
enabled: bool,
file_name: String,
directory: String,
pub(crate) enabled: bool,
pub(crate) file_name: String,
pub(crate) directory: String,
}
impl ExportPcap {

View File

@@ -1,7 +1,10 @@
#[derive(Default)]
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)]
#[serde(default)]
pub struct Filters {
expanded: bool,
bpf: String,
pub(crate) expanded: bool,
pub(crate) bpf: String,
}
impl Filters {

View File

@@ -14,7 +14,7 @@
use crate::report::types::sort_type::SortType;
use crate::utils::types::file_info::FileInfo;
use crate::utils::types::web_page::WebPage;
use crate::{Language, ReportSortType, StyleType};
use crate::{Language, StyleType};
#[derive(Debug, Clone)]
/// Messages types that permit reacting to application interactions/subscriptions
@@ -34,7 +34,7 @@ pub enum Message {
/// Select data representation to use
DataReprSelection(DataRepr),
/// Select report sort type to be displayed (inspect page)
ReportSortSelection(ReportSortType),
ReportSortSelection(SortType),
/// Select host sort type to be displayed (overview page)
HostSortSelection(SortType),
/// Select service sort type to be displayed (overview page)

View File

@@ -1,4 +1,7 @@
pub mod conf;
pub mod config_window;
pub mod export_pcap;
pub mod filters;
pub mod message;
pub mod settings;
pub mod timing_events;

34
src/gui/types/settings.rs Normal file
View File

@@ -0,0 +1,34 @@
use serde::{Deserialize, Serialize};
use crate::gui::styles::types::gradient_type::GradientType;
use crate::notifications::types::notifications::Notifications;
use crate::{Language, StyleType};
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
#[serde(default)]
pub struct Settings {
pub color_gradient: GradientType,
pub language: Language,
pub scale_factor: f64,
pub mmdb_country: String,
pub mmdb_asn: String,
pub style_path: String,
pub notifications: Notifications,
// StyleType should be last in order to deserialize as a table properly
pub style: StyleType,
}
impl Default for Settings {
fn default() -> Self {
Settings {
color_gradient: GradientType::default(),
language: Language::default(),
scale_factor: 1.0,
mmdb_country: String::new(),
mmdb_asn: String::new(),
style_path: String::new(),
notifications: Notifications::default(),
style: StyleType::default(),
}
}
}

View File

@@ -7,12 +7,10 @@
use iced::advanced::graphics::image::image_rs::ImageFormat;
#[cfg(target_os = "linux")]
use iced::window::settings::PlatformSpecific;
use iced::{Font, Pixels, Settings, application, window};
use iced::{Font, Pixels, application, window};
use chart::types::traffic_chart::TrafficChart;
use cli::handle_cli_args;
use configs::types::config_device::ConfigDevice;
use configs::types::config_settings::ConfigSettings;
use gui::pages::types::running_page::RunningPage;
use gui::sniffer::Sniffer;
use gui::styles::style_constants::FONT_SIZE_BODY;
@@ -22,18 +20,16 @@
use networking::types::ip_version::IpVersion;
use networking::types::protocol::Protocol;
use networking::types::service::Service;
use report::types::report_sort_type::ReportSortType;
use translations::types::language::Language;
use utils::formatted_strings::print_cli_welcome_message;
use crate::configs::types::config_window::{ConfigWindow, ToPosition, ToSize};
use crate::configs::types::configs::{CONFIGS, Configs};
use crate::gui::sniffer::FONT_FAMILY_NAME;
use crate::gui::styles::style_constants::{ICONS_BYTES, SARASA_MONO_BOLD_BYTES, SARASA_MONO_BYTES};
use crate::gui::types::conf::CONF;
use crate::gui::types::config_window::{ConfigWindow, ToPosition, ToSize};
mod chart;
mod cli;
mod configs;
mod countries;
mod gui;
mod mmdb;
@@ -62,7 +58,7 @@ pub fn main() -> iced::Result {
_gag2 = gag2;
}
let configs = CONFIGS.clone();
let conf = CONF.clone();
let boot_task_chain = handle_cli_args();
#[cfg(debug_assertions)]
@@ -78,10 +74,10 @@ pub fn main() -> iced::Result {
print_cli_welcome_message();
let ConfigWindow { size, position, .. } = configs.window;
let ConfigWindow { size, position, .. } = conf.window;
application(SNIFFNET_TITLECASE, Sniffer::update, Sniffer::view)
.settings(Settings {
.settings(iced::Settings {
// id needed for Linux Wayland; should match StartupWMClass in .desktop file; see issue #292
id: Some(String::from(SNIFFNET_LOWERCASE)),
fonts: vec![
@@ -114,5 +110,5 @@ pub fn main() -> iced::Result {
.subscription(Sniffer::subscription)
.theme(Sniffer::theme)
.scale_factor(Sniffer::scale_factor)
.run_with(move || (Sniffer::new(configs), boot_task_chain))
.run_with(move || (Sniffer::new(conf), boot_task_chain))
}

View File

@@ -1,3 +1,4 @@
use crate::gui::types::conf::Conf;
use crate::gui::types::filters::Filters;
use crate::networking::types::my_device::MyDevice;
use crate::networking::types::my_link_type::MyLinkType;
@@ -5,6 +6,7 @@
use crate::translations::translations_4::capture_file_translation;
use crate::translations::types::language::Language;
use pcap::{Active, Address, Capture, Error, Packet, Savefile, Stat};
use serde::{Deserialize, Serialize};
pub enum CaptureContext {
Live(Live),
@@ -155,6 +157,19 @@ pub enum CaptureSource {
}
impl CaptureSource {
pub fn from_conf(conf: &Conf) -> Self {
match conf.capture_source_picklist {
CaptureSourcePicklist::Device => {
let device = conf.device.to_my_device();
Self::Device(device)
}
CaptureSourcePicklist::File => {
let path = conf.import_pcap_path.clone();
Self::File(MyPcapImport::new(path))
}
}
}
pub fn title(&self, language: Language) -> &str {
match self {
Self::Device(_) => network_adapter_translation(language),
@@ -222,7 +237,7 @@ pub fn new(path: String) -> Self {
}
}
#[derive(Clone, Eq, PartialEq, Debug, Copy, Default)]
#[derive(Clone, Eq, PartialEq, Debug, Copy, Default, Serialize, Deserialize)]
pub enum CaptureSourcePicklist {
#[default]
Device,

View File

@@ -0,0 +1,42 @@
use crate::networking::types::my_device::MyDevice;
use pcap::{Device, DeviceFlags};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
#[serde(default)]
pub struct ConfigDevice {
pub device_name: String,
}
impl Default for ConfigDevice {
fn default() -> Self {
Self {
device_name: Device::lookup()
.unwrap_or(None)
.unwrap_or_else(|| Device {
name: String::new(),
desc: None,
addresses: vec![],
flags: DeviceFlags::empty(),
})
.name,
}
}
}
impl ConfigDevice {
pub fn to_my_device(&self) -> MyDevice {
for device in Device::list().unwrap_or_default() {
if device.name.eq(&self.device_name) {
return MyDevice::from_pcap_device(device);
}
}
let standard_device = Device::lookup().unwrap_or(None).unwrap_or_else(|| Device {
name: String::new(),
desc: None,
addresses: vec![],
flags: DeviceFlags::empty(),
});
MyDevice::from_pcap_device(standard_device)
}
}

View File

@@ -3,6 +3,7 @@
pub mod asn;
pub mod bogon;
pub mod capture_context;
pub mod config_device;
pub mod data_info;
pub mod data_info_host;
pub mod data_representation;

View File

@@ -6,6 +6,7 @@
/// Used to contain the notifications configuration set by the user
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Debug)]
#[serde(default)]
pub struct Notifications {
pub volume: u8,
pub data_notification: DataNotification,
@@ -32,6 +33,7 @@ pub enum Notification {
}
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug, Copy)]
#[serde(default)]
pub struct DataNotification {
/// Data representation
pub data_repr: DataRepr,
@@ -101,6 +103,7 @@ pub fn from(value: &str, existing: Option<Self>) -> Self {
}
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug, Copy)]
#[serde(default)]
pub struct FavoriteNotification {
/// Flag to determine if this notification is enabled
pub notify_on_favorite: bool,

View File

@@ -50,7 +50,7 @@ pub fn get_searched_entries(
all_results.sort_by(|&(_, a), &(_, b)| {
a.compare(
b,
sniffer.report_sort_type.data_sort,
sniffer.conf.report_sort_type,
sniffer.traffic_chart.data_repr,
)
});

View File

@@ -1,4 +1,3 @@
pub mod report_col;
pub mod report_sort_type;
pub mod search_parameters;
pub mod sort_type;

View File

@@ -1,78 +0,0 @@
use std::fmt::Debug;
use iced::widget::Text;
use crate::gui::styles::button::ButtonType;
use crate::gui::styles::types::style_type::StyleType;
use crate::report::types::sort_type::SortType;
/// Struct representing the possible kinds of sort for displayed relevant connections.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct ReportSortType {
pub data_sort: SortType,
}
impl ReportSortType {
pub fn next_sort(self) -> Self {
Self {
data_sort: self.data_sort.next_sort(),
}
}
pub fn icon<'a>(self) -> Text<'a, StyleType> {
self.data_sort.icon()
}
pub fn button_type(self) -> ButtonType {
self.data_sort.button_type()
}
}
#[cfg(test)]
mod tests {
use crate::report::types::report_sort_type::ReportSortType;
use crate::report::types::sort_type::SortType;
#[test]
fn test_next_report_sort() {
let mut sort = ReportSortType::default();
assert_eq!(
sort,
ReportSortType {
data_sort: SortType::Neutral,
}
);
sort = sort.next_sort();
assert_eq!(
sort,
ReportSortType {
data_sort: SortType::Descending,
}
);
sort = sort.next_sort();
assert_eq!(
sort,
ReportSortType {
data_sort: SortType::Ascending,
}
);
sort = sort.next_sort();
assert_eq!(
sort,
ReportSortType {
data_sort: SortType::Neutral,
}
);
sort = sort.next_sort();
assert_eq!(
sort,
ReportSortType {
data_sort: SortType::Descending,
}
);
}
}

View File

@@ -1,10 +1,10 @@
use iced::widget::Text;
use crate::gui::styles::button::ButtonType;
use crate::gui::styles::types::style_type::StyleType;
use crate::utils::types::icon::Icon;
use iced::widget::Text;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub enum SortType {
Ascending,
Descending,