Skip to content

Commit 51e0396

Browse files
feat: improve IP blacklist import feedback
1 parent e4534b0 commit 51e0396

3 files changed

Lines changed: 337 additions & 8 deletions

File tree

src/gui/pages/settings_general_page.rs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@ use crate::gui::styles::text::TextType;
1515
use crate::gui::types::message::Message;
1616
use crate::gui::types::settings::Settings;
1717
use crate::mmdb::types::mmdb_reader::{MmdbReader, MmdbReaders};
18-
use crate::networking::types::ip_blacklist::IpBlacklist;
18+
use crate::networking::types::ip_blacklist::{IpBlacklist, IpBlacklistLoadStatus};
1919
use crate::translations::translations::language_translation;
2020
use crate::translations::translations_2::country_translation;
2121
use crate::translations::translations_3::{
2222
mmdb_files_translation, params_not_editable_translation, zoom_translation,
2323
};
2424
use crate::translations::translations_4::share_feedback_translation;
25-
use crate::translations::translations_5::ip_blacklist_translation;
25+
use crate::translations::translations_5::{
26+
ip_blacklist_file_read_error_translation, ip_blacklist_loaded_translation,
27+
ip_blacklist_loading_translation, ip_blacklist_no_valid_entries_translation,
28+
ip_blacklist_not_selected_translation, ip_blacklist_translation,
29+
};
2630
use crate::utils::formatted_strings::get_path_termination_string;
2731
use crate::utils::types::file_info::FileInfo;
2832
use crate::utils::types::icon::Icon;
@@ -331,6 +335,8 @@ fn blacklist_selection<'a>(
331335
};
332336

333337
let message = Message::LoadIpBlacklist;
338+
let status = blacklist_status_text(custom_path, ip_blacklist, language);
339+
let status_text_type = blacklist_status_text_type(ip_blacklist);
334340

335341
Column::new()
336342
.width(Length::Fill)
@@ -363,6 +369,44 @@ fn blacklist_selection<'a>(
363369
button_clear_mmdb(message, is_editable)
364370
}),
365371
)
372+
.push(Text::new(status).class(status_text_type).size(12))
373+
}
374+
375+
fn blacklist_status_text(
376+
custom_path: &str,
377+
ip_blacklist: &IpBlacklist,
378+
language: Language,
379+
) -> String {
380+
if custom_path.is_empty() {
381+
return ip_blacklist_not_selected_translation(language).to_string();
382+
}
383+
384+
match ip_blacklist.status() {
385+
IpBlacklistLoadStatus::NotSelected => {
386+
ip_blacklist_not_selected_translation(language).to_string()
387+
}
388+
IpBlacklistLoadStatus::Loading => ip_blacklist_loading_translation(language).to_string(),
389+
IpBlacklistLoadStatus::FileReadError => {
390+
ip_blacklist_file_read_error_translation(language).to_string()
391+
}
392+
IpBlacklistLoadStatus::NoValidEntries { .. } => {
393+
ip_blacklist_no_valid_entries_translation(language).to_string()
394+
}
395+
IpBlacklistLoadStatus::Loaded {
396+
ip_count,
397+
network_count,
398+
..
399+
} => ip_blacklist_loaded_translation(language, *ip_count, *network_count),
400+
}
401+
}
402+
403+
fn blacklist_status_text_type(ip_blacklist: &IpBlacklist) -> TextType {
404+
match ip_blacklist.status() {
405+
IpBlacklistLoadStatus::FileReadError | IpBlacklistLoadStatus::NoValidEntries { .. } => {
406+
TextType::Danger
407+
}
408+
_ => TextType::Standard,
409+
}
366410
}
367411

368412
fn button_clear_mmdb<'a>(

src/networking/types/ip_blacklist.rs

Lines changed: 99 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,40 @@ use std::sync::Arc;
55
use ipnet::IpNet;
66
use prefix_trie::joint::set::JointPrefixSet;
77

8+
#[derive(Clone, Debug, Default, PartialEq, Eq)]
9+
pub enum IpBlacklistLoadStatus {
10+
#[default]
11+
NotSelected,
12+
Loading,
13+
Loaded {
14+
ip_count: usize,
15+
network_count: usize,
16+
ignored_lines: usize,
17+
},
18+
FileReadError,
19+
NoValidEntries {
20+
ignored_lines: usize,
21+
},
22+
}
23+
824
#[derive(Clone, Default, Debug)]
925
pub struct IpBlacklist {
1026
ips: Arc<HashSet<IpAddr>>,
1127
networks: Arc<JointPrefixSet<IpNet>>,
12-
is_loading: bool,
28+
status: IpBlacklistLoadStatus,
1329
}
1430

1531
impl IpBlacklist {
1632
pub async fn from_file(path: String) -> Self {
1733
let Ok(buf) = tokio::fs::read_to_string(&path).await else {
18-
return IpBlacklist::default();
34+
return IpBlacklist {
35+
status: IpBlacklistLoadStatus::FileReadError,
36+
..IpBlacklist::default()
37+
};
1938
};
2039
let mut ips = HashSet::new();
2140
let mut networks = JointPrefixSet::new();
41+
let mut ignored_lines = 0;
2242
for line in buf.lines() {
2343
let Some(first) = line.split_whitespace().next() else {
2444
continue;
@@ -28,12 +48,26 @@ impl IpBlacklist {
2848
ips.insert(ip);
2949
} else if let Ok(network) = first.parse::<IpNet>() {
3050
networks.insert(network);
51+
} else {
52+
ignored_lines += 1;
3153
}
3254
}
55+
let ip_count = ips.len();
56+
let network_count = networks.len();
57+
let status = if ip_count == 0 && network_count == 0 {
58+
IpBlacklistLoadStatus::NoValidEntries { ignored_lines }
59+
} else {
60+
IpBlacklistLoadStatus::Loaded {
61+
ip_count,
62+
network_count,
63+
ignored_lines,
64+
}
65+
};
66+
3367
IpBlacklist {
3468
ips: Arc::new(ips),
3569
networks: Arc::new(networks),
36-
is_loading: false,
70+
status,
3771
}
3872
}
3973

@@ -42,15 +76,22 @@ impl IpBlacklist {
4276
}
4377

4478
pub fn is_invalid(&self) -> bool {
45-
self.ips.is_empty() && self.networks.is_empty() && !self.is_loading
79+
matches!(
80+
self.status,
81+
IpBlacklistLoadStatus::FileReadError | IpBlacklistLoadStatus::NoValidEntries { .. }
82+
)
4683
}
4784

4885
pub fn is_loading(&self) -> bool {
49-
self.is_loading
86+
matches!(self.status, IpBlacklistLoadStatus::Loading)
87+
}
88+
89+
pub fn status(&self) -> &IpBlacklistLoadStatus {
90+
&self.status
5091
}
5192

5293
pub fn start_loading(&mut self) {
53-
self.is_loading = true;
94+
self.status = IpBlacklistLoadStatus::Loading;
5495
}
5596
}
5697

@@ -68,6 +109,14 @@ mod tests {
68109
assert!(!blacklist.is_loading());
69110
assert_eq!(blacklist.ips.len(), 4);
70111
assert_eq!(blacklist.networks.len(), 0);
112+
assert_eq!(
113+
blacklist.status(),
114+
&IpBlacklistLoadStatus::Loaded {
115+
ip_count: 4,
116+
network_count: 0,
117+
ignored_lines: 4,
118+
}
119+
);
71120

72121
assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8))));
73122
assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 3, 255))));
@@ -89,6 +138,10 @@ mod tests {
89138
assert!(!blacklist.is_loading());
90139
assert_eq!(blacklist.ips.len(), 0);
91140
assert_eq!(blacklist.networks.len(), 0);
141+
assert_eq!(
142+
blacklist.status(),
143+
&IpBlacklistLoadStatus::NoValidEntries { ignored_lines: 6 }
144+
);
92145

93146
assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8))));
94147
assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))));
@@ -106,6 +159,14 @@ mod tests {
106159
assert!(!blacklist.is_loading());
107160
assert_eq!(blacklist.ips.len(), 2);
108161
assert_eq!(blacklist.networks.len(), 4);
162+
assert_eq!(
163+
blacklist.status(),
164+
&IpBlacklistLoadStatus::Loaded {
165+
ip_count: 2,
166+
network_count: 4,
167+
ignored_lines: 3,
168+
}
169+
);
109170

110171
assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8))));
111172
assert!(blacklist.contains(&"2001:db8::1".parse::<IpAddr>().unwrap()));
@@ -137,6 +198,14 @@ mod tests {
137198
assert!(!blacklist.is_loading());
138199
assert_eq!(blacklist.ips.len(), 0);
139200
assert_eq!(blacklist.networks.len(), 1);
201+
assert_eq!(
202+
blacklist.status(),
203+
&IpBlacklistLoadStatus::Loaded {
204+
ip_count: 0,
205+
network_count: 1,
206+
ignored_lines: 0,
207+
}
208+
);
140209

141210
assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 3, 1))));
142211
assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 4, 1))));
@@ -170,4 +239,28 @@ mod tests {
170239
assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 237, 0))));
171240
assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 233, 160, 0))));
172241
}
242+
243+
#[tokio::test]
244+
async fn test_ip_blacklist_read_error() {
245+
let blacklist =
246+
IpBlacklist::from_file("resources/test/does_not_exist_blacklist.txt".to_string()).await;
247+
248+
assert!(blacklist.is_invalid());
249+
assert!(!blacklist.is_loading());
250+
assert_eq!(blacklist.ips.len(), 0);
251+
assert_eq!(blacklist.networks.len(), 0);
252+
assert_eq!(blacklist.status(), &IpBlacklistLoadStatus::FileReadError);
253+
assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8))));
254+
}
255+
256+
#[test]
257+
fn test_ip_blacklist_loading_status() {
258+
let mut blacklist = IpBlacklist::default();
259+
260+
blacklist.start_loading();
261+
262+
assert!(blacklist.is_loading());
263+
assert!(!blacklist.is_invalid());
264+
assert_eq!(blacklist.status(), &IpBlacklistLoadStatus::Loading);
265+
}
173266
}

0 commit comments

Comments
 (0)