Skip to content

Commit 605c9d8

Browse files
committed
Native: Multiple File Setup & File Concatenation
* Multiple Files view with support for concatenate and open each file in new tab. * Add concatenate to session setups types and start sessions with concat origin. * Implementation of scanned directories in backend service. * Add menu items for scanning directory to find matching files. * Opening multiple files form both menu and cli will end end up in multiple files setup view. * Refactoring for host service regarding file operations.
1 parent 06e2165 commit 605c9d8

File tree

35 files changed

+1416
-142
lines changed

35 files changed

+1416
-142
lines changed

application/apps/indexer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ clap = { version = "4", features = ["derive"] }
8282
egui = "0.33"
8383
eframe = { version = "0.33", default-features = false }
8484
rfd = "0.17"
85+
egui_extras = { version = "0.33", default-features = false }
8586
egui_table = "0.6"
8687
egui_plot = "0.34"
8788
# We don't have direct dependency to winit but we need to specify it here to activate

application/apps/indexer/gui/application/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ eframe = { workspace = true, default_features = false, features = [
5555
"x11", # To support older Linux distributions (restores one of the default features)
5656
] }
5757

58+
egui_extras.workspace = true
5859
egui_table.workspace = true
5960
egui_plot.workspace = true
6061
egui-phosphor = { workspace = true, features = ["regular", "fill"] }

application/apps/indexer/gui/application/src/host/command.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::path::PathBuf;
22

3+
use stypes::FileFormat;
34
use uuid::Uuid;
45

56
use crate::host::{
@@ -10,13 +11,30 @@ use crate::host::{
1011
/// Host commands to be sent from UI to its service.
1112
#[derive(Debug, Clone)]
1213
pub enum HostCommand {
14+
/// Opens the files, prompting the user with the setup UI
15+
/// if multiple files are provided.
1316
OpenFiles(Vec<PathBuf>),
17+
/// Bypasses the setup UI and opens each file in a separate
18+
/// session immediately.
19+
OpenAsSessions(Vec<PathBuf>),
20+
/// Scans a directory for files matching the given format,
21+
/// then opens them via [`HostCommand::OpenFiles`].
22+
OpenFromDirectory {
23+
dir_path: PathBuf,
24+
target_format: FileFormat,
25+
},
26+
/// Concatenate the provided files grouping them via their format.
27+
/// This will start a session for text files directly or will open
28+
/// session setup for other file formats.
29+
ConcatFiles(Vec<(PathBuf, FileFormat)>),
30+
/// Opens sessions setup for connection sources.
1431
ConnectionSessionSetup {
1532
stream: StreamNames,
1633
parser: ParserNames,
1734
},
1835
StartSession(Box<StartSessionParam>),
1936
CloseSessionSetup(Uuid),
37+
CloseMultiSetup(Uuid),
2038
Close,
2139
}
2240

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use egui::Color32;
2+
3+
const HIGHLIGHT_COLORS: [ColorPair; 10] = [
4+
// Red
5+
ColorPair::new(
6+
Color32::from_rgb(255, 255, 255),
7+
Color32::from_rgb(180, 40, 40),
8+
),
9+
// Green
10+
ColorPair::new(
11+
Color32::from_rgb(255, 255, 255),
12+
Color32::from_rgb(40, 160, 40),
13+
),
14+
// Blue
15+
ColorPair::new(
16+
Color32::from_rgb(255, 255, 255),
17+
Color32::from_rgb(40, 80, 180),
18+
),
19+
// Orange
20+
ColorPair::new(Color32::from_rgb(0, 0, 0), Color32::from_rgb(220, 120, 20)),
21+
// Purple
22+
ColorPair::new(
23+
Color32::from_rgb(255, 255, 255),
24+
Color32::from_rgb(140, 40, 180),
25+
),
26+
// Teal
27+
ColorPair::new(Color32::from_rgb(0, 0, 0), Color32::from_rgb(20, 160, 160)),
28+
// Pink
29+
ColorPair::new(
30+
Color32::from_rgb(255, 255, 255),
31+
Color32::from_rgb(220, 60, 140),
32+
),
33+
// Lime
34+
ColorPair::new(Color32::from_rgb(0, 0, 0), Color32::from_rgb(160, 200, 20)),
35+
// Brown
36+
ColorPair::new(
37+
Color32::from_rgb(255, 255, 255),
38+
Color32::from_rgb(120, 80, 40),
39+
),
40+
// Slate
41+
ColorPair::new(
42+
Color32::from_rgb(255, 255, 255),
43+
Color32::from_rgb(80, 100, 120),
44+
),
45+
];
46+
47+
#[allow(unused)]
48+
/// Represents a foreground and background color combination.
49+
#[derive(Debug, Clone)]
50+
pub struct ColorPair {
51+
/// Foreground color.
52+
pub fg: Color32,
53+
/// Background color.
54+
pub bg: Color32,
55+
}
56+
57+
impl ColorPair {
58+
/// Creates a new [`ColorPair`] with the provided arguments.
59+
pub const fn new(fg: Color32, bg: Color32) -> Self {
60+
Self { fg, bg }
61+
}
62+
}
63+
64+
/// Returns a list of available highlighting [`ColorPair`]s.
65+
pub fn highlighting_colors() -> Vec<ColorPair> {
66+
HIGHLIGHT_COLORS.to_vec()
67+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/// Formats a byte count into a human-readable string with units (B, KB, MB, GB).
2+
///
3+
/// Precision is kept to two decimal places for units larger than bytes.
4+
pub fn format_file_size(bytes: u64) -> String {
5+
const KB: u64 = 1024;
6+
const MB: u64 = 1024 * KB;
7+
const GB: u64 = 1024 * MB;
8+
9+
if bytes < KB {
10+
format!("{} B", bytes)
11+
} else if bytes < MB {
12+
format!("{:.2} KB", bytes as f64 / KB as f64)
13+
} else if bytes < GB {
14+
format!("{:.2} MB", bytes as f64 / MB as f64)
15+
} else {
16+
format!("{:.2} GB", bytes as f64 / GB as f64)
17+
}
18+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1+
pub mod colors;
2+
pub mod file_utls;
13
pub mod parsers;
24
pub mod sources;
5+
pub mod ui_utls;

application/apps/indexer/gui/application/src/host/common/parsers.rs

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::fmt::Display;
22

3-
use stypes::ParserType;
3+
use stypes::{FileFormat, ParserType};
44

55
use crate::host::{common::sources::StreamNames, ui::session_setup::state::parsers::ParserConfig};
66

@@ -45,7 +45,16 @@ impl ParserNames {
4545
}
4646
}
4747

48-
pub const fn is_compatible(self, stream: StreamNames) -> bool {
48+
pub const fn is_compatible_file(self, format: FileFormat) -> bool {
49+
match format {
50+
FileFormat::PcapNG | FileFormat::PcapLegacy | FileFormat::Binary => {
51+
self.support_binary_files()
52+
}
53+
FileFormat::Text => self.support_text_files(),
54+
}
55+
}
56+
57+
pub const fn is_compatible_stream(self, stream: StreamNames) -> bool {
4958
use ParserNames as Parser;
5059
use StreamNames as Stream;
5160

@@ -93,3 +102,74 @@ impl From<&ParserConfig> for ParserNames {
93102
}
94103
}
95104
}
105+
106+
#[cfg(test)]
107+
mod tests {
108+
use super::*;
109+
use stypes::FileFormat;
110+
111+
#[test]
112+
fn test_file_compatibility() {
113+
let binary_formats = [
114+
FileFormat::PcapNG,
115+
FileFormat::PcapLegacy,
116+
FileFormat::Binary,
117+
];
118+
let text_formats = [FileFormat::Text];
119+
120+
let binary_parsers = [ParserNames::Dlt, ParserNames::SomeIP, ParserNames::Plugins];
121+
let text_parsers = [ParserNames::Text, ParserNames::Plugins];
122+
123+
// Test Binary Formats
124+
for format in binary_formats {
125+
for parser in binary_parsers {
126+
assert!(
127+
parser.is_compatible_file(format),
128+
"Parser {parser} should be compatible with {format:?}"
129+
);
130+
}
131+
// Text parser should NOT be compatible with binary
132+
assert!(!ParserNames::Text.is_compatible_file(format));
133+
}
134+
135+
// Test Text Formats
136+
for format in text_formats {
137+
for parser in text_parsers {
138+
assert!(
139+
parser.is_compatible_file(format),
140+
"Parser {parser} should be compatible with {format:?}"
141+
);
142+
}
143+
// Binary parsers (except Plugins) should NOT be compatible with text
144+
assert!(!ParserNames::Dlt.is_compatible_file(format));
145+
assert!(!ParserNames::SomeIP.is_compatible_file(format));
146+
}
147+
}
148+
149+
#[test]
150+
fn test_stream_compatibility() {
151+
// Text: Compatible with Process, Serial. NOT Tcp, Udp
152+
assert!(ParserNames::Text.is_compatible_stream(StreamNames::Process));
153+
assert!(ParserNames::Text.is_compatible_stream(StreamNames::Serial));
154+
assert!(!ParserNames::Text.is_compatible_stream(StreamNames::Tcp));
155+
assert!(!ParserNames::Text.is_compatible_stream(StreamNames::Udp));
156+
157+
// Dlt/SomeIP: Compatible with Tcp, Udp, Serial. NOT Process
158+
for parser in [ParserNames::Dlt, ParserNames::SomeIP] {
159+
assert!(parser.is_compatible_stream(StreamNames::Tcp));
160+
assert!(parser.is_compatible_stream(StreamNames::Udp));
161+
assert!(parser.is_compatible_stream(StreamNames::Serial));
162+
assert!(!parser.is_compatible_stream(StreamNames::Process));
163+
}
164+
165+
// Plugins: Compatible with everything
166+
for stream in [
167+
StreamNames::Process,
168+
StreamNames::Serial,
169+
StreamNames::Tcp,
170+
StreamNames::Udp,
171+
] {
172+
assert!(ParserNames::Plugins.is_compatible_stream(stream));
173+
}
174+
}
175+
}

application/apps/indexer/gui/application/src/host/common/sources.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ impl StreamNames {
3030
}
3131

3232
pub fn is_compatible(self, parser: ParserNames) -> bool {
33-
parser.is_compatible(self)
33+
parser.is_compatible_stream(self)
3434
}
3535
}
3636

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use egui::{Frame, Margin, Ui};
2+
3+
/// Frame used to group controls in side views.
4+
pub fn side_panel_group_frame(ui: &mut Ui) -> Frame {
5+
Frame::group(ui.style())
6+
.fill(ui.style().visuals.faint_bg_color)
7+
.inner_margin(Margin::symmetric(10, 8))
8+
.outer_margin(Margin::symmetric(0, 4))
9+
}
10+
11+
pub fn main_panel_group_frame(ui: &mut Ui) -> Frame {
12+
Frame::group(ui.style())
13+
.fill(ui.style().visuals.faint_bg_color)
14+
.inner_margin(Margin::symmetric(12, 12))
15+
.outer_margin(Margin::symmetric(0, 4))
16+
}
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use uuid::Uuid;
22

3-
use crate::{host::ui::session_setup::state::SessionSetupState, session::InitSessionParams};
3+
use crate::{
4+
host::ui::{multi_setup::state::MultiFileState, session_setup::state::SessionSetupState},
5+
session::InitSessionParams,
6+
};
47

58
/// Messages sent from the host service to the UI.
69
#[derive(Debug)]
@@ -9,6 +12,8 @@ pub enum HostMessage {
912
SessionSetupOpened(SessionSetupState),
1013
/// Close session setup with the provided id.
1114
SessionSetupClosed { id: Uuid },
15+
/// Close multiple files setup with the provided id.
16+
MultiSetupClose { id: Uuid },
1217

1318
/// A new session has been successfully created.
1419
SessionCreated {
@@ -17,7 +22,8 @@ pub enum HostMessage {
1722
/// instead of creating a new tab for the session.
1823
session_setup_id: Option<Uuid>,
1924
},
20-
25+
/// Open Session Setup for concatenating files.
26+
MultiFilesSetup(MultiFileState),
2127
/// Host service has finished shutting down; the UI should now exit.
2228
Shutdown,
2329
}

0 commit comments

Comments
 (0)