diff --git a/.all-contributorsrc b/.all-contributorsrc
index 7db83d3d..adc2fb71 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -601,7 +601,8 @@
"avatar_url": "https://avatars.githubusercontent.com/u/171437458?v=4",
"profile": "https://github.com/shu-kitamura",
"contributions": [
- "code"
+ "code",
+ "translation"
]
},
{
@@ -721,6 +722,60 @@
"contributions": [
"platform"
]
+ },
+ {
+ "login": "islameehassan",
+ "name": "islameehassan",
+ "avatar_url": "https://avatars.githubusercontent.com/u/98806155?v=4",
+ "profile": "https://github.com/islameehassan",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "lionrayonnant",
+ "name": "Lion Rayonnant",
+ "avatar_url": "https://avatars.githubusercontent.com/u/106342136?v=4",
+ "profile": "https://github.com/lionrayonnant",
+ "contributions": [
+ "translation"
+ ]
+ },
+ {
+ "login": "18601673727",
+ "name": "18601673727",
+ "avatar_url": "https://avatars.githubusercontent.com/u/3302620?v=4",
+ "profile": "https://github.com/18601673727",
+ "contributions": [
+ "translation"
+ ]
+ },
+ {
+ "login": "AlleM43",
+ "name": "AlleM43",
+ "avatar_url": "https://avatars.githubusercontent.com/u/16104173?v=4",
+ "profile": "https://github.com/AlleM43",
+ "contributions": [
+ "platform"
+ ]
+ },
+ {
+ "login": "aris1009",
+ "name": "Aris Konstantoulas",
+ "avatar_url": "https://avatars.githubusercontent.com/u/25184469?v=4",
+ "profile": "https://github.com/aris1009",
+ "contributions": [
+ "translation"
+ ]
+ },
+ {
+ "login": "mmseng",
+ "name": "Matt Seng",
+ "avatar_url": "https://avatars.githubusercontent.com/u/8218085?v=4",
+ "profile": "https://github.com/mmseng",
+ "contributions": [
+ "financial"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index fd513caa..a7693ffa 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -1,9 +1,6 @@
name: Docker
on:
- # push:
- # tags:
- # - 'v[0-9]+.[0-9]+.[0-9]+'
workflow_dispatch:
jobs:
@@ -12,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Extract version from Cargo.toml
id: cargo-version
@@ -21,6 +18,13 @@ jobs:
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "version=$VERSION" >> $GITHUB_OUTPUT
+ - name: Check there is no existing image with the same version
+ run: |
+ if docker manifest inspect ghcr.io/gyulyvgc/sniffnet:${{ env.VERSION }}; then
+ echo "Image with version ${{ env.VERSION }} already exists"
+ exit 1
+ fi
+
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -43,4 +47,3 @@ jobs:
tags: |
ghcr.io/gyulyvgc/sniffnet:latest
ghcr.io/gyulyvgc/sniffnet:${{ env.VERSION }}
-# ghcr.io/gyulyvgc/sniffnet:${{ github.ref_name }}
diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml
index f89dbbe4..3fa67ab0 100644
--- a/.github/workflows/package.yml
+++ b/.github/workflows/package.yml
@@ -38,7 +38,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Install Linux dependencies
if: matrix.os == 'ubuntu'
@@ -126,7 +126,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Install dependencies
run: apt-get update -y && apt-get install -y curl build-essential
@@ -139,7 +139,7 @@ jobs:
- name: Install packaging tools
run: cargo install cargo-deb
- - uses: actions/download-artifact@v4
+ - uses: actions/download-artifact@v5
with:
name: build-ubuntu-${{ matrix.target }}
path: target/
@@ -158,6 +158,63 @@ jobs:
path: artifacts/
if-no-files-found: error
+ appimage:
+ runs-on: ubuntu-latest
+ container:
+ image: debian:latest
+ options: --privileged
+ needs: deb
+ strategy:
+ fail-fast: true
+ matrix:
+ include:
+ - arch: amd64
+ appimgarch: x86_64
+ - arch: arm64
+ appimgarch: aarch64
+ - arch: i386
+ appimgarch: i686
+ - arch: armhf
+ appimgarch: armhf
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v5
+
+ - name: Install dependencies
+ run: apt-get update -y && apt-get install -y git build-essential graphicsmagick-imagemagick-compat wget file desktop-file-utils libfuse2
+
+ - name: Download Debian package
+ uses: actions/download-artifact@v5
+ with:
+ name: deb-${{ matrix.arch }}
+ path: /target/
+
+ - name: Download pkg2appimage
+ shell: bash
+ run: git clone https://github.com/AppImageCommunity/pkg2appimage.git
+
+ - name: Replace placeholders
+ shell: bash
+ run: |
+ sed -i -e 's/REPLACE_TAG/Sniffnet_LinuxDEB_${{ matrix.arch }}.deb/g' resources/packaging/linux/AppImage/sniffnet.yml
+ sed -i -e 's/binary-${arch}/binary-${{ matrix.arch }}/g' pkg2appimage/functions.sh
+
+ - name: Repackage for AppImage
+ shell: bash
+ run: |
+ cd pkg2appimage
+ ARCH=${{ matrix.appimgarch }} FUNCTIONS_SH=$(pwd)/functions.sh ./pkg2appimage ../resources/packaging/linux/AppImage/sniffnet.yml
+ mkdir /out
+ mv out/*.AppImage /out/Sniffnet_LinuxAppImage_${{ matrix.arch }}.AppImage
+
+ - name: Upload package artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: appimage-${{ matrix.arch }}
+ path: /out/
+ if-no-files-found: error
+
rpm:
runs-on: ubuntu-latest
container:
@@ -174,7 +231,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Install dependencies
run: dnf update -y && dnf install -y @development-tools patchelf
@@ -187,7 +244,7 @@ jobs:
- name: Install packaging tools
run: cargo install cargo-generate-rpm
- - uses: actions/download-artifact@v4
+ - uses: actions/download-artifact@v5
with:
name: build-ubuntu-${{ matrix.target }}
path: target/
@@ -221,7 +278,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
@@ -231,7 +288,7 @@ jobs:
cargo install toml-cli
brew install create-dmg
- - uses: actions/download-artifact@v4
+ - uses: actions/download-artifact@v5
with:
name: build-macos-${{ matrix.target }}
path: target/
@@ -285,7 +342,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Install dependencies
shell: powershell
@@ -306,7 +363,7 @@ jobs:
- name: Install packaging tools
run: cargo install cargo-wix
- - uses: actions/download-artifact@v4
+ - uses: actions/download-artifact@v5
with:
name: build-windows-${{ matrix.target }}
path: target/
@@ -318,9 +375,29 @@ jobs:
cargo wix --no-build --nocapture --target ${{ matrix.target }}
Move-Item -Path target\wix\sniffnet*.msi -Destination .\artifacts\Sniffnet_Windows_${{ matrix.arch }}.msi
- - name: Upload package artifacts
+ - name: Upload unsigned package artifacts
+ id: upload-unsigned-artifact
uses: actions/upload-artifact@v4
with:
name: msi-${{ matrix.arch }}
path: artifacts/
if-no-files-found: error
+
+ - name: Sign package artifacts
+ uses: signpath/github-action-submit-signing-request@v1.3
+ with:
+ api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
+ organization-id: '3b533e02-73c3-4908-a018-d09a34498a6a'
+ project-slug: 'sniffnet'
+ signing-policy-slug: 'release-signing'
+ github-artifact-id: '${{ steps.upload-unsigned-artifact.outputs.artifact-id }}'
+ wait-for-completion: true
+ output-artifact-directory: './artifacts'
+
+ - name: Upload signed package artifacts (overwrite unsigned)
+ uses: actions/upload-artifact@v4
+ with:
+ name: msi-${{ matrix.arch }}
+ path: artifacts/
+ if-no-files-found: error
+ overwrite: true
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 85f91c52..82704275 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -32,7 +32,7 @@ jobs:
- os: windows
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
diff --git a/.gitignore b/.gitignore
index fe36f504..bc65973c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -57,3 +57,6 @@ $RECYCLE.BIN/
### Custom... ###
lcov.info
*.pcap
+node_modules
+package.json
+yarn.lock
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7604354..84e205b0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,10 +3,21 @@ # Changelog
All Sniffnet releases with the relative changes are documented in this file.
## [UNRELEASED]
+- Enhanced traffic filtering capabilities: Berkeley Packet Filter ([#937](https://github.com/GyulyVGC/sniffnet/pull/937) — fixes [#810](https://github.com/GyulyVGC/sniffnet/issues/810))
+- Added support for `Linux SLL` link type, enabling to monitor the `any` interface on Linux ([#945](https://github.com/GyulyVGC/sniffnet/pull/945))
+- 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))
- Uzbek ([#834](https://github.com/GyulyVGC/sniffnet/pull/834))
+ - Simplified Chinese ([#838](https://github.com/GyulyVGC/sniffnet/pull/838))
+ - Japanese ([#849](https://github.com/GyulyVGC/sniffnet/pull/849))
+ - French ([#864](https://github.com/GyulyVGC/sniffnet/pull/864))
+ - Greek ([#879](https://github.com/GyulyVGC/sniffnet/pull/879))
+- Fix support for IPinfo's databases (the most recent version renamed the `country` field to `country_code`)
## [1.4.0] - 2025-06-27
- Import PCAP files ([#795](https://github.com/GyulyVGC/sniffnet/pull/795) — fixes [#283](https://github.com/GyulyVGC/sniffnet/issues/283))
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 9aeb7a50..0f0831f6 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -6,105 +6,111 @@
+  18601673727 🌍 |
 ADS Fund 💵 |
 Abdullah 🤔 🖋 |
 Adriano 🤔 |
 Aguacero 🌧️ 🌍 |
 Ahmet Kaan GÜMÜŞ 🌍 |
 Alexandr Shashkin 🐛 |
-  AmadeusGraves 🌍 |
+  AlleM43 📦 |
+  AmadeusGraves 🌍 |
 Angelos Bousis 🌍 |
 Antoine Colombier ⚠️ 🌍 |
+  Aris Konstantoulas 🌍 |
 BugsQuanti 🌍 |
 Charpy 🌍 |
+
+
 Christoph Wanja 💵 |
 Colin Delahunty ⚠️ |
 Cornelius Roemer 🤔 |
-
-
 CosminPerRam 💻 |
 Cristiano 💻 🤔 |
 Cthulu201 💵 |
 Dinar Shagaliev 🌍 |
+
+
 Dominic Kim 🌍 |
 Echo 💵 |
 Embers-of-the-Fire 🌍 |
-
-
 Francisco Salgueiro 🌍 |
 GNUser 📖 📦 |
 George Shuklin 🌍 |
 Giusy Digital 🐛 |
+
+
 Hiroki Tagato 📦 |
 Hubert 🌍 |
 Hüseyin Fahri Uzun 🌍 |
-
-
 IPinfo 💵 |
 Ilmi2 💵 |
 Jan Walter 💵 |
 Jauder Ho 🚇 |
+
+
 Joshua Megnauth 💻 🎨 |
 Julian Schmid 💻 🤔 |
 LiChenG-P 💻 |
-
-
 Liam OBrien 🤔 |
 Limdongju 🌍 |
+  Lion Rayonnant 🌍 |
 Ludwig Stecher 🤔 💻 |
-  Marc Gavilán 🌍 |
-  Marco Cadetg 📦 |
-  Matthias Braun 📖 |
-  Michel Hansma 🎨 ️️️️♿️ |
+  Marc Gavilán 🌍 |
+  Marco Cadetg 📦 |
+  Matt Seng 💵 |
+  Matthias Braun 📖 |
+  Michel Hansma 🎨 ️️️️♿️ |
 Morgan Hill 🛡️ |
 Muhammadali Hakimov 🌍 |
+
+
 Nubi 🌍 |
 Oleksii Filonenko 🌍 |
 Orhun Parmaksız 📖 📦 💵 |
 Peter Dave Hello 🌍 |
 Phil Clifford 📦 |
-
-
 Quetzal-coalt 🌍 |
 Ron 🤔 |
+
+
 Safaraliev Maxim 🌍 |
 Shawn Yeager 💵 |
 SignPath GmbH 📦 |
 The Artifex 🌍 📦 |
 Trịnh Duy Hưng 🌍 |
-
-
 TyseEX 🐛 |
 Victor Nilsson 🌍 💻 |
+
+
 Wang Zishi 🌍 |
 Yevhen 🌍 |
 Ylva 🌍 |
 ZEROF 💵 |
 ZeroDot1 🎨 ️️️️♿️ |
-
-
 clr 📖 🌍 |
 ervinpopescu 🌍 |
+
+
 glitsj16 📦 |
 guilherme-demarchi 🌍 |
 hirotake111 🌍 |
+  islameehassan 💻 |
 louis-ym4 🎨 |
 luca3s 🌍 |
+  pia 🌍 |
-  pia 🌍 |
 pin 📦 |
-  shu-kitamura 💻 |
+  shu-kitamura 💻 🌍 |
 starccy 💻 |
 tiansheng li 💵 |
 vtiinanen 🌍 |
 yossarian 🌍 |
-
-
 陈寒彤 🌍 |
diff --git a/Cargo.lock b/Cargo.lock
index 28ee19c3..c68b0b6c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -14,9 +14,9 @@ dependencies = [
[[package]]
name = "ab_glyph_rasterizer"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169"
+checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
[[package]]
name = "addr2line"
@@ -80,12 +80,12 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "alsa"
-version = "0.9.1"
+version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43"
+checksum = "bdc00893e7a970727e9304671b2c88577b4cfe53dc64019fdfdf9683573a09c4"
dependencies = [
"alsa-sys",
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"cfg-if",
"libc",
]
@@ -107,7 +107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046"
dependencies = [
"android-properties",
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"cc",
"cesu8",
"jni",
@@ -144,9 +144,9 @@ dependencies = [
[[package]]
name = "anstream"
-version = "0.6.19"
+version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
+checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -174,22 +174,22 @@ dependencies = [
[[package]]
name = "anstyle-query"
-version = "1.1.3"
+version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
+checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
dependencies = [
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
]
[[package]]
name = "anstyle-wincon"
-version = "3.0.9"
+version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
+checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
dependencies = [
"anstyle",
"once_cell_polyfill",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -247,7 +247,7 @@ dependencies = [
"wayland-backend",
"wayland-client",
"wayland-protocols",
- "zbus 5.9.0",
+ "zbus 5.10.0",
]
[[package]]
@@ -276,9 +276,9 @@ dependencies = [
[[package]]
name = "async-executor"
-version = "1.13.2"
+version = "1.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa"
+checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8"
dependencies = [
"async-task",
"concurrent-queue",
@@ -319,9 +319,9 @@ dependencies = [
[[package]]
name = "async-lock"
-version = "3.4.0"
+version = "3.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
+checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc"
dependencies = [
"event-listener",
"event-listener-strategy",
@@ -365,7 +365,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -394,13 +394,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
[[package]]
name = "async-trait"
-version = "0.1.88"
+version = "0.1.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
+checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -453,9 +453,9 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bit_field"
-version = "0.10.2"
+version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
+checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
[[package]]
name = "bitflags"
@@ -465,9 +465,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.9.1"
+version = "2.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
+checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d"
[[package]]
name = "block"
@@ -499,7 +499,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2"
dependencies = [
- "objc2 0.6.1",
+ "objc2 0.6.2",
]
[[package]]
@@ -529,22 +529,22 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
[[package]]
name = "bytemuck"
-version = "1.23.1"
+version = "1.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
+checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
-version = "1.10.0"
+version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4"
+checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -565,7 +565,7 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"log",
"polling",
"rustix 0.38.44",
@@ -587,9 +587,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.2.30"
+version = "1.2.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7"
+checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc"
dependencies = [
"jobserver",
"libc",
@@ -604,9 +604,9 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cfg-if"
-version = "1.0.1"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
+checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "cfg_aliases"
@@ -634,9 +634,9 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.5.41"
+version = "4.5.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9"
+checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57"
dependencies = [
"clap_builder",
"clap_derive",
@@ -644,9 +644,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.41"
+version = "4.5.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d"
+checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41"
dependencies = [
"anstream",
"anstyle",
@@ -656,14 +656,14 @@ dependencies = [
[[package]]
name = "clap_derive"
-version = "4.5.41"
+version = "4.5.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
+checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -791,7 +791,7 @@ checksum = "f29222b549d4e3ded127989d523da9e928918d0d0d7f7c1690b439d0d538bae9"
dependencies = [
"directories",
"serde",
- "thiserror 2.0.12",
+ "thiserror 2.0.16",
"toml 0.8.23",
]
@@ -840,7 +840,7 @@ version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"core-foundation 0.10.1",
"core-graphics-types 0.2.0",
"foreign-types 0.5.0",
@@ -864,7 +864,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"core-foundation 0.10.1",
"libc",
]
@@ -889,7 +889,7 @@ version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"fontdb 0.16.2",
"log",
"rangemap",
@@ -1019,7 +1019,7 @@ version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e3d747f100290a1ca24b752186f61f6637e1deffe3bf6320de6fcb29510a307"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"libloading 0.8.8",
"winapi",
]
@@ -1048,9 +1048,9 @@ checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f"
[[package]]
name = "data-url"
-version = "0.3.1"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
+checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
[[package]]
name = "dconf_rs"
@@ -1111,7 +1111,7 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
- "redox_users 0.5.0",
+ "redox_users 0.5.2",
"windows-sys 0.60.2",
]
@@ -1127,10 +1127,10 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"block2 0.6.1",
"libc",
- "objc2 0.6.1",
+ "objc2 0.6.2",
]
[[package]]
@@ -1141,7 +1141,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -1161,14 +1161,14 @@ checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
[[package]]
name = "dns-lookup"
-version = "2.0.4"
+version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc"
+checksum = "853d5bcf0b73bd5e6d945b976288621825c7166e9f06c5a035ae1aaf42d1b64f"
dependencies = [
"cfg-if",
"libc",
"socket2",
- "windows-sys 0.48.0",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -1195,7 +1195,7 @@ version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"bytemuck",
"drm-ffi",
"drm-fourcc",
@@ -1267,7 +1267,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -1325,9 +1325,9 @@ dependencies = [
[[package]]
name = "etherparse"
-version = "0.18.0"
+version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ff83a5facf1a7cbfef93cfb48d6d4fb6a1f42d8ac2341a96b3255acb4d4f860"
+checksum = "b119b9796ff800751a220394b8b3613f26dd30c48f254f6837e64c464872d1c7"
dependencies = [
"arrayvec",
]
@@ -1343,9 +1343,9 @@ dependencies = [
[[package]]
name = "event-listener"
-version = "5.4.0"
+version = "5.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae"
+checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
dependencies = [
"concurrent-queue",
"parking",
@@ -1516,7 +1516,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -1533,9 +1533,9 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
[[package]]
name = "form_urlencoded"
-version = "1.2.1"
+version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
dependencies = [
"percent-encoding",
]
@@ -1591,9 +1591,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-lite"
-version = "2.6.0"
+version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
+checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
dependencies = [
"fastrand",
"futures-core",
@@ -1610,7 +1610,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -1741,9 +1741,9 @@ checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3"
[[package]]
name = "glob"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
+checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "glow"
@@ -1772,7 +1772,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"gpu-alloc-types",
]
@@ -1782,7 +1782,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
]
[[package]]
@@ -1804,7 +1804,7 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"gpu-descriptor-types",
"hashbrown 0.14.5",
]
@@ -1815,7 +1815,7 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
]
[[package]]
@@ -1830,9 +1830,9 @@ dependencies = [
[[package]]
name = "h2"
-version = "0.4.11"
+version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785"
+checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
dependencies = [
"atomic-waker",
"bytes",
@@ -1878,9 +1878,9 @@ dependencies = [
[[package]]
name = "hashbrown"
-version = "0.15.4"
+version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
[[package]]
name = "hassle-rs"
@@ -1888,7 +1888,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"com",
"libc",
"libloading 0.8.8",
@@ -1969,19 +1969,21 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "hyper"
-version = "1.6.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80"
+checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
dependencies = [
+ "atomic-waker",
"bytes",
"futures-channel",
- "futures-util",
+ "futures-core",
"h2",
"http",
"http-body",
"httparse",
"itoa",
"pin-project-lite",
+ "pin-utils",
"smallvec",
"tokio",
"want",
@@ -2022,9 +2024,9 @@ dependencies = [
[[package]]
name = "hyper-util"
-version = "0.1.15"
+version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df"
+checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
dependencies = [
"base64",
"bytes",
@@ -2091,7 +2093,7 @@ version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0013a238275494641bf8f1732a23a808196540dc67b22ff97099c044ae4c8a1c"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"bytes",
"dark-light",
"glam",
@@ -2139,7 +2141,7 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba25a18cfa6d5cc160aca7e1b34f73ccdff21680fa8702168c09739767b6c66f"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"bytemuck",
"cosmic-text",
"half",
@@ -2205,7 +2207,7 @@ version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15708887133671d2bcc6c1d01d1f176f43a64d6cdc3b2bf893396c3ee498295f"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"bytemuck",
"futures",
"glam",
@@ -2345,9 +2347,9 @@ dependencies = [
[[package]]
name = "idna"
-version = "1.0.3"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
dependencies = [
"idna_adapter",
"smallvec",
@@ -2390,12 +2392,12 @@ checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
[[package]]
name = "indexmap"
-version = "2.10.0"
+version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
+checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9"
dependencies = [
"equivalent",
- "hashbrown 0.15.4",
+ "hashbrown 0.15.5",
]
[[package]]
@@ -2409,11 +2411,11 @@ dependencies = [
[[package]]
name = "io-uring"
-version = "0.7.8"
+version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013"
+checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"cfg-if",
"libc",
]
@@ -2485,9 +2487,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
-version = "0.1.33"
+version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
+checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
"getrandom 0.3.3",
"libc",
@@ -2550,11 +2552,12 @@ dependencies = [
[[package]]
name = "kurbo"
-version = "0.11.2"
+version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c"
+checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62"
dependencies = [
"arrayvec",
+ "euclid",
"smallvec",
]
@@ -2572,9 +2575,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]]
name = "libc"
-version = "0.2.174"
+version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
+checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
name = "libloading"
@@ -2593,7 +2596,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
dependencies = [
"cfg-if",
- "windows-targets 0.53.2",
+ "windows-targets 0.53.3",
]
[[package]]
@@ -2604,13 +2607,13 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]]
name = "libredox"
-version = "0.1.6"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0"
+checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"libc",
- "redox_syscall 0.5.14",
+ "redox_syscall 0.5.17",
]
[[package]]
@@ -2745,7 +2748,7 @@ dependencies = [
"log",
"memchr",
"serde",
- "thiserror 2.0.12",
+ "thiserror 2.0.16",
]
[[package]]
@@ -2756,9 +2759,9 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "memmap2"
-version = "0.9.7"
+version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28"
+checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7"
dependencies = [
"libc",
]
@@ -2778,7 +2781,7 @@ version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"block",
"core-graphics-types 0.1.3",
"foreign-types 0.5.0",
@@ -2816,9 +2819,9 @@ dependencies = [
[[package]]
name = "mutate_once"
-version = "0.1.1"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b"
+checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af"
[[package]]
name = "naga"
@@ -2827,7 +2830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843"
dependencies = [
"bit-set",
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"codespan-reporting",
"hexf-parse",
"indexmap",
@@ -2863,7 +2866,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"jni-sys",
"log",
"ndk-sys 0.6.0+11769913",
@@ -2902,7 +2905,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"cfg-if",
"cfg_aliases 0.2.1",
"libc",
@@ -2915,7 +2918,7 @@ version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"cfg-if",
"cfg_aliases 0.2.1",
"libc",
@@ -2940,7 +2943,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -3002,7 +3005,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -3033,9 +3036,9 @@ dependencies = [
[[package]]
name = "objc2"
-version = "0.6.1"
+version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551"
+checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc"
dependencies = [
"objc2-encode",
]
@@ -3046,7 +3049,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"block2 0.5.1",
"libc",
"objc2 0.5.2",
@@ -3062,9 +3065,9 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"block2 0.6.1",
- "objc2 0.6.1",
+ "objc2 0.6.2",
"objc2-foundation 0.3.1",
]
@@ -3074,9 +3077,9 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10cbe18d879e20a4aea544f8befe38bcf52255eb63d3f23eca2842f3319e4c07"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"libc",
- "objc2 0.6.1",
+ "objc2 0.6.2",
"objc2-core-audio",
"objc2-core-audio-types",
"objc2-core-foundation",
@@ -3089,7 +3092,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"block2 0.5.1",
"objc2 0.5.2",
"objc2-core-location",
@@ -3114,7 +3117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca44961e888e19313b808f23497073e3f6b3c22bb485056674c8b49f3b025c82"
dependencies = [
"dispatch2",
- "objc2 0.6.1",
+ "objc2 0.6.2",
"objc2-core-audio-types",
"objc2-core-foundation",
]
@@ -3125,8 +3128,8 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0f1cc99bb07ad2ddb6527ddf83db6a15271bb036b3eb94b801cd44fdc666ee1"
dependencies = [
- "bitflags 2.9.1",
- "objc2 0.6.1",
+ "bitflags 2.9.3",
+ "objc2 0.6.2",
]
[[package]]
@@ -3135,7 +3138,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"block2 0.5.1",
"objc2 0.5.2",
"objc2-foundation 0.2.2",
@@ -3147,9 +3150,9 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"dispatch2",
- "objc2 0.6.1",
+ "objc2 0.6.2",
]
[[package]]
@@ -3188,7 +3191,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"block2 0.5.1",
"dispatch",
"libc",
@@ -3201,8 +3204,8 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c"
dependencies = [
- "bitflags 2.9.1",
- "objc2 0.6.1",
+ "bitflags 2.9.3",
+ "objc2 0.6.2",
"objc2-core-foundation",
]
@@ -3224,7 +3227,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"block2 0.5.1",
"objc2 0.5.2",
"objc2-foundation 0.2.2",
@@ -3236,7 +3239,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"block2 0.5.1",
"objc2 0.5.2",
"objc2-foundation 0.2.2",
@@ -3259,7 +3262,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"block2 0.5.1",
"objc2 0.5.2",
"objc2-cloud-kit",
@@ -3291,7 +3294,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"block2 0.5.1",
"objc2 0.5.2",
"objc2-core-location",
@@ -3334,7 +3337,7 @@ version = "0.10.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"cfg-if",
"foreign-types 0.3.2",
"libc",
@@ -3351,7 +3354,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -3428,14 +3431,14 @@ dependencies = [
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
name = "owned_ttf_parser"
-version = "0.25.0"
+version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4"
+checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b"
dependencies = [
"ttf-parser 0.25.1",
]
@@ -3461,7 +3464,7 @@ dependencies = [
"by_address",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -3513,7 +3516,7 @@ checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall 0.5.14",
+ "redox_syscall 0.5.17",
"smallvec",
"windows-targets 0.52.6",
]
@@ -3541,9 +3544,9 @@ dependencies = [
[[package]]
name = "percent-encoding"
-version = "2.3.1"
+version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "phf"
@@ -3557,22 +3560,22 @@ dependencies = [
[[package]]
name = "phf"
-version = "0.12.1"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7"
+checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
dependencies = [
- "phf_shared 0.12.1",
+ "phf_shared 0.13.1",
"serde",
]
[[package]]
name = "phf_codegen"
-version = "0.12.1"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efbdcb6f01d193b17f0b9c3360fa7e0e620991b193ff08702f78b3ce365d7e61"
+checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1"
dependencies = [
- "phf_generator 0.12.1",
- "phf_shared 0.12.1",
+ "phf_generator 0.13.1",
+ "phf_shared 0.13.1",
]
[[package]]
@@ -3587,12 +3590,12 @@ dependencies = [
[[package]]
name = "phf_generator"
-version = "0.12.1"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cbb1126afed61dd6368748dae63b1ee7dc480191c6262a3b4ff1e29d86a6c5b"
+checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
dependencies = [
"fastrand",
- "phf_shared 0.12.1",
+ "phf_shared 0.13.1",
]
[[package]]
@@ -3605,7 +3608,7 @@ dependencies = [
"phf_shared 0.11.3",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -3619,9 +3622,9 @@ dependencies = [
[[package]]
name = "phf_shared"
-version = "0.12.1"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981"
+checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
dependencies = [
"siphasher",
]
@@ -3649,7 +3652,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -3727,9 +3730,9 @@ dependencies = [
[[package]]
name = "polling"
-version = "3.9.0"
+version = "3.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7"
+checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829"
dependencies = [
"cfg-if",
"concurrent-queue",
@@ -3780,9 +3783,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.95"
+version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [
"unicode-ident",
]
@@ -3795,7 +3798,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"version_check",
"yansi",
]
@@ -3826,9 +3829,9 @@ dependencies = [
[[package]]
name = "quinn"
-version = "0.11.8"
+version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8"
+checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
dependencies = [
"bytes",
"cfg_aliases 0.2.1",
@@ -3838,7 +3841,7 @@ dependencies = [
"rustc-hash 2.1.1",
"rustls",
"socket2",
- "thiserror 2.0.12",
+ "thiserror 2.0.16",
"tokio",
"tracing",
"web-time",
@@ -3846,9 +3849,9 @@ dependencies = [
[[package]]
name = "quinn-proto"
-version = "0.11.12"
+version = "0.11.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e"
+checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
dependencies = [
"bytes",
"getrandom 0.3.3",
@@ -3859,7 +3862,7 @@ dependencies = [
"rustls",
"rustls-pki-types",
"slab",
- "thiserror 2.0.12",
+ "thiserror 2.0.16",
"tinyvec",
"tracing",
"web-time",
@@ -3867,16 +3870,16 @@ dependencies = [
[[package]]
name = "quinn-udp"
-version = "0.5.13"
+version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970"
+checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
dependencies = [
"cfg_aliases 0.2.1",
"libc",
"once_cell",
"socket2",
"tracing",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -3961,9 +3964,9 @@ checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde"
[[package]]
name = "rangemap"
-version = "1.5.1"
+version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684"
+checksum = "f93e7e49bb0bf967717f7bd674458b3d6b0c5f48ec7e3038166026a69fc22223"
[[package]]
name = "raw-window-handle"
@@ -3973,9 +3976,9 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
[[package]]
name = "rayon"
-version = "1.10.0"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
dependencies = [
"either",
"rayon-core",
@@ -3983,9 +3986,9 @@ dependencies = [
[[package]]
name = "rayon-core"
-version = "1.12.1"
+version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
@@ -4021,11 +4024,11 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.5.14"
+version = "0.5.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de3a5d9f0aba1dbcec1cc47f0ff94a4b778fe55bca98a6dfa92e4e094e57b1c4"
+checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
]
[[package]]
@@ -4041,20 +4044,20 @@ dependencies = [
[[package]]
name = "redox_users"
-version = "0.5.0"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
+checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [
"getrandom 0.2.16",
"libredox",
- "thiserror 2.0.12",
+ "thiserror 2.0.16",
]
[[package]]
name = "regex"
-version = "1.11.1"
+version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
dependencies = [
"aho-corasick",
"memchr",
@@ -4064,9 +4067,9 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.4.9"
+version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
dependencies = [
"aho-corasick",
"memchr",
@@ -4075,9 +4078,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
-version = "0.8.5"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
[[package]]
name = "relative-path"
@@ -4093,9 +4096,9 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
[[package]]
name = "reqwest"
-version = "0.12.22"
+version = "0.12.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531"
+checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb"
dependencies = [
"base64",
"bytes",
@@ -4162,7 +4165,7 @@ dependencies = [
"dispatch2",
"js-sys",
"log",
- "objc2 0.6.1",
+ "objc2 0.6.2",
"objc2-app-kit 0.3.1",
"objc2-core-foundation",
"objc2-foundation 0.3.1",
@@ -4218,21 +4221,20 @@ checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
[[package]]
name = "rstest"
-version = "0.25.0"
+version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d"
+checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49"
dependencies = [
"futures-timer",
"futures-util",
"rstest_macros",
- "rustc_version",
]
[[package]]
name = "rstest_macros"
-version = "0.25.0"
+version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746"
+checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0"
dependencies = [
"cfg-if",
"glob",
@@ -4242,7 +4244,7 @@ dependencies = [
"regex",
"relative-path",
"rustc_version",
- "syn 2.0.104",
+ "syn 2.0.106",
"unicode-ident",
]
@@ -4258,9 +4260,9 @@ dependencies = [
[[package]]
name = "rustc-demangle"
-version = "0.1.25"
+version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
+checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
[[package]]
name = "rustc-hash"
@@ -4289,7 +4291,7 @@ version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"errno 0.3.13",
"libc",
"linux-raw-sys 0.4.15",
@@ -4302,7 +4304,7 @@ version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"errno 0.3.13",
"libc",
"linux-raw-sys 0.9.4",
@@ -4311,9 +4313,9 @@ dependencies = [
[[package]]
name = "rustls"
-version = "0.23.29"
+version = "0.23.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1"
+checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc"
dependencies = [
"once_cell",
"ring",
@@ -4346,9 +4348,9 @@ dependencies = [
[[package]]
name = "rustrict"
-version = "0.7.35"
+version = "0.7.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6dfe349049fa49baa564f8483d40e7561ff19ccaa308ab4f844bb59d2c5d8d34"
+checksum = "d93719b9aa6a53f9beae62fe36f34ed88226be314aea6829031ed0f878ca493d"
dependencies = [
"arrayvec",
"bitflags 1.3.2",
@@ -4362,9 +4364,9 @@ dependencies = [
[[package]]
name = "rustversion"
-version = "1.0.21"
+version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "rustybuzz"
@@ -4372,7 +4374,7 @@ version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"bytemuck",
"libm",
"smallvec",
@@ -4400,9 +4402,9 @@ dependencies = [
[[package]]
name = "scc"
-version = "2.3.4"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22b2d775fb28f245817589471dd49c5edf64237f4a19d10ce9a92ff4651a27f4"
+checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc"
dependencies = [
"sdd",
]
@@ -4453,7 +4455,7 @@ version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"core-foundation 0.9.4",
"core-foundation-sys",
"libc",
@@ -4499,14 +4501,14 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
name = "serde_json"
-version = "1.0.141"
+version = "1.0.143"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
+checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
dependencies = [
"itoa",
"memchr",
@@ -4522,7 +4524,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -4584,7 +4586,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -4606,9 +4608,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
-version = "1.4.5"
+version = "1.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
+checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
dependencies = [
"libc",
]
@@ -4646,9 +4648,9 @@ dependencies = [
[[package]]
name = "slab"
-version = "0.4.10"
+version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
+checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]]
name = "slotmap"
@@ -4671,7 +4673,7 @@ version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"calloop",
"calloop-wayland-source",
"cursor-icon",
@@ -4725,9 +4727,9 @@ dependencies = [
"iced",
"maxminddb",
"pcap",
- "phf 0.12.1",
+ "phf 0.13.1",
"phf_codegen",
- "phf_shared 0.12.1",
+ "phf_shared 0.13.1",
"plotters",
"plotters-iced",
"reqwest",
@@ -4740,18 +4742,18 @@ dependencies = [
"serial_test",
"splines",
"tokio",
- "toml 0.9.2",
+ "toml 0.9.5",
"winres",
]
[[package]]
name = "socket2"
-version = "0.5.10"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
+checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [
"libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -4774,7 +4776,7 @@ dependencies = [
"objc2-foundation 0.2.2",
"objc2-quartz-core",
"raw-window-handle",
- "redox_syscall 0.5.14",
+ "redox_syscall 0.5.17",
"rustix 0.38.44",
"tiny-xlib",
"wasm-bindgen",
@@ -4792,7 +4794,7 @@ version = "0.3.0+sdk-1.3.268.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
]
[[package]]
@@ -4846,7 +4848,7 @@ version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc"
dependencies = [
- "kurbo 0.11.2",
+ "kurbo 0.11.3",
"siphasher",
]
@@ -4923,9 +4925,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.104"
+version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
+checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [
"proc-macro2",
"quote",
@@ -4949,7 +4951,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -4967,7 +4969,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"core-foundation 0.9.4",
"system-configuration-sys",
]
@@ -4984,15 +4986,15 @@ dependencies = [
[[package]]
name = "tempfile"
-version = "3.20.0"
+version = "3.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
+checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e"
dependencies = [
"fastrand",
"getrandom 0.3.3",
"once_cell",
"rustix 1.0.8",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -5015,11 +5017,11 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "2.0.12"
+version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
dependencies = [
- "thiserror-impl 2.0.12",
+ "thiserror-impl 2.0.16",
]
[[package]]
@@ -5030,18 +5032,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
name = "thiserror-impl"
-version = "2.0.12"
+version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -5106,9 +5108,9 @@ dependencies = [
[[package]]
name = "tinyvec"
-version = "1.9.0"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71"
+checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
dependencies = [
"tinyvec_macros",
]
@@ -5121,9 +5123,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
-version = "1.46.1"
+version = "1.47.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17"
+checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
dependencies = [
"backtrace",
"bytes",
@@ -5134,7 +5136,7 @@ dependencies = [
"slab",
"socket2",
"tokio-macros",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -5145,7 +5147,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -5170,9 +5172,9 @@ dependencies = [
[[package]]
name = "tokio-util"
-version = "0.7.15"
+version = "0.7.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
+checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
dependencies = [
"bytes",
"futures-core",
@@ -5204,9 +5206,9 @@ dependencies = [
[[package]]
name = "toml"
-version = "0.9.2"
+version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac"
+checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8"
dependencies = [
"indexmap",
"serde",
@@ -5251,9 +5253,9 @@ dependencies = [
[[package]]
name = "toml_parser"
-version = "1.0.1"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30"
+checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10"
dependencies = [
"winnow",
]
@@ -5291,7 +5293,7 @@ version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"bytes",
"futures-util",
"http",
@@ -5334,7 +5336,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -5470,9 +5472,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
-version = "2.5.4"
+version = "2.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
+checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
dependencies = [
"form_urlencoded",
"idna",
@@ -5497,7 +5499,7 @@ dependencies = [
"flate2",
"fontdb 0.18.0",
"imagesize",
- "kurbo 0.11.2",
+ "kurbo 0.11.3",
"log",
"pico-args",
"roxmltree",
@@ -5593,7 +5595,7 @@ dependencies = [
"log",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"wasm-bindgen-shared",
]
@@ -5628,7 +5630,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -5659,13 +5661,13 @@ dependencies = [
[[package]]
name = "wayland-backend"
-version = "0.3.10"
+version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121"
+checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35"
dependencies = [
"cc",
"downcast-rs",
- "rustix 0.38.44",
+ "rustix 1.0.8",
"scoped-tls",
"smallvec",
"wayland-sys",
@@ -5673,12 +5675,12 @@ dependencies = [
[[package]]
name = "wayland-client"
-version = "0.31.10"
+version = "0.31.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61"
+checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d"
dependencies = [
- "bitflags 2.9.1",
- "rustix 0.38.44",
+ "bitflags 2.9.3",
+ "rustix 1.0.8",
"wayland-backend",
"wayland-scanner",
]
@@ -5689,29 +5691,29 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"cursor-icon",
"wayland-backend",
]
[[package]]
name = "wayland-cursor"
-version = "0.31.10"
+version = "0.31.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182"
+checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29"
dependencies = [
- "rustix 0.38.44",
+ "rustix 1.0.8",
"wayland-client",
"xcursor",
]
[[package]]
name = "wayland-protocols"
-version = "0.32.8"
+version = "0.32.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a"
+checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"wayland-backend",
"wayland-client",
"wayland-scanner",
@@ -5719,11 +5721,11 @@ dependencies = [
[[package]]
name = "wayland-protocols-plasma"
-version = "0.3.8"
+version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175"
+checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"wayland-backend",
"wayland-client",
"wayland-protocols",
@@ -5732,11 +5734,11 @@ dependencies = [
[[package]]
name = "wayland-protocols-wlr"
-version = "0.3.8"
+version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf"
+checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"wayland-backend",
"wayland-client",
"wayland-protocols",
@@ -5745,9 +5747,9 @@ dependencies = [
[[package]]
name = "wayland-scanner"
-version = "0.31.6"
+version = "0.31.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484"
+checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3"
dependencies = [
"proc-macro2",
"quick-xml",
@@ -5756,9 +5758,9 @@ dependencies = [
[[package]]
name = "wayland-sys"
-version = "0.31.6"
+version = "0.31.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615"
+checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142"
dependencies = [
"dlib",
"log",
@@ -5834,7 +5836,7 @@ checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a"
dependencies = [
"arrayvec",
"bit-vec",
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"cfg_aliases 0.1.1",
"codespan-reporting",
"indexmap",
@@ -5862,7 +5864,7 @@ dependencies = [
"arrayvec",
"ash",
"bit-set",
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"block",
"cfg_aliases 0.1.1",
"core-graphics-types 0.1.3",
@@ -5903,7 +5905,7 @@ version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"js-sys",
"web-sys",
]
@@ -5932,11 +5934,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22"
dependencies = [
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -6019,7 +6021,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -6030,7 +6032,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -6099,15 +6101,6 @@ dependencies = [
"windows-targets 0.42.2",
]
-[[package]]
-name = "windows-sys"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
-dependencies = [
- "windows-targets 0.48.5",
-]
-
[[package]]
name = "windows-sys"
version = "0.52.0"
@@ -6132,7 +6125,7 @@ version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
- "windows-targets 0.53.2",
+ "windows-targets 0.53.3",
]
[[package]]
@@ -6183,10 +6176,11 @@ dependencies = [
[[package]]
name = "windows-targets"
-version = "0.53.2"
+version = "0.53.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
+checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
dependencies = [
+ "windows-link",
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0",
@@ -6409,14 +6403,14 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "winit"
-version = "0.30.11"
+version = "0.30.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4409c10174df8779dc29a4788cac85ed84024ccbc1743b776b21a520ee1aaf4"
+checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732"
dependencies = [
"ahash 0.8.12",
"android-activity",
"atomic-waker",
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"block2 0.5.1",
"bytemuck",
"calloop",
@@ -6461,9 +6455,9 @@ dependencies = [
[[package]]
name = "winnow"
-version = "0.7.12"
+version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
+checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
dependencies = [
"memchr",
]
@@ -6492,7 +6486,7 @@ version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
]
[[package]]
@@ -6555,7 +6549,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5"
dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.3",
"dlib",
"log",
"once_cell",
@@ -6612,7 +6606,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"synstructure",
]
@@ -6656,9 +6650,9 @@ dependencies = [
[[package]]
name = "zbus"
-version = "5.9.0"
+version = "5.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bb4f9a464286d42851d18a605f7193b8febaf5b0919d71c6399b7b26e5b0aad"
+checksum = "67a073be99ace1adc48af593701c8015cd9817df372e14a1a6b0ee8f8bf043be"
dependencies = [
"async-broadcast",
"async-executor",
@@ -6680,11 +6674,11 @@ dependencies = [
"serde_repr",
"tracing",
"uds_windows",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
"winnow",
- "zbus_macros 5.9.0",
+ "zbus_macros 5.10.0",
"zbus_names 4.2.0",
- "zvariant 5.6.0",
+ "zvariant 5.7.0",
]
[[package]]
@@ -6696,23 +6690,23 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"zvariant_utils 2.1.0",
]
[[package]]
name = "zbus_macros"
-version = "5.9.0"
+version = "5.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef9859f68ee0c4ee2e8cde84737c78e3f4c54f946f2a38645d0d4c7a95327659"
+checksum = "0e80cd713a45a49859dcb648053f63265f4f2851b6420d47a958e5697c68b131"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"zbus_names 4.2.0",
- "zvariant 5.6.0",
- "zvariant_utils 3.2.0",
+ "zvariant 5.7.0",
+ "zvariant_utils 3.2.1",
]
[[package]]
@@ -6735,7 +6729,7 @@ dependencies = [
"serde",
"static_assertions",
"winnow",
- "zvariant 5.6.0",
+ "zvariant 5.7.0",
]
[[package]]
@@ -6761,7 +6755,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -6781,7 +6775,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"synstructure",
]
@@ -6804,9 +6798,9 @@ dependencies = [
[[package]]
name = "zerovec"
-version = "0.11.2"
+version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
+checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
dependencies = [
"yoke",
"zerofrom",
@@ -6821,7 +6815,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
@@ -6848,17 +6842,17 @@ dependencies = [
[[package]]
name = "zvariant"
-version = "5.6.0"
+version = "5.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d91b3680bb339216abd84714172b5138a4edac677e641ef17e1d8cb1b3ca6e6f"
+checksum = "999dd3be73c52b1fccd109a4a81e4fcd20fab1d3599c8121b38d04e1419498db"
dependencies = [
"endi",
"enumflags2",
"serde",
"url",
"winnow",
- "zvariant_derive 5.6.0",
- "zvariant_utils 3.2.0",
+ "zvariant_derive 5.7.0",
+ "zvariant_utils 3.2.1",
]
[[package]]
@@ -6870,21 +6864,21 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
"zvariant_utils 2.1.0",
]
[[package]]
name = "zvariant_derive"
-version = "5.6.0"
+version = "5.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a8c68501be459a8dbfffbe5d792acdd23b4959940fc87785fb013b32edbc208"
+checksum = "6643fd0b26a46d226bd90d3f07c1b5321fe9bb7f04673cb37ac6d6883885b68e"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.104",
- "zvariant_utils 3.2.0",
+ "syn 2.0.106",
+ "zvariant_utils 3.2.1",
]
[[package]]
@@ -6895,19 +6889,18 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.104",
+ "syn 2.0.106",
]
[[package]]
name = "zvariant_utils"
-version = "3.2.0"
+version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34"
+checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599"
dependencies = [
"proc-macro2",
"quote",
"serde",
- "static_assertions",
- "syn 2.0.104",
+ "syn 2.0.106",
"winnow",
]
diff --git a/Cargo.toml b/Cargo.toml
index ba545074..98a243e5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -38,7 +38,7 @@ strip = true
[dependencies]
pcap = "2.3.0"
-etherparse = "0.18.0"
+etherparse = "0.19.0"
chrono = { version = "0.4.41", default-features = false, features = ["clock"] }
plotters = { version = "0.3.7", default-features = false, features = ["area_series", "line_series"] }
iced = { version = "0.13.1", features = ["tokio", "svg", "advanced", "lazy", "image"] }
@@ -47,39 +47,39 @@ maxminddb = "0.26.0"
confy = "1.0.0"
serde = { version = "1.0.219", default-features = false, features = ["derive"] }
rodio = { version = "0.21.1", default-features = false, features = ["mp3", "playback"] }
-dns-lookup = "2.0.4"
-toml = "0.9.2"
+dns-lookup = "3.0.0"
+toml = "0.9.5"
ctrlc = { version = "3.4.7", features = ["termination"] }
rfd = "0.15.4"
-phf = "0.12.1"
-phf_shared = "0.12.1"
+phf = "0.13.1"
+phf_shared = "0.13.1"
splines = "5.0.0"
-clap = { version = "4.5.41", features = ["derive"] }
-tokio = { version = "1.46.1", features = ["macros"] }
+clap = { version = "4.5.46", features = ["derive"] }
+tokio = { version = "1.47.1", features = ["macros"] }
async-channel = "2.5.0"
[target.'cfg(windows)'.dependencies]
gag = "1.0.0"
[target.'cfg(not(target_arch = "powerpc64"))'.dependencies]
-reqwest = { version = "0.12.22", default-features = false, features = ["json", "rustls-tls"] }
+reqwest = { version = "0.12.23", default-features = false, features = ["json", "rustls-tls"] }
[target.'cfg(target_arch = "powerpc64")'.dependencies]
-reqwest = { version = "0.12.22", features = ["json"] }
+reqwest = { version = "0.12.23", features = ["json"] }
#───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
[dev-dependencies]
serde_test = "1.0.177"
-rstest = "0.25.0"
+rstest = "0.26.1"
serial_test = { version = "3.2.0", default-features = false }
#───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
[build-dependencies]
-phf_codegen = "0.12.1"
-phf_shared = "0.12.1"
-rustrict = { version = "0.7.35", default-features = false, features = ["censor"] }
+phf_codegen = "0.13.1"
+phf_shared = "0.13.1"
+rustrict = { version = "0.7.36", default-features = false, features = ["censor"] }
[target."cfg(windows)".build-dependencies]
winres = "0.1.12"
diff --git a/README.md b/README.md
index 524e1d0f..b3e95376 100644
--- a/README.md
+++ b/README.md
@@ -59,9 +59,33 @@ ## _Support Sniffnet's development_ 💖
## Download
-|
|
|
|
|
-|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
-| [64‑bit](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_Windows_64-bit.msi) \| [32‑bit](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_Windows_32-bit.msi) | [Intel](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_macOS_Intel.dmg) \| [Apple silicon](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_macOS_AppleSilicon.dmg) | [amd64](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_amd64.deb) \| [arm64](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_arm64.deb) \| [i386](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_i386.deb) \| [armhf](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_armhf.deb) | [x86_64](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxRPM_x86_64.rpm) \| [aarch64](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxRPM_aarch64.rpm) |
+
Links in the table above will download the latest version of Sniffnet directly from [GitHub releases](https://github.com/GyulyVGC/sniffnet/releases).
Not what you're looking for? Check out [alternative installation methods](https://github.com/GyulyVGC/sniffnet/wiki/Alternative-installation-methods).
@@ -136,6 +160,7 @@ ## Acknowledgements
- A big shout-out to [all the contributors](https://github.com/GyulyVGC/sniffnet/blob/main/CONTRIBUTORS.md) of Sniffnet!
- The graphical user interface has been realized with [iced](https://github.com/iced-rs/iced), a cross-platform GUI library for Rust focused on simplicity and type-safety
- IP geolocation and ASN data are provided by [MaxMind](https://www.maxmind.com)
+- Free code signing for Windows Installer is provided by [SignPath.io](https://about.signpath.io/), certificate by [SignPath Foundation](https://signpath.org/)
- [Sniffnet](https://ads.fund/token/0xadfc251f8ef00ceaeca2b5c1882dabe5db0833df) project is supported by ADS.FUND
- Last but not least, thanks to [every single stargazer](https://github.com/GyulyVGC/sniffnet/stargazers): all forms of support made it possible to keep improving Sniffnet!
diff --git a/giscus.json b/giscus.json
new file mode 100644
index 00000000..80f1a31a
--- /dev/null
+++ b/giscus.json
@@ -0,0 +1,5 @@
+{
+ "origins": ["https://sniffnet.net"],
+ "originsRegex": ["http://localhost:[0-9]+"],
+ "defaultCommentOrder": "oldest"
+}
\ No newline at end of file
diff --git a/resources/fonts/full/subset_characters.txt b/resources/fonts/full/subset_characters.txt
index 33c02891..64a7260b 100644
--- a/resources/fonts/full/subset_characters.txt
+++ b/resources/fonts/full/subset_characters.txt
@@ -150,6 +150,7 @@ z
Ț
ț
ʼ
+Ά
Έ
Ή
Ό
@@ -158,15 +159,20 @@ z
Γ
Δ
Ε
+Θ
Κ
+Λ
Μ
Ν
Ξ
+Ο
Π
Ρ
Σ
Τ
+Υ
Φ
+Χ
ά
έ
ή
@@ -333,10 +339,8 @@ z
ừ
ử
ữ
-•
…
→
-⏎
─
│
╭
@@ -352,6 +356,7 @@ z
」
あ
い
+え
お
か
が
@@ -454,6 +459,7 @@ z
义
也
了
+予
互
些
交
@@ -486,6 +492,7 @@ z
値
值
偏
+停
傳
僅
元
@@ -498,6 +505,7 @@ z
关
其
内
+再
出
击
分
@@ -525,6 +533,7 @@ z
原
参
參
+反
发
取
受
@@ -564,6 +573,7 @@ z
士
変
复
+外
多
夢
夹
@@ -608,6 +618,7 @@ z
從
志
总
+恢
息
您
情
@@ -695,9 +706,11 @@ z
檔
檢
欢
+止
正
此
每
+比
気
沒
没
@@ -711,6 +724,7 @@ z
深
淺
清
+済
渐
測
源
@@ -721,6 +735,7 @@ z
為
無
版
+特
率
現
生
@@ -754,6 +769,7 @@ z
篩
类
紀
+約
索
累
細
@@ -785,6 +801,7 @@ z
處
號
表
+被
裡
製
複
@@ -805,6 +822,7 @@ z
認
語
誤
+読
調
請
计
@@ -815,6 +833,7 @@ z
语
误
请
+读
資
贴
超
@@ -823,6 +842,7 @@ z
転
輸
输
+込
达
过
近
@@ -873,8 +893,10 @@ z
顯
页
项
+预
题
饋
+馈
體
黑
가
diff --git a/resources/fonts/subset/sarasa-mono-sc-bold.subset.ttf b/resources/fonts/subset/sarasa-mono-sc-bold.subset.ttf
index 0e28e1ab..be965920 100644
Binary files a/resources/fonts/subset/sarasa-mono-sc-bold.subset.ttf and b/resources/fonts/subset/sarasa-mono-sc-bold.subset.ttf differ
diff --git a/resources/fonts/subset/sarasa-mono-sc-regular.subset.ttf b/resources/fonts/subset/sarasa-mono-sc-regular.subset.ttf
index f33f1e57..71dde468 100644
Binary files a/resources/fonts/subset/sarasa-mono-sc-regular.subset.ttf and b/resources/fonts/subset/sarasa-mono-sc-regular.subset.ttf differ
diff --git a/resources/packaging/linux/AppImage/sniffnet.yml b/resources/packaging/linux/AppImage/sniffnet.yml
new file mode 100644
index 00000000..da04ab96
--- /dev/null
+++ b/resources/packaging/linux/AppImage/sniffnet.yml
@@ -0,0 +1,9 @@
+app: Sniffnet
+ingredients:
+ dist: bookworm
+ packages:
+ - libpcap0.8
+ sources:
+ - deb https://deb.debian.org/debian stable main
+ debs:
+ - /target/REPLACE_TAG
diff --git a/resources/repository/badges/linux.svg b/resources/repository/badges/linux.svg
new file mode 100644
index 00000000..51830469
--- /dev/null
+++ b/resources/repository/badges/linux.svg
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/resources/repository/badges/linux_deb.svg b/resources/repository/badges/linux_deb.svg
deleted file mode 100644
index ee254170..00000000
--- a/resources/repository/badges/linux_deb.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/resources/repository/badges/linux_rpm.svg b/resources/repository/badges/linux_rpm.svg
deleted file mode 100644
index b5688b2a..00000000
--- a/resources/repository/badges/linux_rpm.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/resources/repository/badges/macos.svg b/resources/repository/badges/macos.svg
index 865f992c..f046a790 100644
--- a/resources/repository/badges/macos.svg
+++ b/resources/repository/badges/macos.svg
@@ -1 +1,13 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/resources/repository/badges/windows.svg b/resources/repository/badges/windows.svg
index e877fa7c..40c1eacd 100644
--- a/resources/repository/badges/windows.svg
+++ b/resources/repository/badges/windows.svg
@@ -1 +1,13 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/resources/test/ipinfo_asn_sample.mmdb b/resources/test/ipinfo_asn_sample.mmdb
index 6e86ae38..b1e36399 100644
Binary files a/resources/test/ipinfo_asn_sample.mmdb and b/resources/test/ipinfo_asn_sample.mmdb differ
diff --git a/resources/test/ipinfo_country_asn_sample.mmdb b/resources/test/ipinfo_country_asn_sample.mmdb
deleted file mode 100644
index b68cd28d..00000000
Binary files a/resources/test/ipinfo_country_asn_sample.mmdb and /dev/null differ
diff --git a/resources/test/ipinfo_country_sample.mmdb b/resources/test/ipinfo_country_sample.mmdb
deleted file mode 100644
index bbb4987c..00000000
Binary files a/resources/test/ipinfo_country_sample.mmdb and /dev/null differ
diff --git a/resources/test/ipinfo_lite_sample.mmdb b/resources/test/ipinfo_lite_sample.mmdb
new file mode 100644
index 00000000..da15dab2
Binary files /dev/null and b/resources/test/ipinfo_lite_sample.mmdb differ
diff --git a/resources/test/ipinfo_location_sample.mmdb b/resources/test/ipinfo_location_sample.mmdb
new file mode 100644
index 00000000..aee65ad5
Binary files /dev/null and b/resources/test/ipinfo_location_sample.mmdb differ
diff --git a/src/chart/manage_chart_data.rs b/src/chart/manage_chart_data.rs
index 27558ec3..f194672e 100644
--- a/src/chart/manage_chart_data.rs
+++ b/src/chart/manage_chart_data.rs
@@ -1,7 +1,7 @@
-use splines::{Interpolation, Key, Spline};
-
use crate::TrafficChart;
+use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::info_traffic::InfoTraffic;
+use splines::{Interpolation, Key, Spline};
impl TrafficChart {
pub fn update_charts_data(&mut self, info_traffic_msg: &InfoTraffic, no_more_packets: bool) {
@@ -16,13 +16,21 @@ pub fn update_charts_data(&mut self, info_traffic_msg: &InfoTraffic, no_more_pac
self.ticks += 1;
#[allow(clippy::cast_precision_loss)]
- let out_bytes_entry = -(info_traffic_msg.tot_data_info.outgoing_bytes() as f32);
+ let out_bytes_entry = -(info_traffic_msg
+ .tot_data_info
+ .outgoing_data(DataRepr::Bytes) as f32);
#[allow(clippy::cast_precision_loss)]
- let in_bytes_entry = info_traffic_msg.tot_data_info.incoming_bytes() as f32;
+ let in_bytes_entry = info_traffic_msg
+ .tot_data_info
+ .incoming_data(DataRepr::Bytes) as f32;
#[allow(clippy::cast_precision_loss)]
- let out_packets_entry = -(info_traffic_msg.tot_data_info.outgoing_packets() as f32);
+ let out_packets_entry = -(info_traffic_msg
+ .tot_data_info
+ .outgoing_data(DataRepr::Packets) as f32);
#[allow(clippy::cast_precision_loss)]
- let in_packets_entry = info_traffic_msg.tot_data_info.incoming_packets() as f32;
+ let in_packets_entry = info_traffic_msg
+ .tot_data_info
+ .incoming_data(DataRepr::Packets) as f32;
let out_bytes_point = (tot_seconds, out_bytes_entry);
let in_bytes_point = (tot_seconds, in_bytes_entry);
@@ -145,10 +153,10 @@ fn reduce_all_time_data(all_time: &mut Vec<(f32, f32)>) {
while all_time.len() > 150 {
let mut new_vec = Vec::new();
all_time.iter().enumerate().for_each(|(i, (x, y))| {
- if i % 2 == 0 {
- if let Some(next) = all_time.get(i + 1) {
- new_vec.push((*x, (y + next.1) / 2.0));
- }
+ if i % 2 == 0
+ && let Some(next) = all_time.get(i + 1)
+ {
+ new_vec.push((*x, (y + next.1) / 2.0));
}
});
*all_time = new_vec;
@@ -213,8 +221,9 @@ mod tests {
use crate::chart::manage_chart_data::{ChartSeries, get_max, get_min};
use crate::networking::types::data_info::DataInfo;
+ use crate::networking::types::data_representation::DataRepr;
use crate::utils::types::timestamp::Timestamp;
- use crate::{ChartType, InfoTraffic, Language, StyleType, TrafficChart};
+ use crate::{InfoTraffic, Language, StyleType, TrafficChart};
fn spline_from_vec(vec: Vec<(i32, i32)>) -> Spline {
Spline::from_vec(
@@ -310,7 +319,7 @@ fn test_chart_data_updates() {
min_packets: -1000.0,
max_packets: 21000.0,
language: Language::default(),
- chart_type: ChartType::Packets,
+ data_repr: DataRepr::Packets,
style: StyleType::default(),
thumbnail: false,
is_live_capture: true,
@@ -318,8 +327,6 @@ fn test_chart_data_updates() {
no_more_packets: false,
};
let mut info_traffic = InfoTraffic {
- all_bytes: 0,
- all_packets: 0,
tot_data_info,
dropped_packets: 0,
..Default::default()
diff --git a/src/chart/types/chart_type.rs b/src/chart/types/chart_type.rs
deleted file mode 100644
index 0e339435..00000000
--- a/src/chart/types/chart_type.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use crate::Language;
-use crate::translations::translations::{bytes_translation, packets_translation};
-use serde::{Deserialize, Serialize};
-
-/// Enum representing the possible kind of chart displayed.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
-pub enum ChartType {
- Packets,
- Bytes,
-}
-
-impl ChartType {
- pub(crate) const ALL: [ChartType; 2] = [ChartType::Bytes, ChartType::Packets];
-
- pub fn get_label(&self, language: Language) -> &str {
- match self {
- ChartType::Packets => packets_translation(language),
- ChartType::Bytes => bytes_translation(language),
- }
- }
-}
diff --git a/src/chart/types/donut_chart.rs b/src/chart/types/donut_chart.rs
index b2f392b7..14566466 100644
--- a/src/chart/types/donut_chart.rs
+++ b/src/chart/types/donut_chart.rs
@@ -1,7 +1,6 @@
-use crate::chart::types::chart_type::ChartType;
use crate::gui::styles::donut::Catalog;
use crate::gui::styles::style_constants::{FONT_SIZE_FOOTER, FONT_SIZE_SUBTITLE};
-use crate::networking::types::byte_multiple::ByteMultiple;
+use crate::networking::types::data_representation::DataRepr;
use iced::alignment::{Horizontal, Vertical};
use iced::widget::canvas::path::Arc;
use iced::widget::canvas::{Frame, Text};
@@ -10,10 +9,9 @@
use std::f32::consts;
pub struct DonutChart {
- chart_type: ChartType,
+ data_repr: DataRepr,
incoming: u128,
outgoing: u128,
- filtered_out: u128,
dropped: u128,
font: Font,
thumbnail: bool,
@@ -21,19 +19,17 @@ pub struct DonutChart {
impl DonutChart {
fn new(
- chart_type: ChartType,
+ data_repr: DataRepr,
incoming: u128,
outgoing: u128,
- filtered_out: u128,
dropped: u128,
font: Font,
thumbnail: bool,
) -> Self {
Self {
- chart_type,
+ data_repr,
incoming,
outgoing,
- filtered_out,
dropped,
font,
thumbnail,
@@ -41,24 +37,19 @@ fn new(
}
fn total(&self) -> u128 {
- self.incoming + self.outgoing + self.filtered_out + self.dropped
+ self.incoming + self.outgoing + self.dropped
}
fn title(&self) -> String {
let total = self.total();
- if self.chart_type.eq(&ChartType::Bytes) {
- ByteMultiple::formatted_string(total)
- } else {
- total.to_string()
- }
+ self.data_repr.formatted_string(total)
}
- fn angles(&self) -> [(Radians, Radians); 4] {
+ fn angles(&self) -> [(Radians, Radians); 3] {
#[allow(clippy::cast_precision_loss)]
let mut values = [
self.incoming as f32,
self.outgoing as f32,
- self.filtered_out as f32,
self.dropped as f32,
];
let total: f32 = values.iter().sum();
@@ -105,12 +96,7 @@ fn draw(
let radius = (frame.width().min(frame.height()) / 2.0) * 0.9;
let style = ::style(theme, &::default());
- let colors = [
- style.incoming,
- style.outgoing,
- style.filtered_out,
- style.dropped,
- ];
+ let colors = [style.incoming, style.outgoing, style.dropped];
for ((start_angle, end_angle), color) in self.angles().into_iter().zip(colors) {
let path = canvas::Path::new(|builder| {
@@ -150,10 +136,9 @@ fn draw(
}
pub fn donut_chart(
- chart_type: ChartType,
+ data_repr: DataRepr,
incoming: u128,
outgoing: u128,
- filtered_out: u128,
dropped: u128,
font: Font,
thumbnail: bool,
@@ -164,13 +149,7 @@ pub fn donut_chart(
Length::Fixed(110.0)
};
iced::widget::canvas(DonutChart::new(
- chart_type,
- incoming,
- outgoing,
- filtered_out,
- dropped,
- font,
- thumbnail,
+ data_repr, incoming, outgoing, dropped, font, thumbnail,
))
.width(size)
.height(size)
diff --git a/src/chart/types/mod.rs b/src/chart/types/mod.rs
index ca3f7e1b..c84d0fef 100644
--- a/src/chart/types/mod.rs
+++ b/src/chart/types/mod.rs
@@ -1,3 +1,2 @@
-pub mod chart_type;
pub mod donut_chart;
pub mod traffic_chart;
diff --git a/src/chart/types/traffic_chart.rs b/src/chart/types/traffic_chart.rs
index 835ff54c..ccb7efad 100644
--- a/src/chart/types/traffic_chart.rs
+++ b/src/chart/types/traffic_chart.rs
@@ -15,24 +15,25 @@
use crate::gui::styles::style_constants::CHARTS_LINE_BORDER;
use crate::gui::styles::types::palette::to_rgb_color;
use crate::gui::types::message::Message;
+use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::traffic_direction::TrafficDirection;
use crate::translations::translations::{incoming_translation, outgoing_translation};
use crate::utils::error_logger::{ErrorLogger, Location};
use crate::utils::formatted_strings::{get_formatted_num_seconds, get_formatted_timestamp};
use crate::utils::types::timestamp::Timestamp;
-use crate::{ByteMultiple, ChartType, Language, StyleType, location};
+use crate::{Language, StyleType, location};
/// Struct defining the chart to be displayed in gui run page
pub struct TrafficChart {
/// Current time interval number
pub ticks: u32,
- /// Sent bytes filtered and their time occurrence
+ /// Sent bytes and their time occurrence
pub out_bytes: ChartSeries,
- /// Received bytes filtered and their time occurrence
+ /// Received bytes and their time occurrence
pub in_bytes: ChartSeries,
- /// Sent packets filtered and their time occurrence
+ /// Sent packets and their time occurrence
pub out_packets: ChartSeries,
- /// Received packets filtered and their time occurrence
+ /// Received packets and their time occurrence
pub in_packets: ChartSeries,
/// Minimum number of bytes per time interval (computed on last 30 intervals)
pub min_bytes: f32,
@@ -45,7 +46,7 @@ pub struct TrafficChart {
/// Language used for the chart legend
pub language: Language,
/// Packets or bytes
- pub chart_type: ChartType,
+ pub data_repr: DataRepr,
/// Style of the chart
pub style: StyleType,
/// Whether the chart is for the thumbnail page
@@ -71,7 +72,7 @@ pub fn new(style: StyleType, language: Language) -> Self {
min_packets: 0.0,
max_packets: 0.0,
language,
- chart_type: ChartType::Bytes,
+ data_repr: DataRepr::Bytes,
style,
thumbnail: false,
is_live_capture: true,
@@ -80,7 +81,7 @@ pub fn new(style: StyleType, language: Language) -> Self {
}
}
- pub fn view(&self) -> Element {
+ pub fn view(&self) -> Element<'_, Message, StyleType> {
let x_labels = if self.is_live_capture || self.thumbnail {
None
} else {
@@ -115,8 +116,8 @@ pub fn view(&self) -> Element {
.into()
}
- pub fn change_kind(&mut self, kind: ChartType) {
- self.chart_type = kind;
+ pub fn change_kind(&mut self, kind: DataRepr) {
+ self.data_repr = kind;
}
pub fn change_language(&mut self, language: Language) {
@@ -169,9 +170,10 @@ fn x_axis_range(&self) -> Range {
}
fn y_axis_range(&self) -> Range {
- let (min, max) = match self.chart_type {
- ChartType::Packets => (self.min_packets, self.max_packets),
- ChartType::Bytes => (self.min_bytes, self.max_bytes),
+ let (min, max) = match self.data_repr {
+ DataRepr::Packets => (self.min_packets, self.max_packets),
+ DataRepr::Bytes => (self.min_bytes, self.max_bytes),
+ DataRepr::Bits => (self.min_bytes * 8.0, self.max_bytes * 8.0),
};
let fs = max - min;
let gap = fs * 0.05;
@@ -186,12 +188,12 @@ fn font<'a>(&self, size: f64) -> TextStyle<'a> {
}
fn spline_to_plot(&self, direction: TrafficDirection) -> &Spline {
- match self.chart_type {
- ChartType::Packets => match direction {
+ match self.data_repr {
+ DataRepr::Packets => match direction {
TrafficDirection::Incoming => &self.in_packets.spline,
TrafficDirection::Outgoing => &self.out_packets.spline,
},
- ChartType::Bytes => match direction {
+ DataRepr::Bytes | DataRepr::Bits => match direction {
TrafficDirection::Incoming => &self.in_bytes.spline,
TrafficDirection::Outgoing => &self.out_bytes.spline,
},
@@ -219,11 +221,16 @@ fn area_series(
let color = self.series_color(direction);
let alpha = self.style.get_extension().alpha_chart_badge;
let spline = self.spline_to_plot(direction);
+ let multiplier = if self.data_repr == DataRepr::Bits {
+ 8.0
+ } else {
+ 1.0
+ };
let data = match spline.keys() {
// if we have only one tick, we need to add a second point to draw the area
- [k] => vec![(0.0, k.value), (0.1, k.value)],
- _ => sample_spline(spline),
+ [k] => vec![(0.0, k.value * multiplier), (0.1, k.value * multiplier)],
+ _ => sample_spline(spline, multiplier),
};
AreaSeries::new(data, 0.0, color.mix(alpha.into()))
@@ -283,12 +290,10 @@ fn build_chart(
.max_light_lines(0)
.label_style(self.font(12.5))
.y_labels(min(5, y_labels))
- .y_label_formatter(if self.chart_type.eq(&ChartType::Packets) {
- &|packets| packets.abs().to_string()
- } else {
+ .y_label_formatter(
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
- &|bytes| ByteMultiple::formatted_string(bytes.abs() as u128)
- })
+ &|amount| self.data_repr.formatted_string(amount.abs() as u128),
+ )
.x_labels(min(6, x_labels))
.x_label_formatter(
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
@@ -331,7 +336,7 @@ fn build_chart(
}
}
-fn sample_spline(spline: &Spline) -> Vec<(f32, f32)> {
+fn sample_spline(spline: &Spline, multiplier: f32) -> Vec<(f32, f32)> {
let pts = spline.len() * 10; // 10 samples per key
let mut ret_val = Vec::new();
let len = spline.len();
@@ -348,7 +353,7 @@ fn sample_spline(spline: &Spline) -> Vec<(f32, f32)> {
for i in 0..pts {
#[allow(clippy::cast_precision_loss)]
let x = first_x + delta * i as f32;
- let p = spline.clamped_sample(x).unwrap_or_default();
+ let p = spline.clamped_sample(x).unwrap_or_default() * multiplier;
ret_val.push((x, p));
}
ret_val
@@ -402,7 +407,7 @@ fn test_spline_samples() {
let eps = 0.001;
let pts = spline.len() * 10;
- let samples = sample_spline(&spline);
+ let samples = sample_spline(&spline, 1.0);
assert_eq!(samples.len(), pts);
let delta = samples[1].0 - samples[0].0;
diff --git a/src/cli/mod.rs b/src/cli/mod.rs
index 0256167c..138a88cc 100644
--- a/src/cli/mod.rs
+++ b/src/cli/mod.rs
@@ -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,
#[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 {
}
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 {
.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 {
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());
}
}
diff --git a/src/configs/mod.rs b/src/configs/mod.rs
deleted file mode 100644
index cd408564..00000000
--- a/src/configs/mod.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod types;
diff --git a/src/configs/types/config_device.rs b/src/configs/types/config_device.rs
deleted file mode 100644
index f33226d1..00000000
--- a/src/configs/types/config_device.rs
+++ /dev/null
@@ -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::(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::test_path())
- .unwrap_or_else(|_| ConfigDevice::default())
- }
-
- pub fn store(self) -> Result<(), confy::ConfyError> {
- confy::store_path(ConfigDevice::test_path(), self)
- }
- }
-}
diff --git a/src/configs/types/config_settings.rs b/src/configs/types/config_settings.rs
deleted file mode 100644
index 284b674b..00000000
--- a/src/configs/types/config_settings.rs
+++ /dev/null
@@ -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::(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::test_path())
- .unwrap_or_else(|_| ConfigSettings::default())
- }
-
- pub fn store(self) -> Result<(), confy::ConfyError> {
- confy::store_path(ConfigSettings::test_path(), self)
- }
- }
-}
diff --git a/src/configs/types/configs.rs b/src/configs/types/configs.rs
deleted file mode 100644
index db66fd88..00000000
--- a/src/configs/types/configs.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-use crate::{ConfigDevice, ConfigSettings, ConfigWindow};
-use confy::ConfyError;
-
-pub static CONFIGS: std::sync::LazyLock = 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(())
- }
-}
diff --git a/src/configs/types/mod.rs b/src/configs/types/mod.rs
deleted file mode 100644
index 76d46b33..00000000
--- a/src/configs/types/mod.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-pub mod config_device;
-pub mod config_settings;
-pub mod config_window;
-pub mod configs;
diff --git a/src/gui/components/header.rs b/src/gui/components/header.rs
index f90cca1e..5f0b980a 100644
--- a/src/gui/components/header.rs
+++ b/src/gui/components/header.rs
@@ -5,28 +5,27 @@
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;
use crate::{Language, SNIFFNET_TITLECASE, StyleType};
-pub fn header(sniffer: &Sniffer) -> Container {
+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 {
);
}
- 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()
diff --git a/src/gui/components/tab.rs b/src/gui/components/tab.rs
index a70bca4a..cbf5e033 100644
--- a/src/gui/components/tab.rs
+++ b/src/gui/components/tab.rs
@@ -99,12 +99,12 @@ fn new_page_tab<'a>(
.align_y(alignment::Alignment::Center),
);
- if let Some(num) = unread {
- if num > 0 {
- content = content
- .push(Space::with_width(7))
- .push(notifications_badge(font_headers, num));
- }
+ if let Some(num) = unread
+ && num > 0
+ {
+ content = content
+ .push(Space::with_width(7))
+ .push(notifications_badge(font_headers, num));
}
content = content.push(horizontal_space());
diff --git a/src/gui/pages/connection_details_page.rs b/src/gui/pages/connection_details_page.rs
index 3e479ee3..f26a53d4 100644
--- a/src/gui/pages/connection_details_page.rs
+++ b/src/gui/pages/connection_details_page.rs
@@ -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,
@@ -15,6 +16,7 @@
use crate::networking::types::address_port_pair::AddressPortPair;
use crate::networking::types::arp_type::ArpType;
use crate::networking::types::bogon::is_bogon;
+use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::Host;
use crate::networking::types::icmp_type::IcmpType;
use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
@@ -33,7 +35,7 @@
};
use crate::utils::formatted_strings::{get_formatted_timestamp, get_socket_address};
use crate::utils::types::icon::Icon;
-use crate::{ByteMultiple, 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;
@@ -44,17 +46,18 @@
pub fn connection_details_page(
sniffer: &Sniffer,
key: AddressPortPair,
-) -> Container {
+) -> Container<'_, Message, StyleType> {
Container::new(page_content(sniffer, &key))
}
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;
@@ -130,7 +133,7 @@ fn page_content<'a>(sniffer: &Sniffer, key: &AddressPortPair) -> Container<'a, M
dest_col = dest_col.push(host_info_col);
}
- let col_info = col_info(key, &val, font, language);
+ let col_info = col_info(key, &val, data_repr, font, language);
let content = assemble_widgets(col_info, source_col, dest_col);
@@ -172,6 +175,7 @@ fn page_header<'a>(
fn col_info<'a>(
key: &AddressPortPair,
val: &InfoAddressPortPair,
+ data_repr: DataRepr,
font: Font,
language: Language,
) -> Column<'a, Message, StyleType> {
@@ -221,12 +225,13 @@ fn col_info<'a>(
incoming_translation(language).to_lowercase()
}
),
- &format!(
- "{}\n {} {}",
- ByteMultiple::formatted_string(val.transmitted_bytes),
- val.transmitted_packets,
- packets_translation(language)
- ),
+ &(data_repr.formatted_string(val.transmitted_data(data_repr))
+ + if data_repr == DataRepr::Packets {
+ format!(" {}", packets_translation(language))
+ } else {
+ String::new()
+ }
+ .as_ref()),
font,
));
@@ -295,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
diff --git a/src/gui/pages/initial_page.rs b/src/gui/pages/initial_page.rs
index eb39e06d..5294355d 100644
--- a/src/gui/pages/initial_page.rs
+++ b/src/gui/pages/initial_page.rs
@@ -2,18 +2,8 @@
//!
//! It contains elements to select network adapter and traffic filters.
-use std::collections::HashSet;
use std::fmt::Write;
-use iced::Length::FillPortion;
-use iced::widget::scrollable::Direction;
-use iced::widget::tooltip::Position;
-use iced::widget::{
- Button, Checkbox, Column, Container, Row, Rule, Scrollable, Space, Text, TextInput, Tooltip,
- button, center,
-};
-use iced::{Alignment, Font, Length, Padding, alignment};
-
use crate::gui::components::button::button_open_file;
use crate::gui::sniffer::Sniffer;
use crate::gui::styles::button::ButtonType;
@@ -21,287 +11,171 @@
use crate::gui::styles::scrollbar::ScrollbarType;
use crate::gui::styles::style_constants::{FONT_SIZE_SUBTITLE, FONT_SIZE_TITLE};
use crate::gui::styles::text::TextType;
-use crate::gui::styles::text_input::TextInputType;
use crate::gui::styles::types::gradient_type::GradientType;
use crate::gui::types::export_pcap::ExportPcap;
+use crate::gui::types::filters::Filters;
use crate::gui::types::message::Message;
-use crate::networking::types::capture_context::CaptureSource;
-use crate::networking::types::filters::Filters;
-use crate::networking::types::ip_collection::AddressCollection;
-use crate::networking::types::port_collection::PortCollection;
+use crate::gui::types::settings::Settings;
+use crate::networking::types::capture_context::{CaptureSource, CaptureSourcePicklist};
use crate::translations::translations::{
- address_translation, addresses_translation, choose_adapters_translation,
- ip_version_translation, protocol_translation, select_filters_translation, start_translation,
+ address_translation, addresses_translation, network_adapter_translation, start_translation,
};
use crate::translations::translations_3::{
- directory_translation, export_capture_translation, file_name_translation, port_translation,
+ directory_translation, export_capture_translation, file_name_translation,
};
-use crate::translations::translations_4::import_capture_translation;
-use crate::utils::formatted_strings::{get_invalid_filters_string, get_path_termination_string};
+use crate::translations::translations_4::capture_file_translation;
+use crate::translations::translations_5::{filter_traffic_translation, traffic_source_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, IpVersion, Language, Protocol, StyleType};
+use crate::{Language, StyleType};
+use iced::Length::FillPortion;
+use iced::widget::scrollable::Direction;
+use iced::widget::{
+ Button, Checkbox, Column, Container, PickList, Row, Scrollable, Space, Text, TextInput, button,
+ center, vertical_space,
+};
+use iced::{Alignment, Font, Length, Padding, alignment};
/// Computes the body of gui initial page
-pub fn initial_page(sniffer: &Sniffer) -> Container {
- let ConfigSettings {
+pub fn initial_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
+ 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;
- let col_adapter = get_col_adapter(sniffer, font);
- let col_import_pcap = get_col_import_pcap(
- language,
- font,
- &sniffer.capture_source,
- &sniffer.import_pcap_path,
- );
- let col_capture_source = Column::new().push(col_adapter).push(col_import_pcap);
+ let col_data_source = get_col_data_source(sniffer, font, language);
- let ip_active = &sniffer.filters.ip_versions;
- let col_ip_buttons = col_ip_buttons(ip_active, font, language);
-
- let protocol_active = &sniffer.filters.protocols;
- let col_protocol_buttons = col_protocol_buttons(protocol_active, font, language);
-
- let address_active = &sniffer.filters.address_str;
- let col_address_filter = col_address_input(address_active, font, language);
-
- let port_active = &sniffer.filters.port_str;
- let col_port_filter = col_port_input(port_active, font, language);
-
- let filters_pane = Column::new()
- .width(FillPortion(6))
- .padding(10)
- .spacing(15)
- .push(
- select_filters_translation(language)
- .font(font)
- .class(TextType::Title)
- .size(FONT_SIZE_TITLE),
- )
- .push(
- Row::new()
- .spacing(20)
- .push(col_ip_buttons)
- .push(col_protocol_buttons),
- )
- .push(
- Row::new()
- .spacing(20)
- .push(col_address_filter)
- .push(col_port_filter),
- )
- .push(Rule::horizontal(40))
- .push(get_export_pcap_group(
- &sniffer.capture_source,
- &sniffer.export_pcap,
+ let col_checkboxes = Column::new()
+ .spacing(10)
+ .push(get_filters_group(&sniffer.conf.filters, font, language))
+ .push_maybe(get_export_pcap_group_maybe(
+ sniffer.conf.capture_source_picklist,
+ &sniffer.conf.export_pcap,
language,
font,
+ ));
+
+ let is_capture_source_consistent = sniffer.is_capture_source_consistent();
+ let right_col = Column::new()
+ .width(FillPortion(1))
+ .padding(10)
+ .push(Space::with_height(76))
+ .push(col_checkboxes)
+ .push(vertical_space())
+ .push(button_start(
+ font_headers,
+ language,
+ color_gradient,
+ is_capture_source_consistent,
))
- .push(
- Container::new(button_start(
- font,
- language,
- color_gradient,
- &sniffer.filters,
- ))
- .width(Length::Fill)
- .height(Length::Fill)
- .align_y(Alignment::Start)
- .align_x(Alignment::Center),
- );
+ .push(vertical_space());
let body = Column::new().push(Space::with_height(5)).push(
Row::new()
- .push(col_capture_source)
- .push(Space::with_width(30))
- .push(filters_pane),
+ .push(col_data_source)
+ .push(Space::with_width(15))
+ .push(right_col),
);
Container::new(body).height(Length::Fill)
}
-fn col_ip_buttons(
- active_ip_filters: &HashSet,
- font: Font,
- language: Language,
-) -> Column {
- let mut buttons_row = Row::new().spacing(5).padding(Padding::ZERO.left(5));
- for option in IpVersion::ALL {
- let is_active = active_ip_filters.contains(&option);
- let check_symbol = if is_active { "✔" } else { "✘" };
- buttons_row = buttons_row.push(
- Button::new(
- Text::new(format!("{option} {check_symbol}"))
- .align_x(Alignment::Center)
- .align_y(Alignment::Center)
- .font(font),
- )
- .width(80)
- .height(35)
- .class(if is_active {
- ButtonType::BorderedRoundSelected
- } else {
- ButtonType::BorderedRound
- })
- .on_press(Message::IpVersionSelection(option, !is_active)),
- );
- }
-
- Column::new()
- .width(Length::Fill)
- .spacing(7)
- .push(
- Text::new(ip_version_translation(language))
- .font(font)
- .class(TextType::Subtitle)
- .size(FONT_SIZE_SUBTITLE),
- )
- .push(buttons_row)
-}
-
-fn col_protocol_buttons(
- active_protocol_filters: &HashSet,
- font: Font,
- language: Language,
-) -> Column {
- let mut buttons_row = Row::new().spacing(5).padding(Padding::ZERO.left(5));
- for option in Protocol::ALL {
- let is_active = active_protocol_filters.contains(&option);
- let check_symbol = if is_active { "✔" } else { "✘" };
- buttons_row = buttons_row.push(
- Button::new(
- Text::new(format!("{option} {check_symbol}"))
- .align_x(Alignment::Center)
- .align_y(Alignment::Center)
- .font(font),
- )
- .width(80)
- .height(35)
- .class(if is_active {
- ButtonType::BorderedRoundSelected
- } else {
- ButtonType::BorderedRound
- })
- .on_press(Message::ProtocolSelection(option, !is_active)),
- );
- }
-
- Column::new()
- .width(Length::Fill)
- .spacing(7)
- .push(
- Text::new(protocol_translation(language))
- .font(font)
- .class(TextType::Subtitle)
- .size(FONT_SIZE_SUBTITLE),
- )
- .push(buttons_row)
-}
-
-fn col_address_input(value: &str, font: Font, language: Language) -> Column {
- let is_error = if value.is_empty() {
- false
- } else {
- AddressCollection::new(value).is_none()
- };
- let input_row = Row::new().padding(Padding::ZERO.left(5)).push(
- TextInput::new(AddressCollection::PLACEHOLDER_STR, value)
- .padding([3, 5])
- .on_input(Message::AddressFilter)
- .font(font)
- .width(310)
- .class(if is_error {
- TextInputType::Error
- } else {
- TextInputType::Standard
- }),
- );
-
- Column::new()
- .width(Length::Fill)
- .spacing(7)
- .push(
- Text::new(address_translation(language))
- .font(font)
- .class(TextType::Subtitle)
- .size(FONT_SIZE_SUBTITLE),
- )
- .push(input_row)
-}
-
-fn col_port_input(value: &str, font: Font, language: Language) -> Column {
- let is_error = if value.is_empty() {
- false
- } else {
- PortCollection::new(value).is_none()
- };
- let input_row = Row::new().padding(Padding::ZERO.left(5)).push(
- TextInput::new(PortCollection::PLACEHOLDER_STR, value)
- .padding([3, 5])
- .on_input(Message::PortFilter)
- .font(font)
- .width(180)
- .class(if is_error {
- TextInputType::Error
- } else {
- TextInputType::Standard
- }),
- );
-
- Column::new()
- .width(Length::Fill)
- .spacing(7)
- .push(
- Text::new(port_translation(language))
- .font(font)
- .class(TextType::Subtitle)
- .size(FONT_SIZE_SUBTITLE),
- )
- .push(input_row)
-}
-
-fn button_start(
- font: Font,
+fn button_start<'a>(
+ font_headers: Font,
language: Language,
color_gradient: GradientType,
- filters: &Filters,
-) -> Tooltip {
- let mut content = button(
- Icon::Rocket
- .to_text()
- .size(25)
+ is_capture_source_consistent: bool,
+) -> Button<'a, Message, StyleType> {
+ button(
+ Text::new(start_translation(language))
+ .font(font_headers)
+ .size(FONT_SIZE_TITLE)
+ .width(Length::Fill)
.align_x(alignment::Alignment::Center)
.align_y(alignment::Alignment::Center),
)
- .padding(10)
- .height(80)
- .width(160)
- .class(ButtonType::Gradient(color_gradient));
-
- let mut tooltip = start_translation(language).to_string();
- //tooltip.push_str(" [⏎]");
- let mut position = Position::Top;
-
- if filters.are_valid() {
- content = content.on_press(Message::Start);
+ .padding(20)
+ .width(Length::Fill)
+ .class(ButtonType::Gradient(color_gradient))
+ .on_press_maybe(if is_capture_source_consistent {
+ Some(Message::Start)
} else {
- tooltip = get_invalid_filters_string(filters, language);
- position = Position::FollowCursor;
- }
-
- Tooltip::new(content, Text::new(tooltip).font(font), position)
- .gap(5)
- .class(ContainerType::Tooltip)
+ None
+ })
}
-fn get_col_adapter(sniffer: &Sniffer, font: Font) -> Column {
- let ConfigSettings { language, .. } = sniffer.configs.settings;
+fn get_col_data_source(
+ sniffer: &Sniffer,
+ font: Font,
+ language: Language,
+) -> Column<'_, Message, StyleType> {
+ let current_option = if sniffer.conf.capture_source_picklist == CaptureSourcePicklist::Device {
+ network_adapter_translation(language)
+ } else {
+ capture_file_translation(language)
+ };
+ let picklist = PickList::new(
+ [
+ network_adapter_translation(language),
+ capture_file_translation(language),
+ ],
+ Some(current_option),
+ move |option| {
+ if option == network_adapter_translation(language) {
+ Message::SetCaptureSource(CaptureSourcePicklist::Device)
+ } else {
+ Message::SetCaptureSource(CaptureSourcePicklist::File)
+ }
+ },
+ )
+ .padding([2, 7])
+ .font(font);
+ let mut col = Column::new()
+ .align_x(Alignment::Center)
+ .padding(Padding::new(10.0).top(30))
+ .spacing(30)
+ .height(Length::Fill)
+ .width(FillPortion(1))
+ .push(
+ Row::new()
+ .spacing(10)
+ .push(
+ Text::new(traffic_source_translation(language))
+ .font(font)
+ .class(TextType::Title)
+ .size(FONT_SIZE_TITLE),
+ )
+ .push(picklist),
+ );
+
+ match &sniffer.conf.capture_source_picklist {
+ CaptureSourcePicklist::Device => {
+ col = col.push(get_col_adapter(sniffer, font, language));
+ }
+ CaptureSourcePicklist::File => {
+ col = col.push(get_col_import_pcap(
+ language,
+ font,
+ &sniffer.capture_source,
+ &sniffer.conf.import_pcap_path,
+ ));
+ }
+ }
+
+ col
+}
+
+fn get_col_adapter(
+ sniffer: &Sniffer,
+ font: Font,
+ language: Language,
+) -> Column<'_, Message, StyleType> {
let mut dev_str_list = vec![];
for my_dev in &sniffer.my_devices {
let mut title = String::new();
@@ -342,16 +216,8 @@ fn get_col_adapter(sniffer: &Sniffer, font: Font) -> Column
}
Column::new()
- .padding(10)
.spacing(5)
.height(Length::Fill)
- .width(FillPortion(4))
- .push(
- choose_adapters_translation(language)
- .font(font)
- .class(TextType::Title)
- .size(FONT_SIZE_TITLE),
- )
.push(if dev_str_list.is_empty() {
Into::>::into(center(
Icon::get_hourglass(sniffer.dots_pulse.0.len()).size(60),
@@ -359,7 +225,7 @@ fn get_col_adapter(sniffer: &Sniffer, font: Font) -> Column
} else {
Scrollable::with_direction(
dev_str_list.into_iter().fold(
- Column::new().padding(13).spacing(5),
+ Column::new().padding(Padding::ZERO.right(13)).spacing(5),
|scroll_adapters, (name, title, subtitle, addrs)| {
let addrs_text = if addrs.is_empty() {
None
@@ -424,7 +290,7 @@ fn get_col_import_pcap<'a>(
let content = Column::new()
.width(Length::Fill)
- .align_x(Alignment::Center)
+ .align_x(alignment::Alignment::Center)
.spacing(5)
.push(button_row);
@@ -439,29 +305,59 @@ fn get_col_import_pcap<'a>(
})
.on_press(Message::SetPcapImport(path.to_string())),
)
- .padding(13);
+ .padding(Padding::ZERO.right(13));
- Column::new()
- .padding(10)
- .spacing(5)
- .width(FillPortion(4))
- .push(
- Text::new(import_capture_translation(language))
- .font(font)
- .class(TextType::Title)
- .size(FONT_SIZE_TITLE),
- )
- .push(button)
+ Column::new().spacing(5).push(button)
}
-fn get_export_pcap_group<'a>(
- cs: &CaptureSource,
+fn get_filters_group<'a>(
+ filters: &Filters,
+ font: Font,
+ language: Language,
+) -> Container<'a, Message, StyleType> {
+ let expanded = filters.expanded();
+ let bpf = filters.bpf();
+
+ let caption = filter_traffic_translation(language);
+ let checkbox = Checkbox::new(caption, expanded)
+ .on_toggle(move |_| Message::ToggleFilters)
+ .size(18)
+ .font(font);
+
+ let mut ret_val = Column::new().spacing(10).push(checkbox);
+
+ if expanded {
+ let input = TextInput::new("", bpf)
+ .on_input(Message::BpfFilter)
+ .padding([2, 5])
+ .font(font);
+ let inner_col = Column::new()
+ .spacing(10)
+ .padding(Padding::ZERO.left(26))
+ .push(
+ Row::new()
+ .align_y(Alignment::Center)
+ .spacing(5)
+ .push(Text::new("BPF:").font(font))
+ .push(input),
+ );
+ ret_val = ret_val.push(inner_col);
+ }
+
+ Container::new(ret_val)
+ .padding(15)
+ .width(Length::Fill)
+ .class(ContainerType::BorderedRound)
+}
+
+fn get_export_pcap_group_maybe<'a>(
+ cs_pick: CaptureSourcePicklist,
export_pcap: &ExportPcap,
language: Language,
font: Font,
-) -> Container<'a, Message, StyleType> {
- if matches!(cs, CaptureSource::File(_)) {
- return Container::new(Space::with_height(Length::Fill));
+) -> Option> {
+ if cs_pick == CaptureSourcePicklist::File {
+ return None;
}
let enabled = export_pcap.enabled();
@@ -479,7 +375,7 @@ fn get_export_pcap_group<'a>(
if enabled {
let inner_col = Column::new()
.spacing(10)
- .padding(Padding::ZERO.left(45))
+ .padding(Padding::ZERO.left(26))
.push(
Row::new()
.align_y(Alignment::Center)
@@ -489,8 +385,7 @@ fn get_export_pcap_group<'a>(
TextInput::new(ExportPcap::DEFAULT_FILE_NAME, file_name)
.on_input(Message::OutputPcapFile)
.padding([2, 5])
- .font(font)
- .width(200),
+ .font(font),
),
)
.push(
@@ -511,12 +406,10 @@ fn get_export_pcap_group<'a>(
ret_val = ret_val.push(inner_col);
}
- Container::new(
+ Some(
Container::new(ret_val)
- .padding(10)
+ .padding(15)
.width(Length::Fill)
.class(ContainerType::BorderedRound),
)
- .height(Length::Fill)
- .align_y(Alignment::Start)
}
diff --git a/src/gui/pages/inspect_page.rs b/src/gui/pages/inspect_page.rs
index 0274f63a..c2cdc0f5 100644
--- a/src/gui/pages/inspect_page.rs
+++ b/src/gui/pages/inspect_page.rs
@@ -11,7 +11,6 @@
};
use iced::{Alignment, Font, Length, Padding, Pixels, alignment};
-use crate::chart::types::chart_type::ChartType;
use crate::gui::components::tab::get_pages_tabs;
use crate::gui::components::types::my_modal::MyModal;
use crate::gui::pages::overview_page::{get_bars, get_bars_length};
@@ -22,28 +21,30 @@
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::byte_multiple::ByteMultiple;
use crate::networking::types::data_info::DataInfo;
+use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host_data_states::HostStates;
use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
use crate::networking::types::traffic_direction::TrafficDirection;
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 {
- let ConfigSettings {
+pub fn inspect_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
+ let Settings {
style, language, ..
- } = sniffer.configs.settings;
+ } = sniffer.conf.settings;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
@@ -75,7 +76,8 @@ pub fn inspect_page(sniffer: &Sniffer) -> Container {
language,
&sniffer.search,
font,
- sniffer.report_sort_type,
+ sniffer.conf.report_sort_type,
+ sniffer.traffic_chart.data_repr,
))
.push(Space::with_height(4))
.push(Rule::horizontal(5))
@@ -97,7 +99,7 @@ pub fn inspect_page(sniffer: &Sniffer) -> Container {
.align_y(Alignment::Center)
.align_x(Alignment::Center)
.padding(Padding::new(7.0).top(10).bottom(3))
- .width(1042)
+ .width(947)
.class(ContainerType::BorderedRound),
);
@@ -105,9 +107,10 @@ pub fn inspect_page(sniffer: &Sniffer) -> Container {
}
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;
let (search_results, results_number, agglomerate) = get_searched_entries(sniffer);
@@ -122,12 +125,17 @@ fn report<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
let end_entry_num = start_entry_num + search_results.len() - 1;
for report_entry in search_results {
scroll_report = scroll_report.push(
- button(row_report_entry(&report_entry.0, &report_entry.1, font))
- .padding(2)
- .on_press(Message::ShowModal(MyModal::ConnectionDetails(
- report_entry.0,
- )))
- .class(ButtonType::Neutral),
+ button(row_report_entry(
+ &report_entry.0,
+ &report_entry.1,
+ data_repr,
+ font,
+ ))
+ .padding(2)
+ .on_press(Message::ShowModal(MyModal::ConnectionDetails(
+ report_entry.0,
+ )))
+ .class(ButtonType::Neutral),
);
}
if results_number > 0 {
@@ -144,7 +152,7 @@ fn report<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
.push(get_agglomerates_row(
font,
agglomerate,
- sniffer.traffic_chart.chart_type,
+ sniffer.traffic_chart.data_repr,
))
.push(Rule::horizontal(5))
.push(get_change_page_row(
@@ -177,12 +185,13 @@ fn report_header_row(
language: Language,
search_params: &SearchParameters,
font: Font,
- sort_type: ReportSortType,
-) -> Row {
+ sort_type: SortType,
+ data_repr: DataRepr,
+) -> Row<'_, Message, StyleType> {
let mut ret_val = Row::new().padding([0, 2]).align_y(Alignment::Center);
for report_col in ReportCol::ALL {
let (title_display, title_small_display, tooltip_val) =
- title_report_col_display(&report_col, language);
+ title_report_col_display(&report_col, data_repr, language);
let title_row = Row::new()
.align_y(Alignment::End)
.push(Text::new(title_display).font(font))
@@ -208,7 +217,9 @@ fn report_header_row(
.width(report_col.get_width())
.height(56)
.push(title_tooltip);
- if report_col != ReportCol::Packets && report_col != ReportCol::Bytes {
+ if report_col == ReportCol::Data {
+ col_header = col_header.push(sort_arrows(sort_type));
+ } else {
col_header = col_header.push(
Container::new(filter_input(
report_col.get_filter_input_type(),
@@ -218,8 +229,6 @@ fn report_header_row(
.height(Length::Fill)
.align_y(Alignment::Center),
);
- } else {
- col_header = col_header.push(sort_arrows(sort_type, &report_col));
}
ret_val = ret_val.push(col_header);
}
@@ -228,10 +237,11 @@ fn report_header_row(
fn title_report_col_display(
report_col: &ReportCol,
+ data_repr: DataRepr,
language: Language,
) -> (String, String, String) {
let max_chars = report_col.get_max_chars(Some(language));
- let title = report_col.get_title(language);
+ let title = report_col.get_title(language, data_repr);
let title_direction_info = report_col.get_title_direction_info(language);
let chars_title = title.chars().collect::>();
let chars_title_direction_info = title_direction_info.chars().collect::>();
@@ -261,21 +271,16 @@ fn title_report_col_display(
}
}
-fn sort_arrows<'a>(
- active_sort_type: ReportSortType,
- report_col: &ReportCol,
-) -> Container<'a, Message, StyleType> {
+fn sort_arrows<'a>(active_sort_type: SortType) -> Container<'a, Message, StyleType> {
Container::new(
button(
active_sort_type
- .icon(report_col)
+ .icon()
.align_x(Alignment::Center)
.align_y(Alignment::Center),
)
- .class(active_sort_type.button_type(report_col))
- .on_press(Message::ReportSortSelection(
- active_sort_type.next_sort(report_col),
- )),
+ .class(active_sort_type.button_type())
+ .on_press(Message::ReportSortSelection(active_sort_type.next_sort())),
)
.align_y(Alignment::Center)
.height(Length::Fill)
@@ -284,6 +289,7 @@ fn sort_arrows<'a>(
fn row_report_entry<'a>(
key: &AddressPortPair,
val: &InfoAddressPortPair,
+ data_repr: DataRepr,
font: Font,
) -> Row<'a, Message, StyleType> {
let text_type = if val.traffic_direction == TrafficDirection::Outgoing {
@@ -296,7 +302,7 @@ fn row_report_entry<'a>(
for report_col in ReportCol::ALL {
let max_chars = report_col.get_max_chars(None);
- let col_value = report_col.get_value(key, val);
+ let col_value = report_col.get_value(key, val, data_repr);
ret_val = ret_val.push(
Container::new(
Text::new(if col_value.len() <= max_chars {
@@ -476,7 +482,7 @@ fn filter_combobox(
combo_box_state: &combo_box::State,
search_params: SearchParameters,
font: Font,
-) -> Container {
+) -> Container<'_, Message, StyleType> {
let filter_value = filter_input_type.current_value(&search_params).to_string();
let is_filter_active = !filter_value.is_empty();
@@ -550,31 +556,22 @@ fn get_button_change_page<'a>(increment: bool) -> Button<'a, Message, StyleType>
fn get_agglomerates_row<'a>(
font: Font,
tot: DataInfo,
- chart_type: ChartType,
+ data_repr: DataRepr,
) -> Row<'a, Message, StyleType> {
- let tot_packets = tot.tot_packets();
- let tot_bytes = tot.tot_bytes();
-
- let (in_length, out_length) = get_bars_length(chart_type, &tot, &tot);
+ let (in_length, out_length) = get_bars_length(data_repr, &tot, &tot);
let bars = get_bars(in_length, out_length).width(ReportCol::FILTER_COLUMNS_WIDTH);
- let bytes_col = Column::new()
+ let data_col = Column::new()
.align_x(Alignment::Center)
- .width(ReportCol::Bytes.get_width())
- .push(Text::new(ByteMultiple::formatted_string(tot_bytes)).font(font));
-
- let packets_col = Column::new()
- .align_x(Alignment::Center)
- .width(ReportCol::Packets.get_width())
- .push(Text::new(tot_packets.to_string()).font(font));
+ .width(ReportCol::Data.get_width())
+ .push(Text::new(data_repr.formatted_string(tot.tot_data(data_repr))).font(font));
Row::new()
.padding([0, 2])
.height(40)
.align_y(Alignment::Center)
.push(bars)
- .push(bytes_col)
- .push(packets_col)
+ .push(data_col)
}
fn get_change_page_row<'a>(
@@ -633,6 +630,7 @@ fn button_clear_filter<'a>(
#[cfg(test)]
mod tests {
use crate::gui::pages::inspect_page::title_report_col_display;
+ use crate::networking::types::data_representation::DataRepr;
use crate::report::types::report_col::ReportCol;
use crate::translations::types::language::Language;
@@ -641,78 +639,81 @@ fn test_table_titles_display_and_tooltip_values_for_each_language() {
// check glyph len when adding new language...
assert_eq!(Language::ALL.len(), 22);
for report_col in ReportCol::ALL {
- for language in Language::ALL {
- let (title, title_small, tooltip_val) =
- title_report_col_display(&report_col, language);
- let title_chars = title.chars().collect::>();
- let title_small_chars = title_small.chars().collect::>();
- let max_chars = report_col.get_max_chars(Some(language));
- if tooltip_val.is_empty() {
- // all is entirely displayed
- assert!(title_chars.len() + title_small_chars.len() <= max_chars);
- assert_eq!(title, report_col.get_title(language));
- assert_eq!(title_small, report_col.get_title_direction_info(language));
- } else {
- // tooltip is the full concatenation
- assert_eq!(
- tooltip_val,
- [
- report_col.get_title(language),
- report_col.get_title_direction_info(language)
- ]
- .concat()
- );
- if report_col.get_title_direction_info(language).len() == 0 {
- // displayed values have max len -1 (they include "…" that counts for 2 units)
- assert_eq!(title_chars.len() + title_small_chars.len(), max_chars - 1);
+ for data_repr in DataRepr::ALL {
+ for language in Language::ALL {
+ let (title, title_small, tooltip_val) =
+ title_report_col_display(&report_col, data_repr, language);
+ let title_chars = title.chars().collect::>();
+ let title_small_chars = title_small.chars().collect::>();
+ let max_chars = report_col.get_max_chars(Some(language));
+ if tooltip_val.is_empty() {
+ // all is entirely displayed
+ assert!(title_chars.len() + title_small_chars.len() <= max_chars);
+ assert_eq!(title, report_col.get_title(language, data_repr));
+ assert_eq!(title_small, report_col.get_title_direction_info(language));
} else {
- match title_chars.len() {
- x if x == max_chars - 4 || x == max_chars - 3 => {
- assert_eq!(title_small_chars.len(), 1)
- }
- _ => assert_eq!(
- title_chars.len() + title_small_chars.len(),
- max_chars - 1
- ),
- }
- }
- if title != report_col.get_title(language) {
- // first title part is not full, so second one is suspensions
- assert_eq!(title_small, "…");
- // check len wrt max
- assert!(title_chars.len() >= max_chars - 4);
- // first title part is max - 2 chars of full self
+ // tooltip is the full concatenation
assert_eq!(
- title,
- report_col
- .get_title(language)
- .chars()
- .collect::>()[..max_chars - 2]
- .iter()
- .collect::()
+ tooltip_val,
+ [
+ report_col.get_title(language, data_repr),
+ report_col.get_title_direction_info(language)
+ ]
+ .concat()
);
- } else {
- // first part is untouched
- // second title part is max - title.len - 2 chars of full self, plus suspensions
- let mut second_part = [
- &report_col
- .get_title_direction_info(language)
- .chars()
- .collect::>()[..max_chars - 2 - title_chars.len()]
- .iter()
- .collect::(),
- "…",
- ]
- .concat();
- if second_part == String::from(" (…") || second_part == String::from(" …")
- {
- second_part = String::from("…");
+ if report_col.get_title_direction_info(language).len() == 0 {
+ // displayed values have max len -1 (they include "…" that counts for 2 units)
+ assert_eq!(title_chars.len() + title_small_chars.len(), max_chars - 1);
+ } else {
+ match title_chars.len() {
+ x if x == max_chars - 4 || x == max_chars - 3 => {
+ assert_eq!(title_small_chars.len(), 1)
+ }
+ _ => assert_eq!(
+ title_chars.len() + title_small_chars.len(),
+ max_chars - 1
+ ),
+ }
+ }
+ if title != report_col.get_title(language, data_repr) {
+ // first title part is not full, so second one is suspensions
+ assert_eq!(title_small, "…");
+ // check len wrt max
+ assert!(title_chars.len() >= max_chars - 4);
+ // first title part is max - 2 chars of full self
+ assert_eq!(
+ title,
+ report_col
+ .get_title(language, data_repr)
+ .chars()
+ .collect::>()[..max_chars - 2]
+ .iter()
+ .collect::()
+ );
+ } else {
+ // first part is untouched
+ // second title part is max - title.len - 2 chars of full self, plus suspensions
+ let mut second_part = [
+ &report_col
+ .get_title_direction_info(language)
+ .chars()
+ .collect::>()[..max_chars - 2 - title_chars.len()]
+ .iter()
+ .collect::(),
+ "…",
+ ]
+ .concat();
+ if second_part == String::from(" (…")
+ || second_part == String::from(" …")
+ {
+ second_part = String::from("…");
+ }
+ assert_eq!(title_small, second_part);
+ // second part never terminates with "(…"
+ assert!(!title_small.ends_with("(…"));
+ // second part never terminates with " …"
+ assert!(!title_small.ends_with(" …"));
}
- assert_eq!(title_small, second_part);
- // second part never terminates with "(…"
- assert!(!title_small.ends_with("(…"));
- // second part never terminates with " …"
- assert!(!title_small.ends_with(" …"));
}
}
}
diff --git a/src/gui/pages/notifications_page.rs b/src/gui/pages/notifications_page.rs
index 0249de8d..3c92f6d7 100644
--- a/src/gui/pages/notifications_page.rs
+++ b/src/gui/pages/notifications_page.rs
@@ -1,4 +1,3 @@
-use crate::chart::types::chart_type::ChartType;
use crate::countries::country_utils::get_computer_tooltip;
use crate::countries::flags_pictures::FLAGS_HEIGHT_BIG;
use crate::gui::components::header::get_button_settings;
@@ -11,8 +10,10 @@
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;
use crate::networking::types::host::Host;
use crate::networking::types::service::Service;
use crate::networking::types::traffic_type::TrafficType;
@@ -21,13 +22,12 @@
};
use crate::report::types::sort_type::SortType;
use crate::translations::translations::{
- bytes_exceeded_translation, clear_all_translation, favorite_transmitted_translation,
- no_notifications_received_translation, no_notifications_set_translation,
- only_last_30_translation, packets_exceeded_translation, per_second_translation,
+ clear_all_translation, favorite_transmitted_translation, no_notifications_received_translation,
+ no_notifications_set_translation, only_last_30_translation, per_second_translation,
threshold_translation,
};
use crate::utils::types::icon::Icon;
-use crate::{ByteMultiple, 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,13 +38,13 @@
use std::cmp::max;
/// Computes the body of gui notifications page
-pub fn notifications_page(sniffer: &Sniffer) -> Container {
- let ConfigSettings {
+pub fn notifications_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
+ 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;
@@ -128,7 +128,7 @@ fn body_no_notifications_received(
font: Font,
language: Language,
dots: &str,
-) -> Column {
+) -> Column<'_, Message, StyleType> {
Column::new()
.padding(5)
.spacing(5)
@@ -150,13 +150,9 @@ fn data_notification_log<'a>(
language: Language,
font: Font,
) -> Container<'a, Message, StyleType> {
- let chart_type = logged_notification.chart_type;
- let data_string = if chart_type == ChartType::Bytes {
- ByteMultiple::formatted_string(logged_notification.threshold.into())
- } else {
- logged_notification.threshold.to_string()
- };
- let icon = if chart_type == ChartType::Packets {
+ let data_repr = logged_notification.data_repr;
+ let data_string = data_repr.formatted_string(logged_notification.threshold.into());
+ let icon = if data_repr == DataRepr::Packets {
Icon::PacketsThreshold
} else {
Icon::BytesThreshold
@@ -184,13 +180,9 @@ fn data_notification_log<'a>(
.push(Text::new(logged_notification.timestamp.clone()).font(font)),
)
.push(
- Text::new(if chart_type == ChartType::Bytes {
- bytes_exceeded_translation(language)
- } else {
- packets_exceeded_translation(language)
- })
- .class(TextType::Title)
- .font(font),
+ Text::new(data_repr.data_exceeded_translation(language).to_string())
+ .class(TextType::Title)
+ .font(font),
)
.push(
Text::new(threshold_str)
@@ -222,14 +214,14 @@ fn data_notification_log<'a>(
fn favorite_notification_log<'a>(
logged_notification: &FavoriteTransmitted,
first_entry_data_info: DataInfo,
- chart_type: ChartType,
+ data_repr: DataRepr,
language: Language,
font: Font,
) -> Container<'a, Message, StyleType> {
let host_bar = host_bar(
&logged_notification.host,
&logged_notification.data_info_host,
- chart_type,
+ data_repr,
first_entry_data_info,
font,
language,
@@ -291,10 +283,10 @@ 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;
- let chart_type = sniffer.traffic_chart.chart_type;
+ } = sniffer.conf.settings;
+ let data_repr = sniffer.traffic_chart.data_repr;
let font = style.get_extension().font;
let mut ret_val = Column::new()
.padding(Padding::ZERO.right(15))
@@ -306,7 +298,7 @@ fn logged_notifications<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType>
.0
.iter()
.map(LoggedNotification::data_info)
- .max_by(|d1, d2| d1.compare(d2, SortType::Ascending, chart_type))
+ .max_by(|d1, d2| d1.compare(d2, SortType::Ascending, data_repr))
.unwrap_or_default();
for logged_notification in &sniffer.logged_notifications.0 {
@@ -323,7 +315,7 @@ fn logged_notifications<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType>
favorite_notification_log(
favorite_transmitted,
first_entry_data_info,
- chart_type,
+ data_repr,
language,
font,
)
@@ -339,10 +331,10 @@ fn threshold_bar<'a>(
language: Language,
font: Font,
) -> Row<'a, Message, StyleType> {
- let chart_type = logged_notification.chart_type;
+ let data_repr = logged_notification.data_repr;
let data_info = logged_notification.data_info;
let (incoming_bar_len, outgoing_bar_len) =
- get_bars_length(chart_type, &first_entry_data_info, &data_info);
+ get_bars_length(data_repr, &first_entry_data_info, &data_info);
Row::new()
.align_y(Alignment::Center)
@@ -358,16 +350,9 @@ fn threshold_bar<'a>(
.push(
Column::new()
.spacing(1)
- .push(
- Row::new().push(horizontal_space()).push(
- Text::new(if chart_type.eq(&ChartType::Packets) {
- data_info.tot_packets().to_string()
- } else {
- ByteMultiple::formatted_string(data_info.tot_bytes())
- })
- .font(font),
- ),
- )
+ .push(Row::new().push(horizontal_space()).push(
+ Text::new(data_repr.formatted_string(data_info.tot_data(data_repr))).font(font),
+ ))
.push(get_bars(incoming_bar_len, outgoing_bar_len)),
)
}
@@ -424,7 +409,7 @@ fn data_notification_extra<'a>(
let host_bar = host_bar(
host,
data_info_host,
- logged_notification.chart_type,
+ logged_notification.data_repr,
first_data_info_host,
font,
language,
@@ -442,7 +427,7 @@ fn data_notification_extra<'a>(
let service_bar = service_bar(
service,
data_info,
- logged_notification.chart_type,
+ logged_notification.data_repr,
first_data_info_service,
font,
);
diff --git a/src/gui/pages/overview_page.rs b/src/gui/pages/overview_page.rs
index 9f8341e2..41ed59df 100644
--- a/src/gui/pages/overview_page.rs
+++ b/src/gui/pages/overview_page.rs
@@ -1,7 +1,7 @@
//! Module defining the run page of the application.
//!
//! It contains elements to display traffic statistics: chart, detailed connections data
-//! and overall statistics about the filtered traffic.
+//! and overall statistics about the traffic.
use crate::chart::types::donut_chart::donut_chart;
use crate::countries::country_utils::get_flag_tooltip;
@@ -12,14 +12,16 @@
use crate::gui::styles::container::ContainerType;
use crate::gui::styles::rule::RuleType;
use crate::gui::styles::scrollbar::ScrollbarType;
-use crate::gui::styles::style_constants::FONT_SIZE_TITLE;
+use crate::gui::styles::style_constants::{FONT_SIZE_FOOTER, FONT_SIZE_TITLE};
use crate::gui::styles::text::TextType;
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;
-use crate::networking::types::filters::Filters;
+use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::Host;
use crate::networking::types::service::Service;
use crate::report::get_report_entries::{get_host_entries, get_service_entries};
@@ -27,18 +29,16 @@
use crate::report::types::sort_type::SortType;
use crate::translations::translations::{
active_filters_translation, error_translation, incoming_translation, no_addresses_translation,
- none_translation, outgoing_translation, some_observed_translation, traffic_rate_translation,
- waiting_translation,
+ none_translation, outgoing_translation, traffic_rate_translation, waiting_translation,
};
use crate::translations::translations_2::{
data_representation_translation, dropped_translation, host_translation,
only_top_30_items_translation,
};
use crate::translations::translations_3::{service_translation, unsupported_link_type_translation};
-use crate::translations::translations_4::{excluded_translation, reading_from_pcap_translation};
-use crate::utils::formatted_strings::get_active_filters_string;
+use crate::translations::translations_4::reading_from_pcap_translation;
use crate::utils::types::icon::Icon;
-use crate::{ByteMultiple, ChartType, ConfigSettings, Language, RunningPage, StyleType};
+use crate::{Language, RunningPage, StyleType};
use iced::Length::{Fill, FillPortion};
use iced::alignment::{Horizontal, Vertical};
use iced::widget::scrollable::Direction;
@@ -48,76 +48,79 @@
Button, Column, Container, Row, Rule, Scrollable, Space, Text, Tooltip, button,
horizontal_space, vertical_space,
};
-use iced::{Alignment, Font, Length, Padding};
+use iced::{Alignment, Element, Font, Length, Padding};
use std::fmt::Write;
/// Computes the body of gui overview page
-pub fn overview_page(sniffer: &Sniffer) -> Container {
- let ConfigSettings {
+pub fn overview_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
+ 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 observed = sniffer.info_traffic.all_packets;
- let filtered = sniffer.info_traffic.tot_data_info.tot_packets();
+ let container_chart = container_chart(sniffer, font);
- match (observed, filtered) {
- (0, 0) => {
- //no packets observed at all
- body = body_no_packets(&sniffer.capture_source, font, language, dots);
- }
- (observed, 0) => {
- //no packets have been filtered but some have been observed
- body = body_no_observed(&sniffer.filters, observed, font, language, dots);
- }
- (_observed, _filtered) => {
- //observed > filtered > 0 || observed = filtered > 0
- 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)
- .spacing(10)
- .align_x(Alignment::Center)
- .push(
- Row::new()
- .height(280)
- .spacing(10)
- .push(container_info)
- .push(container_chart),
- )
- .push(container_report);
- }
- }
- }
+ body = body
+ .width(Length::Fill)
+ .padding(10)
+ .spacing(10)
+ .align_x(Alignment::Center)
+ .push(
+ Row::new()
+ .height(280)
+ .spacing(10)
+ .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> {
+ 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,
@@ -170,31 +173,6 @@ fn body_no_packets<'a>(
.push(Space::with_height(FillPortion(2)))
}
-fn body_no_observed<'a>(
- filters: &Filters,
- observed: u128,
- font: Font,
- language: Language,
- dots: &str,
-) -> Column<'a, Message, StyleType> {
- let tot_packets_text = some_observed_translation(language, observed)
- .align_x(Alignment::Center)
- .font(font);
-
- Column::new()
- .width(Length::Fill)
- .padding(10)
- .spacing(10)
- .align_x(Alignment::Center)
- .push(vertical_space())
- .push(Icon::Funnel.to_text().size(60))
- .push(get_active_filters_col(filters, language, font))
- .push(Rule::horizontal(20))
- .push(tot_packets_text)
- .push(Text::new(dots.to_owned()).font(font).size(50))
- .push(Space::with_height(FillPortion(2)))
-}
-
fn body_pcap_error<'a>(
pcap_error: &'a str,
dots: &'a str,
@@ -241,20 +219,24 @@ 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 chart_type = sniffer.traffic_chart.chart_type;
+ 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, chart_type, 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)
- .max_by(|d1, d2| d1.compare(d2, SortType::Ascending, chart_type))
+ .max_by(|d1, d2| d1.compare(d2, SortType::Ascending, data_repr))
.unwrap_or_default();
for (host, data_info_host) in &entries {
@@ -263,7 +245,7 @@ fn col_host<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
let host_bar = host_bar(
host,
data_info_host,
- chart_type,
+ data_repr,
first_entry_data_info,
font,
language,
@@ -304,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,
)),
)
@@ -318,24 +300,28 @@ 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 chart_type = sniffer.traffic_chart.chart_type;
+ 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, chart_type, 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)
- .max_by(|d1, d2| d1.compare(d2, SortType::Ascending, chart_type))
+ .max_by(|d1, d2| d1.compare(d2, SortType::Ascending, data_repr))
.unwrap_or_default();
for (service, data_info) in &entries {
- let content = service_bar(service, data_info, chart_type, first_entry_data_info, font);
+ let content = service_bar(service, data_info, data_repr, first_entry_data_info, font);
scroll_service = scroll_service.push(
button(content)
@@ -368,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,
)),
)
@@ -384,16 +370,13 @@ fn col_service<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
pub fn host_bar<'a>(
host: &Host,
data_info_host: &DataInfoHost,
- chart_type: ChartType,
+ data_repr: DataRepr,
first_entry_data_info: DataInfo,
font: Font,
language: Language,
) -> Row<'a, Message, StyleType> {
- let (incoming_bar_len, outgoing_bar_len) = get_bars_length(
- chart_type,
- &first_entry_data_info,
- &data_info_host.data_info,
- );
+ let (incoming_bar_len, outgoing_bar_len) =
+ get_bars_length(data_repr, &first_entry_data_info, &data_info_host.data_info);
Row::new()
.height(FLAGS_HEIGHT_BIG)
@@ -422,11 +405,10 @@ pub fn host_bar<'a>(
)
.push(horizontal_space())
.push(
- Text::new(if chart_type.eq(&ChartType::Packets) {
- data_info_host.data_info.tot_packets().to_string()
- } else {
- ByteMultiple::formatted_string(data_info_host.data_info.tot_bytes())
- })
+ Text::new(
+ data_repr
+ .formatted_string(data_info_host.data_info.tot_data(data_repr)),
+ )
.font(font),
),
)
@@ -437,12 +419,12 @@ pub fn host_bar<'a>(
pub fn service_bar<'a>(
service: &Service,
data_info: &DataInfo,
- chart_type: ChartType,
+ data_repr: DataRepr,
first_entry_data_info: DataInfo,
font: Font,
) -> Row<'a, Message, StyleType> {
let (incoming_bar_len, outgoing_bar_len) =
- get_bars_length(chart_type, &first_entry_data_info, data_info);
+ get_bars_length(data_repr, &first_entry_data_info, data_info);
Row::new()
.height(FLAGS_HEIGHT_BIG)
@@ -456,28 +438,29 @@ pub fn service_bar<'a>(
.push(Text::new(service.to_string()).font(font))
.push(horizontal_space())
.push(
- Text::new(if chart_type.eq(&ChartType::Packets) {
- data_info.tot_packets().to_string()
- } else {
- ByteMultiple::formatted_string(data_info.tot_bytes())
- })
- .font(font),
+ Text::new(data_repr.formatted_string(data_info.tot_data(data_repr)))
+ .font(font),
),
)
.push(get_bars(incoming_bar_len, outgoing_bar_len)),
)
}
-fn col_info<'a>(sniffer: &Sniffer) -> Container<'a, Message, StyleType> {
- let ConfigSettings {
+fn col_info(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
+ 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);
+ 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.chart_type);
+ col_data_representation(language, font, sniffer.traffic_chart.data_repr);
let donut_row = donut_row(language, font, sniffer);
@@ -507,8 +490,8 @@ fn col_info<'a>(sniffer: &Sniffer) -> Container<'a, Message, StyleType> {
.class(ContainerType::BorderedRound)
}
-fn container_chart(sniffer: &Sniffer, font: Font) -> Container {
- let ConfigSettings { language, .. } = sniffer.configs.settings;
+fn container_chart(sniffer: &Sniffer, font: Font) -> Container<'_, Message, StyleType> {
+ let Settings { language, .. } = sniffer.conf.settings;
let traffic_chart = &sniffer.traffic_chart;
Container::new(
@@ -534,6 +517,7 @@ fn col_device<'a>(
language: Language,
font: Font,
cs: &CaptureSource,
+ filters: &'a Filters,
) -> Column<'a, Message, StyleType> {
let link_type = cs.get_link_type();
#[cfg(not(target_os = "windows"))]
@@ -541,21 +525,55 @@ fn col_device<'a>(
#[cfg(target_os = "windows")]
let cs_info = cs.get_desc().unwrap_or(cs.get_name());
+ let filters_desc: Element = if filters.is_some_filter_active() {
+ Row::new()
+ .spacing(10)
+ .push(Text::new("BPF").font(font))
+ .push(get_info_tooltip(filters.bpf().to_string(), font))
+ .into()
+ } else {
+ Text::new(none_translation(language)).font(font).into()
+ };
+
Column::new()
.height(Length::Fill)
.spacing(10)
- .push(TextType::highlighted_subtitle_with_desc(
- cs.title(language),
- &cs_info,
- font,
- ))
- .push(link_type.link_type_col(language, font))
+ .push(
+ Column::new()
+ .push(
+ Text::new(format!("{}:", cs.title(language)))
+ .class(TextType::Subtitle)
+ .font(font),
+ )
+ .push(
+ Row::new()
+ .spacing(10)
+ .push(Text::new(format!(" {}", &cs_info)).font(font))
+ .push(get_info_tooltip(
+ link_type.full_print_on_one_line(language),
+ font,
+ )),
+ ),
+ )
+ .push(
+ Column::new()
+ .push(
+ Text::new(format!("{}:", active_filters_translation(language)))
+ .class(TextType::Subtitle)
+ .font(font),
+ )
+ .push(
+ Row::new()
+ .push(Text::new(" ".to_string()).font(font))
+ .push(filters_desc),
+ ),
+ )
}
fn col_data_representation<'a>(
language: Language,
font: Font,
- chart_type: ChartType,
+ data_repr: DataRepr,
) -> Column<'a, Message, StyleType> {
let mut ret_val = Column::new().spacing(5).push(
Text::new(format!("{}:", data_representation_translation(language)))
@@ -563,77 +581,61 @@ fn col_data_representation<'a>(
.font(font),
);
- for option in ChartType::ALL {
- let is_active = chart_type.eq(&option);
- ret_val = ret_val.push(
- Button::new(
- Text::new(option.get_label(language).to_owned())
- .width(Length::Fill)
- .align_x(Alignment::Center)
- .align_y(Alignment::Center)
- .font(font),
- )
- .width(Length::Fill)
- .height(33)
- .class(if is_active {
- ButtonType::BorderedRoundSelected
- } else {
- ButtonType::BorderedRound
- })
- .on_press(Message::ChartSelection(option)),
- );
- }
+ let [bits, bytes, packets] = DataRepr::ALL.map(|option| {
+ let is_active = data_repr.eq(&option);
+ Button::new(
+ Text::new(option.get_label(language).to_owned())
+ .width(Length::Fill)
+ .align_x(Alignment::Center)
+ .align_y(Alignment::Center)
+ .font(font),
+ )
+ .width(Length::Fill)
+ .height(33)
+ .class(if is_active {
+ ButtonType::BorderedRoundSelected
+ } else {
+ ButtonType::BorderedRound
+ })
+ .on_press(Message::DataReprSelection(option))
+ });
+
+ ret_val = ret_val
+ .push(Row::new().spacing(5).push(bits).push(bytes))
+ .push(packets);
+
ret_val
}
-fn donut_row<'a>(
+fn donut_row(
language: Language,
font: Font,
sniffer: &Sniffer,
-) -> Container<'a, Message, StyleType> {
- let chart_type = sniffer.traffic_chart.chart_type;
- let filters = &sniffer.filters;
+) -> Container<'_, Message, StyleType> {
+ let data_repr = sniffer.traffic_chart.data_repr;
- let (in_data, out_data, filtered_out, dropped) =
- sniffer.info_traffic.get_thumbnail_data(chart_type);
-
- let legend_entry_filtered = if filters.none_active() {
- None
- } else {
- Some(donut_legend_entry(
- filtered_out,
- chart_type,
- RuleType::FilteredOut,
- filters,
- font,
- language,
- ))
- };
+ let (in_data, out_data, dropped) = sniffer.info_traffic.get_thumbnail_data(data_repr);
let legend_col = Column::new()
.spacing(5)
.push(donut_legend_entry(
in_data,
- chart_type,
+ data_repr,
RuleType::Incoming,
- filters,
font,
language,
))
.push(donut_legend_entry(
out_data,
- chart_type,
+ data_repr,
RuleType::Outgoing,
- filters,
font,
language,
))
- .push_maybe(legend_entry_filtered)
.push(donut_legend_entry(
dropped,
- chart_type,
+ data_repr,
RuleType::Dropped,
- filters,
font,
language,
));
@@ -642,10 +644,9 @@ fn donut_row<'a>(
.align_y(Vertical::Center)
.spacing(20)
.push(donut_chart(
- chart_type,
+ data_repr,
in_data,
out_data,
- filtered_out,
dropped,
font,
sniffer.thumbnail,
@@ -661,32 +662,20 @@ fn donut_row<'a>(
fn donut_legend_entry<'a>(
value: u128,
- chart_type: ChartType,
+ data_repr: DataRepr,
rule_type: RuleType,
- filters: &Filters,
font: Font,
language: Language,
) -> Row<'a, Message, StyleType> {
- let value_text = if chart_type.eq(&ChartType::Bytes) {
- ByteMultiple::formatted_string(value)
- } else {
- value.to_string()
- };
+ let value_text = data_repr.formatted_string(value);
let label = match rule_type {
RuleType::Incoming => incoming_translation(language),
RuleType::Outgoing => outgoing_translation(language),
- RuleType::FilteredOut => excluded_translation(language),
RuleType::Dropped => dropped_translation(language),
_ => "",
};
- let tooltip = if matches!(rule_type, RuleType::FilteredOut) {
- Some(get_active_filters_tooltip(filters, language, font))
- } else {
- None
- };
-
Row::new()
.spacing(10)
.align_y(Alignment::Center)
@@ -696,28 +685,18 @@ fn donut_legend_entry<'a>(
.push(Rule::horizontal(1).class(rule_type)),
)
.push(Text::new(format!("{label}: {value_text}")).font(font))
- .push_maybe(tooltip)
}
const MIN_BARS_LENGTH: f32 = 4.0;
pub fn get_bars_length(
- chart_type: ChartType,
+ data_repr: DataRepr,
first_entry: &DataInfo,
data_info: &DataInfo,
) -> (u16, u16) {
- let (in_val, out_val, first_entry_tot_val) = match chart_type {
- ChartType::Packets => (
- data_info.incoming_packets(),
- data_info.outgoing_packets(),
- first_entry.tot_packets(),
- ),
- ChartType::Bytes => (
- data_info.incoming_bytes(),
- data_info.outgoing_bytes(),
- first_entry.tot_bytes(),
- ),
- };
+ let in_val = data_info.incoming_data(data_repr);
+ let out_val = data_info.outgoing_data(data_repr);
+ let first_entry_tot_val = first_entry.tot_data(data_repr);
let tot_val = in_val + out_val;
if tot_val == 0 {
@@ -808,46 +787,12 @@ fn get_star_button<'a>(is_favorite: bool, host: Host) -> Button<'a, Message, Sty
.on_press(Message::AddOrRemoveFavorite(host, !is_favorite))
}
-fn get_active_filters_col<'a>(
- filters: &Filters,
- language: Language,
- font: Font,
-) -> Column<'a, Message, StyleType> {
- let mut ret_val = Column::new().push(
- Text::new(active_filters_translation(language))
- .font(font)
- .class(TextType::Subtitle),
- );
-
- if filters.none_active() {
- ret_val = ret_val.push(Text::new(format!(" {}", none_translation(language))).font(font));
- } else {
- let filters_string = get_active_filters_string(filters, language);
- ret_val = ret_val.push(Row::new().push(Text::new(filters_string).font(font)));
- }
- ret_val
-}
-
-fn get_active_filters_tooltip<'a>(
- filters: &Filters,
- language: Language,
- font: Font,
-) -> Tooltip<'a, Message, StyleType> {
- let filters_string = get_active_filters_string(filters, language);
-
- let mut ret_val = Column::new().push(
- Text::new(active_filters_translation(language))
- .font(font)
- .class(TextType::Subtitle),
- );
-
- ret_val = ret_val.push(Row::new().push(Text::new(filters_string).font(font)));
-
+fn get_info_tooltip<'a>(info_str: String, font: Font) -> Tooltip<'a, Message, StyleType> {
Tooltip::new(
Container::new(
Text::new("i")
+ .size(FONT_SIZE_FOOTER)
.font(font)
- .size(15)
.line_height(LineHeight::Relative(1.0)),
)
.align_x(Alignment::Center)
@@ -855,7 +800,7 @@ fn get_active_filters_tooltip<'a>(
.height(20)
.width(20)
.class(ContainerType::BadgeInfo),
- ret_val,
+ Text::new(info_str).font(font),
Position::FollowCursor,
)
.class(ContainerType::Tooltip)
@@ -881,20 +826,24 @@ fn sort_arrows<'a>(
#[cfg(test)]
mod tests {
- use crate::chart::types::chart_type::ChartType;
use crate::gui::pages::overview_page::{MIN_BARS_LENGTH, get_bars_length};
use crate::networking::types::data_info::DataInfo;
+ use crate::networking::types::data_representation::DataRepr;
#[test]
fn test_get_bars_length_simple() {
let first_entry = DataInfo::new_for_tests(50, 50, 150, 50);
let data_info = DataInfo::new_for_tests(25, 55, 165, 30);
assert_eq!(
- get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(25, 55)
);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
+ (83, 15)
+ );
+ assert_eq!(
+ get_bars_length(DataRepr::Bits, &first_entry, &data_info),
(83, 15)
);
}
@@ -904,21 +853,21 @@ fn test_get_bars_length_normalize_small_values() {
let first_entry = DataInfo::new_for_tests(50, 50, 150, 50);
let mut data_info = DataInfo::new_for_tests(2, 1, 1, 0);
assert_eq!(
- get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16 / 2, MIN_BARS_LENGTH as u16 / 2)
);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16, 0)
);
data_info = DataInfo::new_for_tests(0, 3, 0, 2);
assert_eq!(
- get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(0, MIN_BARS_LENGTH as u16)
);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(0, MIN_BARS_LENGTH as u16)
);
}
@@ -929,31 +878,31 @@ fn test_get_bars_length_normalize_very_small_values() {
DataInfo::new_for_tests(u128::MAX / 2, u128::MAX / 2, u128::MAX / 2, u128::MAX / 2);
let mut data_info = DataInfo::new_for_tests(1, 1, 1, 1);
assert_eq!(
- get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16 / 2, MIN_BARS_LENGTH as u16 / 2)
);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16 / 2, MIN_BARS_LENGTH as u16 / 2)
);
data_info = DataInfo::new_for_tests(0, 1, 0, 1);
assert_eq!(
- get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(0, MIN_BARS_LENGTH as u16)
);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(0, MIN_BARS_LENGTH as u16)
);
data_info = DataInfo::new_for_tests(1, 0, 1, 0);
assert_eq!(
- get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16, 0)
);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16, 0)
);
}
@@ -964,93 +913,93 @@ fn test_get_bars_length_complex() {
let mut data_info = DataInfo::new_for_tests(0, 9, 0, 10);
assert_eq!(
- get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(0, 16)
);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(0, 71)
);
data_info = DataInfo::new_for_tests(9, 0, 13, 0);
assert_eq!(
- get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(16, 0)
);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(93, 0)
);
data_info = DataInfo::new_for_tests(4, 5, 6, 7);
assert_eq!(
- get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(7, 9)
);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(43, 50)
);
data_info = DataInfo::new_for_tests(5, 4, 7, 6);
assert_eq!(
- get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(9, 7)
);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(50, 43)
);
data_info = DataInfo::new_for_tests(1, 8, 1, 12);
assert_eq!(
- get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16 / 2, 14)
);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(7, 86)
);
data_info = DataInfo::new_for_tests(8, 1, 12, 1);
assert_eq!(
- get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(14, MIN_BARS_LENGTH as u16 / 2)
);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(86, 7)
);
data_info = DataInfo::new_for_tests(6, 1, 10, 1);
assert_eq!(
- get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(11, MIN_BARS_LENGTH as u16 / 2)
);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(71, 7)
);
data_info = DataInfo::new_for_tests(1, 6, 1, 9);
assert_eq!(
- get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(MIN_BARS_LENGTH as u16 / 2, 11,)
);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(7, 64)
);
data_info = DataInfo::new_for_tests(1, 6, 5, 5);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(36, 36)
);
data_info = DataInfo::new_for_tests(0, 0, 0, 0);
assert_eq!(
- get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ get_bars_length(DataRepr::Packets, &first_entry, &data_info),
(0, 0)
);
assert_eq!(
- get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ get_bars_length(DataRepr::Bytes, &first_entry, &data_info),
(0, 0)
);
}
diff --git a/src/gui/pages/settings_general_page.rs b/src/gui/pages/settings_general_page.rs
index f4b2aafc..902b9ad0 100644
--- a/src/gui/pages/settings_general_page.rs
+++ b/src/gui/pages/settings_general_page.rs
@@ -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 {
- let ConfigSettings {
+pub fn settings_general_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
+ 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;
@@ -56,16 +57,16 @@ pub fn settings_general_page(sniffer: &Sniffer) -> Container
.class(ContainerType::Modal)
}
-fn column_all_general_setting(sniffer: &Sniffer, font: Font) -> Column {
- let ConfigSettings {
+fn column_all_general_setting(sniffer: &Sniffer, font: Font) -> Column<'_, Message, StyleType> {
+ 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)
diff --git a/src/gui/pages/settings_notifications_page.rs b/src/gui/pages/settings_notifications_page.rs
index 6c10c1c8..c1df9e9a 100644
--- a/src/gui/pages/settings_notifications_page.rs
+++ b/src/gui/pages/settings_notifications_page.rs
@@ -3,7 +3,6 @@
use iced::widget::{Checkbox, Column, Container, Row, Scrollable, Space, Text, TextInput};
use iced::{Alignment, Font, Length, Padding};
-use crate::chart::types::chart_type::ChartType;
use crate::gui::components::button::button_hide;
use crate::gui::components::tab::get_settings_tabs;
use crate::gui::pages::types::settings_page::SettingsPage;
@@ -14,6 +13,8 @@
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;
@@ -71,11 +72,14 @@ pub fn settings_notifications_page<'a>(sniffer: &Sniffer) -> Container<'a, Messa
.push(Space::with_height(5));
let volume_notification_col = Column::new()
+ .padding(5)
+ .spacing(10)
.align_x(Alignment::Center)
.width(Length::Fill)
.push(volume_slider(language, font, notifications.volume))
.push(Scrollable::with_direction(
Column::new()
+ .spacing(10)
.align_x(Alignment::Center)
.width(Length::Fill)
.push(get_data_notify(
@@ -103,7 +107,7 @@ fn get_data_notify<'a>(
data_notification: DataNotification,
language: Language,
font: Font,
-) -> Column<'a, Message, StyleType> {
+) -> Container<'a, Message, StyleType> {
let checkbox = Checkbox::new(
data_exceeded_translation(language),
data_notification.threshold.is_some(),
@@ -133,18 +137,16 @@ fn get_data_notify<'a>(
let mut ret_val = Column::new().spacing(15).push(checkbox);
if data_notification.threshold.is_none() {
- Column::new().padding(5).push(
- Container::new(ret_val)
- .padding(10)
- .width(700)
- .class(ContainerType::BorderedRound),
- )
+ Container::new(ret_val)
+ .padding(15)
+ .width(700)
+ .class(ContainerType::BorderedRound)
} else {
let data_representation_row = row_data_representation(
data_notification,
language,
font,
- data_notification.chart_type,
+ data_notification.data_repr,
);
let input_row = input_group_bytes(data_notification, font, language);
let sound_row = sound_buttons(Notification::Data(data_notification), font, language);
@@ -152,12 +154,11 @@ fn get_data_notify<'a>(
.push(sound_row)
.push(data_representation_row)
.push(input_row);
- Column::new().padding(5).push(
- Container::new(ret_val)
- .padding(10)
- .width(700)
- .class(ContainerType::BorderedRound),
- )
+
+ Container::new(ret_val)
+ .padding(15)
+ .width(700)
+ .class(ContainerType::BorderedRound)
}
}
@@ -165,7 +166,7 @@ fn get_favorite_notify<'a>(
favorite_notification: FavoriteNotification,
language: Language,
font: Font,
-) -> Column<'a, Message, StyleType> {
+) -> Container<'a, Message, StyleType> {
let checkbox = Checkbox::new(
favorite_transmitted_translation(language),
favorite_notification.notify_on_favorite,
@@ -192,19 +193,15 @@ fn get_favorite_notify<'a>(
language,
);
ret_val = ret_val.push(sound_row);
- Column::new().padding(5).push(
- Container::new(ret_val)
- .padding(10)
- .width(700)
- .class(ContainerType::BorderedRound),
- )
+ Container::new(ret_val)
+ .padding(15)
+ .width(700)
+ .class(ContainerType::BorderedRound)
} else {
- Column::new().padding(5).push(
- Container::new(ret_val)
- .padding(10)
- .width(700)
- .class(ContainerType::BorderedRound),
- )
+ Container::new(ret_val)
+ .padding(15)
+ .width(700)
+ .class(ContainerType::BorderedRound)
}
}
@@ -220,7 +217,7 @@ fn input_group_bytes<'a>(
let input_row = Row::new()
.spacing(5)
.align_y(Alignment::Center)
- .push(Space::with_width(45))
+ .padding(Padding::ZERO.left(26))
.push(Text::new(format!("{}:", threshold_translation(language))).font(font))
.push(
TextInput::new(
@@ -305,7 +302,7 @@ fn sound_buttons<'a>(
.width(Length::Shrink)
.align_y(Alignment::Center)
.spacing(5)
- .push(Space::with_width(45))
+ .padding(Padding::ZERO.left(26))
.push(Text::new(format!("{}:", sound_translation(language))).font(font));
for option in Sound::ALL {
@@ -372,17 +369,17 @@ fn row_data_representation<'a>(
data_notification: DataNotification,
language: Language,
font: Font,
- chart_type: ChartType,
+ data_repr: DataRepr,
) -> Row<'a, Message, StyleType> {
let mut ret_val = Row::new()
.width(Length::Shrink)
.align_y(Alignment::Center)
.spacing(5)
- .push(Space::with_width(45))
+ .padding(Padding::ZERO.left(26))
.push(Text::new(format!("{}:", data_representation_translation(language))).font(font));
- for option in ChartType::ALL {
- let is_active = chart_type.eq(&option);
+ for option in DataRepr::ALL {
+ let is_active = data_repr.eq(&option);
ret_val = ret_val.push(
Button::new(
Text::new(option.get_label(language).to_owned())
@@ -400,7 +397,7 @@ fn row_data_representation<'a>(
})
.on_press(Message::UpdateNotificationSettings(
Notification::Data(DataNotification {
- chart_type: option,
+ data_repr: option,
..data_notification
}),
false,
diff --git a/src/gui/pages/settings_style_page.rs b/src/gui/pages/settings_style_page.rs
index f3a41a61..efbd68fe 100644
--- a/src/gui/pages/settings_style_page.rs
+++ b/src/gui/pages/settings_style_page.rs
@@ -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 {
- let ConfigSettings {
+pub fn settings_style_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
+ let Settings {
style,
language,
color_gradient,
style_path,
..
- } = sniffer.configs.settings.clone();
+ } = sniffer.conf.settings.clone();
let PaletteExtension {
font, font_headers, ..
} = style.get_extension();
diff --git a/src/gui/pages/thumbnail_page.rs b/src/gui/pages/thumbnail_page.rs
index 86a3ae61..17914b48 100644
--- a/src/gui/pages/thumbnail_page.rs
+++ b/src/gui/pages/thumbnail_page.rs
@@ -4,14 +4,14 @@
use iced::widget::{Column, Container, Row, Rule, Space, Text, vertical_space};
use iced::{Alignment, Font, Length};
-use crate::chart::types::chart_type::ChartType;
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;
use crate::report::get_report_entries::{get_host_entries, get_service_entries};
@@ -23,13 +23,16 @@
const MAX_CHARS_SERVICE: usize = 13;
/// Computes the body of the thumbnail view
-pub fn thumbnail_page(sniffer: &Sniffer) -> Container {
- let ConfigSettings { style, .. } = sniffer.configs.settings;
+pub fn thumbnail_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
+ let Settings { style, .. } = sniffer.conf.settings;
let font = style.get_extension().font;
- let filtered = sniffer.info_traffic.tot_data_info.tot_packets();
+ let tot_packets = sniffer
+ .info_traffic
+ .tot_data_info
+ .tot_data(DataRepr::Packets);
- if filtered == 0 {
+ if tot_packets == 0 {
return Container::new(
Column::new()
.push(vertical_space())
@@ -41,19 +44,18 @@ pub fn thumbnail_page(sniffer: &Sniffer) -> Container {
}
let info_traffic = &sniffer.info_traffic;
- let chart_type = sniffer.traffic_chart.chart_type;
+ let data_repr = sniffer.traffic_chart.data_repr;
- let (in_data, out_data, filtered_out, dropped) = info_traffic.get_thumbnail_data(chart_type);
+ let (in_data, out_data, dropped) = info_traffic.get_thumbnail_data(data_repr);
let charts = Row::new()
.padding(5)
.height(Length::Fill)
.align_y(Alignment::Center)
.push(donut_chart(
- chart_type,
+ data_repr,
in_data,
out_data,
- filtered_out,
dropped,
font,
sniffer.thumbnail,
@@ -71,16 +73,16 @@ pub fn thumbnail_page(sniffer: &Sniffer) -> Container {
.align_y(Alignment::Start)
.push(host_col(
info_traffic,
- chart_type,
+ data_repr,
font,
- sniffer.host_sort_type,
+ sniffer.conf.host_sort_type,
))
.push(Rule::vertical(10))
.push(service_col(
info_traffic,
- chart_type,
+ data_repr,
font,
- sniffer.service_sort_type,
+ sniffer.conf.service_sort_type,
));
let content = Column::new()
@@ -93,7 +95,7 @@ pub fn thumbnail_page(sniffer: &Sniffer) -> Container {
fn host_col<'a>(
info_traffic: &InfoTraffic,
- chart_type: ChartType,
+ data_repr: DataRepr,
font: Font,
sort_type: SortType,
) -> Column<'a, Message, StyleType> {
@@ -101,7 +103,7 @@ fn host_col<'a>(
.padding([0, 5])
.spacing(3)
.width(Length::FillPortion(2));
- let hosts = get_host_entries(info_traffic, chart_type, sort_type);
+ let hosts = get_host_entries(info_traffic, data_repr, sort_type);
let mut thumbnail_hosts = Vec::new();
for (host, data_info_host) in &hosts {
@@ -136,12 +138,12 @@ fn host_col<'a>(
fn service_col<'a>(
info_traffic: &InfoTraffic,
- chart_type: ChartType,
+ data_repr: DataRepr,
font: Font,
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, chart_type, sort_type);
+ let services = get_service_entries(info_traffic, data_repr, sort_type);
let n_entry = min(services.len(), MAX_ENTRIES);
for (service, _) in services.get(..n_entry).unwrap_or_default() {
service_col = service_col.push(
diff --git a/src/gui/pages/types/running_page.rs b/src/gui/pages/types/running_page.rs
index a37817c8..f0693964 100644
--- a/src/gui/pages/types/running_page.rs
+++ b/src/gui/pages/types/running_page.rs
@@ -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()
}
diff --git a/src/gui/pages/types/settings_page.rs b/src/gui/pages/types/settings_page.rs
index 2dbaf9ca..d2457507 100644
--- a/src/gui/pages/types/settings_page.rs
+++ b/src/gui/pages/types/settings_page.rs
@@ -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,
diff --git a/src/gui/sniffer.rs b/src/gui/sniffer.rs
index 79f74228..e30aea97 100644
--- a/src/gui/sniffer.rs
+++ b/src/gui/sniffer.rs
@@ -1,5 +1,6 @@
//! Module defining the application structure: messages, updates, subscriptions.
+use crate::gui::pages::overview_page::waiting_page;
use async_channel::Receiver;
use iced::Event::{Keyboard, Window};
use iced::keyboard::key::Named;
@@ -17,9 +18,6 @@
use std::thread;
use std::time::Duration;
-use crate::configs::types::config_window::{
- ConfigWindow, PositionTuple, ScaleAndCheck, SizeTuple, ToPoint, ToSize,
-};
use crate::gui::components::footer::footer;
use crate::gui::components::header::header;
use crate::gui::components::modal::{get_clear_all_overlay, get_exit_overlay, modal};
@@ -37,46 +35,48 @@
use crate::gui::pages::types::settings_page::SettingsPage;
use crate::gui::styles::types::custom_palette::{CustomPalette, ExtraStyles};
use crate::gui::styles::types::palette::Palette;
-use crate::gui::types::export_pcap::ExportPcap;
+use crate::gui::types::conf::Conf;
+use crate::gui::types::config_window::{
+ ConfigWindow, PositionTuple, ScaleAndCheck, SizeTuple, ToPoint, ToSize,
+};
use crate::gui::types::message::Message;
+use crate::gui::types::settings::Settings;
use crate::gui::types::timing_events::TimingEvents;
use crate::mmdb::asn::ASN_MMDB;
use crate::mmdb::country::COUNTRY_MMDB;
use crate::mmdb::types::mmdb_reader::{MmdbReader, MmdbReaders};
use crate::networking::parse_packets::BackendTrafficMessage;
use crate::networking::parse_packets::parse_packets;
-use crate::networking::types::capture_context::{CaptureContext, CaptureSource, MyPcapImport};
-use crate::networking::types::filters::Filters;
+use crate::networking::types::capture_context::{
+ CaptureContext, CaptureSource, CaptureSourcePicklist, MyPcapImport,
+};
+use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::{Host, HostMessage};
use crate::networking::types::host_data_states::HostDataStates;
use crate::networking::types::info_traffic::InfoTraffic;
-use crate::networking::types::ip_collection::AddressCollection;
use crate::networking::types::my_device::MyDevice;
-use crate::networking::types::port_collection::PortCollection;
use crate::notifications::notify_and_log::notify_and_log;
use crate::notifications::types::logged_notification::LoggedNotification;
use crate::notifications::types::notifications::{DataNotification, Notification};
use crate::notifications::types::sound::{Sound, play};
use crate::report::get_report_entries::get_searched_entries;
-use crate::report::types::report_sort_type::ReportSortType;
use crate::report::types::search_parameters::SearchParameters;
-use crate::report::types::sort_type::SortType;
use crate::translations::types::language::Language;
use crate::utils::check_updates::set_newer_release_status;
use crate::utils::error_logger::{ErrorLogger, Location};
use crate::utils::types::file_info::FileInfo;
use crate::utils::types::web_page::WebPage;
-use crate::{ConfigSettings, Configs, StyleType, TrafficChart, location};
+use crate::{StyleType, TrafficChart, location};
pub const FONT_FAMILY_NAME: &str = "Sarasa Mono SC for Sniffnet";
pub const ICON_FONT_FAMILY_NAME: &str = "Icons for Sniffnet";
-/// Struct on which the gui is based
+/// Struct on which the GUI is based
///
-/// It contains gui statuses and network traffic statistics
+/// It carries statuses, network traffic statistics, and more
pub struct Sniffer {
- /// Application's configurations: settings, window properties, name of last device sniffed
- pub configs: Configs,
+ /// Parameters that are persistent across runs
+ pub conf: Conf,
/// Capture receiver clone (to close the channel after every run), with the current capture id (to ignore pending messages from previous captures)
pub current_capture_rx: (usize, Option>),
/// Capture data
@@ -93,28 +93,18 @@ pub struct Sniffer {
pub capture_source: CaptureSource,
/// List of network devices
pub my_devices: Vec,
- /// Active filters on the observed traffic
- pub filters: Filters,
/// Signals if a pcap error occurred
pub pcap_error: Option,
/// Messages status
pub dots_pulse: (String, u8),
/// Chart displayed
pub traffic_chart: TrafficChart,
- /// Report sort type (inspect page)
- pub report_sort_type: ReportSortType,
- /// Host sort type (overview page)
- pub host_sort_type: SortType,
- /// Service sort type (overview page)
- pub service_sort_type: SortType,
/// Currently displayed modal; None if no modal is displayed
pub modal: Option,
/// Currently displayed settings page; None if settings is closed
pub settings_page: Option,
- /// Remembers the last opened setting page
- pub last_opened_setting: SettingsPage,
- /// Defines the current running page
- pub running_page: RunningPage,
+ /// Defines the current running page; None if initial page
+ pub running_page: Option,
/// Number of unread notifications
pub unread_notifications: usize,
/// Search parameters of inspect page
@@ -125,49 +115,40 @@ pub struct Sniffer {
pub mmdb_readers: MmdbReaders,
/// Time-related events
pub timing_events: TimingEvents,
- /// Information about PCAP file export
- pub export_pcap: ExportPcap,
/// Whether thumbnail mode is currently active
pub thumbnail: bool,
/// Window id
pub id: Option,
/// Host data for filter dropdowns (comboboxes)
pub host_data_states: HostDataStates,
- /// Import path for PCAP file
- pub import_pcap_path: String,
}
impl Sniffer {
- pub fn new(configs: Configs) -> Self {
- let ConfigSettings {
+ pub fn new(conf: Conf) -> Self {
+ let Settings {
style,
language,
mmdb_country,
mmdb_asn,
..
- } = configs.settings.clone();
- let device = configs.device.to_my_device();
+ } = conf.settings.clone();
+ let capture_source = CaptureSource::from_conf(&conf);
Self {
- configs,
+ conf,
current_capture_rx: (0, None),
info_traffic: InfoTraffic::default(),
addresses_resolved: HashMap::new(),
favorite_hosts: HashSet::new(),
logged_notifications: (VecDeque::new(), 0),
newer_release_available: None,
- capture_source: CaptureSource::Device(device),
+ capture_source,
my_devices: Vec::new(),
- filters: Filters::default(),
pcap_error: None,
dots_pulse: (".".to_string(), 0),
traffic_chart: TrafficChart::new(style, language),
- report_sort_type: ReportSortType::default(),
- host_sort_type: SortType::default(),
- service_sort_type: SortType::default(),
modal: None,
settings_page: None,
- last_opened_setting: SettingsPage::Notifications,
- running_page: RunningPage::Init,
+ running_page: None,
unread_notifications: 0,
search: SearchParameters::default(),
page_number: 1,
@@ -176,11 +157,9 @@ pub fn new(configs: Configs) -> Self {
asn: Arc::new(MmdbReader::from(&mmdb_asn, ASN_MMDB)),
},
timing_events: TimingEvents::default(),
- export_pcap: ExportPcap::default(),
thumbnail: false,
id: None,
host_data_states: HostDataStates::default(),
- import_pcap_path: String::new(),
}
}
@@ -278,51 +257,45 @@ pub fn update(&mut self, message: Message) -> Task {
}
}
Message::DeviceSelection(name) => self.set_device(&name),
- Message::IpVersionSelection(version, insert) => {
- if insert {
- self.filters.ip_versions.insert(version);
+ Message::SetCaptureSource(cs_pick) => {
+ self.conf.capture_source_picklist = cs_pick;
+ return if cs_pick == CaptureSourcePicklist::File {
+ Task::done(Message::SetPcapImport(self.conf.import_pcap_path.clone()))
} else {
- self.filters.ip_versions.remove(&version);
- }
+ Task::done(Message::DeviceSelection(
+ self.conf.device.device_name.clone(),
+ ))
+ };
}
- Message::ProtocolSelection(protocol, insert) => {
- if insert {
- self.filters.protocols.insert(protocol);
- } else {
- self.filters.protocols.remove(&protocol);
- }
+ Message::ToggleFilters => {
+ self.conf.filters.toggle();
}
- Message::AddressFilter(value) => {
- if let Some(collection) = AddressCollection::new(&value) {
- self.filters.address_collection = collection;
- }
- self.filters.address_str = value;
+ Message::BpfFilter(value) => {
+ self.conf.filters.set_bpf(value);
}
- Message::PortFilter(value) => {
- if let Some(collection) = PortCollection::new(&value) {
- self.filters.port_collection = collection;
- }
- self.filters.port_str = value;
- }
- Message::ChartSelection(unit) => self.traffic_chart.change_kind(unit),
+ Message::DataReprSelection(unit) => self.traffic_chart.change_kind(unit),
Message::ReportSortSelection(sort) => {
self.page_number = 1;
- self.report_sort_type = sort;
+ self.conf.report_sort_type = sort;
}
Message::OpenWebPage(web_page) => Self::open_web(&web_page),
- Message::Start => return self.start(),
+ Message::Start => {
+ if self.is_capture_source_consistent() {
+ return self.start();
+ }
+ }
Message::Reset => self.reset(),
Message::Style(style) => {
- self.configs.settings.style = style;
+ self.conf.settings.style = style;
self.traffic_chart.change_style(style);
}
Message::LoadStyle(path) => {
- self.configs.settings.style_path.clone_from(&path);
+ self.conf.settings.style_path.clone_from(&path);
if let Ok(palette) = Palette::from_file(path) {
let style = StyleType::Custom(ExtraStyles::CustomToml(
CustomPalette::from_palette(palette),
));
- self.configs.settings.style = style;
+ self.conf.settings.style = style;
self.traffic_chart.change_style(style);
}
}
@@ -336,22 +309,24 @@ pub fn update(&mut self, message: Message) -> Task {
Message::OpenSettings(settings_page) => {
if self.modal.is_none() {
self.settings_page = Some(settings_page);
+ self.conf.last_opened_setting = settings_page;
}
}
Message::OpenLastSettings => {
if self.modal.is_none() && self.settings_page.is_none() {
- self.settings_page = Some(self.last_opened_setting);
+ self.settings_page = Some(self.conf.last_opened_setting);
}
}
Message::CloseSettings => self.close_settings(),
Message::ChangeRunningPage(running_page) => {
- self.running_page = running_page;
+ self.running_page = Some(running_page);
+ self.conf.last_opened_page = running_page;
if running_page.eq(&RunningPage::Notifications) {
self.unread_notifications = 0;
}
}
Message::LanguageSelection(language) => {
- self.configs.settings.language = language;
+ self.conf.settings.language = language;
self.traffic_chart.change_language(language);
}
Message::UpdateNotificationSettings(notification, emit_sound) => {
@@ -359,7 +334,7 @@ pub fn update(&mut self, message: Message) -> Task {
}
Message::ChangeVolume(volume) => {
play(Sound::Pop, volume);
- self.configs.settings.notifications.volume = volume;
+ self.conf.settings.notifications.volume = volume;
}
Message::ClearAllNotifications => {
self.logged_notifications.0 = VecDeque::new();
@@ -385,7 +360,8 @@ pub fn update(&mut self, message: Message) -> Task {
self.host_data_states.update_states(¶meters);
self.page_number = 1;
- self.running_page = RunningPage::Inspect;
+ self.running_page = Some(RunningPage::Inspect);
+ self.conf.last_opened_page = RunningPage::Inspect;
self.search = parameters;
}
Message::UpdatePageNumber(increment) => {
@@ -398,7 +374,9 @@ pub fn update(&mut self, message: Message) -> Task {
}
}
Message::ArrowPressed(increment) => {
- if self.running_page.eq(&RunningPage::Inspect)
+ if self
+ .running_page
+ .is_some_and(|p| p.eq(&RunningPage::Inspect))
&& self.settings_page.is_none()
&& self.modal.is_none()
{
@@ -407,41 +385,40 @@ pub fn update(&mut self, message: Message) -> Task {
}
Message::WindowFocused => self.timing_events.focus_now(),
Message::GradientsSelection(gradient_type) => {
- self.configs.settings.color_gradient = gradient_type;
+ self.conf.settings.color_gradient = gradient_type;
}
Message::ChangeScaleFactor(slider_val) => {
let scale_factor_str = format!("{:.1}", 3.0_f64.powf(slider_val));
- self.configs.settings.scale_factor = scale_factor_str.parse().unwrap_or(1.0);
+ self.conf.settings.scale_factor = scale_factor_str.parse().unwrap_or(1.0);
}
Message::WindowMoved(x, y) => {
- let scale_factor = self.configs.settings.scale_factor;
+ let scale_factor = self.conf.settings.scale_factor;
let scaled = PositionTuple(x, y).scale_and_check(scale_factor);
if self.thumbnail {
- self.configs.window.thumbnail_position = scaled;
+ self.conf.window.thumbnail_position = scaled;
} else {
- self.configs.window.position = scaled;
+ self.conf.window.position = scaled;
}
}
Message::WindowResized(width, height) => {
if !self.thumbnail {
- let scale_factor = self.configs.settings.scale_factor;
- self.configs.window.size =
- SizeTuple(width, height).scale_and_check(scale_factor);
+ let scale_factor = self.conf.settings.scale_factor;
+ self.conf.window.size = SizeTuple(width, height).scale_and_check(scale_factor);
} else if !self.timing_events.was_just_thumbnail_enter() {
return Task::done(Message::ToggleThumbnail(true));
}
}
Message::CustomCountryDb(db) => {
- self.configs.settings.mmdb_country.clone_from(&db);
+ self.conf.settings.mmdb_country.clone_from(&db);
self.mmdb_readers.country = Arc::new(MmdbReader::from(&db, COUNTRY_MMDB));
}
Message::CustomAsnDb(db) => {
- self.configs.settings.mmdb_asn.clone_from(&db);
+ self.conf.settings.mmdb_asn.clone_from(&db);
self.mmdb_readers.asn = Arc::new(MmdbReader::from(&db, ASN_MMDB));
}
Message::QuitWrapper => return self.quit_wrapper(),
Message::Quit => {
- let _ = self.configs.clone().store();
+ let _ = self.conf.clone().store();
return window::close(self.id.unwrap_or_else(Id::unique));
}
Message::CopyIp(ip) => {
@@ -450,24 +427,24 @@ pub fn update(&mut self, message: Message) -> Task {
}
Message::OpenFile(old_file, file_info, consumer_message) => {
return Task::perform(
- Self::open_file(old_file, file_info, self.configs.settings.language),
+ Self::open_file(old_file, file_info, self.conf.settings.language),
consumer_message,
);
}
Message::HostSortSelection(sort_type) => {
- self.host_sort_type = sort_type;
+ self.conf.host_sort_type = sort_type;
}
Message::ServiceSortSelection(sort_type) => {
- self.service_sort_type = sort_type;
+ self.conf.service_sort_type = sort_type;
}
Message::ToggleExportPcap => {
- self.export_pcap.toggle();
+ self.conf.export_pcap.toggle();
}
Message::OutputPcapDir(path) => {
- self.export_pcap.set_directory(path);
+ self.conf.export_pcap.set_directory(path);
}
Message::OutputPcapFile(name) => {
- self.export_pcap.set_file_name(&name);
+ self.conf.export_pcap.set_file_name(&name);
}
Message::ToggleThumbnail(triggered_by_resize) => {
let window_id = self.id.unwrap_or_else(Id::unique);
@@ -476,9 +453,9 @@ pub fn update(&mut self, message: Message) -> Task {
self.traffic_chart.thumbnail = self.thumbnail;
return if self.thumbnail {
- let scale_factor = self.configs.settings.scale_factor;
+ let scale_factor = self.conf.settings.scale_factor;
let size = ConfigWindow::thumbnail_size(scale_factor).to_size();
- let position = self.configs.window.thumbnail_position;
+ let position = self.conf.window.thumbnail_position;
self.timing_events.thumbnail_enter_now();
Task::batch([
window::maximize(window_id, false),
@@ -488,7 +465,10 @@ pub fn update(&mut self, message: Message) -> Task {
window::change_level(window_id, Level::AlwaysOnTop),
])
} else {
- if self.running_page.eq(&RunningPage::Notifications) {
+ if self
+ .running_page
+ .is_some_and(|p| p.eq(&RunningPage::Notifications))
+ {
self.unread_notifications = 0;
}
let mut commands = vec![
@@ -496,8 +476,8 @@ pub fn update(&mut self, message: Message) -> Task {
window::change_level(window_id, Level::Normal),
];
if !triggered_by_resize {
- let size = self.configs.window.size.to_size();
- let position = self.configs.window.position.to_point();
+ let size = self.conf.window.size.to_size();
+ let position = self.conf.window.position.to_point();
commands.push(window::move_to(window_id, position));
commands.push(window::resize(window_id, size));
}
@@ -512,7 +492,7 @@ pub fn update(&mut self, message: Message) -> Task {
}
}
Message::CtrlTPressed => {
- if self.running_page.ne(&RunningPage::Init)
+ if self.running_page.is_some()
&& self.settings_page.is_none()
&& self.modal.is_none()
&& !self.timing_events.was_just_thumbnail_enter()
@@ -521,16 +501,16 @@ pub fn update(&mut self, message: Message) -> Task {
}
}
Message::ScaleFactorShortcut(increase) => {
- let scale_factor = self.configs.settings.scale_factor;
+ let scale_factor = self.conf.settings.scale_factor;
if !(scale_factor > 2.99 && increase || scale_factor < 0.31 && !increase) {
let delta = if increase { 0.1 } else { -0.1 };
- self.configs.settings.scale_factor += delta;
+ self.conf.settings.scale_factor += delta;
}
}
Message::SetNewerReleaseStatus(status) => self.newer_release_available = status,
Message::SetPcapImport(path) => {
if !path.is_empty() {
- self.import_pcap_path.clone_from(&path);
+ self.conf.import_pcap_path.clone_from(&path);
self.capture_source = CaptureSource::File(MyPcapImport::new(path));
}
}
@@ -565,13 +545,13 @@ pub fn update(&mut self, message: Message) -> Task {
Task::none()
}
- pub fn view(&self) -> Element {
- let ConfigSettings {
+ pub fn view(&self) -> Element<'_, Message, StyleType> {
+ let Settings {
style,
language,
color_gradient,
..
- } = self.configs.settings;
+ } = self.conf.settings;
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
@@ -581,10 +561,18 @@ pub fn view(&self) -> Element {
thumbnail_page(self)
} else {
match self.running_page {
- RunningPage::Init => initial_page(self),
- RunningPage::Overview => overview_page(self),
- RunningPage::Inspect => inspect_page(self),
- RunningPage::Notifications => notifications_page(self),
+ None => initial_page(self),
+ Some(running_page) => {
+ if let Some(waiting_page) = waiting_page(self) {
+ waiting_page
+ } else {
+ match running_page {
+ RunningPage::Overview => overview_page(self),
+ RunningPage::Inspect => inspect_page(self),
+ RunningPage::Notifications => notifications_page(self),
+ }
+ }
+ }
}
};
@@ -654,11 +642,11 @@ pub fn subscription(&self) -> Subscription {
}
pub fn theme(&self) -> StyleType {
- self.configs.settings.style
+ self.conf.settings.style
}
pub fn scale_factor(&self) -> f64 {
- self.configs.settings.scale_factor
+ self.conf.settings.scale_factor
}
/// Updates threshold if it hasn't been edited for a while
@@ -666,17 +654,13 @@ fn update_threshold(&mut self) {
// Ignore if just edited
if let Some(temp_threshold) = self.timing_events.threshold_adjust_expired_take() {
// Apply the temporary threshold to the actual config
- self.configs
- .settings
- .notifications
- .data_notification
- .threshold = temp_threshold.threshold;
- self.configs
+ self.conf.settings.notifications.data_notification.threshold = temp_threshold.threshold;
+ self.conf
.settings
.notifications
.data_notification
.byte_multiple = temp_threshold.byte_multiple;
- self.configs
+ self.conf
.settings
.notifications
.data_notification
@@ -686,29 +670,25 @@ fn update_threshold(&mut self) {
fn refresh_data(&mut self, mut msg: InfoTraffic, no_more_packets: bool) {
self.info_traffic.refresh(&mut msg);
- if self.info_traffic.tot_data_info.tot_packets() == 0 {
+ if self.info_traffic.tot_data_info.tot_data(DataRepr::Packets) == 0 {
return;
}
let emitted_notifications = notify_and_log(
&mut self.logged_notifications,
- self.configs.settings.notifications,
+ self.conf.settings.notifications,
&msg,
&self.favorite_hosts,
&self.capture_source,
);
- if self.thumbnail || self.running_page.ne(&RunningPage::Notifications) {
+ if self.thumbnail
+ || self
+ .running_page
+ .is_some_and(|p| p.ne(&RunningPage::Notifications))
+ {
self.unread_notifications += emitted_notifications;
}
self.traffic_chart.update_charts_data(&msg, no_more_packets);
- if let CaptureSource::Device(device) = &self.capture_source {
- let current_device_name = device.get_name().clone();
- // update ConfigDevice stored if different from last sniffed device
- let last_device_name_sniffed = self.configs.device.device_name.clone();
- if current_device_name.ne(&last_device_name_sniffed) {
- self.configs.device.device_name = current_device_name;
- }
- }
// update host dropdowns
self.host_data_states.update_states(&self.search);
}
@@ -739,18 +719,19 @@ fn start(&mut self) -> Task {
let current_device_name = &self.capture_source.get_name();
self.set_device(current_device_name);
}
- let pcap_path = self.export_pcap.full_path();
- let capture_context = CaptureContext::new(&self.capture_source, pcap_path.as_ref());
+ let pcap_path = self.conf.export_pcap.full_path();
+ let capture_context =
+ CaptureContext::new(&self.capture_source, pcap_path.as_ref(), &self.conf.filters);
self.pcap_error = capture_context.error().map(ToString::to_string);
- self.running_page = RunningPage::Overview;
+ self.running_page = Some(self.conf.last_opened_page);
if capture_context.error().is_none() {
// no pcap error
let curr_cap_id = self.current_capture_rx.0;
- let filters = self.filters.clone();
let mmdb_readers = self.mmdb_readers.clone();
self.capture_source
.set_link_type(capture_context.my_link_type());
+ self.capture_source.set_addresses();
let capture_source = self.capture_source.clone();
self.traffic_chart
.change_capture_source(matches!(capture_source, CaptureSource::Device(_)));
@@ -761,7 +742,6 @@ fn start(&mut self) -> Task {
parse_packets(
curr_cap_id,
capture_source,
- &filters,
&mmdb_readers,
capture_context,
&tx,
@@ -787,9 +767,9 @@ fn reset(&mut self) {
if let Some(rx) = &self.current_capture_rx.1 {
rx.close();
}
- let ConfigSettings {
+ let Settings {
style, language, ..
- } = self.configs.settings;
+ } = self.conf.settings;
// increment capture id to ignore pending messages from previous captures
self.current_capture_rx = (self.current_capture_rx.0 + 1, None);
self.info_traffic = InfoTraffic::default();
@@ -798,12 +778,9 @@ fn reset(&mut self) {
self.logged_notifications = (VecDeque::new(), 0);
self.pcap_error = None;
self.traffic_chart = TrafficChart::new(style, language);
- self.report_sort_type = ReportSortType::default();
- self.host_sort_type = SortType::default();
- self.service_sort_type = SortType::default();
self.modal = None;
self.settings_page = None;
- self.running_page = RunningPage::Init;
+ self.running_page = None;
self.unread_notifications = 0;
self.search = SearchParameters::default();
self.page_number = 1;
@@ -814,6 +791,7 @@ fn reset(&mut self) {
fn set_device(&mut self, name: &str) {
for my_dev in &self.my_devices {
if my_dev.get_name().eq(&name) {
+ self.conf.device.device_name = name.to_string();
self.capture_source = CaptureSource::Device(my_dev.clone());
break;
}
@@ -822,13 +800,8 @@ fn set_device(&mut self, name: &str) {
fn fetch_devices(&mut self) {
self.my_devices.clear();
+ self.capture_source.set_addresses();
for dev in Device::list().log_err(location!()).unwrap_or_default() {
- if matches!(&self.capture_source, CaptureSource::Device(_))
- && dev.name.eq(&self.capture_source.get_name())
- {
- // refresh active addresses
- self.capture_source.set_addresses(dev.addresses.clone());
- }
let my_dev = MyDevice::from_pcap_device(dev);
self.my_devices.push(my_dev);
}
@@ -854,10 +827,7 @@ fn add_or_remove_favorite(&mut self, host: &Host, add: bool) {
}
fn close_settings(&mut self) {
- if let Some(page) = self.settings_page {
- self.last_opened_setting = page;
- self.settings_page = None;
- }
+ self.settings_page = None;
}
/// Don't update adjustments to threshold immediately:
@@ -865,10 +835,10 @@ fn close_settings(&mut self) {
/// Threshold adjustments are saved in `self.timing_events.threshold_adjust` and then applied
/// after timeout
fn update_notifications_settings(&mut self, notification: Notification, emit_sound: bool) {
- let notifications = self.configs.settings.notifications;
+ let notifications = self.conf.settings.notifications;
let sound = match notification {
Notification::Data(DataNotification {
- chart_type,
+ data_repr,
threshold,
byte_multiple,
sound,
@@ -880,7 +850,7 @@ fn update_notifications_settings(&mut self, notification: Notification, emit_sou
|| temp_threshold.previous_threshold != previous_threshold
{
temp_threshold = DataNotification {
- chart_type,
+ data_repr,
threshold,
byte_multiple,
sound,
@@ -889,37 +859,29 @@ fn update_notifications_settings(&mut self, notification: Notification, emit_sou
self.timing_events.threshold_adjust_now(temp_threshold);
}
if threshold.is_some() != notifications.data_notification.threshold.is_some() {
- self.configs
- .settings
- .notifications
- .data_notification
- .threshold = threshold;
- self.configs
+ self.conf.settings.notifications.data_notification.threshold = threshold;
+ self.conf
.settings
.notifications
.data_notification
.byte_multiple = byte_multiple;
- self.configs
+ self.conf
.settings
.notifications
.data_notification
.previous_threshold = previous_threshold;
}
- self.configs.settings.notifications.data_notification.sound = sound;
- self.configs
- .settings
- .notifications
- .data_notification
- .chart_type = chart_type;
+ self.conf.settings.notifications.data_notification.sound = sound;
+ self.conf.settings.notifications.data_notification.data_repr = data_repr;
sound
}
Notification::Favorite(favorite_notification) => {
- self.configs.settings.notifications.favorite_notification = favorite_notification;
+ self.conf.settings.notifications.favorite_notification = favorite_notification;
favorite_notification.sound
}
};
if emit_sound {
- play(sound, self.configs.settings.notifications.volume);
+ play(sound, self.conf.settings.notifications.volume);
}
}
@@ -928,7 +890,7 @@ fn get_temp_threshold(&self) -> DataNotification {
if let Some(temp_threshold) = self.timing_events.temp_threshold() {
temp_threshold
} else {
- let notifications = self.configs.settings.notifications;
+ let notifications = self.conf.settings.notifications;
notifications.data_notification
}
}
@@ -937,26 +899,29 @@ fn switch_page(&mut self, next: bool) {
match (self.running_page, self.settings_page, self.modal.is_none()) {
(_, Some(current_setting), true) => {
// Settings opened
- if next {
- self.settings_page = Some(current_setting.next());
+ let new_setting = if next {
+ current_setting.next()
} else {
- self.settings_page = Some(current_setting.previous());
- }
+ current_setting.previous()
+ };
+ self.settings_page = Some(new_setting);
+ self.conf.last_opened_setting = new_setting;
}
- (
- RunningPage::Inspect | RunningPage::Notifications | RunningPage::Overview,
- None,
- true,
- ) => {
+ (Some(current_page), None, true) => {
// Running with no overlays
- if self.info_traffic.tot_data_info.tot_packets() > 0 {
- // Running with no overlays and some packets filtered
- self.running_page = if next {
- self.running_page.next()
+ if self.info_traffic.tot_data_info.tot_data(DataRepr::Packets) > 0 {
+ // Running with no overlays and some packets
+ let new_page = if next {
+ current_page.next()
} else {
- self.running_page.previous()
+ current_page.previous()
};
- if self.running_page.eq(&RunningPage::Notifications) {
+ self.running_page = Some(new_page);
+ self.conf.last_opened_page = new_page;
+ if self
+ .running_page
+ .is_some_and(|p| p.eq(&RunningPage::Notifications))
+ {
self.unread_notifications = 0;
}
}
@@ -966,13 +931,8 @@ fn switch_page(&mut self, next: bool) {
}
fn shortcut_return(&mut self) -> Task {
- if self.running_page.eq(&RunningPage::Init)
- && self.settings_page.is_none()
- && self.modal.is_none()
- {
- if self.filters.are_valid() {
- return Task::done(Message::Start);
- }
+ if self.running_page.is_none() && self.settings_page.is_none() && self.modal.is_none() {
+ return Task::done(Message::Start);
} else if self.modal.eq(&Some(MyModal::Reset)) {
return Task::done(Message::Reset);
} else if self.modal.eq(&Some(MyModal::Quit)) {
@@ -994,8 +954,9 @@ fn shortcut_esc(&mut self) -> Task {
// also called when the backspace shortcut is pressed
fn reset_button_pressed(&mut self) -> Task {
- if self.running_page.ne(&RunningPage::Init) {
- return if self.info_traffic.all_packets == 0 && self.settings_page.is_none() {
+ if self.running_page.is_some() {
+ let tot_packets = self.info_traffic.tot_data_info.tot_data(DataRepr::Packets);
+ return if tot_packets == 0 && self.settings_page.is_none() {
Task::done(Message::Reset)
} else {
Task::done(Message::ShowModal(MyModal::Reset))
@@ -1005,7 +966,8 @@ fn reset_button_pressed(&mut self) -> Task {
}
fn quit_wrapper(&mut self) -> Task {
- if self.running_page.eq(&RunningPage::Init) || self.info_traffic.all_packets == 0 {
+ let tot_packets = self.info_traffic.tot_data_info.tot_data(DataRepr::Packets);
+ if self.running_page.is_none() || tot_packets == 0 {
Task::done(Message::Quit)
} else if self.thumbnail {
// TODO: uncomment once issue #653 is fixed
@@ -1020,7 +982,9 @@ fn quit_wrapper(&mut self) -> Task {
}
fn shortcut_ctrl_d(&mut self) -> Task {
- if self.running_page.eq(&RunningPage::Notifications)
+ if self
+ .running_page
+ .is_some_and(|p| p.eq(&RunningPage::Notifications))
&& !self.logged_notifications.0.is_empty()
{
return Task::done(Message::ShowModal(MyModal::ClearAll));
@@ -1091,6 +1055,13 @@ fn register_sigint_handler() -> Task {
Task::run(rx, |()| Message::Quit)
}
+
+ pub fn is_capture_source_consistent(&self) -> bool {
+ self.conf.capture_source_picklist == CaptureSourcePicklist::Device
+ && matches!(self.capture_source, CaptureSource::Device(_))
+ || self.conf.capture_source_picklist == CaptureSourcePicklist::File
+ && matches!(self.capture_source, CaptureSource::File(_))
+ }
}
#[cfg(test)]
@@ -1104,15 +1075,22 @@ mod tests {
use serial_test::{parallel, serial};
- use crate::configs::types::config_window::{PositionTuple, SizeTuple};
use crate::countries::types::country::Country;
use crate::gui::components::types::my_modal::MyModal;
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::message::Message;
+ use crate::gui::types::settings::Settings;
use crate::gui::types::timing_events::TimingEvents;
+ use crate::networking::types::capture_context::CaptureSourcePicklist;
+ use crate::networking::types::config_device::ConfigDevice;
use crate::networking::types::data_info::DataInfo;
+ use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::Host;
use crate::networking::types::traffic_direction::TrafficDirection;
use crate::notifications::types::logged_notification::{
@@ -1122,216 +1100,120 @@ mod tests {
DataNotification, FavoriteNotification, Notification, Notifications,
};
use crate::notifications::types::sound::Sound;
- use crate::report::types::report_col::ReportCol;
use crate::report::types::sort_type::SortType;
- use crate::{
- ByteMultiple, ChartType, ConfigDevice, ConfigSettings, ConfigWindow, Configs, IpVersion,
- Language, Protocol, ReportSortType, RunningPage, Sniffer, StyleType,
- };
+ use crate::{ByteMultiple, ConfigWindow, Language, RunningPage, Sniffer, StyleType};
// helpful to clean up files generated from tests
impl Drop for Sniffer {
fn drop(&mut self) {
- let settings_path_str = ConfigSettings::test_path();
- let settings_path = Path::new(&settings_path_str);
- if settings_path.exists() {
- remove_file(ConfigSettings::test_path()).unwrap();
- }
-
- let device_path_str = ConfigDevice::test_path();
- let device_path = Path::new(&device_path_str);
- if device_path.exists() {
- remove_file(ConfigDevice::test_path()).unwrap();
- }
-
- let window_path_str = ConfigWindow::test_path();
- let window_path = Path::new(&window_path_str);
- if window_path.exists() {
- remove_file(ConfigWindow::test_path()).unwrap();
+ let conf_path_str = Conf::test_path();
+ let conf_path = Path::new(&conf_path_str);
+ if conf_path.exists() {
+ remove_file(Conf::test_path()).unwrap();
}
}
}
- #[test]
- #[parallel] // needed to not collide with other tests generating configs files
- fn test_correctly_update_ip_version() {
- let mut sniffer = Sniffer::new(Configs::default());
-
- assert_eq!(sniffer.filters.ip_versions, HashSet::from(IpVersion::ALL));
- sniffer.update(Message::IpVersionSelection(IpVersion::IPv6, true));
- assert_eq!(sniffer.filters.ip_versions, HashSet::from(IpVersion::ALL));
- sniffer.update(Message::IpVersionSelection(IpVersion::IPv4, false));
- assert_eq!(
- sniffer.filters.ip_versions,
- HashSet::from([IpVersion::IPv6])
- );
- sniffer.update(Message::IpVersionSelection(IpVersion::IPv6, false));
- assert_eq!(sniffer.filters.ip_versions, HashSet::new());
- }
-
- #[test]
- #[parallel] // needed to not collide with other tests generating configs files
- fn test_correctly_update_protocol() {
- let mut sniffer = Sniffer::new(Configs::default());
-
- assert_eq!(sniffer.filters.protocols, HashSet::from(Protocol::ALL));
- sniffer.update(Message::ProtocolSelection(Protocol::UDP, true));
- assert_eq!(sniffer.filters.protocols, HashSet::from(Protocol::ALL));
- sniffer.update(Message::ProtocolSelection(Protocol::UDP, false));
- assert_eq!(
- sniffer.filters.protocols,
- HashSet::from([Protocol::TCP, Protocol::ICMP, Protocol::ARP])
- );
- sniffer.update(Message::ProtocolSelection(Protocol::TCP, false));
- assert_eq!(
- sniffer.filters.protocols,
- HashSet::from([Protocol::ICMP, Protocol::ARP])
- );
- sniffer.update(Message::ProtocolSelection(Protocol::ICMP, false));
- assert_eq!(sniffer.filters.protocols, HashSet::from([Protocol::ARP]));
- sniffer.update(Message::ProtocolSelection(Protocol::ARP, false));
- assert_eq!(sniffer.filters.protocols, HashSet::new());
- sniffer.update(Message::ProtocolSelection(Protocol::UDP, true));
- assert_eq!(sniffer.filters.protocols, HashSet::from([Protocol::UDP]));
- }
-
#[test]
#[parallel] // needed to not collide with other tests generating configs files
fn test_correctly_update_chart_kind() {
- let mut sniffer = Sniffer::new(Configs::default());
+ let mut sniffer = Sniffer::new(Conf::default());
- assert_eq!(sniffer.traffic_chart.chart_type, ChartType::Bytes);
- sniffer.update(Message::ChartSelection(ChartType::Packets));
- assert_eq!(sniffer.traffic_chart.chart_type, ChartType::Packets);
- sniffer.update(Message::ChartSelection(ChartType::Packets));
- assert_eq!(sniffer.traffic_chart.chart_type, ChartType::Packets);
- sniffer.update(Message::ChartSelection(ChartType::Bytes));
- assert_eq!(sniffer.traffic_chart.chart_type, ChartType::Bytes);
+ assert_eq!(sniffer.traffic_chart.data_repr, DataRepr::Bytes);
+ sniffer.update(Message::DataReprSelection(DataRepr::Packets));
+ assert_eq!(sniffer.traffic_chart.data_repr, DataRepr::Packets);
+ sniffer.update(Message::DataReprSelection(DataRepr::Packets));
+ assert_eq!(sniffer.traffic_chart.data_repr, DataRepr::Packets);
+ sniffer.update(Message::DataReprSelection(DataRepr::Bytes));
+ assert_eq!(sniffer.traffic_chart.data_repr, DataRepr::Bytes);
+ sniffer.update(Message::DataReprSelection(DataRepr::Bits));
+ assert_eq!(sniffer.traffic_chart.data_repr, DataRepr::Bits);
}
#[test]
#[parallel] // needed to not collide with other tests generating configs files
fn test_correctly_update_report_sort_kind() {
- let mut sniffer = Sniffer::new(Configs::default());
+ let mut sniffer = Sniffer::new(Conf::default());
- let sort = ReportSortType {
- byte_sort: SortType::Neutral,
- packet_sort: SortType::Neutral,
- };
+ let sort = SortType::Neutral;
- assert_eq!(sniffer.report_sort_type, sort);
+ assert_eq!(sniffer.conf.report_sort_type, sort);
+ sniffer.update(Message::ReportSortSelection(sort.next_sort()));
+ assert_eq!(sniffer.conf.report_sort_type, SortType::Descending);
+ sniffer.update(Message::ReportSortSelection(sort.next_sort().next_sort()));
+ assert_eq!(sniffer.conf.report_sort_type, SortType::Ascending);
sniffer.update(Message::ReportSortSelection(
- sort.next_sort(&ReportCol::Bytes),
+ sort.next_sort().next_sort().next_sort(),
));
- assert_eq!(
- sniffer.report_sort_type,
- ReportSortType {
- byte_sort: SortType::Descending,
- packet_sort: SortType::Neutral
- }
- );
- sniffer.update(Message::ReportSortSelection(
- sort.next_sort(&ReportCol::Bytes)
- .next_sort(&ReportCol::Bytes),
- ));
- assert_eq!(
- sniffer.report_sort_type,
- ReportSortType {
- byte_sort: SortType::Ascending,
- packet_sort: SortType::Neutral
- }
- );
- sniffer.update(Message::ReportSortSelection(
- sort.next_sort(&ReportCol::Bytes)
- .next_sort(&ReportCol::Packets),
- ));
- assert_eq!(
- sniffer.report_sort_type,
- ReportSortType {
- byte_sort: SortType::Neutral,
- packet_sort: SortType::Descending
- }
- );
- sniffer.update(Message::ReportSortSelection(
- sort.next_sort(&ReportCol::Bytes)
- .next_sort(&ReportCol::Bytes)
- .next_sort(&ReportCol::Bytes),
- ));
- assert_eq!(
- sniffer.report_sort_type,
- ReportSortType {
- byte_sort: SortType::Neutral,
- packet_sort: SortType::Neutral
- }
- );
+ assert_eq!(sniffer.conf.report_sort_type, SortType::Neutral);
}
#[test]
#[parallel] // needed to not collide with other tests generating configs files
fn test_correctly_update_host_sort_kind() {
- let mut sniffer = Sniffer::new(Configs::default());
+ let mut sniffer = Sniffer::new(Conf::default());
let mut sort = SortType::Neutral;
- assert_eq!(sniffer.host_sort_type, sort);
+ assert_eq!(sniffer.conf.host_sort_type, sort);
sort = sort.next_sort();
sniffer.update(Message::HostSortSelection(sort));
- assert_eq!(sniffer.host_sort_type, SortType::Descending);
+ assert_eq!(sniffer.conf.host_sort_type, SortType::Descending);
sort = sort.next_sort();
sniffer.update(Message::HostSortSelection(sort));
- assert_eq!(sniffer.host_sort_type, SortType::Ascending);
+ assert_eq!(sniffer.conf.host_sort_type, SortType::Ascending);
sort = sort.next_sort();
sniffer.update(Message::HostSortSelection(sort));
- assert_eq!(sniffer.host_sort_type, SortType::Neutral);
+ assert_eq!(sniffer.conf.host_sort_type, SortType::Neutral);
}
#[test]
#[parallel] // needed to not collide with other tests generating configs files
fn test_correctly_update_service_sort_kind() {
- let mut sniffer = Sniffer::new(Configs::default());
+ let mut sniffer = Sniffer::new(Conf::default());
let mut sort = SortType::Neutral;
- assert_eq!(sniffer.service_sort_type, sort);
+ assert_eq!(sniffer.conf.service_sort_type, sort);
sort = sort.next_sort();
sniffer.update(Message::ServiceSortSelection(sort));
- assert_eq!(sniffer.service_sort_type, SortType::Descending);
+ assert_eq!(sniffer.conf.service_sort_type, SortType::Descending);
sort = sort.next_sort();
sniffer.update(Message::ServiceSortSelection(sort));
- assert_eq!(sniffer.service_sort_type, SortType::Ascending);
+ assert_eq!(sniffer.conf.service_sort_type, SortType::Ascending);
sort = sort.next_sort();
sniffer.update(Message::ServiceSortSelection(sort));
- assert_eq!(sniffer.service_sort_type, SortType::Neutral);
+ assert_eq!(sniffer.conf.service_sort_type, SortType::Neutral);
}
#[test]
#[parallel] // needed to not collide with other tests generating configs files
fn test_correctly_update_style() {
- let mut sniffer = Sniffer::new(Configs::default());
+ let mut sniffer = Sniffer::new(Conf::default());
sniffer.update(Message::Style(StyleType::MonAmour));
- assert_eq!(sniffer.configs.settings.style, StyleType::MonAmour);
+ assert_eq!(sniffer.conf.settings.style, StyleType::MonAmour);
sniffer.update(Message::Style(StyleType::Day));
- assert_eq!(sniffer.configs.settings.style, StyleType::Day);
+ assert_eq!(sniffer.conf.settings.style, StyleType::Day);
sniffer.update(Message::Style(StyleType::Night));
- assert_eq!(sniffer.configs.settings.style, StyleType::Night);
+ assert_eq!(sniffer.conf.settings.style, StyleType::Night);
sniffer.update(Message::Style(StyleType::DeepSea));
- assert_eq!(sniffer.configs.settings.style, StyleType::DeepSea);
+ assert_eq!(sniffer.conf.settings.style, StyleType::DeepSea);
sniffer.update(Message::Style(StyleType::DeepSea));
- assert_eq!(sniffer.configs.settings.style, StyleType::DeepSea);
+ assert_eq!(sniffer.conf.settings.style, StyleType::DeepSea);
}
#[test]
#[parallel] // needed to not collide with other tests generating configs files
fn test_dots_pulse_update() {
// every kind of message will the integer, but only Periodic will update the string
- let mut sniffer = Sniffer::new(Configs::default());
+ let mut sniffer = Sniffer::new(Conf::default());
assert_eq!(sniffer.dots_pulse, (".".to_string(), 0));
@@ -1357,7 +1239,7 @@ fn test_dots_pulse_update() {
#[test]
#[parallel] // needed to not collide with other tests generating configs files
fn test_modify_favorite_connections() {
- let mut sniffer = Sniffer::new(Configs::default());
+ let mut sniffer = Sniffer::new(Conf::default());
// remove 1
sniffer.update(Message::AddOrRemoveFavorite(
Host {
@@ -1538,16 +1420,22 @@ fn test_modify_favorite_connections() {
#[test]
#[parallel] // needed to not collide with other tests generating configs files
fn test_show_and_hide_modal_and_settings() {
- let mut sniffer = Sniffer::new(Configs::default());
+ let mut sniffer = Sniffer::new(Conf::default());
assert_eq!(sniffer.modal, None);
assert_eq!(sniffer.settings_page, None);
- assert_eq!(sniffer.last_opened_setting, SettingsPage::Notifications);
+ assert_eq!(
+ sniffer.conf.last_opened_setting,
+ SettingsPage::Notifications
+ );
// open settings
sniffer.update(Message::OpenLastSettings);
assert_eq!(sniffer.modal, None);
assert_eq!(sniffer.settings_page, Some(SettingsPage::Notifications));
- assert_eq!(sniffer.last_opened_setting, SettingsPage::Notifications);
+ assert_eq!(
+ sniffer.conf.last_opened_setting,
+ SettingsPage::Notifications
+ );
// switch settings page
sniffer.update(Message::OpenSettings(SettingsPage::Appearance));
assert_eq!(sniffer.modal, None);
@@ -1559,17 +1447,17 @@ fn test_show_and_hide_modal_and_settings() {
sniffer.update(Message::ShowModal(MyModal::Quit));
assert_eq!(sniffer.modal, None);
assert_eq!(sniffer.settings_page, Some(SettingsPage::General));
- assert_eq!(sniffer.last_opened_setting, SettingsPage::Notifications);
+ assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::General);
// close settings
sniffer.update(Message::CloseSettings);
assert_eq!(sniffer.modal, None);
assert_eq!(sniffer.settings_page, None);
- assert_eq!(sniffer.last_opened_setting, SettingsPage::General);
+ assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::General);
// reopen settings
sniffer.update(Message::OpenLastSettings);
assert_eq!(sniffer.modal, None);
assert_eq!(sniffer.settings_page, Some(SettingsPage::General));
- assert_eq!(sniffer.last_opened_setting, SettingsPage::General);
+ assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::General);
// switch settings page
sniffer.update(Message::OpenSettings(SettingsPage::Appearance));
assert_eq!(sniffer.modal, None);
@@ -1578,66 +1466,66 @@ fn test_show_and_hide_modal_and_settings() {
sniffer.update(Message::CloseSettings);
assert_eq!(sniffer.modal, None);
assert_eq!(sniffer.settings_page, None);
- assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance);
+ assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance);
// open clear all modal
sniffer.update(Message::ShowModal(MyModal::ClearAll));
assert_eq!(sniffer.modal, Some(MyModal::ClearAll));
assert_eq!(sniffer.settings_page, None);
- assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance);
+ assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance);
// try opening settings with clear all modal opened
sniffer.update(Message::OpenLastSettings);
assert_eq!(sniffer.modal, Some(MyModal::ClearAll));
assert_eq!(sniffer.settings_page, None);
- assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance);
+ assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance);
// try opening quit modal with clear all modal opened
sniffer.update(Message::ShowModal(MyModal::Quit));
assert_eq!(sniffer.modal, Some(MyModal::ClearAll));
assert_eq!(sniffer.settings_page, None);
- assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance);
+ assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance);
// close clear all modal
sniffer.update(Message::HideModal);
assert_eq!(sniffer.modal, None);
assert_eq!(sniffer.settings_page, None);
- assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance);
+ assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance);
// open quit modal
sniffer.update(Message::ShowModal(MyModal::Quit));
assert_eq!(sniffer.modal, Some(MyModal::Quit));
assert_eq!(sniffer.settings_page, None);
- assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance);
+ assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance);
// try opening settings with clear all modal opened
sniffer.update(Message::OpenLastSettings);
assert_eq!(sniffer.modal, Some(MyModal::Quit));
assert_eq!(sniffer.settings_page, None);
- assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance);
+ assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance);
// try opening clear all modal with quit modal opened
sniffer.update(Message::ShowModal(MyModal::ClearAll));
assert_eq!(sniffer.modal, Some(MyModal::Quit));
assert_eq!(sniffer.settings_page, None);
- assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance);
+ assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance);
// close quit modal
sniffer.update(Message::HideModal);
assert_eq!(sniffer.modal, None);
assert_eq!(sniffer.settings_page, None);
- assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance);
+ assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance);
}
#[test]
#[parallel] // needed to not collide with other tests generating configs files
fn test_correctly_update_language() {
- let mut sniffer = Sniffer::new(Configs::default());
+ let mut sniffer = Sniffer::new(Conf::default());
- assert_eq!(sniffer.configs.settings.language, Language::EN);
+ assert_eq!(sniffer.conf.settings.language, Language::EN);
assert_eq!(sniffer.traffic_chart.language, Language::EN);
sniffer.update(Message::LanguageSelection(Language::IT));
- assert_eq!(sniffer.configs.settings.language, Language::IT);
+ assert_eq!(sniffer.conf.settings.language, Language::IT);
assert_eq!(sniffer.traffic_chart.language, Language::IT);
sniffer.update(Message::LanguageSelection(Language::IT));
- assert_eq!(sniffer.configs.settings.language, Language::IT);
+ assert_eq!(sniffer.conf.settings.language, Language::IT);
assert_eq!(sniffer.traffic_chart.language, Language::IT);
sniffer.update(Message::LanguageSelection(Language::ZH));
- assert_eq!(sniffer.configs.settings.language, Language::ZH);
+ assert_eq!(sniffer.conf.settings.language, Language::ZH);
assert_eq!(sniffer.traffic_chart.language, Language::ZH);
}
@@ -1659,10 +1547,10 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
// Simulate an update to apply the settings
sniffer.update(Message::Periodic);
}
- let mut sniffer = Sniffer::new(Configs::default());
+ let mut sniffer = Sniffer::new(Conf::default());
let bytes_notification_init = DataNotification {
- chart_type: ChartType::Bytes,
+ data_repr: DataRepr::Bytes,
threshold: None,
byte_multiple: ByteMultiple::KB,
sound: Sound::Pop,
@@ -1670,7 +1558,7 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
};
let bytes_notification_toggled_on = DataNotification {
- chart_type: ChartType::Bytes,
+ data_repr: DataRepr::Bytes,
threshold: Some(800_000),
byte_multiple: ByteMultiple::GB,
sound: Sound::Pop,
@@ -1678,7 +1566,7 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
};
let bytes_notification_adjusted_threshold_sound_off = DataNotification {
- chart_type: ChartType::Bytes,
+ data_repr: DataRepr::Bytes,
threshold: Some(3),
byte_multiple: ByteMultiple::KB,
sound: Sound::None,
@@ -1686,7 +1574,7 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
};
let bytes_notification_sound_off_only = DataNotification {
- chart_type: ChartType::Bytes,
+ data_repr: DataRepr::Bytes,
threshold: Some(800_000),
byte_multiple: ByteMultiple::GB,
sound: Sound::None,
@@ -1704,36 +1592,36 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
};
// initial default state
- assert_eq!(sniffer.configs.settings.notifications.volume, 60);
- assert_eq!(sniffer.configs.settings.notifications.volume, 60);
+ assert_eq!(sniffer.conf.settings.notifications.volume, 60);
+ assert_eq!(sniffer.conf.settings.notifications.volume, 60);
assert_eq!(
- sniffer.configs.settings.notifications.data_notification,
+ sniffer.conf.settings.notifications.data_notification,
bytes_notification_init
);
assert_eq!(
- sniffer.configs.settings.notifications.favorite_notification,
+ sniffer.conf.settings.notifications.favorite_notification,
fav_notification_init
);
// change volume
sniffer.update(Message::ChangeVolume(95));
- assert_eq!(sniffer.configs.settings.notifications.volume, 95);
+ assert_eq!(sniffer.conf.settings.notifications.volume, 95);
assert_eq!(
- sniffer.configs.settings.notifications.data_notification,
+ sniffer.conf.settings.notifications.data_notification,
bytes_notification_init,
);
assert_eq!(
- sniffer.configs.settings.notifications.favorite_notification,
+ sniffer.conf.settings.notifications.favorite_notification,
fav_notification_init,
);
assert_eq!(
- sniffer.configs.settings.notifications.data_notification,
+ sniffer.conf.settings.notifications.data_notification,
bytes_notification_init
);
assert_eq!(
- sniffer.configs.settings.notifications.favorite_notification,
+ sniffer.conf.settings.notifications.favorite_notification,
fav_notification_init
);
@@ -1745,7 +1633,7 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
// Verify that toggling threshold is applied immediately
assert_eq!(
- sniffer.configs.settings.notifications.data_notification,
+ sniffer.conf.settings.notifications.data_notification,
bytes_notification_toggled_on,
);
@@ -1757,19 +1645,19 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
// Verify adjusted threshold is not applied before timeout expires,
// and rest is applied immediately
assert_eq!(
- sniffer.configs.settings.notifications.data_notification,
+ sniffer.conf.settings.notifications.data_notification,
bytes_notification_sound_off_only,
);
expire_notifications_timeout(&mut sniffer);
- assert_eq!(sniffer.configs.settings.notifications.volume, 95);
+ assert_eq!(sniffer.conf.settings.notifications.volume, 95);
assert_eq!(
- sniffer.configs.settings.notifications.data_notification,
+ sniffer.conf.settings.notifications.data_notification,
bytes_notification_adjusted_threshold_sound_off
);
assert_eq!(
- sniffer.configs.settings.notifications.favorite_notification,
+ sniffer.conf.settings.notifications.favorite_notification,
fav_notification_init,
);
@@ -1782,18 +1670,18 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
// Verify threshold is not applied before timeout expires,
// and rest is applied immediately
assert_eq!(
- sniffer.configs.settings.notifications.favorite_notification,
+ sniffer.conf.settings.notifications.favorite_notification,
fav_notification_new,
);
// And the rest is intact
- assert_eq!(sniffer.configs.settings.notifications.volume, 95);
+ assert_eq!(sniffer.conf.settings.notifications.volume, 95);
assert_eq!(
- sniffer.configs.settings.notifications.data_notification,
+ sniffer.conf.settings.notifications.data_notification,
bytes_notification_adjusted_threshold_sound_off
);
assert_eq!(
- sniffer.configs.settings.notifications.favorite_notification,
+ sniffer.conf.settings.notifications.favorite_notification,
fav_notification_new
);
}
@@ -1801,12 +1689,12 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
#[test]
#[parallel] // needed to not collide with other tests generating configs files
fn test_clear_all_notifications() {
- let mut sniffer = Sniffer::new(Configs::default());
+ let mut sniffer = Sniffer::new(Conf::default());
sniffer.logged_notifications.0 =
VecDeque::from([LoggedNotification::DataThresholdExceeded(
DataThresholdExceeded {
id: 1,
- chart_type: ChartType::Packets,
+ data_repr: DataRepr::Packets,
threshold: 0,
data_info: DataInfo::default(),
timestamp: "".to_string(),
@@ -1828,40 +1716,53 @@ fn test_clear_all_notifications() {
#[test]
#[parallel] // needed to not collide with other tests generating configs files
fn test_correctly_switch_running_and_settings_pages() {
- let mut sniffer = Sniffer::new(Configs::default());
+ let mut sniffer = Sniffer::new(Conf::default());
// initial status
assert_eq!(sniffer.settings_page, None);
assert_eq!(sniffer.modal, None);
- assert_eq!(sniffer.running_page, RunningPage::Init);
+ assert!(sniffer.running_page.is_none());
// nothing changes
sniffer.update(Message::SwitchPage(true));
assert_eq!(sniffer.settings_page, None);
+ assert_eq!(
+ sniffer.conf.last_opened_setting,
+ SettingsPage::Notifications
+ );
assert_eq!(sniffer.modal, None);
- assert_eq!(sniffer.running_page, RunningPage::Init);
+ assert!(sniffer.running_page.is_none());
// switch settings
sniffer.update(Message::OpenLastSettings);
assert_eq!(sniffer.settings_page, Some(SettingsPage::Notifications));
- assert_eq!(sniffer.running_page, RunningPage::Init);
+ assert_eq!(
+ sniffer.conf.last_opened_setting,
+ SettingsPage::Notifications
+ );
+ assert!(sniffer.running_page.is_none());
sniffer.update(Message::SwitchPage(false));
assert_eq!(sniffer.settings_page, Some(SettingsPage::General));
+ assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::General);
assert_eq!(sniffer.modal, None);
- assert_eq!(sniffer.running_page, RunningPage::Init);
+ assert!(sniffer.running_page.is_none());
sniffer.update(Message::SwitchPage(true));
assert_eq!(sniffer.settings_page, Some(SettingsPage::Notifications));
+ assert_eq!(
+ sniffer.conf.last_opened_setting,
+ SettingsPage::Notifications
+ );
assert_eq!(sniffer.modal, None);
- assert_eq!(sniffer.running_page, RunningPage::Init);
+ assert!(sniffer.running_page.is_none());
sniffer.update(Message::CloseSettings);
assert_eq!(sniffer.settings_page, None);
- assert_eq!(sniffer.running_page, RunningPage::Init);
+ assert!(sniffer.running_page.is_none());
// change state to running
- sniffer.running_page = RunningPage::Overview;
+ sniffer.running_page = Some(RunningPage::Overview);
assert_eq!(sniffer.settings_page, None);
assert_eq!(sniffer.modal, None);
- assert_eq!(sniffer.running_page, RunningPage::Overview);
+ assert_eq!(sniffer.running_page, Some(RunningPage::Overview));
// switch with closed setting and no packets received => nothing changes
sniffer.update(Message::SwitchPage(true));
- assert_eq!(sniffer.running_page, RunningPage::Overview);
+ assert_eq!(sniffer.running_page, Some(RunningPage::Overview));
assert_eq!(sniffer.settings_page, None);
// switch with closed setting and some packets received => change running page
sniffer
@@ -1869,56 +1770,45 @@ fn test_correctly_switch_running_and_settings_pages() {
.tot_data_info
.add_packet(0, TrafficDirection::Outgoing);
sniffer.update(Message::SwitchPage(true));
- assert_eq!(sniffer.running_page, RunningPage::Inspect);
+ assert_eq!(sniffer.running_page, Some(RunningPage::Inspect));
assert_eq!(sniffer.settings_page, None);
// switch with opened settings => change settings
sniffer.update(Message::OpenLastSettings);
- assert_eq!(sniffer.running_page, RunningPage::Inspect);
+ assert_eq!(sniffer.running_page, Some(RunningPage::Inspect));
assert_eq!(sniffer.settings_page, Some(SettingsPage::Notifications));
+ assert_eq!(
+ sniffer.conf.last_opened_setting,
+ SettingsPage::Notifications
+ );
sniffer.update(Message::SwitchPage(true));
- assert_eq!(sniffer.running_page, RunningPage::Inspect);
+ assert_eq!(sniffer.running_page, Some(RunningPage::Inspect));
assert_eq!(sniffer.settings_page, Some(SettingsPage::Appearance));
+ assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance);
// focus the window and try to switch => nothing changes
sniffer.update(Message::WindowFocused);
sniffer.update(Message::SwitchPage(true));
- assert_eq!(sniffer.running_page, RunningPage::Inspect);
+ assert_eq!(sniffer.running_page, Some(RunningPage::Inspect));
assert_eq!(sniffer.settings_page, Some(SettingsPage::Appearance));
}
#[test]
#[serial] // needed to not collide with other tests generating configs files
- fn test_config_settings() {
- let path_string = ConfigSettings::test_path();
+ fn test_conf() {
+ let path_string = Conf::test_path();
let path = Path::new(&path_string);
assert!(!path.exists());
- let mut sniffer = Sniffer::new(Configs::load());
+ let mut sniffer = Sniffer::new(Conf::load());
assert!(path.exists());
// check that the current settings are the default ones
- let settings_start = sniffer.configs.settings.clone();
- assert_eq!(
- settings_start,
- ConfigSettings {
- color_gradient: GradientType::None,
- language: Language::EN,
- scale_factor: 1.0,
- mmdb_country: "".to_string(),
- mmdb_asn: "".to_string(),
- style_path: "".to_string(),
- notifications: Notifications {
- volume: 60,
- data_notification: Default::default(),
- favorite_notification: Default::default()
- },
- style: StyleType::Custom(ExtraStyles::A11yDark)
- }
- );
+ let conf_start = sniffer.conf.clone();
+ assert_eq!(conf_start, Conf::default(),);
- // change some configs by sending messages
+ // change some conf by sending messages
sniffer.update(Message::GradientsSelection(GradientType::Wild));
sniffer.update(Message::LanguageSelection(Language::ZH));
sniffer.update(Message::ChangeScaleFactor(0.0));
@@ -1930,64 +1820,22 @@ fn test_config_settings() {
)));
sniffer.update(Message::Style(StyleType::Custom(ExtraStyles::DraculaDark)));
sniffer.update(Message::ChangeVolume(100));
-
- // quit the app by sending a CloseRequested message
- sniffer.update(Message::Quit);
-
- assert!(path.exists());
-
- // check that updated configs are inherited by a new sniffer instance
- let settings_end = Sniffer::new(Configs::load()).configs.settings.clone();
- assert_eq!(
- settings_end,
- ConfigSettings {
- color_gradient: GradientType::Wild,
- language: Language::ZH,
- scale_factor: 1.0,
- mmdb_country: "countrymmdb".to_string(),
- mmdb_asn: "asnmmdb".to_string(),
- style_path: format!(
- "{}/resources/themes/catppuccin.toml",
- env!("CARGO_MANIFEST_DIR")
- ),
- notifications: Notifications {
- volume: 100,
- data_notification: Default::default(),
- favorite_notification: Default::default()
- },
- style: StyleType::Custom(ExtraStyles::DraculaDark)
- }
- );
- }
-
- #[test]
- #[serial] // needed to not collide with other tests generating configs files
- fn test_config_window() {
- let path_string = ConfigWindow::test_path();
- let path = Path::new(&path_string);
-
- assert!(!path.exists());
-
- let mut sniffer = Sniffer::new(Configs::load());
-
- assert!(path.exists());
-
- // check that the current window properties are the default ones
- let window_start = sniffer.configs.window;
- assert_eq!(
- window_start,
- ConfigWindow {
- position: PositionTuple(0.0, 0.0),
- size: SizeTuple(1190.0, 670.0),
- thumbnail_position: PositionTuple(0.0, 0.0),
- }
- );
-
- // change window properties by sending messages
sniffer.update(Message::WindowMoved(-10.0, 555.0));
sniffer.update(Message::WindowResized(1000.0, 999.0));
sniffer.thumbnail = true;
sniffer.update(Message::WindowMoved(40.0, 40.0));
+ sniffer.update(Message::SetCaptureSource(CaptureSourcePicklist::File));
+ sniffer.update(Message::ToggleFilters);
+ sniffer.update(Message::BpfFilter("tcp or udp".to_string()));
+ sniffer.update(Message::ReportSortSelection(SortType::Ascending));
+ sniffer.update(Message::HostSortSelection(SortType::Descending));
+ sniffer.update(Message::ServiceSortSelection(SortType::Descending));
+ sniffer.update(Message::OpenSettings(SettingsPage::Appearance));
+ sniffer.update(Message::ToggleExportPcap);
+ sniffer.update(Message::OutputPcapFile("test.cap".to_string()));
+ sniffer.update(Message::OutputPcapDir("/".to_string()));
+ sniffer.update(Message::SetPcapImport("/test.pcap".to_string()));
+ sniffer.update(Message::ChangeRunningPage(RunningPage::Notifications));
// quit the app by sending a CloseRequested message
sniffer.update(Message::Quit);
@@ -1995,13 +1843,49 @@ fn test_config_window() {
assert!(path.exists());
// check that updated configs are inherited by a new sniffer instance
- let window_end = Sniffer::new(Configs::load()).configs.window.clone();
+ let conf_end = Sniffer::new(Conf::load()).conf.clone();
assert_eq!(
- window_end,
- ConfigWindow {
- position: PositionTuple(-10.0, 555.0),
- size: SizeTuple(1000.0, 999.0),
- thumbnail_position: PositionTuple(40.0, 40.0),
+ conf_end,
+ Conf {
+ settings: Settings {
+ color_gradient: GradientType::Wild,
+ language: Language::ZH,
+ scale_factor: 1.0,
+ mmdb_country: "countrymmdb".to_string(),
+ mmdb_asn: "asnmmdb".to_string(),
+ style_path: format!(
+ "{}/resources/themes/catppuccin.toml",
+ env!("CARGO_MANIFEST_DIR")
+ ),
+ notifications: Notifications {
+ volume: 100,
+ data_notification: Default::default(),
+ favorite_notification: Default::default()
+ },
+ style: StyleType::Custom(ExtraStyles::DraculaDark),
+ },
+ window: ConfigWindow {
+ position: PositionTuple(-10.0, 555.0),
+ size: SizeTuple(1000.0, 999.0),
+ thumbnail_position: PositionTuple(40.0, 40.0),
+ },
+ device: ConfigDevice::default(),
+ capture_source_picklist: CaptureSourcePicklist::File,
+ filters: Filters {
+ expanded: true,
+ bpf: "tcp or udp".to_string(),
+ },
+ report_sort_type: SortType::Ascending,
+ host_sort_type: SortType::Descending,
+ service_sort_type: SortType::Descending,
+ last_opened_setting: SettingsPage::Appearance,
+ last_opened_page: RunningPage::Notifications,
+ export_pcap: ExportPcap {
+ enabled: true,
+ file_name: "test.cap".to_string(),
+ directory: "/".to_string()
+ },
+ import_pcap_path: "/test.pcap".to_string(),
}
);
}
@@ -2009,95 +1893,95 @@ fn test_config_window() {
#[test]
#[parallel] // needed to not collide with other tests generating configs files
fn test_window_resized() {
- let mut sniffer = Sniffer::new(Configs::default());
+ let mut sniffer = Sniffer::new(Conf::default());
assert!(!sniffer.thumbnail);
- let factor = sniffer.configs.settings.scale_factor;
+ let factor = sniffer.conf.settings.scale_factor;
assert_eq!(factor, 1.0);
- assert_eq!(sniffer.configs.window.size, SizeTuple(1190.0, 670.0));
+ assert_eq!(sniffer.conf.window.size, SizeTuple(1190.0, 670.0));
assert_eq!(
ConfigWindow::thumbnail_size(factor),
SizeTuple(360.0, 222.0)
);
sniffer.update(Message::WindowResized(850.0, 600.0));
- assert_eq!(sniffer.configs.window.size, SizeTuple(850.0, 600.0));
+ assert_eq!(sniffer.conf.window.size, SizeTuple(850.0, 600.0));
sniffer.update(Message::ChangeScaleFactor(0.369));
- let factor = sniffer.configs.settings.scale_factor;
+ let factor = sniffer.conf.settings.scale_factor;
assert_eq!(factor, 1.5);
assert_eq!(
ConfigWindow::thumbnail_size(factor),
SizeTuple(540.0, 333.0)
);
sniffer.update(Message::WindowResized(1000.0, 800.0));
- assert_eq!(sniffer.configs.window.size, SizeTuple(1500.0, 1200.0));
+ assert_eq!(sniffer.conf.window.size, SizeTuple(1500.0, 1200.0));
sniffer.update(Message::ChangeScaleFactor(-0.631));
- let factor = sniffer.configs.settings.scale_factor;
+ let factor = sniffer.conf.settings.scale_factor;
assert_eq!(factor, 0.5);
assert_eq!(
ConfigWindow::thumbnail_size(factor),
SizeTuple(180.0, 111.0)
);
sniffer.update(Message::WindowResized(1000.0, 800.0));
- assert_eq!(sniffer.configs.window.size, SizeTuple(500.0, 400.0));
+ assert_eq!(sniffer.conf.window.size, SizeTuple(500.0, 400.0));
}
#[test]
#[parallel] // needed to not collide with other tests generating configs files
fn test_window_moved() {
- let mut sniffer = Sniffer::new(Configs::default());
+ let mut sniffer = Sniffer::new(Conf::default());
assert!(!sniffer.thumbnail);
- assert_eq!(sniffer.configs.settings.scale_factor, 1.0);
- assert_eq!(sniffer.configs.window.position, PositionTuple(0.0, 0.0));
+ assert_eq!(sniffer.conf.settings.scale_factor, 1.0);
+ assert_eq!(sniffer.conf.window.position, PositionTuple(0.0, 0.0));
assert_eq!(
- sniffer.configs.window.thumbnail_position,
+ sniffer.conf.window.thumbnail_position,
PositionTuple(0.0, 0.0)
);
sniffer.update(Message::WindowMoved(850.0, 600.0));
- assert_eq!(sniffer.configs.window.position, PositionTuple(850.0, 600.0));
+ assert_eq!(sniffer.conf.window.position, PositionTuple(850.0, 600.0));
assert_eq!(
- sniffer.configs.window.thumbnail_position,
+ sniffer.conf.window.thumbnail_position,
PositionTuple(0.0, 0.0)
);
sniffer.thumbnail = true;
sniffer.update(Message::WindowMoved(400.0, 600.0));
- assert_eq!(sniffer.configs.window.position, PositionTuple(850.0, 600.0));
+ assert_eq!(sniffer.conf.window.position, PositionTuple(850.0, 600.0));
assert_eq!(
- sniffer.configs.window.thumbnail_position,
+ sniffer.conf.window.thumbnail_position,
PositionTuple(400.0, 600.0)
);
sniffer.update(Message::ChangeScaleFactor(0.369));
- assert_eq!(sniffer.configs.settings.scale_factor, 1.5);
+ assert_eq!(sniffer.conf.settings.scale_factor, 1.5);
sniffer.update(Message::WindowMoved(20.0, 40.0));
- assert_eq!(sniffer.configs.window.position, PositionTuple(850.0, 600.0));
+ assert_eq!(sniffer.conf.window.position, PositionTuple(850.0, 600.0));
assert_eq!(
- sniffer.configs.window.thumbnail_position,
+ sniffer.conf.window.thumbnail_position,
PositionTuple(30.0, 60.0)
);
sniffer.thumbnail = false;
sniffer.update(Message::WindowMoved(-20.0, 300.0));
- assert_eq!(sniffer.configs.window.position, PositionTuple(-30.0, 450.0));
+ assert_eq!(sniffer.conf.window.position, PositionTuple(-30.0, 450.0));
assert_eq!(
- sniffer.configs.window.thumbnail_position,
+ sniffer.conf.window.thumbnail_position,
PositionTuple(30.0, 60.0)
);
sniffer.update(Message::ChangeScaleFactor(-0.631));
- assert_eq!(sniffer.configs.settings.scale_factor, 0.5);
+ assert_eq!(sniffer.conf.settings.scale_factor, 0.5);
sniffer.update(Message::WindowMoved(500.0, -100.0));
- assert_eq!(sniffer.configs.window.position, PositionTuple(250.0, -50.0));
+ assert_eq!(sniffer.conf.window.position, PositionTuple(250.0, -50.0));
assert_eq!(
- sniffer.configs.window.thumbnail_position,
+ sniffer.conf.window.thumbnail_position,
PositionTuple(30.0, 60.0)
);
sniffer.thumbnail = true;
sniffer.update(Message::WindowMoved(-2.0, -34.0));
- assert_eq!(sniffer.configs.window.position, PositionTuple(250.0, -50.0));
+ assert_eq!(sniffer.conf.window.position, PositionTuple(250.0, -50.0));
assert_eq!(
- sniffer.configs.window.thumbnail_position,
+ sniffer.conf.window.thumbnail_position,
PositionTuple(-1.0, -17.0)
);
}
@@ -2105,7 +1989,7 @@ fn test_window_moved() {
#[test]
#[parallel] // needed to not collide with other tests generating configs files
fn test_toggle_thumbnail() {
- let mut sniffer = Sniffer::new(Configs::default());
+ let mut sniffer = Sniffer::new(Conf::default());
assert!(!sniffer.thumbnail);
assert!(!sniffer.traffic_chart.thumbnail);
@@ -2132,21 +2016,21 @@ fn test_toggle_thumbnail() {
#[test]
#[parallel] // needed to not collide with other tests generating configs files
fn test_scale_factor_shortcut() {
- let mut sniffer = Sniffer::new(Configs::default());
- assert_eq!(sniffer.configs.settings.scale_factor, 1.0);
+ let mut sniffer = Sniffer::new(Conf::default());
+ assert_eq!(sniffer.conf.settings.scale_factor, 1.0);
sniffer.update(Message::ScaleFactorShortcut(true));
- assert_eq!(sniffer.configs.settings.scale_factor, 1.1);
+ assert_eq!(sniffer.conf.settings.scale_factor, 1.1);
sniffer.update(Message::ScaleFactorShortcut(false));
- assert_eq!(sniffer.configs.settings.scale_factor, 1.0);
+ assert_eq!(sniffer.conf.settings.scale_factor, 1.0);
sniffer.update(Message::ScaleFactorShortcut(false));
- assert_eq!(sniffer.configs.settings.scale_factor, 0.9);
+ assert_eq!(sniffer.conf.settings.scale_factor, 0.9);
for _ in 0..100 {
sniffer.update(Message::ScaleFactorShortcut(true));
}
assert_eq!(
- format!("{:.2}", sniffer.configs.settings.scale_factor),
+ format!("{:.2}", sniffer.conf.settings.scale_factor),
"3.00".to_string()
);
@@ -2154,7 +2038,7 @@ fn test_scale_factor_shortcut() {
sniffer.update(Message::ScaleFactorShortcut(false));
}
assert_eq!(
- format!("{:.2}", sniffer.configs.settings.scale_factor),
+ format!("{:.2}", sniffer.conf.settings.scale_factor),
"0.30".to_string()
);
}
diff --git a/src/gui/styles/button.rs b/src/gui/styles/button.rs
index ee75d3fb..899e19e3 100644
--- a/src/gui/styles/button.rs
+++ b/src/gui/styles/button.rs
@@ -64,7 +64,9 @@ fn active(&self, style: &StyleType) -> Style {
radius: match self {
ButtonType::Neutral => 0.0.into(),
ButtonType::TabActive | ButtonType::TabInactive => Radius::new(0).bottom(30),
- ButtonType::BorderedRound | ButtonType::BorderedRoundSelected => 12.0.into(),
+ ButtonType::BorderedRound
+ | ButtonType::BorderedRoundSelected
+ | ButtonType::Gradient(_) => 12.0.into(),
ButtonType::Starred | ButtonType::NotStarred => 100.0.into(),
_ => BORDER_BUTTON_RADIUS.into(),
},
@@ -154,7 +156,9 @@ fn hovered(&self, style: &StyleType) -> Style {
radius: match self {
ButtonType::Neutral => 0.0.into(),
ButtonType::TabActive | ButtonType::TabInactive => Radius::new(0).bottom(30),
- ButtonType::BorderedRound | ButtonType::BorderedRoundSelected => 12.0.into(),
+ ButtonType::BorderedRound
+ | ButtonType::BorderedRoundSelected
+ | ButtonType::Gradient(_) => 12.0.into(),
ButtonType::Starred | ButtonType::NotStarred => 100.0.into(),
_ => BORDER_BUTTON_RADIUS.into(),
},
@@ -208,7 +212,7 @@ fn disabled(&self, style: &StyleType) -> Style {
_ => Background::Color(ext.buttons_color),
}),
border: Border {
- radius: BORDER_BUTTON_RADIUS.into(),
+ radius: 12.0.into(),
width: BORDER_WIDTH,
color: Color {
a: ext.alpha_chart_badge,
@@ -216,7 +220,7 @@ fn disabled(&self, style: &StyleType) -> Style {
},
},
text_color: Color {
- a: ext.alpha_chart_badge,
+ a: 0.5,
..colors.text_headers
},
shadow: Shadow::default(),
diff --git a/src/gui/styles/donut.rs b/src/gui/styles/donut.rs
index 1cb768f8..a63b0251 100644
--- a/src/gui/styles/donut.rs
+++ b/src/gui/styles/donut.rs
@@ -25,8 +25,7 @@ fn active(&self, style: &StyleType) -> Style {
incoming: colors.secondary,
outgoing: colors.outgoing,
text_color: colors.text_body,
- filtered_out: ext.buttons_color,
- dropped: ext.red_alert_color,
+ dropped: ext.buttons_color,
}
}
}
@@ -48,7 +47,6 @@ pub struct Style {
pub(crate) text_color: Color,
pub(crate) incoming: Color,
pub(crate) outgoing: Color,
- pub(crate) filtered_out: Color,
pub(crate) dropped: Color,
}
diff --git a/src/gui/styles/rule.rs b/src/gui/styles/rule.rs
index d44f7cea..f197c6fb 100644
--- a/src/gui/styles/rule.rs
+++ b/src/gui/styles/rule.rs
@@ -13,7 +13,6 @@ pub enum RuleType {
PaletteColor(Color, u16),
Incoming,
Outgoing,
- FilteredOut,
Dropped,
}
@@ -26,8 +25,7 @@ fn appearance(&self, style: &StyleType) -> Style {
RuleType::Incoming => colors.secondary,
RuleType::Outgoing => colors.outgoing,
RuleType::PaletteColor(color, _) => *color,
- RuleType::Dropped => ext.red_alert_color,
- RuleType::FilteredOut => ext.buttons_color,
+ RuleType::Dropped => ext.buttons_color,
RuleType::Standard => Color {
a: ext.alpha_round_borders,
..ext.buttons_color
diff --git a/src/gui/styles/text_input.rs b/src/gui/styles/text_input.rs
index 0f12efc4..63d0d5b8 100644
--- a/src/gui/styles/text_input.rs
+++ b/src/gui/styles/text_input.rs
@@ -13,7 +13,7 @@ pub enum TextInputType {
#[default]
Standard,
Badge,
- Error,
+ // Error,
}
const TEXT_INPUT_BORDER_RADIUS: f32 = 5.0;
@@ -25,7 +25,8 @@ fn active(&self, style: &StyleType) -> Style {
Style {
background: Background::Color(match self {
TextInputType::Badge => Color::TRANSPARENT,
- _ => Color {
+ // TextInputType::Error |
+ TextInputType::Standard => Color {
a: ext.alpha_round_borders,
..ext.buttons_color
},
@@ -36,7 +37,7 @@ fn active(&self, style: &StyleType) -> Style {
color: match self {
TextInputType::Badge => Color::TRANSPARENT,
TextInputType::Standard => ext.buttons_color,
- TextInputType::Error => ext.red_alert_color,
+ // TextInputType::Error => ext.red_alert_color,
},
},
icon: Color {
@@ -51,17 +52,17 @@ fn active(&self, style: &StyleType) -> Style {
fn focused(&self, style: &StyleType) -> Style {
let colors = style.get_palette();
- let ext = style.get_extension();
+ // let ext = style.get_extension();
let is_nightly = style.get_extension().is_nightly;
Style {
background: Background::Color(colors.primary),
border: Border {
radius: TEXT_INPUT_BORDER_RADIUS.into(),
width: BORDER_WIDTH,
- color: match self {
- TextInputType::Error => ext.red_alert_color,
- _ => colors.secondary,
- },
+ color: colors.secondary, // match self {
+ // TextInputType::Error => ext.red_alert_color,
+ // _ => colors.secondary,
+ // },
},
icon: Color {
a: if is_nightly { 0.2 } else { 0.7 },
@@ -114,15 +115,16 @@ fn hovered(&self, style: &StyleType) -> Style {
Style {
background: Background::Color(match self {
TextInputType::Badge => Color::TRANSPARENT,
- _ => ext.buttons_color,
+ // TextInputType::Error |
+ TextInputType::Standard => ext.buttons_color,
}),
border: Border {
radius: TEXT_INPUT_BORDER_RADIUS.into(),
width: BORDER_WIDTH,
- color: match self {
- TextInputType::Error => ext.red_alert_color,
- _ => colors.secondary,
- },
+ color: colors.secondary, // match self {
+ // TextInputType::Error => ext.red_alert_color,
+ // _ => colors.secondary,
+ // },
},
icon: Color {
a: if ext.is_nightly { 0.2 } else { 0.7 },
@@ -140,7 +142,8 @@ fn disabled(&self, style: &StyleType) -> Style {
Style {
background: Background::Color(match self {
TextInputType::Badge => Color::TRANSPARENT,
- _ => Color {
+ // TextInputType::Error |
+ TextInputType::Standard => Color {
a: ext.alpha_round_containers,
..ext.buttons_color
},
@@ -154,10 +157,10 @@ fn disabled(&self, style: &StyleType) -> Style {
a: ext.alpha_round_borders,
..ext.buttons_color
},
- TextInputType::Error => Color {
- a: ext.alpha_round_borders,
- ..ext.red_alert_color
- },
+ // TextInputType::Error => Color {
+ // a: ext.alpha_round_borders,
+ // ..ext.red_alert_color
+ // },
},
},
icon: Color {
diff --git a/src/gui/types/conf.rs b/src/gui/types/conf.rs
new file mode 100644
index 00000000..34417cc8
--- /dev/null
+++ b/src/gui/types/conf.rs
@@ -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 = 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::(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::test_path()).unwrap_or_else(|_| Conf::default())
+ }
+
+ pub fn store(self) -> Result<(), confy::ConfyError> {
+ confy::store_path(Conf::test_path(), self)
+ }
+ }
+}
diff --git a/src/configs/types/config_window.rs b/src/gui/types/config_window.rs
similarity index 71%
rename from src/configs/types/config_window.rs
rename to src/gui/types/config_window.rs
index 03bb74fc..fa855d3c 100644
--- a/src/configs/types/config_window.rs
+++ b/src/gui/types/config_window.rs
@@ -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::(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::test_path())
- .unwrap_or_else(|_| ConfigWindow::default())
- }
-
- pub fn store(self) -> Result<(), confy::ConfyError> {
- confy::store_path(ConfigWindow::test_path(), self)
- }
- }
-}
diff --git a/src/gui/types/export_pcap.rs b/src/gui/types/export_pcap.rs
index 93420d8b..dd463170 100644
--- a/src/gui/types/export_pcap.rs
+++ b/src/gui/types/export_pcap.rs
@@ -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 {
diff --git a/src/gui/types/filters.rs b/src/gui/types/filters.rs
new file mode 100644
index 00000000..e23e9f15
--- /dev/null
+++ b/src/gui/types/filters.rs
@@ -0,0 +1,87 @@
+use serde::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)]
+#[serde(default)]
+pub struct Filters {
+ pub(crate) expanded: bool,
+ pub(crate) bpf: String,
+}
+
+impl Filters {
+ pub fn toggle(&mut self) {
+ self.expanded = !self.expanded;
+ }
+
+ pub fn set_bpf(&mut self, bpf: String) {
+ self.bpf = bpf;
+ }
+
+ pub fn expanded(&self) -> bool {
+ self.expanded
+ }
+
+ pub fn bpf(&self) -> &str {
+ &self.bpf
+ }
+
+ pub fn is_some_filter_active(&self) -> bool {
+ self.expanded && !self.bpf.trim().is_empty()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_default() {
+ let filters = Filters::default();
+ assert_eq!(filters.expanded(), false);
+ assert_eq!(filters.bpf(), "");
+ }
+
+ #[test]
+ fn test_toggle() {
+ let mut filters = Filters::default();
+ assert_eq!(filters.expanded(), false);
+
+ filters.toggle();
+ assert_eq!(filters.expanded(), true);
+
+ filters.toggle();
+ assert_eq!(filters.expanded(), false);
+ }
+
+ #[test]
+ fn test_set_bpf() {
+ let mut filters = Filters::default();
+ assert_eq!(filters.bpf(), "");
+
+ filters.set_bpf("tcp port 80".to_string());
+ assert_eq!(filters.bpf(), "tcp port 80");
+
+ filters.set_bpf(" udp port 53 ".to_string());
+ assert_eq!(filters.bpf(), " udp port 53 ");
+ }
+
+ #[test]
+ fn test_is_some_filter_active() {
+ let mut filters = Filters::default();
+ assert_eq!(filters.is_some_filter_active(), false);
+
+ filters.toggle();
+ assert_eq!(filters.is_some_filter_active(), false);
+
+ filters.set_bpf("tcp port 80".to_string());
+ assert_eq!(filters.is_some_filter_active(), true);
+
+ filters.toggle();
+ assert_eq!(filters.is_some_filter_active(), false);
+
+ filters.toggle();
+ assert_eq!(filters.is_some_filter_active(), true);
+
+ filters.set_bpf(" \t \n ".to_string());
+ assert_eq!(filters.is_some_filter_active(), false);
+ }
+}
diff --git a/src/gui/types/message.rs b/src/gui/types/message.rs
index 847c2cb4..07689920 100644
--- a/src/gui/types/message.rs
+++ b/src/gui/types/message.rs
@@ -5,6 +5,8 @@
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::networking::types::capture_context::CaptureSourcePicklist;
+use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::{Host, HostMessage};
use crate::networking::types::info_traffic::InfoTraffic;
use crate::notifications::types::notifications::Notification;
@@ -12,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::{ChartType, IpVersion, Language, Protocol, ReportSortType, StyleType};
+use crate::{Language, StyleType};
#[derive(Debug, Clone)]
/// Messages types that permit reacting to application interactions/subscriptions
@@ -21,20 +23,18 @@ pub enum Message {
StartApp(Option),
/// Sent by the backend parsing packets; includes the capture id, new data, new hosts batched data, and whether an offline capture has finished
TickRun(usize, InfoTraffic, Vec, bool),
+ /// Capture source selected from the picklist
+ SetCaptureSource(CaptureSourcePicklist),
/// Select network device
DeviceSelection(String),
- /// Select IP filter
- IpVersionSelection(IpVersion, bool),
- /// Select protocol filter
- ProtocolSelection(Protocol, bool),
- /// Changed address filter
- AddressFilter(String),
- /// Changed port filter
- PortFilter(String),
- /// Select chart type to be displayed
- ChartSelection(ChartType),
+ /// Toggle BPF filter checkbox
+ ToggleFilters,
+ /// Change BPF filter string
+ BpfFilter(String),
+ /// 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)
diff --git a/src/gui/types/mod.rs b/src/gui/types/mod.rs
index 9bc7a238..f96fc44b 100644
--- a/src/gui/types/mod.rs
+++ b/src/gui/types/mod.rs
@@ -1,3 +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;
diff --git a/src/gui/types/settings.rs b/src/gui/types/settings.rs
new file mode 100644
index 00000000..d6b7aa33
--- /dev/null
+++ b/src/gui/types/settings.rs
@@ -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(),
+ }
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index 04588a65..e7066d6c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,34 +7,29 @@
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::chart_type::ChartType;
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;
use gui::styles::types::style_type::StyleType;
-use networking::types::byte_multiple::ByteMultiple;
+use networking::types::data_representation::ByteMultiple;
use networking::types::info_traffic::InfoTraffic;
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;
@@ -63,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)]
@@ -79,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![
@@ -115,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))
}
diff --git a/src/mmdb/asn.rs b/src/mmdb/asn.rs
index 23e341f9..e120d2ee 100644
--- a/src/mmdb/asn.rs
+++ b/src/mmdb/asn.rs
@@ -77,19 +77,19 @@ fn test_get_asn_with_custom_ipinfo_single_reader() {
assert!(matches!(reader, MmdbReader::Custom(_)));
// known IP
- let res = get_asn(&IpAddr::from([61, 8, 0, 0]), &reader);
- assert_eq!(res.code, "AS1221");
- assert_eq!(res.name, "Telstra Limited");
+ let res = get_asn(&IpAddr::from([185, 72, 2, 28]), &reader);
+ assert_eq!(res.code, "AS202583");
+ assert_eq!(res.name, "AVATEL TELECOM, SA");
// another known IP
- let res = get_asn(&IpAddr::from([206, 180, 34, 99]), &reader);
- assert_eq!(res.code, "AS63344");
- assert_eq!(res.name, "The Reynolds and Reynolds Company");
+ let res = get_asn(&IpAddr::from([89, 187, 198, 0]), &reader);
+ assert_eq!(res.code, "AS210367");
+ assert_eq!(res.name, "Krajska zdravotni, a.s.");
// known IPv6
- let res = get_asn(&IpAddr::from_str("2806:230:2057::").unwrap(), &reader);
- assert_eq!(res.code, "AS11888");
- assert_eq!(res.name, "Television Internacional, S.A. de C.V.");
+ let res = get_asn(&IpAddr::from_str("2408:8957:6280::").unwrap(), &reader);
+ assert_eq!(res.code, "AS17622");
+ assert_eq!(res.name, "China Unicom Guangzhou network");
// unknown IP
let res = get_asn(&IpAddr::from([127, 0, 0, 1]), &reader);
@@ -106,31 +106,29 @@ fn test_get_asn_with_custom_ipinfo_single_reader() {
#[test]
fn test_get_asn_with_custom_ipinfo_combined_reader() {
let reader_1 = MmdbReader::from(
- &String::from("resources/test/ipinfo_country_asn_sample.mmdb"),
+ &String::from("resources/test/ipinfo_lite_sample.mmdb"),
ASN_MMDB,
);
- let reader_2 = MmdbReader::from(
- &String::from("resources/test/ipinfo_country_asn_sample.mmdb"),
- &[],
- );
+ let reader_2 =
+ MmdbReader::from(&String::from("resources/test/ipinfo_lite_sample.mmdb"), &[]);
for reader in vec![reader_1, reader_2] {
assert!(matches!(reader, MmdbReader::Custom(_)));
// known IP
- let res = get_asn(&IpAddr::from([31, 171, 144, 141]), &reader);
- assert_eq!(res.code, "AS197742");
- assert_eq!(res.name, "IBB Energie AG");
+ let res = get_asn(&IpAddr::from([1, 0, 65, 1]), &reader);
+ assert_eq!(res.code, "AS18144");
+ assert_eq!(res.name, "Enecom,Inc.");
// another known IP
- let res = get_asn(&IpAddr::from([103, 112, 220, 111]), &reader);
- assert_eq!(res.code, "AS134077");
- assert_eq!(res.name, "Magik Pivot Company Limited");
+ let res = get_asn(&IpAddr::from([1, 6, 230, 0]), &reader);
+ assert_eq!(res.code, "AS4755");
+ assert_eq!(res.name, "TATA Communications formerly VSNL is Leading ISP");
// known IPv6
- let res = get_asn(&IpAddr::from_str("2a02:6ea0:f001::").unwrap(), &reader);
- assert_eq!(res.code, "AS60068");
- assert_eq!(res.name, "Datacamp Limited");
+ // let res = get_asn(&IpAddr::from_str("2a02:6ea0:f001::").unwrap(), &reader);
+ // assert_eq!(res.code, "AS60068");
+ // assert_eq!(res.name, "Datacamp Limited");
// unknown IP
let res = get_asn(&IpAddr::from([127, 0, 0, 1]), &reader);
diff --git a/src/mmdb/country.rs b/src/mmdb/country.rs
index c23e4d69..9e5cd9a0 100644
--- a/src/mmdb/country.rs
+++ b/src/mmdb/country.rs
@@ -63,11 +63,11 @@ fn test_get_country_with_default_reader() {
#[test]
fn test_get_country_with_custom_ipinfo_single_reader() {
let reader_1 = MmdbReader::from(
- &String::from("resources/test/ipinfo_country_sample.mmdb"),
+ &String::from("resources/test/ipinfo_location_sample.mmdb"),
COUNTRY_MMDB,
);
let reader_2 = MmdbReader::from(
- &String::from("resources/test/ipinfo_country_sample.mmdb"),
+ &String::from("resources/test/ipinfo_location_sample.mmdb"),
&[],
);
@@ -75,16 +75,16 @@ fn test_get_country_with_custom_ipinfo_single_reader() {
assert!(matches!(reader, MmdbReader::Custom(_)));
// known IP
- let res = get_country(&IpAddr::from([2, 2, 146, 0]), &reader);
- assert_eq!(res, Country::GB);
+ let res = get_country(&IpAddr::from([1, 0, 6, 99]), &reader);
+ assert_eq!(res, Country::AU);
// another known IP
- let res = get_country(&IpAddr::from([23, 193, 112, 81]), &reader);
- assert_eq!(res, Country::US);
+ let res = get_country(&IpAddr::from([1, 0, 8, 0]), &reader);
+ assert_eq!(res, Country::CN);
// known IPv6
- let res = get_country(&IpAddr::from_str("2a0e:1d80::").unwrap(), &reader);
- assert_eq!(res, Country::RO);
+ // let res = get_country(&IpAddr::from_str("2a0e:1d80::").unwrap(), &reader);
+ // assert_eq!(res, Country::RO);
// unknown IP
let res = get_country(&IpAddr::from([127, 0, 0, 1]), &reader);
@@ -99,28 +99,26 @@ fn test_get_country_with_custom_ipinfo_single_reader() {
#[test]
fn test_get_country_with_custom_ipinfo_combined_reader() {
let reader_1 = MmdbReader::from(
- &String::from("resources/test/ipinfo_country_asn_sample.mmdb"),
+ &String::from("resources/test/ipinfo_lite_sample.mmdb"),
COUNTRY_MMDB,
);
- let reader_2 = MmdbReader::from(
- &String::from("resources/test/ipinfo_country_asn_sample.mmdb"),
- &[],
- );
+ let reader_2 =
+ MmdbReader::from(&String::from("resources/test/ipinfo_lite_sample.mmdb"), &[]);
for reader in vec![reader_1, reader_2] {
assert!(matches!(reader, MmdbReader::Custom(_)));
// known IP
- let res = get_country(&IpAddr::from([31, 171, 144, 141]), &reader);
- assert_eq!(res, Country::IT);
+ let res = get_country(&IpAddr::from([1, 0, 65, 1]), &reader);
+ assert_eq!(res, Country::JP);
// another known IP
- let res = get_country(&IpAddr::from([103, 112, 220, 111]), &reader);
- assert_eq!(res, Country::TH);
+ let res = get_country(&IpAddr::from([1, 6, 230, 0]), &reader);
+ assert_eq!(res, Country::IN);
// known IPv6
- let res = get_country(&IpAddr::from_str("2a02:6ea0:f001::").unwrap(), &reader);
- assert_eq!(res, Country::AR);
+ // let res = get_country(&IpAddr::from_str("2a02:6ea0:f001::").unwrap(), &reader);
+ // assert_eq!(res, Country::AR);
// unknown IP
let res = get_country(&IpAddr::from([127, 0, 0, 1]), &reader);
diff --git a/src/mmdb/types/mmdb_country_entry.rs b/src/mmdb/types/mmdb_country_entry.rs
index fffa5b6e..279e0b2e 100644
--- a/src/mmdb/types/mmdb_country_entry.rs
+++ b/src/mmdb/types/mmdb_country_entry.rs
@@ -30,7 +30,9 @@ fn get_country(&self) -> Country {
Self::Standard(StandardCountryEntry {
country: Some(StandardCountryEntryInner { iso_code: Some(c) }),
})
- | Self::Ipinfo(IpinfoCountryEntry { country: Some(c) }) => Country::from_str(c),
+ | Self::Ipinfo(IpinfoCountryEntry {
+ country_code: Some(c),
+ }) => Country::from_str(c),
_ => Country::ZZ,
}
}
@@ -49,5 +51,5 @@ struct StandardCountryEntryInner<'a> {
#[derive(Deserialize)]
struct IpinfoCountryEntry<'a> {
- country: Option<&'a str>,
+ country_code: Option<&'a str>,
}
diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs
index 90037db6..a8120356 100644
--- a/src/networking/manage_packets.rs
+++ b/src/networking/manage_packets.rs
@@ -1,7 +1,9 @@
use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
-use etherparse::{EtherType, LaxPacketHeaders, LinkHeader, NetHeaders, TransportHeader};
+use etherparse::{
+ ArpHardwareId, EtherType, LaxPacketHeaders, LinkHeader, NetHeaders, TransportHeader,
+};
use pcap::Address;
use crate::networking::types::address_port_pair::AddressPortPair;
@@ -81,13 +83,28 @@ fn analyze_link_header(
mac_address2: &mut Option,
exchanged_bytes: &mut u128,
) {
- if let Some(LinkHeader::Ethernet2(header)) = link_header {
- *exchanged_bytes += 14;
- *mac_address1 = Some(mac_from_dec_to_hex(header.source));
- *mac_address2 = Some(mac_from_dec_to_hex(header.destination));
- } else {
- *mac_address1 = None;
- *mac_address2 = None;
+ match link_header {
+ Some(LinkHeader::Ethernet2(header)) => {
+ *exchanged_bytes += 14;
+ *mac_address1 = Some(mac_from_dec_to_hex(header.source));
+ *mac_address2 = Some(mac_from_dec_to_hex(header.destination));
+ }
+ Some(LinkHeader::LinuxSll(header)) => {
+ *exchanged_bytes += 16;
+ *mac_address1 = if header.sender_address_valid_length == 6
+ && header.arp_hrd_type == ArpHardwareId::ETHERNET
+ && let Ok(sender) = header.sender_address[0..6].try_into()
+ {
+ Some(mac_from_dec_to_hex(sender))
+ } else {
+ None
+ };
+ *mac_address2 = None;
+ }
+ None => {
+ *mac_address1 = None;
+ *mac_address2 = None;
+ }
}
}
@@ -151,7 +168,7 @@ fn analyze_network_header(
*arp_type = ArpType::from_etherparse(arp_packet.operation);
true
}
- _ => false,
+ None => false,
}
}
@@ -192,7 +209,7 @@ fn analyze_transport_header(
*icmp_type = IcmpTypeV6::from_etherparse(&icmpv6_header.icmp_type);
true
}
- _ => false,
+ None => false,
}
}
@@ -337,14 +354,15 @@ fn get_traffic_direction(
.collect();
// first let's handle TCP and UDP loopback
- if source_ip.is_loopback() && destination_ip.is_loopback() {
- if let (Some(sport), Some(dport)) = (source_port, dest_port) {
- return if sport > dport {
- TrafficDirection::Outgoing
- } else {
- TrafficDirection::Incoming
- };
- }
+ if source_ip.is_loopback()
+ && destination_ip.is_loopback()
+ && let (Some(sport), Some(dport)) = (source_port, dest_port)
+ {
+ return if sport > dport {
+ TrafficDirection::Outgoing
+ } else {
+ TrafficDirection::Incoming
+ };
}
// if interface_addresses is empty, check if the IP is a bogon (useful when importing pcap files)
@@ -1499,7 +1517,7 @@ fn test_get_service_different_tcp_udp() {
#[test]
fn test_get_service_not_applicable() {
- for p in Protocol::ALL {
+ for p in [Protocol::TCP, Protocol::UDP, Protocol::ICMP, Protocol::ARP] {
for d in [TrafficDirection::Incoming, TrafficDirection::Outgoing] {
for (p1, p2) in [(None, Some(443)), (None, None), (Some(443), None)] {
let key = AddressPortPair::new(
diff --git a/src/networking/parse_packets.rs b/src/networking/parse_packets.rs
index b18ce3a3..fb733279 100644
--- a/src/networking/parse_packets.rs
+++ b/src/networking/parse_packets.rs
@@ -14,7 +14,6 @@
use crate::networking::types::capture_context::{CaptureContext, CaptureSource};
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_host::DataInfoHost;
-use crate::networking::types::filters::Filters;
use crate::networking::types::host::{Host, HostMessage};
use crate::networking::types::icmp_type::IcmpType;
use crate::networking::types::info_traffic::InfoTraffic;
@@ -26,10 +25,8 @@
use crate::utils::types::timestamp::Timestamp;
use async_channel::Sender;
use dns_lookup::lookup_addr;
-use etherparse::err::ip::{HeaderError, LaxHeaderSliceError};
-use etherparse::err::{Layer, LenError};
-use etherparse::{LaxPacketHeaders, LenSource};
-use pcap::{Address, Device, Packet};
+use etherparse::{EtherType, LaxPacketHeaders};
+use pcap::{Address, Packet};
use std::collections::HashMap;
use std::net::IpAddr;
use std::sync::{Arc, Mutex};
@@ -40,7 +37,6 @@
pub fn parse_packets(
cap_id: usize,
mut cs: CaptureSource,
- filters: &Filters,
mmdb_readers: &MmdbReaders,
capture_context: CaptureContext,
tx: &Sender,
@@ -97,7 +93,7 @@ pub fn parse_packets(
}
}
Ok(packet) => {
- if let Ok(headers) = get_sniffable_headers(&packet, my_link_type) {
+ if let Some(headers) = get_sniffable_headers(&packet, my_link_type) {
#[allow(clippy::useless_conversion)]
let secs = i64::from(packet.header.ts.tv_sec);
#[allow(clippy::useless_conversion)]
@@ -137,148 +133,139 @@ pub fn parse_packets(
continue;
};
- let passed_filters = filters.matches(&packet_filters_fields);
- if passed_filters {
- // save this packet to PCAP file
- if let Some(file) = savefile.as_mut() {
- file.write(&packet);
- }
- // update the map
- let (traffic_direction, service) = modify_or_insert_in_map(
- &mut info_traffic_msg,
- &key,
- &cs,
- mac_addresses,
- icmp_type,
- arp_type,
- exchanged_bytes,
- );
+ // save this packet to PCAP file
+ if let Some(file) = savefile.as_mut() {
+ file.write(&packet);
+ }
+ // update the map
+ let (traffic_direction, service) = modify_or_insert_in_map(
+ &mut info_traffic_msg,
+ &key,
+ &cs,
+ mac_addresses,
+ icmp_type,
+ arp_type,
+ exchanged_bytes,
+ );
- info_traffic_msg
- .tot_data_info
- .add_packet(exchanged_bytes, traffic_direction);
+ info_traffic_msg
+ .tot_data_info
+ .add_packet(exchanged_bytes, traffic_direction);
- // check the rDNS status of this address and act accordingly
- let address_to_lookup = get_address_to_lookup(&key, traffic_direction);
- let mut r_dns_waiting_resolution = false;
- let mut resolutions_lock = resolutions_state.lock().unwrap();
- let r_dns_already_resolved = resolutions_lock
- .addresses_resolved
+ // check the rDNS status of this address and act accordingly
+ let address_to_lookup = get_address_to_lookup(&key, traffic_direction);
+ let mut r_dns_waiting_resolution = false;
+ let mut resolutions_lock = resolutions_state.lock().unwrap();
+ let r_dns_already_resolved = resolutions_lock
+ .addresses_resolved
+ .contains_key(&address_to_lookup);
+ if !r_dns_already_resolved {
+ r_dns_waiting_resolution = resolutions_lock
+ .addresses_waiting_resolution
.contains_key(&address_to_lookup);
- if !r_dns_already_resolved {
- r_dns_waiting_resolution = resolutions_lock
- .addresses_waiting_resolution
- .contains_key(&address_to_lookup);
- }
-
- match (r_dns_waiting_resolution, r_dns_already_resolved) {
- (false, false) => {
- // rDNS not requested yet (first occurrence of this address to lookup)
-
- // Add this address to the map of addresses waiting for a resolution
- // Useful to NOT perform again a rDNS lookup for this entry
- resolutions_lock.addresses_waiting_resolution.insert(
- address_to_lookup,
- DataInfo::new_with_first_packet(
- exchanged_bytes,
- traffic_direction,
- ),
- );
- drop(resolutions_lock);
-
- // launch new thread to resolve host name
- let key2 = key;
- let resolutions_state2 = resolutions_state.clone();
- let new_hosts_to_send2 = new_hosts_to_send.clone();
- let interface_addresses = cs.get_addresses().clone();
- let mmdb_readers_2 = mmdb_readers.clone();
- let tx2 = tx.clone();
- let _ = thread::Builder::new()
- .name("thread_reverse_dns_lookup".to_string())
- .spawn(move || {
- reverse_dns_lookup(
- &resolutions_state2,
- &new_hosts_to_send2,
- &key2,
- traffic_direction,
- &interface_addresses,
- &mmdb_readers_2,
- &tx2,
- );
- })
- .log_err(location!());
- }
- (true, false) => {
- // waiting for a previously requested rDNS resolution
- // update the corresponding waiting address data
- resolutions_lock
- .addresses_waiting_resolution
- .entry(address_to_lookup)
- .and_modify(|data_info| {
- data_info.add_packet(exchanged_bytes, traffic_direction);
- });
- drop(resolutions_lock);
- }
- (_, true) => {
- // rDNS already resolved
- // update the corresponding host's data info
- let host = resolutions_lock
- .addresses_resolved
- .get(&address_to_lookup)
- .unwrap_or(&Host::default())
- .clone();
- drop(resolutions_lock);
- info_traffic_msg
- .hosts
- .entry(host)
- .and_modify(|data_info_host| {
- data_info_host
- .data_info
- .add_packet(exchanged_bytes, traffic_direction);
- })
- .or_insert_with(|| {
- let my_interface_addresses = cs.get_addresses();
- let traffic_type = get_traffic_type(
- &address_to_lookup,
- my_interface_addresses,
- traffic_direction,
- );
- let is_loopback = address_to_lookup.is_loopback();
- let is_local = is_local_connection(
- &address_to_lookup,
- my_interface_addresses,
- );
- let is_bogon = is_bogon(&address_to_lookup);
- DataInfoHost {
- data_info: DataInfo::new_with_first_packet(
- exchanged_bytes,
- traffic_direction,
- ),
- is_favorite: false,
- is_loopback,
- is_local,
- is_bogon,
- traffic_type,
- }
- });
- }
- }
-
- //increment the packet count for the sniffed service
- info_traffic_msg
- .services
- .entry(service)
- .and_modify(|data_info| {
- data_info.add_packet(exchanged_bytes, traffic_direction);
- })
- .or_insert_with(|| {
- DataInfo::new_with_first_packet(exchanged_bytes, traffic_direction)
- });
}
- //increment number of sniffed packets and bytes
- info_traffic_msg.all_packets += 1;
- info_traffic_msg.all_bytes += exchanged_bytes;
+ match (r_dns_waiting_resolution, r_dns_already_resolved) {
+ (false, false) => {
+ // rDNS not requested yet (first occurrence of this address to lookup)
+
+ // Add this address to the map of addresses waiting for a resolution
+ // Useful to NOT perform again a rDNS lookup for this entry
+ resolutions_lock.addresses_waiting_resolution.insert(
+ address_to_lookup,
+ DataInfo::new_with_first_packet(exchanged_bytes, traffic_direction),
+ );
+ drop(resolutions_lock);
+
+ // launch new thread to resolve host name
+ let key2 = key;
+ let resolutions_state2 = resolutions_state.clone();
+ let new_hosts_to_send2 = new_hosts_to_send.clone();
+ let interface_addresses = cs.get_addresses().clone();
+ let mmdb_readers_2 = mmdb_readers.clone();
+ let tx2 = tx.clone();
+ let _ = thread::Builder::new()
+ .name("thread_reverse_dns_lookup".to_string())
+ .spawn(move || {
+ reverse_dns_lookup(
+ &resolutions_state2,
+ &new_hosts_to_send2,
+ &key2,
+ traffic_direction,
+ &interface_addresses,
+ &mmdb_readers_2,
+ &tx2,
+ );
+ })
+ .log_err(location!());
+ }
+ (true, false) => {
+ // waiting for a previously requested rDNS resolution
+ // update the corresponding waiting address data
+ resolutions_lock
+ .addresses_waiting_resolution
+ .entry(address_to_lookup)
+ .and_modify(|data_info| {
+ data_info.add_packet(exchanged_bytes, traffic_direction);
+ });
+ drop(resolutions_lock);
+ }
+ (_, true) => {
+ // rDNS already resolved
+ // update the corresponding host's data info
+ let host = resolutions_lock
+ .addresses_resolved
+ .get(&address_to_lookup)
+ .unwrap_or(&Host::default())
+ .clone();
+ drop(resolutions_lock);
+ info_traffic_msg
+ .hosts
+ .entry(host)
+ .and_modify(|data_info_host| {
+ data_info_host
+ .data_info
+ .add_packet(exchanged_bytes, traffic_direction);
+ })
+ .or_insert_with(|| {
+ let my_interface_addresses = cs.get_addresses();
+ let traffic_type = get_traffic_type(
+ &address_to_lookup,
+ my_interface_addresses,
+ traffic_direction,
+ );
+ let is_loopback = address_to_lookup.is_loopback();
+ let is_local = is_local_connection(
+ &address_to_lookup,
+ my_interface_addresses,
+ );
+ let is_bogon = is_bogon(&address_to_lookup);
+ DataInfoHost {
+ data_info: DataInfo::new_with_first_packet(
+ exchanged_bytes,
+ traffic_direction,
+ ),
+ is_favorite: false,
+ is_loopback,
+ is_local,
+ is_bogon,
+ traffic_type,
+ }
+ });
+ }
+ }
+
+ //increment the packet count for the sniffed service
+ info_traffic_msg
+ .services
+ .entry(service)
+ .and_modify(|data_info| {
+ data_info.add_packet(exchanged_bytes, traffic_direction);
+ })
+ .or_insert_with(|| {
+ DataInfo::new_with_first_packet(exchanged_bytes, traffic_direction)
+ });
+
// update dropped packets number
if let Ok(stats) = cap.stats() {
info_traffic_msg.dropped_packets = stats.dropped;
@@ -292,27 +279,23 @@ pub fn parse_packets(
fn get_sniffable_headers<'a>(
packet: &'a Packet,
my_link_type: MyLinkType,
-) -> Result, LaxHeaderSliceError> {
+) -> Option> {
match my_link_type {
MyLinkType::Ethernet(_) | MyLinkType::Unsupported(_) | MyLinkType::NotYetAssigned => {
- LaxPacketHeaders::from_ethernet(packet).map_err(LaxHeaderSliceError::Len)
+ LaxPacketHeaders::from_ethernet(packet).ok()
}
MyLinkType::RawIp(_) | MyLinkType::IPv4(_) | MyLinkType::IPv6(_) => {
- LaxPacketHeaders::from_ip(packet)
+ LaxPacketHeaders::from_ip(packet).ok()
}
+ MyLinkType::LinuxSll(_) => from_linux_sll(packet, true),
+ MyLinkType::LinuxSll2(_) => from_linux_sll(packet, false),
MyLinkType::Null(_) | MyLinkType::Loop(_) => from_null(packet),
}
}
-fn from_null(packet: &[u8]) -> Result {
+fn from_null(packet: &[u8]) -> Option> {
if packet.len() <= 4 {
- return Err(LaxHeaderSliceError::Len(LenError {
- required_len: 4,
- len: packet.len(),
- len_source: LenSource::Slice,
- layer: Layer::Ethernet2Header,
- layer_start_offset: 0,
- }));
+ return None;
}
let is_valid_af_inet = {
@@ -333,14 +316,31 @@ fn matches(value: u32) -> bool {
};
if is_valid_af_inet {
- LaxPacketHeaders::from_ip(&packet[4..])
+ LaxPacketHeaders::from_ip(&packet[4..]).ok()
} else {
- Err(LaxHeaderSliceError::Content(
- HeaderError::UnsupportedIpVersion { version_number: 0 },
- ))
+ None
}
}
+fn from_linux_sll(packet: &[u8], is_v1: bool) -> Option> {
+ let header_len = if is_v1 { 16 } else { 20 };
+ if packet.len() <= header_len {
+ return None;
+ }
+
+ let protocol_type = u16::from_be_bytes(if is_v1 {
+ [packet[14], packet[15]]
+ } else {
+ [packet[0], packet[1]]
+ });
+ let payload = &packet[header_len..];
+
+ Some(LaxPacketHeaders::from_ether_type(
+ EtherType(protocol_type),
+ payload,
+ ))
+}
+
fn reverse_dns_lookup(
resolutions_state: &Arc>,
new_hosts_to_send: &Arc>>,
@@ -442,12 +442,7 @@ fn maybe_send_tick_run_live(
new_hosts_to_send.lock().unwrap().drain(..).collect(),
false,
));
- for dev in Device::list().log_err(location!()).unwrap_or_default() {
- if dev.name.eq(&cs.get_name()) {
- cs.set_addresses(dev.addresses);
- break;
- }
- }
+ cs.set_addresses();
}
}
diff --git a/src/networking/types/bogon.rs b/src/networking/types/bogon.rs
index e54fe507..d21fa260 100644
--- a/src/networking/types/bogon.rs
+++ b/src/networking/types/bogon.rs
@@ -1,20 +1,20 @@
-use crate::networking::types::ip_collection::AddressCollection;
+use crate::networking::types::ip_collection::IpCollection;
use std::net::IpAddr;
pub struct Bogon {
- pub range: AddressCollection,
+ pub range: IpCollection,
pub description: &'static str,
}
// IPv4 bogons
static THIS_NETWORK: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("0.0.0.0-0.255.255.255").unwrap(),
+ range: IpCollection::new("0.0.0.0-0.255.255.255").unwrap(),
description: "\"this\" network",
});
static PRIVATE_USE: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new(
+ range: IpCollection::new(
"10.0.0.0-10.255.255.255, 172.16.0.0-172.31.255.255, 192.168.0.0-192.168.255.255",
)
.unwrap(),
@@ -22,112 +22,112 @@ pub struct Bogon {
});
static CARRIER_GRADE: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("100.64.0.0-100.127.255.255").unwrap(),
+ range: IpCollection::new("100.64.0.0-100.127.255.255").unwrap(),
description: "carrier-grade NAT",
});
static LOOPBACK: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("127.0.0.0-127.255.255.255").unwrap(),
+ range: IpCollection::new("127.0.0.0-127.255.255.255").unwrap(),
description: "loopback",
});
static LINK_LOCAL: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("169.254.0.0-169.254.255.255").unwrap(),
+ range: IpCollection::new("169.254.0.0-169.254.255.255").unwrap(),
description: "link local",
});
static IETF_PROTOCOL: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("192.0.0.0-192.0.0.255").unwrap(),
+ range: IpCollection::new("192.0.0.0-192.0.0.255").unwrap(),
description: "IETF protocol assignments",
});
static TEST_NET_1: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("192.0.2.0-192.0.2.255").unwrap(),
+ range: IpCollection::new("192.0.2.0-192.0.2.255").unwrap(),
description: "TEST-NET-1",
});
static NETWORK_INTERCONNECT: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("198.18.0.0-198.19.255.255").unwrap(),
+ range: IpCollection::new("198.18.0.0-198.19.255.255").unwrap(),
description: "network interconnect device benchmark testing",
});
static TEST_NET_2: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("198.51.100.0-198.51.100.255").unwrap(),
+ range: IpCollection::new("198.51.100.0-198.51.100.255").unwrap(),
description: "TEST-NET-2",
});
static TEST_NET_3: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("203.0.113.0-203.0.113.255").unwrap(),
+ range: IpCollection::new("203.0.113.0-203.0.113.255").unwrap(),
description: "TEST-NET-3",
});
static MULTICAST: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("224.0.0.0-239.255.255.255").unwrap(),
+ range: IpCollection::new("224.0.0.0-239.255.255.255").unwrap(),
description: "multicast",
});
static FUTURE_USE: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("240.0.0.0-255.255.255.255").unwrap(),
+ range: IpCollection::new("240.0.0.0-255.255.255.255").unwrap(),
description: "future use",
});
// IPv6 bogons
static NODE_SCOPE_UNSPECIFIED: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("::").unwrap(),
+ range: IpCollection::new("::").unwrap(),
description: "node-scope unicast unspecified",
});
static NODE_SCOPE_LOOPBACK: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("::1").unwrap(),
+ range: IpCollection::new("::1").unwrap(),
description: "node-scope unicast loopback",
});
static IPV4_MAPPED: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("::ffff:0.0.0.0-::ffff:255.255.255.255").unwrap(),
+ range: IpCollection::new("::ffff:0.0.0.0-::ffff:255.255.255.255").unwrap(),
description: "IPv4-mapped",
});
static IPV4_COMPATIBLE: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("::-::255.255.255.255").unwrap(),
+ range: IpCollection::new("::-::255.255.255.255").unwrap(),
description: "IPv4-compatible",
});
static REMOTELY_TRIGGERED: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("100::-100::ffff:ffff:ffff:ffff").unwrap(),
+ range: IpCollection::new("100::-100::ffff:ffff:ffff:ffff").unwrap(),
description: "remotely triggered black hole",
});
static ORCHID: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("2001:10::-2001:1f:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
+ range: IpCollection::new("2001:10::-2001:1f:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
description: "ORCHID",
});
static DOCUMENTATION_PREFIX: std::sync::LazyLock = std::sync::LazyLock::new(|| {
Bogon {
- range: AddressCollection::new("2001:db8::-2001:db8:ffff:ffff:ffff:ffff:ffff:ffff, 3fff::-3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff")
+ range: IpCollection::new("2001:db8::-2001:db8:ffff:ffff:ffff:ffff:ffff:ffff, 3fff::-3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff")
.unwrap(),
description: "documentation prefix",
}
});
static ULA: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
+ range: IpCollection::new("fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
description: "ULA",
});
static LINK_LOCAL_UNICAST: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
+ range: IpCollection::new("fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
description: "link-local unicast",
});
static SITE_LOCAL_UNICAST: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("fec0::-feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
+ range: IpCollection::new("fec0::-feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
description: "site-local unicast",
});
static MULTICAST_V6: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon {
- range: AddressCollection::new("ff00::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
+ range: IpCollection::new("ff00::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
description: "multicast v6",
});
diff --git a/src/networking/types/byte_multiple.rs b/src/networking/types/byte_multiple.rs
deleted file mode 100644
index 670d090a..00000000
--- a/src/networking/types/byte_multiple.rs
+++ /dev/null
@@ -1,225 +0,0 @@
-use std::fmt;
-
-use serde::{Deserialize, Serialize};
-
-/// Enum representing the possible observed values of IP protocol version.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
-pub enum ByteMultiple {
- /// A Byte
- B,
- /// 10^3 Bytes
- KB,
- /// 10^6 Bytes
- MB,
- /// 10^9 Bytes
- GB,
- /// 10^12 Bytes
- TB,
- /// 10^15 Bytes
- PB,
-}
-
-impl fmt::Display for ByteMultiple {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{self:?}")
- }
-}
-
-impl ByteMultiple {
- pub fn multiplier(self) -> u64 {
- match self {
- ByteMultiple::B => 1,
- ByteMultiple::KB => 1_000,
- ByteMultiple::MB => 1_000_000,
- ByteMultiple::GB => 1_000_000_000,
- ByteMultiple::TB => 1_000_000_000_000,
- ByteMultiple::PB => 1_000_000_000_000_000,
- }
- }
-
- fn from_num_bytes(bytes: u128) -> Self {
- match bytes {
- x if (u128::MIN..u128::from(ByteMultiple::KB.multiplier())).contains(&x) => {
- ByteMultiple::B
- }
- x if (u128::from(ByteMultiple::KB.multiplier())
- ..u128::from(ByteMultiple::MB.multiplier()))
- .contains(&x) =>
- {
- ByteMultiple::KB
- }
- x if (u128::from(ByteMultiple::MB.multiplier())
- ..u128::from(ByteMultiple::GB.multiplier()))
- .contains(&x) =>
- {
- ByteMultiple::MB
- }
- x if (u128::from(ByteMultiple::GB.multiplier())
- ..u128::from(ByteMultiple::TB.multiplier()))
- .contains(&x) =>
- {
- ByteMultiple::GB
- }
- x if (u128::from(ByteMultiple::TB.multiplier())
- ..u128::from(ByteMultiple::PB.multiplier()))
- .contains(&x) =>
- {
- ByteMultiple::TB
- }
- _ => ByteMultiple::PB,
- }
- }
-
- pub fn get_char(self) -> String {
- self.to_string()
- .strip_suffix('B')
- .unwrap_or_default()
- .to_owned()
- }
-
- pub fn from_char(ch: char) -> Self {
- match ch.to_ascii_uppercase() {
- 'K' => ByteMultiple::KB,
- 'M' => ByteMultiple::MB,
- 'G' => ByteMultiple::GB,
- 'T' => ByteMultiple::TB,
- 'P' => ByteMultiple::PB,
- _ => ByteMultiple::B,
- }
- }
-
- /// Returns a String representing a quantity of bytes with its proper multiple (B, KB, MB, GB, TB)
- pub fn formatted_string(bytes: u128) -> String {
- #[allow(clippy::cast_precision_loss)]
- let mut n = bytes as f32;
-
- let byte_multiple = ByteMultiple::from_num_bytes(bytes);
-
- #[allow(clippy::cast_precision_loss)]
- let multiplier = byte_multiple.multiplier() as f32;
- n /= multiplier;
- if n > 999.0 && byte_multiple != ByteMultiple::PB {
- // this allows representing e.g. 999_999 as 999 KB instead of 1000 KB
- n = 999.0;
- }
- let precision = usize::from(byte_multiple != ByteMultiple::B && n <= 9.95);
- format!("{n:.precision$} {byte_multiple}")
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_interpret_suffix_correctly() {
- assert_eq!(ByteMultiple::from_char('B'), ByteMultiple::B);
- assert_eq!(ByteMultiple::from_char('k'), ByteMultiple::KB);
- assert_eq!(ByteMultiple::from_char('M'), ByteMultiple::MB);
- assert_eq!(ByteMultiple::from_char('g'), ByteMultiple::GB);
- assert_eq!(ByteMultiple::from_char('t'), ByteMultiple::TB);
- assert_eq!(ByteMultiple::from_char('P'), ByteMultiple::PB);
- }
-
- #[test]
- fn test_interpret_unknown_suffix_correctly() {
- assert_eq!(ByteMultiple::from_char('E'), ByteMultiple::B);
- assert_eq!(ByteMultiple::from_char('y'), ByteMultiple::B);
- }
-
- #[test]
- fn test_byte_multiple_display() {
- assert_eq!(format!("{}", ByteMultiple::B), "B");
- assert_eq!(format!("{}", ByteMultiple::KB), "KB");
- assert_eq!(format!("{}", ByteMultiple::MB), "MB");
- assert_eq!(format!("{}", ByteMultiple::GB), "GB");
- assert_eq!(format!("{}", ByteMultiple::TB), "TB");
- assert_eq!(format!("{}", ByteMultiple::PB), "PB");
- }
-
- #[test]
- fn test_byte_multiple_get_char() {
- assert_eq!(ByteMultiple::B.get_char(), "");
- assert_eq!(ByteMultiple::KB.get_char(), "K");
- assert_eq!(ByteMultiple::MB.get_char(), "M");
- assert_eq!(ByteMultiple::GB.get_char(), "G");
- assert_eq!(ByteMultiple::TB.get_char(), "T");
- assert_eq!(ByteMultiple::PB.get_char(), "P");
- }
-
- #[test]
- fn test_byte_multiple_multiplier() {
- assert_eq!(ByteMultiple::B.multiplier(), 1);
- assert_eq!(ByteMultiple::KB.multiplier(), 1_000);
- assert_eq!(ByteMultiple::MB.multiplier(), 1_000_000);
- assert_eq!(ByteMultiple::GB.multiplier(), 1_000_000_000);
- assert_eq!(ByteMultiple::TB.multiplier(), 1_000_000_000_000);
- assert_eq!(ByteMultiple::PB.multiplier(), 1_000_000_000_000_000);
- }
-
- #[test]
- fn test_byte_multiple_formatted_string() {
- assert_eq!(ByteMultiple::formatted_string(u128::MIN), "0 B");
- assert_eq!(ByteMultiple::formatted_string(1), "1 B");
- assert_eq!(ByteMultiple::formatted_string(82), "82 B");
- assert_eq!(ByteMultiple::formatted_string(999), "999 B");
- assert_eq!(ByteMultiple::formatted_string(1_000), "1.0 KB");
- assert_eq!(ByteMultiple::formatted_string(1_090), "1.1 KB");
- assert_eq!(ByteMultiple::formatted_string(1_990), "2.0 KB");
- assert_eq!(ByteMultiple::formatted_string(9_090), "9.1 KB");
- assert_eq!(ByteMultiple::formatted_string(9_950), "9.9 KB");
- assert_eq!(ByteMultiple::formatted_string(9_951), "10 KB");
- assert_eq!(ByteMultiple::formatted_string(71_324), "71 KB");
- assert_eq!(ByteMultiple::formatted_string(821_789), "822 KB");
- assert_eq!(ByteMultiple::formatted_string(999_499), "999 KB");
- assert_eq!(ByteMultiple::formatted_string(999_999), "999 KB");
- assert_eq!(ByteMultiple::formatted_string(1_000_000), "1.0 MB");
- assert_eq!(ByteMultiple::formatted_string(3_790_000), "3.8 MB");
- assert_eq!(ByteMultiple::formatted_string(9_950_000), "9.9 MB");
- assert_eq!(ByteMultiple::formatted_string(9_951_000), "10 MB");
- assert_eq!(ByteMultiple::formatted_string(49_499_000), "49 MB");
- assert_eq!(ByteMultiple::formatted_string(49_500_000), "50 MB");
- assert_eq!(ByteMultiple::formatted_string(670_900_000), "671 MB");
- assert_eq!(ByteMultiple::formatted_string(998_199_999), "998 MB");
- assert_eq!(ByteMultiple::formatted_string(999_999_999), "999 MB");
- assert_eq!(ByteMultiple::formatted_string(1_000_000_000), "1.0 GB");
- assert_eq!(ByteMultiple::formatted_string(7_770_000_000), "7.8 GB");
- assert_eq!(ByteMultiple::formatted_string(9_950_000_000), "9.9 GB");
- assert_eq!(ByteMultiple::formatted_string(9_951_000_000), "10 GB");
- assert_eq!(ByteMultiple::formatted_string(19_951_000_000), "20 GB");
- assert_eq!(ByteMultiple::formatted_string(399_951_000_000), "400 GB");
- assert_eq!(ByteMultiple::formatted_string(999_999_999_999), "999 GB");
- assert_eq!(ByteMultiple::formatted_string(1_000_000_000_000), "1.0 TB");
- assert_eq!(ByteMultiple::formatted_string(9_950_000_000_000), "9.9 TB");
- assert_eq!(ByteMultiple::formatted_string(9_951_000_000_000), "10 TB");
- assert_eq!(
- ByteMultiple::formatted_string(999_950_000_000_000),
- "999 TB"
- );
- assert_eq!(
- ByteMultiple::formatted_string(999_999_999_999_999),
- "999 TB"
- );
- assert_eq!(
- ByteMultiple::formatted_string(1_000_000_000_000_000),
- "1.0 PB"
- );
- assert_eq!(
- ByteMultiple::formatted_string(1_000_000_000_000_000_0),
- "10 PB"
- );
- assert_eq!(
- ByteMultiple::formatted_string(999_999_999_000_000_000),
- "1000 PB"
- );
- assert_eq!(
- ByteMultiple::formatted_string(1_000_000_000_000_000_000_000),
- "1000000 PB"
- );
- assert_eq!(
- ByteMultiple::formatted_string(u128::MAX / 2),
- "170141184077655307190272 PB"
- );
- assert_eq!(ByteMultiple::formatted_string(u128::MAX), "inf PB");
- }
-}
diff --git a/src/networking/types/capture_context.rs b/src/networking/types/capture_context.rs
index 43e441cf..66cc703c 100644
--- a/src/networking/types/capture_context.rs
+++ b/src/networking/types/capture_context.rs
@@ -1,10 +1,14 @@
-use pcap::{Active, Address, Capture, Error, Packet, Savefile, Stat};
-
+use crate::gui::types::conf::Conf;
+use crate::gui::types::filters::Filters;
+use crate::location;
use crate::networking::types::my_device::MyDevice;
use crate::networking::types::my_link_type::MyLinkType;
use crate::translations::translations::network_adapter_translation;
-use crate::translations::translations_3::file_name_translation;
+use crate::translations::translations_4::capture_file_translation;
use crate::translations::types::language::Language;
+use crate::utils::error_logger::{ErrorLogger, Location};
+use pcap::{Active, Address, Capture, Device, Error, Packet, Savefile, Stat};
+use serde::{Deserialize, Serialize};
pub enum CaptureContext {
Live(Live),
@@ -14,11 +18,19 @@ pub enum CaptureContext {
}
impl CaptureContext {
- pub fn new(source: &CaptureSource, pcap_out_path: Option<&String>) -> Self {
- let cap_type = match CaptureType::from_source(source, pcap_out_path) {
+ pub fn new(source: &CaptureSource, pcap_out_path: Option<&String>, filters: &Filters) -> Self {
+ let mut cap_type = match CaptureType::from_source(source, pcap_out_path) {
Ok(c) => c,
Err(e) => return Self::Error(e.to_string()),
};
+
+ // only apply BPF filter if it is active, and return an error if it fails to apply
+ if filters.is_some_filter_active()
+ && let Err(e) = cap_type.set_bpf(filters.bpf())
+ {
+ return Self::Error(e.to_string());
+ }
+
let cap = match cap_type {
CaptureType::Live(cap) => cap,
CaptureType::Offline(cap) => return Self::new_offline(cap),
@@ -97,7 +109,7 @@ pub enum CaptureType {
}
impl CaptureType {
- pub fn next_packet(&mut self) -> Result {
+ pub fn next_packet(&mut self) -> Result, Error> {
match self {
Self::Live(on) => on.next_packet(),
Self::Offline(off) => off.next_packet(),
@@ -123,7 +135,7 @@ fn from_source(source: &CaptureSource, pcap_out_path: Option<&String>) -> Result
} else {
200 // limit stored packets slice dimension (to keep more in the buffer)
})
- .immediate_mode(true) // parse packets ASAP
+ .immediate_mode(false)
.timeout(150) // ensure UI is updated even if no packets are captured
.open()?;
Ok(Self::Live(cap))
@@ -131,6 +143,13 @@ fn from_source(source: &CaptureSource, pcap_out_path: Option<&String>) -> Result
CaptureSource::File(file) => Ok(Self::Offline(Capture::from_file(&file.path)?)),
}
}
+
+ fn set_bpf(&mut self, bpf: &str) -> Result<(), Error> {
+ match self {
+ Self::Live(cap) => cap.filter(bpf, true),
+ Self::Offline(cap) => cap.filter(bpf, true),
+ }
+ }
}
#[derive(Clone)]
@@ -140,10 +159,23 @@ 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),
- Self::File(_) => file_name_translation(language),
+ Self::File(_) => capture_file_translation(language),
}
}
@@ -154,9 +186,21 @@ pub fn get_addresses(&self) -> &Vec {
}
}
- pub fn set_addresses(&mut self, addresses: Vec) {
- if let Self::Device(device) = self {
- device.set_addresses(addresses);
+ pub fn set_addresses(&mut self) {
+ if let Self::Device(my_device) = self {
+ let mut addresses = Vec::new();
+ for dev in Device::list().log_err(location!()).unwrap_or_default() {
+ if matches!(
+ my_device.get_link_type(),
+ MyLinkType::LinuxSll(_) | MyLinkType::LinuxSll2(_)
+ ) {
+ addresses.extend(dev.addresses);
+ } else if dev.name.eq(my_device.get_name()) {
+ addresses.extend(dev.addresses);
+ break;
+ }
+ }
+ my_device.set_addresses(addresses);
}
}
@@ -206,3 +250,10 @@ pub fn new(path: String) -> Self {
}
}
}
+
+#[derive(Clone, Eq, PartialEq, Debug, Copy, Default, Serialize, Deserialize)]
+pub enum CaptureSourcePicklist {
+ #[default]
+ Device,
+ File,
+}
diff --git a/src/networking/types/config_device.rs b/src/networking/types/config_device.rs
new file mode 100644
index 00000000..c3d993f7
--- /dev/null
+++ b/src/networking/types/config_device.rs
@@ -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)
+ }
+}
diff --git a/src/networking/types/data_info.rs b/src/networking/types/data_info.rs
index 5c5d99af..975e93e1 100644
--- a/src/networking/types/data_info.rs
+++ b/src/networking/types/data_info.rs
@@ -1,6 +1,6 @@
//! Module defining the `DataInfo` struct, which represents incoming and outgoing packets and bytes.
-use crate::chart::types::chart_type::ChartType;
+use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::traffic_direction::TrafficDirection;
use crate::report::types::sort_type::SortType;
use std::cmp::Ordering;
@@ -23,37 +23,26 @@ pub struct DataInfo {
}
impl DataInfo {
- pub fn incoming_packets(&self) -> u128 {
- self.incoming_packets
- }
-
- pub fn outgoing_packets(&self) -> u128 {
- self.outgoing_packets
- }
-
- pub fn incoming_bytes(&self) -> u128 {
- self.incoming_bytes
- }
-
- pub fn outgoing_bytes(&self) -> u128 {
- self.outgoing_bytes
- }
-
- pub fn tot_packets(&self) -> u128 {
- self.incoming_packets + self.outgoing_packets
- }
-
- pub fn tot_bytes(&self) -> u128 {
- self.incoming_bytes + self.outgoing_bytes
- }
-
- pub fn tot_data(&self, chart_type: ChartType) -> u128 {
- match chart_type {
- ChartType::Packets => self.tot_packets(),
- ChartType::Bytes => self.tot_bytes(),
+ pub fn incoming_data(&self, data_repr: DataRepr) -> u128 {
+ match data_repr {
+ DataRepr::Packets => self.incoming_packets,
+ DataRepr::Bytes => self.incoming_bytes,
+ DataRepr::Bits => self.incoming_bytes * 8,
}
}
+ pub fn outgoing_data(&self, data_repr: DataRepr) -> u128 {
+ match data_repr {
+ DataRepr::Packets => self.outgoing_packets,
+ DataRepr::Bytes => self.outgoing_bytes,
+ DataRepr::Bits => self.outgoing_bytes * 8,
+ }
+ }
+
+ pub fn tot_data(&self, data_repr: DataRepr) -> u128 {
+ self.incoming_data(data_repr) + self.outgoing_data(data_repr)
+ }
+
pub fn add_packet(&mut self, bytes: u128, traffic_direction: TrafficDirection) {
if traffic_direction.eq(&TrafficDirection::Outgoing) {
self.outgoing_packets += 1;
@@ -103,18 +92,11 @@ pub fn refresh(&mut self, rhs: Self) {
self.final_instant = rhs.final_instant;
}
- pub fn compare(&self, other: &Self, sort_type: SortType, chart_type: ChartType) -> Ordering {
- match chart_type {
- ChartType::Packets => match sort_type {
- SortType::Ascending => self.tot_packets().cmp(&other.tot_packets()),
- SortType::Descending => other.tot_packets().cmp(&self.tot_packets()),
- SortType::Neutral => other.final_instant.cmp(&self.final_instant),
- },
- ChartType::Bytes => match sort_type {
- SortType::Ascending => self.tot_bytes().cmp(&other.tot_bytes()),
- SortType::Descending => other.tot_bytes().cmp(&self.tot_bytes()),
- SortType::Neutral => other.final_instant.cmp(&self.final_instant),
- },
+ pub fn compare(&self, other: &Self, sort_type: SortType, data_repr: DataRepr) -> Ordering {
+ match sort_type {
+ SortType::Ascending => self.tot_data(data_repr).cmp(&other.tot_data(data_repr)),
+ SortType::Descending => other.tot_data(data_repr).cmp(&self.tot_data(data_repr)),
+ SortType::Neutral => other.final_instant.cmp(&self.final_instant),
}
}
@@ -146,3 +128,115 @@ fn default() -> Self {
}
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::networking::types::traffic_direction::TrafficDirection;
+
+ #[test]
+ fn test_data_info() {
+ // in_packets: 0, out_packets: 0, in_bytes: 0, out_bytes: 0
+ let mut data_info_1 = DataInfo::new_with_first_packet(123, TrafficDirection::Incoming);
+ // 1, 0, 123, 0
+ data_info_1.add_packet(100, TrafficDirection::Incoming);
+ // 2, 0, 223, 0
+ data_info_1.add_packet(200, TrafficDirection::Outgoing);
+ // 2, 1, 223, 200
+ data_info_1.add_packets(11, 1200, TrafficDirection::Outgoing);
+ // 2, 12, 223, 1400
+ data_info_1.add_packets(5, 500, TrafficDirection::Incoming);
+ // 7, 12, 723, 1400
+
+ assert_eq!(data_info_1.incoming_packets, 7);
+ assert_eq!(data_info_1.outgoing_packets, 12);
+ assert_eq!(data_info_1.incoming_bytes, 723);
+ assert_eq!(data_info_1.outgoing_bytes, 1400);
+
+ assert_eq!(data_info_1.tot_data(DataRepr::Packets), 19);
+ assert_eq!(data_info_1.tot_data(DataRepr::Bytes), 2123);
+ assert_eq!(data_info_1.tot_data(DataRepr::Bits), 16984);
+
+ assert_eq!(data_info_1.incoming_data(DataRepr::Packets), 7);
+ assert_eq!(data_info_1.incoming_data(DataRepr::Bytes), 723);
+ assert_eq!(data_info_1.incoming_data(DataRepr::Bits), 5784);
+
+ assert_eq!(data_info_1.outgoing_data(DataRepr::Packets), 12);
+ assert_eq!(data_info_1.outgoing_data(DataRepr::Bytes), 1400);
+ assert_eq!(data_info_1.outgoing_data(DataRepr::Bits), 11200);
+
+ let mut data_info_2 = DataInfo::new_with_first_packet(100, TrafficDirection::Outgoing);
+ // 0, 1, 0, 100
+ data_info_2.add_packets(19, 300, TrafficDirection::Outgoing);
+ // 0, 20, 0, 400
+
+ assert_eq!(data_info_2.incoming_packets, 0);
+ assert_eq!(data_info_2.outgoing_packets, 20);
+ assert_eq!(data_info_2.incoming_bytes, 0);
+ assert_eq!(data_info_2.outgoing_bytes, 400);
+
+ assert_eq!(data_info_2.tot_data(DataRepr::Packets), 20);
+ assert_eq!(data_info_2.tot_data(DataRepr::Bytes), 400);
+ assert_eq!(data_info_2.tot_data(DataRepr::Bits), 3200);
+
+ assert_eq!(data_info_2.incoming_data(DataRepr::Packets), 0);
+ assert_eq!(data_info_2.incoming_data(DataRepr::Bytes), 0);
+ assert_eq!(data_info_2.incoming_data(DataRepr::Bits), 0);
+
+ assert_eq!(data_info_2.outgoing_data(DataRepr::Packets), 20);
+ assert_eq!(data_info_2.outgoing_data(DataRepr::Bytes), 400);
+ assert_eq!(data_info_2.outgoing_data(DataRepr::Bits), 3200);
+
+ // compare data_info_1 and data_info_2
+
+ assert_eq!(
+ data_info_1.compare(&data_info_2, SortType::Ascending, DataRepr::Packets),
+ Ordering::Less
+ );
+ assert_eq!(
+ data_info_1.compare(&data_info_2, SortType::Descending, DataRepr::Packets),
+ Ordering::Greater
+ );
+ assert_eq!(
+ data_info_1.compare(&data_info_2, SortType::Neutral, DataRepr::Packets),
+ Ordering::Greater
+ );
+
+ assert_eq!(
+ data_info_1.compare(&data_info_2, SortType::Ascending, DataRepr::Bytes),
+ Ordering::Greater
+ );
+ assert_eq!(
+ data_info_1.compare(&data_info_2, SortType::Descending, DataRepr::Bytes),
+ Ordering::Less
+ );
+ assert_eq!(
+ data_info_1.compare(&data_info_2, SortType::Neutral, DataRepr::Bytes),
+ Ordering::Greater
+ );
+
+ assert_eq!(
+ data_info_1.compare(&data_info_2, SortType::Ascending, DataRepr::Bits),
+ Ordering::Greater
+ );
+ assert_eq!(
+ data_info_1.compare(&data_info_2, SortType::Descending, DataRepr::Bits),
+ Ordering::Less
+ );
+ assert_eq!(
+ data_info_1.compare(&data_info_2, SortType::Neutral, DataRepr::Bits),
+ Ordering::Greater
+ );
+
+ // refresh data_info_1 with data_info_2
+ assert!(data_info_1.final_instant < data_info_2.final_instant);
+ data_info_1.refresh(data_info_2);
+
+ // data_info_1 should now contain the sum of both data_info_1 and data_info_2
+ assert_eq!(data_info_1.incoming_packets, 7);
+ assert_eq!(data_info_1.outgoing_packets, 32);
+ assert_eq!(data_info_1.incoming_bytes, 723);
+ assert_eq!(data_info_1.outgoing_bytes, 1800);
+ assert_eq!(data_info_1.final_instant, data_info_2.final_instant);
+ }
+}
diff --git a/src/networking/types/data_representation.rs b/src/networking/types/data_representation.rs
new file mode 100644
index 00000000..8e289b27
--- /dev/null
+++ b/src/networking/types/data_representation.rs
@@ -0,0 +1,523 @@
+use crate::translations::translations::{
+ bytes_exceeded_translation, bytes_translation, packets_exceeded_translation,
+ packets_translation,
+};
+use crate::translations::translations_4::{bits_exceeded_translation, bits_translation};
+use crate::translations::types::language::Language;
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
+pub enum DataRepr {
+ Packets,
+ Bytes,
+ Bits,
+}
+
+impl DataRepr {
+ pub(crate) const ALL: [DataRepr; 3] = [DataRepr::Bits, DataRepr::Bytes, DataRepr::Packets];
+
+ pub fn get_label(&self, language: Language) -> &str {
+ match self {
+ DataRepr::Packets => packets_translation(language),
+ DataRepr::Bytes => bytes_translation(language),
+ DataRepr::Bits => bits_translation(language),
+ }
+ }
+
+ /// Returns a String representing a quantity of traffic (packets / bytes / bits) with the proper multiple if applicable
+ pub fn formatted_string(self, amount: u128) -> String {
+ if self == DataRepr::Packets {
+ return amount.to_string();
+ }
+
+ #[allow(clippy::cast_precision_loss)]
+ let mut n = amount as f32;
+
+ let byte_multiple = ByteMultiple::from_amount(amount);
+
+ #[allow(clippy::cast_precision_loss)]
+ let multiplier = byte_multiple.multiplier() as f32;
+ n /= multiplier;
+ if n > 999.0 && byte_multiple != ByteMultiple::PB {
+ // this allows representing e.g. 999_999 as 999 KB instead of 1000 KB
+ n = 999.0;
+ }
+ let precision = usize::from(byte_multiple != ByteMultiple::B && n <= 9.95);
+ format!("{n:.precision$} {}", byte_multiple.pretty_print(self))
+ .trim()
+ .to_string()
+ }
+
+ pub fn data_exceeded_translation(&self, language: Language) -> &str {
+ match self {
+ DataRepr::Packets => packets_exceeded_translation(language),
+ DataRepr::Bytes => bytes_exceeded_translation(language),
+ DataRepr::Bits => bits_exceeded_translation(language),
+ }
+ }
+}
+
+/// Represents a Byte or bit multiple for displaying values in a human-readable format.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
+pub enum ByteMultiple {
+ /// A Byte
+ B,
+ /// 10^3 Bytes
+ KB,
+ /// 10^6 Bytes
+ MB,
+ /// 10^9 Bytes
+ GB,
+ /// 10^12 Bytes
+ TB,
+ /// 10^15 Bytes
+ PB,
+}
+
+impl ByteMultiple {
+ pub fn multiplier(self) -> u64 {
+ match self {
+ ByteMultiple::B => 1,
+ ByteMultiple::KB => 1_000,
+ ByteMultiple::MB => 1_000_000,
+ ByteMultiple::GB => 1_000_000_000,
+ ByteMultiple::TB => 1_000_000_000_000,
+ ByteMultiple::PB => 1_000_000_000_000_000,
+ }
+ }
+
+ fn from_amount(bytes: u128) -> Self {
+ match bytes {
+ x if (u128::MIN..u128::from(ByteMultiple::KB.multiplier())).contains(&x) => {
+ ByteMultiple::B
+ }
+ x if (u128::from(ByteMultiple::KB.multiplier())
+ ..u128::from(ByteMultiple::MB.multiplier()))
+ .contains(&x) =>
+ {
+ ByteMultiple::KB
+ }
+ x if (u128::from(ByteMultiple::MB.multiplier())
+ ..u128::from(ByteMultiple::GB.multiplier()))
+ .contains(&x) =>
+ {
+ ByteMultiple::MB
+ }
+ x if (u128::from(ByteMultiple::GB.multiplier())
+ ..u128::from(ByteMultiple::TB.multiplier()))
+ .contains(&x) =>
+ {
+ ByteMultiple::GB
+ }
+ x if (u128::from(ByteMultiple::TB.multiplier())
+ ..u128::from(ByteMultiple::PB.multiplier()))
+ .contains(&x) =>
+ {
+ ByteMultiple::TB
+ }
+ _ => ByteMultiple::PB,
+ }
+ }
+
+ pub fn get_char(self) -> String {
+ match self {
+ Self::B => String::new(),
+ Self::KB => "K".to_string(),
+ Self::MB => "M".to_string(),
+ Self::GB => "G".to_string(),
+ Self::TB => "T".to_string(),
+ Self::PB => "P".to_string(),
+ }
+ }
+
+ pub fn from_char(ch: char) -> Self {
+ match ch.to_ascii_uppercase() {
+ 'K' => ByteMultiple::KB,
+ 'M' => ByteMultiple::MB,
+ 'G' => ByteMultiple::GB,
+ 'T' => ByteMultiple::TB,
+ 'P' => ByteMultiple::PB,
+ _ => ByteMultiple::B,
+ }
+ }
+
+ fn pretty_print(self, repr: DataRepr) -> String {
+ match repr {
+ DataRepr::Packets => String::new(),
+ DataRepr::Bytes => format!("{}B", self.get_char()),
+ DataRepr::Bits => format!("{}b", self.get_char()),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_interpret_suffix_correctly() {
+ assert_eq!(ByteMultiple::from_char('B'), ByteMultiple::B);
+ assert_eq!(ByteMultiple::from_char('k'), ByteMultiple::KB);
+ assert_eq!(ByteMultiple::from_char('M'), ByteMultiple::MB);
+ assert_eq!(ByteMultiple::from_char('g'), ByteMultiple::GB);
+ assert_eq!(ByteMultiple::from_char('t'), ByteMultiple::TB);
+ assert_eq!(ByteMultiple::from_char('P'), ByteMultiple::PB);
+ }
+
+ #[test]
+ fn test_interpret_unknown_suffix_correctly() {
+ assert_eq!(ByteMultiple::from_char('E'), ByteMultiple::B);
+ assert_eq!(ByteMultiple::from_char('y'), ByteMultiple::B);
+ }
+
+ #[test]
+ fn test_byte_multiple_display() {
+ assert_eq!(
+ format!("{}", ByteMultiple::B.pretty_print(DataRepr::Packets)),
+ ""
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::B.pretty_print(DataRepr::Bytes)),
+ "B"
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::B.pretty_print(DataRepr::Bits)),
+ "b"
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::KB.pretty_print(DataRepr::Packets)),
+ ""
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::KB.pretty_print(DataRepr::Bytes)),
+ "KB"
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::KB.pretty_print(DataRepr::Bits)),
+ "Kb"
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::MB.pretty_print(DataRepr::Packets)),
+ ""
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::MB.pretty_print(DataRepr::Bytes)),
+ "MB"
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::MB.pretty_print(DataRepr::Bits)),
+ "Mb"
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::GB.pretty_print(DataRepr::Packets)),
+ ""
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::GB.pretty_print(DataRepr::Bytes)),
+ "GB"
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::GB.pretty_print(DataRepr::Bits)),
+ "Gb"
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::TB.pretty_print(DataRepr::Packets)),
+ ""
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::TB.pretty_print(DataRepr::Bytes)),
+ "TB"
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::TB.pretty_print(DataRepr::Bits)),
+ "Tb"
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::PB.pretty_print(DataRepr::Packets)),
+ ""
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::PB.pretty_print(DataRepr::Bytes)),
+ "PB"
+ );
+ assert_eq!(
+ format!("{}", ByteMultiple::PB.pretty_print(DataRepr::Bits)),
+ "Pb"
+ );
+ }
+
+ #[test]
+ fn test_byte_multiple_get_char() {
+ assert_eq!(ByteMultiple::B.get_char(), "");
+ assert_eq!(ByteMultiple::KB.get_char(), "K");
+ assert_eq!(ByteMultiple::MB.get_char(), "M");
+ assert_eq!(ByteMultiple::GB.get_char(), "G");
+ assert_eq!(ByteMultiple::TB.get_char(), "T");
+ assert_eq!(ByteMultiple::PB.get_char(), "P");
+ }
+
+ #[test]
+ fn test_byte_multiple_multiplier() {
+ assert_eq!(ByteMultiple::B.multiplier(), 1);
+ assert_eq!(ByteMultiple::KB.multiplier(), 1_000);
+ assert_eq!(ByteMultiple::MB.multiplier(), 1_000_000);
+ assert_eq!(ByteMultiple::GB.multiplier(), 1_000_000_000);
+ assert_eq!(ByteMultiple::TB.multiplier(), 1_000_000_000_000);
+ assert_eq!(ByteMultiple::PB.multiplier(), 1_000_000_000_000_000);
+ }
+
+ #[test]
+ fn test_byte_multiple_formatted_string() {
+ assert_eq!(DataRepr::Packets.formatted_string(u128::MIN), "0");
+ assert_eq!(DataRepr::Bytes.formatted_string(u128::MIN), "0 B");
+ assert_eq!(DataRepr::Bits.formatted_string(u128::MIN), "0 b");
+
+ assert_eq!(DataRepr::Packets.formatted_string(1), "1");
+ assert_eq!(DataRepr::Bytes.formatted_string(1), "1 B");
+ assert_eq!(DataRepr::Bits.formatted_string(1), "1 b");
+
+ assert_eq!(DataRepr::Packets.formatted_string(82), "82");
+ assert_eq!(DataRepr::Bytes.formatted_string(82), "82 B");
+ assert_eq!(DataRepr::Bits.formatted_string(82), "82 b");
+
+ assert_eq!(DataRepr::Packets.formatted_string(999), "999");
+ assert_eq!(DataRepr::Bytes.formatted_string(999), "999 B");
+ assert_eq!(DataRepr::Bits.formatted_string(999), "999 b");
+
+ assert_eq!(DataRepr::Packets.formatted_string(1_000), "1000");
+ assert_eq!(DataRepr::Bytes.formatted_string(1_000), "1.0 KB");
+ assert_eq!(DataRepr::Bits.formatted_string(1_000), "1.0 Kb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(1_090), "1090");
+ assert_eq!(DataRepr::Bytes.formatted_string(1_090), "1.1 KB");
+ assert_eq!(DataRepr::Bits.formatted_string(1_090), "1.1 Kb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(1_990), "1990");
+ assert_eq!(DataRepr::Bytes.formatted_string(1_990), "2.0 KB");
+ assert_eq!(DataRepr::Bits.formatted_string(1_990), "2.0 Kb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(9_090), "9090");
+ assert_eq!(DataRepr::Bytes.formatted_string(9_090), "9.1 KB");
+ assert_eq!(DataRepr::Bits.formatted_string(9_090), "9.1 Kb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(9_950), "9950");
+ assert_eq!(DataRepr::Bytes.formatted_string(9_950), "9.9 KB");
+ assert_eq!(DataRepr::Bits.formatted_string(9_950), "9.9 Kb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(9_951), "9951");
+ assert_eq!(DataRepr::Bytes.formatted_string(9_951), "10 KB");
+ assert_eq!(DataRepr::Bits.formatted_string(9_951), "10 Kb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(71_324), "71324");
+ assert_eq!(DataRepr::Bytes.formatted_string(71_324), "71 KB");
+ assert_eq!(DataRepr::Bits.formatted_string(71_324), "71 Kb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(821_789), "821789");
+ assert_eq!(DataRepr::Bytes.formatted_string(821_789), "822 KB");
+ assert_eq!(DataRepr::Bits.formatted_string(821_789), "822 Kb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(999_499), "999499");
+ assert_eq!(DataRepr::Bytes.formatted_string(999_499), "999 KB");
+ assert_eq!(DataRepr::Bits.formatted_string(999_499), "999 Kb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(999_999), "999999");
+ assert_eq!(DataRepr::Bytes.formatted_string(999_999), "999 KB");
+ assert_eq!(DataRepr::Bits.formatted_string(999_999), "999 Kb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(1_000_000), "1000000");
+ assert_eq!(DataRepr::Bytes.formatted_string(1_000_000), "1.0 MB");
+ assert_eq!(DataRepr::Bits.formatted_string(1_000_000), "1.0 Mb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(3_790_000), "3790000");
+ assert_eq!(DataRepr::Bytes.formatted_string(3_790_000), "3.8 MB");
+ assert_eq!(DataRepr::Bits.formatted_string(3_790_000), "3.8 Mb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(9_950_000), "9950000");
+ assert_eq!(DataRepr::Bytes.formatted_string(9_950_000), "9.9 MB");
+ assert_eq!(DataRepr::Bits.formatted_string(9_950_000), "9.9 Mb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(9_951_000), "9951000");
+ assert_eq!(DataRepr::Bytes.formatted_string(9_951_000), "10 MB");
+ assert_eq!(DataRepr::Bits.formatted_string(9_951_000), "10 Mb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(49_499_000), "49499000");
+ assert_eq!(DataRepr::Bytes.formatted_string(49_499_000), "49 MB");
+ assert_eq!(DataRepr::Bits.formatted_string(49_499_000), "49 Mb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(49_500_000), "49500000");
+ assert_eq!(DataRepr::Bytes.formatted_string(49_500_000), "50 MB");
+ assert_eq!(DataRepr::Bits.formatted_string(49_500_000), "50 Mb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(670_900_000), "670900000");
+ assert_eq!(DataRepr::Bytes.formatted_string(670_900_000), "671 MB");
+ assert_eq!(DataRepr::Bits.formatted_string(670_900_000), "671 Mb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(998_199_999), "998199999");
+ assert_eq!(DataRepr::Bytes.formatted_string(998_199_999), "998 MB");
+ assert_eq!(DataRepr::Bits.formatted_string(998_199_999), "998 Mb");
+
+ assert_eq!(DataRepr::Packets.formatted_string(999_999_999), "999999999");
+ assert_eq!(DataRepr::Bytes.formatted_string(999_999_999), "999 MB");
+ assert_eq!(DataRepr::Bits.formatted_string(999_999_999), "999 Mb");
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(1_000_000_000),
+ "1000000000"
+ );
+ assert_eq!(DataRepr::Bytes.formatted_string(1_000_000_000), "1.0 GB");
+ assert_eq!(DataRepr::Bits.formatted_string(1_000_000_000), "1.0 Gb");
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(7_770_000_000),
+ "7770000000"
+ );
+ assert_eq!(DataRepr::Bytes.formatted_string(7_770_000_000), "7.8 GB");
+ assert_eq!(DataRepr::Bits.formatted_string(7_770_000_000), "7.8 Gb");
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(9_950_000_000),
+ "9950000000"
+ );
+ assert_eq!(DataRepr::Bytes.formatted_string(9_950_000_000), "9.9 GB");
+ assert_eq!(DataRepr::Bits.formatted_string(9_950_000_000), "9.9 Gb");
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(9_951_000_000),
+ "9951000000"
+ );
+ assert_eq!(DataRepr::Bytes.formatted_string(9_951_000_000), "10 GB");
+ assert_eq!(DataRepr::Bits.formatted_string(9_951_000_000), "10 Gb");
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(19_951_000_000),
+ "19951000000"
+ );
+ assert_eq!(DataRepr::Bytes.formatted_string(19_951_000_000), "20 GB");
+ assert_eq!(DataRepr::Bits.formatted_string(19_951_000_000), "20 Gb");
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(399_951_000_000),
+ "399951000000"
+ );
+ assert_eq!(DataRepr::Bytes.formatted_string(399_951_000_000), "400 GB");
+ assert_eq!(DataRepr::Bits.formatted_string(399_951_000_000), "400 Gb");
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(999_999_999_999),
+ "999999999999"
+ );
+ assert_eq!(DataRepr::Bytes.formatted_string(999_999_999_999), "999 GB");
+ assert_eq!(DataRepr::Bits.formatted_string(999_999_999_999), "999 Gb");
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(1_000_000_000_000),
+ "1000000000000"
+ );
+ assert_eq!(
+ DataRepr::Bytes.formatted_string(1_000_000_000_000),
+ "1.0 TB"
+ );
+ assert_eq!(DataRepr::Bits.formatted_string(1_000_000_000_000), "1.0 Tb");
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(9_950_000_000_000),
+ "9950000000000"
+ );
+ assert_eq!(
+ DataRepr::Bytes.formatted_string(9_950_000_000_000),
+ "9.9 TB"
+ );
+ assert_eq!(DataRepr::Bits.formatted_string(9_950_000_000_000), "9.9 Tb");
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(9_951_000_000_000),
+ "9951000000000"
+ );
+ assert_eq!(DataRepr::Bytes.formatted_string(9_951_000_000_000), "10 TB");
+ assert_eq!(DataRepr::Bits.formatted_string(9_951_000_000_000), "10 Tb");
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(999_950_000_000_000),
+ "999950000000000"
+ );
+ assert_eq!(
+ DataRepr::Bytes.formatted_string(999_950_000_000_000),
+ "999 TB"
+ );
+ assert_eq!(
+ DataRepr::Bits.formatted_string(999_950_000_000_000),
+ "999 Tb"
+ );
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(999_999_999_999_999),
+ "999999999999999"
+ );
+ assert_eq!(
+ DataRepr::Bytes.formatted_string(999_999_999_999_999),
+ "999 TB"
+ );
+ assert_eq!(
+ DataRepr::Bits.formatted_string(999_999_999_999_999),
+ "999 Tb"
+ );
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(1_000_000_000_000_000),
+ "1000000000000000"
+ );
+ assert_eq!(
+ DataRepr::Bytes.formatted_string(1_000_000_000_000_000),
+ "1.0 PB"
+ );
+ assert_eq!(
+ DataRepr::Bits.formatted_string(1_000_000_000_000_000),
+ "1.0 Pb"
+ );
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(1_000_000_000_000_000_0),
+ "10000000000000000"
+ );
+ assert_eq!(
+ DataRepr::Bytes.formatted_string(1_000_000_000_000_000_0),
+ "10 PB"
+ );
+ assert_eq!(
+ DataRepr::Bits.formatted_string(1_000_000_000_000_000_0),
+ "10 Pb"
+ );
+ assert_eq!(
+ DataRepr::Packets.formatted_string(999_999_999_000_000_000),
+ "999999999000000000"
+ );
+ assert_eq!(
+ DataRepr::Bytes.formatted_string(999_999_999_000_000_000),
+ "1000 PB"
+ );
+ assert_eq!(
+ DataRepr::Bits.formatted_string(999_999_999_000_000_000),
+ "1000 Pb"
+ );
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(u128::MAX / 2),
+ "170141183460469231731687303715884105727"
+ );
+ assert_eq!(
+ DataRepr::Bytes.formatted_string(u128::MAX / 2),
+ "170141184077655307190272 PB"
+ );
+ assert_eq!(
+ DataRepr::Bits.formatted_string(u128::MAX / 2),
+ "170141184077655307190272 Pb"
+ );
+
+ assert_eq!(
+ DataRepr::Packets.formatted_string(u128::MAX),
+ "340282366920938463463374607431768211455"
+ );
+ assert_eq!(DataRepr::Bytes.formatted_string(u128::MAX), "inf PB");
+ assert_eq!(DataRepr::Bits.formatted_string(u128::MAX), "inf Pb");
+ }
+}
diff --git a/src/networking/types/filters.rs b/src/networking/types/filters.rs
deleted file mode 100644
index 62f7e1e4..00000000
--- a/src/networking/types/filters.rs
+++ /dev/null
@@ -1,112 +0,0 @@
-//! Module defining the `Filters` struct, which represents the possible filters applicable on network traffic.
-
-use std::collections::HashSet;
-
-use crate::networking::types::ip_collection::AddressCollection;
-use crate::networking::types::packet_filters_fields::PacketFiltersFields;
-use crate::networking::types::port_collection::PortCollection;
-use crate::{IpVersion, Protocol};
-
-/// Possible filters applicable to network traffic
-#[derive(Clone)]
-pub struct Filters {
- /// Internet Protocol versions
- pub ip_versions: HashSet,
- /// Protocols
- pub protocols: HashSet,
- /// IP addresses string in Initial page text input
- pub address_str: String,
- /// IP address collection to match against traffic
- pub address_collection: AddressCollection,
- /// Ports string in Initial page text input
- pub port_str: String,
- /// Port collection to match against traffic
- pub port_collection: PortCollection,
-}
-
-impl Default for Filters {
- fn default() -> Self {
- Self {
- ip_versions: HashSet::from(IpVersion::ALL),
- protocols: HashSet::from(Protocol::ALL),
- address_str: String::new(),
- address_collection: AddressCollection::default(),
- port_str: String::new(),
- port_collection: PortCollection::default(),
- }
- }
-}
-
-impl Filters {
- /// Checks whether the filters match the current packet's protocols
- pub fn matches(&self, packet_filters_fields: &PacketFiltersFields) -> bool {
- self.ip_versions.contains(&packet_filters_fields.ip_version)
- && self.protocols.contains(&packet_filters_fields.protocol)
- && (self
- .address_collection
- .contains(&packet_filters_fields.source)
- || self
- .address_collection
- .contains(&packet_filters_fields.dest))
- && (self.port_collection.contains(packet_filters_fields.sport)
- || self.port_collection.contains(packet_filters_fields.dport))
- }
-
- pub fn are_valid(&self) -> bool {
- self.ip_version_valid()
- && self.protocol_valid()
- && self.address_valid()
- && self.port_valid()
- }
-
- pub fn ip_version_valid(&self) -> bool {
- !self.ip_versions.is_empty()
- }
-
- pub fn protocol_valid(&self) -> bool {
- !self.protocols.is_empty()
- }
-
- pub fn address_valid(&self) -> bool {
- AddressCollection::new(&self.address_str).is_some()
- }
-
- pub fn port_valid(&self) -> bool {
- PortCollection::new(&self.port_str).is_some()
- }
-
- pub fn none_active(&self) -> bool {
- !self.ip_version_active()
- && !self.protocol_active()
- && !self.address_active()
- && !self.port_active()
- }
-
- pub fn ip_version_active(&self) -> bool {
- self.ip_versions.len() != IpVersion::ALL.len()
- }
-
- pub fn protocol_active(&self) -> bool {
- self.protocols.len() != Protocol::ALL.len()
- }
-
- pub fn address_active(&self) -> bool {
- self.address_collection != AddressCollection::default()
- }
-
- pub fn port_active(&self) -> bool {
- self.port_collection != PortCollection::default()
- }
-
- pub fn pretty_print_ip(&self) -> String {
- format!("{:?}", self.ip_versions)
- .replace('{', "")
- .replace('}', "")
- }
-
- pub fn pretty_print_protocol(&self) -> String {
- format!("{:?}", self.protocols)
- .replace('{', "")
- .replace('}', "")
- }
-}
diff --git a/src/networking/types/icmp_type.rs b/src/networking/types/icmp_type.rs
index ddb2c31f..6859ab6d 100644
--- a/src/networking/types/icmp_type.rs
+++ b/src/networking/types/icmp_type.rs
@@ -216,15 +216,15 @@ pub fn from_etherparse(icmpv6type: &Icmpv6Type) -> IcmpType {
Icmpv6Type::ParameterProblem(_) => Self::ParameterProblem,
Icmpv6Type::EchoRequest(_) => Self::EchoRequest,
Icmpv6Type::EchoReply(_) => Self::EchoReply,
+ Icmpv6Type::RouterSolicitation => Self::RouterSolicitation,
+ Icmpv6Type::RouterAdvertisement(_) => Self::RouterAdvertisement,
+ Icmpv6Type::NeighborSolicitation => Self::NeighborSolicitation,
+ Icmpv6Type::NeighborAdvertisement(_) => Self::NeighborAdvertisement,
+ Icmpv6Type::Redirect => Self::RedirectMessage,
Icmpv6Type::Unknown { type_u8: id, .. } => match id {
130 => Self::MulticastListenerQuery,
131 => Self::MulticastListenerReport,
132 => Self::MulticastListenerDone,
- 133 => Self::RouterSolicitation,
- 134 => Self::RouterAdvertisement,
- 135 => Self::NeighborSolicitation,
- 136 => Self::NeighborAdvertisement,
- 137 => Self::RedirectMessage,
138 => Self::RouterRenumbering,
139 => Self::ICMPNodeInformationQuery,
140 => Self::ICMPNodeInformationResponse,
diff --git a/src/networking/types/info_address_port_pair.rs b/src/networking/types/info_address_port_pair.rs
index 3e9682d6..a872f85a 100644
--- a/src/networking/types/info_address_port_pair.rs
+++ b/src/networking/types/info_address_port_pair.rs
@@ -1,12 +1,15 @@
//! Module defining the `InfoAddressPortPair` struct, useful to format the output report file and
//! to keep track of statistics about the sniffed traffic.
+use std::cmp::Ordering;
use std::collections::HashMap;
use crate::Service;
use crate::networking::types::arp_type::ArpType;
+use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::icmp_type::IcmpType;
use crate::networking::types::traffic_direction::TrafficDirection;
+use crate::report::types::sort_type::SortType;
use crate::utils::types::timestamp::Timestamp;
/// Struct useful to format the output report file and to keep track of statistics about the sniffed traffic.
@@ -56,4 +59,94 @@ pub fn refresh(&mut self, other: &Self) {
.or_insert(*count);
}
}
+
+ pub fn transmitted_data(&self, data_repr: DataRepr) -> u128 {
+ match data_repr {
+ DataRepr::Packets => self.transmitted_packets,
+ DataRepr::Bytes => self.transmitted_bytes,
+ DataRepr::Bits => self.transmitted_bytes * 8,
+ }
+ }
+
+ pub fn compare(&self, other: &Self, sort_type: SortType, data_repr: DataRepr) -> Ordering {
+ match sort_type {
+ SortType::Ascending => self
+ .transmitted_data(data_repr)
+ .cmp(&other.transmitted_data(data_repr)),
+ SortType::Descending => other
+ .transmitted_data(data_repr)
+ .cmp(&self.transmitted_data(data_repr)),
+ SortType::Neutral => other.final_timestamp.cmp(&self.final_timestamp),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::networking::types::data_representation::DataRepr;
+ use crate::report::types::sort_type::SortType;
+
+ #[test]
+ fn test_info_address_port_pair_data() {
+ let pair1 = InfoAddressPortPair {
+ transmitted_bytes: 1000,
+ transmitted_packets: 10,
+ final_timestamp: Timestamp::new(8, 1300),
+ ..Default::default()
+ };
+ let pair2 = InfoAddressPortPair {
+ transmitted_bytes: 1100,
+ transmitted_packets: 8,
+ final_timestamp: Timestamp::new(15, 0),
+ ..Default::default()
+ };
+
+ assert_eq!(pair1.transmitted_data(DataRepr::Bytes), 1000);
+ assert_eq!(pair1.transmitted_data(DataRepr::Packets), 10);
+ assert_eq!(pair1.transmitted_data(DataRepr::Bits), 8000);
+
+ assert_eq!(pair2.transmitted_data(DataRepr::Bytes), 1100);
+ assert_eq!(pair2.transmitted_data(DataRepr::Packets), 8);
+ assert_eq!(pair2.transmitted_data(DataRepr::Bits), 8800);
+
+ assert_eq!(
+ pair1.compare(&pair2, SortType::Ascending, DataRepr::Bytes),
+ Ordering::Less
+ );
+ assert_eq!(
+ pair1.compare(&pair2, SortType::Descending, DataRepr::Bytes),
+ Ordering::Greater
+ );
+ assert_eq!(
+ pair1.compare(&pair2, SortType::Neutral, DataRepr::Bytes),
+ Ordering::Greater
+ );
+
+ assert_eq!(
+ pair1.compare(&pair2, SortType::Ascending, DataRepr::Packets),
+ Ordering::Greater
+ );
+ assert_eq!(
+ pair1.compare(&pair2, SortType::Descending, DataRepr::Packets),
+ Ordering::Less
+ );
+ assert_eq!(
+ pair1.compare(&pair2, SortType::Neutral, DataRepr::Packets),
+ Ordering::Greater
+ );
+
+ assert_eq!(
+ pair1.compare(&pair2, SortType::Ascending, DataRepr::Bits),
+ Ordering::Less
+ );
+ assert_eq!(
+ pair1.compare(&pair2, SortType::Descending, DataRepr::Bits),
+ Ordering::Greater
+ );
+ assert_eq!(
+ pair1.compare(&pair2, SortType::Neutral, DataRepr::Bits),
+ Ordering::Greater
+ );
+ }
}
diff --git a/src/networking/types/info_traffic.rs b/src/networking/types/info_traffic.rs
index 976e78c7..4d9d16f4 100644
--- a/src/networking/types/info_traffic.rs
+++ b/src/networking/types/info_traffic.rs
@@ -1,8 +1,8 @@
use crate::Service;
-use crate::chart::types::chart_type::ChartType;
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::utils::types::timestamp::Timestamp;
@@ -13,15 +13,11 @@
pub struct InfoTraffic {
/// Total amount of exchanged data
pub tot_data_info: DataInfo,
- /// Total packets including those not filtered
- pub all_packets: u128,
- /// Total bytes including those not filtered
- pub all_bytes: u128,
/// Number of dropped packets
pub dropped_packets: u32,
/// Timestamp of the latest parsed packet
pub last_packet_timestamp: Timestamp,
- /// Map of the filtered traffic
+ /// Map of the traffic
pub map: HashMap,
/// Map of the upper layer services with their data info
pub services: HashMap,
@@ -33,8 +29,6 @@ impl InfoTraffic {
pub fn refresh(&mut self, msg: &mut InfoTraffic) {
self.tot_data_info.refresh(msg.tot_data_info);
- self.all_packets += msg.all_packets;
- self.all_bytes += msg.all_bytes;
self.dropped_packets = msg.dropped_packets;
// it can happen they're equal due to dis-alignments in the PCAP timestamp
@@ -65,27 +59,20 @@ pub fn refresh(&mut self, msg: &mut InfoTraffic) {
}
}
- pub fn get_thumbnail_data(&self, chart_type: ChartType) -> (u128, u128, u128, u128) {
- if chart_type.eq(&ChartType::Bytes) {
- (
- self.tot_data_info.incoming_bytes(),
- self.tot_data_info.outgoing_bytes(),
- self.all_bytes
- - self.tot_data_info.outgoing_bytes()
- - self.tot_data_info.incoming_bytes(),
+ pub fn get_thumbnail_data(&self, data_repr: DataRepr) -> (u128, u128, u128) {
+ let incoming = self.tot_data_info.incoming_data(data_repr);
+ let outgoing = self.tot_data_info.outgoing_data(data_repr);
+ let all = incoming + outgoing;
+ let all_packets = self.tot_data_info.tot_data(DataRepr::Packets);
+ let dropped = match data_repr {
+ DataRepr::Packets => u128::from(self.dropped_packets),
+ DataRepr::Bytes | DataRepr::Bits => {
// assume that the dropped packets have the same size as the average packet
- u128::from(self.dropped_packets) * self.all_bytes / self.all_packets,
- )
- } else {
- (
- self.tot_data_info.incoming_packets(),
- self.tot_data_info.outgoing_packets(),
- self.all_packets
- - self.tot_data_info.outgoing_packets()
- - self.tot_data_info.incoming_packets(),
- u128::from(self.dropped_packets),
- )
- }
+ u128::from(self.dropped_packets) * all / all_packets
+ }
+ };
+
+ (incoming, outgoing, dropped)
}
pub fn take_but_leave_something(&mut self) -> Self {
diff --git a/src/networking/types/ip_collection.rs b/src/networking/types/ip_collection.rs
index 20b469ac..09b0d327 100644
--- a/src/networking/types/ip_collection.rs
+++ b/src/networking/types/ip_collection.rs
@@ -3,18 +3,15 @@
use std::str::FromStr;
#[derive(Debug, Eq, PartialEq, Clone)]
-pub(crate) struct AddressCollection {
+pub(crate) struct IpCollection {
ips: Vec,
ranges: Vec>,
}
-impl AddressCollection {
+impl IpCollection {
const SEPARATOR: char = ',';
const RANGE_SEPARATOR: char = '-';
- pub const PLACEHOLDER_STR: &'static str =
- "0.0.0.0-255.255.255.255, ::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff";
-
pub(crate) fn new(str: &str) -> Option {
let str = str.replace(' ', "");
@@ -62,9 +59,9 @@ pub(crate) fn contains(&self, ip: &IpAddr) -> bool {
}
}
-impl Default for AddressCollection {
+impl Default for IpCollection {
fn default() -> Self {
- AddressCollection {
+ IpCollection {
ips: vec![],
ranges: vec![
RangeInclusive::new(
@@ -89,11 +86,11 @@ mod tests {
use std::ops::RangeInclusive;
use std::str::FromStr;
- use crate::networking::types::ip_collection::AddressCollection;
+ use crate::networking::types::ip_collection::IpCollection;
#[test]
fn test_default_collection_contains_everything() {
- let collection = AddressCollection::default();
+ let collection = IpCollection::default();
assert!(collection.contains(&IpAddr::from_str("1.1.1.1").unwrap()));
assert!(collection.contains(&IpAddr::from_str("0.0.0.0").unwrap()));
assert!(collection.contains(&IpAddr::from_str("255.255.255.255").unwrap()));
@@ -116,8 +113,8 @@ fn test_default_collection_contains_everything() {
#[test]
fn test_new_collections_1() {
assert_eq!(
- AddressCollection::new("1.1.1.1,2.2.2.2").unwrap(),
- AddressCollection {
+ IpCollection::new("1.1.1.1,2.2.2.2").unwrap(),
+ IpCollection {
ips: vec![
IpAddr::from_str("1.1.1.1").unwrap(),
IpAddr::from_str("2.2.2.2").unwrap()
@@ -127,11 +124,9 @@ fn test_new_collections_1() {
);
assert_eq!(
- AddressCollection::new(
- "1.1.1.1, 2.2.2.2, 3.3.3.3 - 5.5.5.5, 10.0.0.1-10.0.0.255,9.9.9.9",
- )
- .unwrap(),
- AddressCollection {
+ IpCollection::new("1.1.1.1, 2.2.2.2, 3.3.3.3 - 5.5.5.5, 10.0.0.1-10.0.0.255,9.9.9.9",)
+ .unwrap(),
+ IpCollection {
ips: vec![
IpAddr::from_str("1.1.1.1").unwrap(),
IpAddr::from_str("2.2.2.2").unwrap(),
@@ -151,8 +146,8 @@ fn test_new_collections_1() {
);
assert_eq!(
- AddressCollection::new(" aaaa::ffff,bbbb::1-cccc::2").unwrap(),
- AddressCollection {
+ IpCollection::new(" aaaa::ffff,bbbb::1-cccc::2").unwrap(),
+ IpCollection {
ips: vec![IpAddr::from_str("aaaa::ffff").unwrap(),],
ranges: vec![RangeInclusive::new(
IpAddr::from_str("bbbb::1").unwrap(),
@@ -165,8 +160,8 @@ fn test_new_collections_1() {
#[test]
fn test_new_collections_2() {
assert_eq!(
- AddressCollection::new("1.1.1.1,2.2.2.2, 8.8.8.8 ").unwrap(),
- AddressCollection {
+ IpCollection::new("1.1.1.1,2.2.2.2, 8.8.8.8 ").unwrap(),
+ IpCollection {
ips: vec![
IpAddr::from_str("1.1.1.1").unwrap(),
IpAddr::from_str("2.2.2.2").unwrap(),
@@ -177,8 +172,8 @@ fn test_new_collections_2() {
);
assert_eq!(
- AddressCollection::new(" 1.1.1.1 -1.1.1.1").unwrap(),
- AddressCollection {
+ IpCollection::new(" 1.1.1.1 -1.1.1.1").unwrap(),
+ IpCollection {
ips: vec![],
ranges: vec![RangeInclusive::new(
IpAddr::from_str("1.1.1.1").unwrap(),
@@ -188,9 +183,9 @@ fn test_new_collections_2() {
);
assert_eq!(
- AddressCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9.9",)
+ IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9.9",)
.unwrap(),
- AddressCollection {
+ IpCollection {
ips: vec![
IpAddr::from_str("1.1.1.1").unwrap(),
IpAddr::from_str("2.2.2.2").unwrap(),
@@ -210,8 +205,8 @@ fn test_new_collections_2() {
);
assert_eq!(
- AddressCollection::new("aaaa::ffff,bbbb::1-cccc::2,ff::dd").unwrap(),
- AddressCollection {
+ IpCollection::new("aaaa::ffff,bbbb::1-cccc::2,ff::dd").unwrap(),
+ IpCollection {
ips: vec![
IpAddr::from_str("aaaa::ffff").unwrap(),
IpAddr::from_str("ff::dd").unwrap()
@@ -227,32 +222,32 @@ fn test_new_collections_2() {
#[test]
fn test_new_collections_invalid() {
assert_eq!(
- AddressCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9"),
+ IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9"),
None
);
assert_eq!(
- AddressCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1:10.0.0.255,9.9.9.9"),
+ IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1:10.0.0.255,9.9.9.9"),
None
);
- assert_eq!(AddressCollection::new("1.1.1.1-aa::ff"), None);
+ assert_eq!(IpCollection::new("1.1.1.1-aa::ff"), None);
- assert_eq!(AddressCollection::new("aa::ff-1.1.1.1"), None);
+ assert_eq!(IpCollection::new("aa::ff-1.1.1.1"), None);
- assert_eq!(AddressCollection::new("aa::ff-aa::ee"), None);
+ assert_eq!(IpCollection::new("aa::ff-aa::ee"), None);
- assert_eq!(AddressCollection::new("1.1.1.1-1.1.0.1"), None);
+ assert_eq!(IpCollection::new("1.1.1.1-1.1.0.1"), None);
- assert_eq!(AddressCollection::new("1.1.1.1-2.2.2.2-3.3.3.3"), None);
+ assert_eq!(IpCollection::new("1.1.1.1-2.2.2.2-3.3.3.3"), None);
- assert_eq!(AddressCollection::new("1.1.1.1-2.2.2.2-"), None);
+ assert_eq!(IpCollection::new("1.1.1.1-2.2.2.2-"), None);
}
#[test]
fn test_ip_collection_contains() {
let collection =
- AddressCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9.9")
+ IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9.9")
.unwrap();
assert!(collection.contains(&IpAddr::from_str("1.1.1.1").unwrap()));
assert!(collection.contains(&IpAddr::from_str("2.2.2.2").unwrap()));
@@ -268,12 +263,12 @@ fn test_ip_collection_contains() {
assert!(!collection.contains(&IpAddr::from_str("9.9.9.10").unwrap()));
assert!(!collection.contains(&IpAddr::from_str("3.3.3.2").unwrap()));
- let collection_2 = AddressCollection::new("1.1.1.0-1.1.9.0").unwrap();
+ let collection_2 = IpCollection::new("1.1.1.0-1.1.9.0").unwrap();
assert!(!collection_2.contains(&IpAddr::from_str("1.1.100.5").unwrap()));
assert!(collection_2.contains(&IpAddr::from_str("1.1.3.255").unwrap()));
// check that ipv4 range doesn't contain ipv6
- let collection_3 = AddressCollection::new("0.0.0.0-255.255.255.255").unwrap();
+ let collection_3 = IpCollection::new("0.0.0.0-255.255.255.255").unwrap();
assert!(!collection_3.contains(&IpAddr::from_str("::").unwrap()));
assert!(!collection_3.contains(&IpAddr::from_str("1111::2222").unwrap()));
}
@@ -281,7 +276,7 @@ fn test_ip_collection_contains() {
#[test]
fn test_ip_collection_contains_ipv6() {
let collection =
- AddressCollection::new( "2001:db8:1234:0000:0000:0000:0000:0000-2001:db8:1234:ffff:ffff:ffff:ffff:ffff,daa::aad,caa::aac").unwrap();
+ IpCollection::new( "2001:db8:1234:0000:0000:0000:0000:0000-2001:db8:1234:ffff:ffff:ffff:ffff:ffff,daa::aad,caa::aac").unwrap();
assert!(
collection
.contains(&IpAddr::from_str("2001:db8:1234:0000:0000:0000:0000:0000").unwrap())
@@ -315,14 +310,14 @@ fn test_ip_collection_contains_ipv6() {
assert!(!collection.contains(&IpAddr::from_str("da::aad").unwrap()));
assert!(!collection.contains(&IpAddr::from_str("caa::aab").unwrap()));
- let collection_2 = AddressCollection::new("aa::bb-aa:1::00").unwrap();
+ let collection_2 = IpCollection::new("aa::bb-aa:1::00").unwrap();
assert!(!collection_2.contains(&IpAddr::from_str("aa:11::0").unwrap()));
assert!(collection_2.contains(&IpAddr::from_str("aa::bc").unwrap()));
assert!(collection_2.contains(&IpAddr::from_str("aa::bbcc").unwrap()));
assert!(collection_2.contains(&IpAddr::from_str("00aa:0001::00").unwrap()));
// check that ipv6 range doesn't contain ipv4
- let collection_3 = AddressCollection::new("0000::0000-ffff::8888").unwrap();
+ let collection_3 = IpCollection::new("0000::0000-ffff::8888").unwrap();
assert!(!collection_3.contains(&IpAddr::from_str("192.168.1.1").unwrap()));
assert!(!collection_3.contains(&IpAddr::from_str("0.0.0.0").unwrap()));
}
diff --git a/src/networking/types/ip_version.rs b/src/networking/types/ip_version.rs
index 1b844472..0ec6928b 100644
--- a/src/networking/types/ip_version.rs
+++ b/src/networking/types/ip_version.rs
@@ -14,29 +14,3 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self:?}")
}
}
-
-impl IpVersion {
- pub(crate) const ALL: [IpVersion; 2] = [IpVersion::IPv4, IpVersion::IPv6];
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_ip_version_display() {
- for version in IpVersion::ALL {
- match version {
- IpVersion::IPv4 => assert_eq!(version.to_string(), "IPv4"),
- IpVersion::IPv6 => assert_eq!(version.to_string(), "IPv6"),
- }
- }
- }
-
- #[test]
- fn test_all_ip_versions_collection() {
- assert_eq!(IpVersion::ALL.len(), 2);
- assert_eq!(IpVersion::ALL.get(0).unwrap(), &IpVersion::IPv4);
- assert_eq!(IpVersion::ALL.get(1).unwrap(), &IpVersion::IPv6);
- }
-}
diff --git a/src/networking/types/mod.rs b/src/networking/types/mod.rs
index 8553faaf..ab112f21 100644
--- a/src/networking/types/mod.rs
+++ b/src/networking/types/mod.rs
@@ -2,11 +2,11 @@
pub mod arp_type;
pub mod asn;
pub mod bogon;
-pub mod byte_multiple;
pub mod capture_context;
+pub mod config_device;
pub mod data_info;
pub mod data_info_host;
-pub mod filters;
+pub mod data_representation;
pub mod host;
pub mod host_data_states;
pub mod icmp_type;
@@ -17,7 +17,6 @@
pub mod my_device;
pub mod my_link_type;
pub mod packet_filters_fields;
-pub mod port_collection;
pub mod protocol;
pub mod service;
pub mod service_query;
diff --git a/src/networking/types/my_link_type.rs b/src/networking/types/my_link_type.rs
index d9d74dd4..cf5187f2 100644
--- a/src/networking/types/my_link_type.rs
+++ b/src/networking/types/my_link_type.rs
@@ -1,11 +1,7 @@
-use iced::Font;
-use iced::widget::Column;
use pcap::Linktype;
-use crate::gui::styles::text::TextType;
-use crate::gui::types::message::Message;
+use crate::Language;
use crate::translations::translations_3::link_type_translation;
-use crate::{Language, StyleType};
/// Currently supported link types
#[derive(Copy, Clone, Default)]
@@ -16,6 +12,8 @@ pub enum MyLinkType {
Loop(Linktype),
IPv4(Linktype),
IPv6(Linktype),
+ LinuxSll(Linktype),
+ LinuxSll2(Linktype),
Unsupported(Linktype),
#[default]
NotYetAssigned,
@@ -34,6 +32,8 @@ pub fn from_pcap_link_type(link_type: Linktype) -> Self {
Linktype::LOOP => Self::Loop(link_type),
Linktype::IPV4 => Self::IPv4(link_type),
Linktype::IPV6 => Self::IPv6(link_type),
+ Linktype::LINUX_SLL => Self::LinuxSll(link_type),
+ Linktype::LINUX_SLL2 => Self::LinuxSll2(link_type),
_ => Self::Unsupported(link_type),
}
}
@@ -46,6 +46,8 @@ pub fn full_print_on_one_line(self, language: Language) -> String {
| Self::Loop(l)
| Self::IPv4(l)
| Self::IPv6(l)
+ | Self::LinuxSll(l)
+ | Self::LinuxSll2(l)
| Self::Unsupported(l) => {
format!(
"{}: {} ({})",
@@ -57,32 +59,4 @@ pub fn full_print_on_one_line(self, language: Language) -> String {
Self::NotYetAssigned => String::new(),
}
}
-
- pub fn link_type_col<'a>(
- self,
- language: Language,
- font: Font,
- ) -> Column<'a, Message, StyleType> {
- match self {
- Self::Null(l)
- | Self::Ethernet(l)
- | Self::RawIp(l)
- | Self::Loop(l)
- | Self::IPv4(l)
- | Self::IPv6(l)
- | Self::Unsupported(l) => {
- let link_info = format!(
- "{} ({})",
- l.get_name().unwrap_or_else(|_| l.0.to_string()),
- l.get_description().unwrap_or_else(|_| String::new())
- );
- TextType::highlighted_subtitle_with_desc(
- link_type_translation(language),
- &link_info,
- font,
- )
- }
- Self::NotYetAssigned => Column::new().height(0),
- }
- }
}
diff --git a/src/networking/types/port_collection.rs b/src/networking/types/port_collection.rs
deleted file mode 100644
index 393c35a4..00000000
--- a/src/networking/types/port_collection.rs
+++ /dev/null
@@ -1,180 +0,0 @@
-use std::ops::RangeInclusive;
-use std::str::FromStr;
-
-#[derive(Debug, Eq, PartialEq, Clone)]
-pub(crate) struct PortCollection {
- pub(crate) ports: Vec,
- pub(crate) ranges: Vec>,
-}
-
-impl PortCollection {
- const SEPARATOR: char = ',';
- const RANGE_SEPARATOR: char = '-';
-
- pub const PLACEHOLDER_STR: &'static str = "0-65535";
-
- pub(crate) fn new(str: &str) -> Option {
- let str = str.replace(' ', "");
-
- if str.is_empty() {
- return Some(Self::default());
- }
-
- let mut ports = Vec::new();
- let mut ranges = Vec::new();
-
- let objects: Vec<&str> = str.split(Self::SEPARATOR).collect();
- for object in objects {
- if object.contains(Self::RANGE_SEPARATOR) {
- // port range
- let mut subparts = object.split(Self::RANGE_SEPARATOR);
- if subparts.clone().count() != 2 {
- return None;
- }
- let (lower_str, upper_str) =
- (subparts.next().unwrap_or(""), subparts.next().unwrap_or(""));
- let lower_port = u16::from_str(lower_str).ok()?;
- let upper_port = u16::from_str(upper_str).ok()?;
- let range = RangeInclusive::new(lower_port, upper_port);
- if range.is_empty() {
- return None;
- }
- ranges.push(range);
- } else {
- // individual port
- let port = u16::from_str(object).ok()?;
- ports.push(port);
- }
- }
-
- Some(Self { ports, ranges })
- }
-
- pub(crate) fn contains(&self, port: Option) -> bool {
- // ignore port filter in case of ICMP or ARP
- let Some(p) = port else {
- return true;
- };
-
- for range in &self.ranges {
- if range.contains(&p) {
- return true;
- }
- }
- self.ports.contains(&p)
- }
-}
-
-impl Default for PortCollection {
- fn default() -> Self {
- PortCollection {
- ports: vec![],
- ranges: vec![RangeInclusive::new(u16::MIN, u16::MAX)],
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::networking::types::port_collection::PortCollection;
-
- #[test]
- fn test_default_collection_contains_everything() {
- let collection = PortCollection::default();
- assert!(collection.contains(Some(0)));
- assert!(collection.contains(Some(1)));
- assert!(collection.contains(Some(2)));
- assert!(collection.contains(Some(80)));
- assert!(collection.contains(Some(8080)));
- assert!(collection.contains(Some(55333)));
- assert!(collection.contains(Some(65535)));
- }
-
- #[test]
- fn test_new_port_collections() {
- assert_eq!(
- PortCollection::new("0").unwrap(),
- PortCollection {
- ports: vec![0],
- ranges: vec![]
- }
- );
-
- assert_eq!(
- PortCollection::new(" 0 ").unwrap(),
- PortCollection {
- ports: vec![0],
- ranges: vec![]
- }
- );
-
- assert_eq!(
- PortCollection::new("1,2,3,4,999").unwrap(),
- PortCollection {
- ports: vec![1, 2, 3, 4, 999],
- ranges: vec![]
- }
- );
-
- assert_eq!(
- PortCollection::new("1, 2, 3, 4, 900-999").unwrap(),
- PortCollection {
- ports: vec![1, 2, 3, 4],
- ranges: vec![900..=999]
- }
- );
-
- assert_eq!(
- PortCollection::new("1 - 999").unwrap(),
- PortCollection {
- ports: vec![],
- ranges: vec![1..=999]
- }
- );
-
- assert_eq!(
- PortCollection::new(" 1,2,10-20,3,4, 999-1200 ").unwrap(),
- PortCollection {
- ports: vec![1, 2, 3, 4],
- ranges: vec![10..=20, 999..=1200]
- }
- );
- }
-
- #[test]
- fn test_new_port_collections_invalid() {
- assert_eq!(PortCollection::new("1,2,10-20,3,4,-1200"), None);
-
- assert_eq!(PortCollection::new("1,2,10-20,3,4,999:1200"), None);
-
- assert_eq!(PortCollection::new("1,2,10-20,3,4,999-1200,"), None);
-
- assert_eq!(PortCollection::new("999-1"), None);
-
- assert_eq!(PortCollection::new("1:999"), None);
-
- assert_eq!(PortCollection::new("1-2-3"), None);
-
- assert_eq!(PortCollection::new("1-2-"), None);
- }
-
- #[test]
- fn test_port_collection_contains() {
- let collection = PortCollection::new("1,2,25-30,55,101-117").unwrap();
- assert!(collection.contains(Some(1)));
- assert!(collection.contains(Some(2)));
- assert!(collection.contains(Some(25)));
- assert!(collection.contains(Some(27)));
- assert!(collection.contains(Some(30)));
- assert!(collection.contains(Some(55)));
- assert!(collection.contains(Some(101)));
- assert!(collection.contains(Some(109)));
- assert!(collection.contains(Some(117)));
- assert!(!collection.contains(Some(4)));
- assert!(!collection.contains(Some(24)));
- assert!(!collection.contains(Some(31)));
- assert!(!collection.contains(Some(100)));
- assert!(!collection.contains(Some(118)));
- assert!(!collection.contains(Some(8080)));
- }
-}
diff --git a/src/networking/types/protocol.rs b/src/networking/types/protocol.rs
index 31f20e62..bef8c720 100644
--- a/src/networking/types/protocol.rs
+++ b/src/networking/types/protocol.rs
@@ -19,33 +19,3 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
-
-impl Protocol {
- pub const ALL: [Protocol; 4] = [Protocol::TCP, Protocol::UDP, Protocol::ICMP, Protocol::ARP];
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_protocol_display() {
- for protocol in Protocol::ALL {
- match protocol {
- Protocol::TCP => assert_eq!(protocol.to_string(), "TCP"),
- Protocol::UDP => assert_eq!(protocol.to_string(), "UDP"),
- Protocol::ICMP => assert_eq!(protocol.to_string(), "ICMP"),
- Protocol::ARP => assert_eq!(protocol.to_string(), "ARP"),
- }
- }
- }
-
- #[test]
- fn test_all_protocols_collection() {
- assert_eq!(Protocol::ALL.len(), 4);
- assert_eq!(Protocol::ALL.get(0).unwrap(), &Protocol::TCP);
- assert_eq!(Protocol::ALL.get(1).unwrap(), &Protocol::UDP);
- assert_eq!(Protocol::ALL.get(2).unwrap(), &Protocol::ICMP);
- assert_eq!(Protocol::ALL.get(3).unwrap(), &Protocol::ARP);
- }
-}
diff --git a/src/notifications/notify_and_log.rs b/src/notifications/notify_and_log.rs
index 93acddd9..67a27a8c 100644
--- a/src/notifications/notify_and_log.rs
+++ b/src/notifications/notify_and_log.rs
@@ -1,8 +1,8 @@
use crate::InfoTraffic;
-use crate::chart::types::chart_type::ChartType;
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::service::Service;
use crate::notifications::types::logged_notification::{
@@ -31,8 +31,8 @@ pub fn notify_and_log(
let data_info = info_traffic_msg.tot_data_info;
// data threshold
if let Some(threshold) = notifications.data_notification.threshold {
- let chart_type = notifications.data_notification.chart_type;
- if data_info.tot_data(chart_type) > u128::from(threshold) {
+ let data_repr = notifications.data_notification.data_repr;
+ if data_info.tot_data(data_repr) > u128::from(threshold) {
//log this notification
logged_notifications.1 += 1;
if logged_notifications.0.len() >= 30 {
@@ -43,13 +43,13 @@ pub fn notify_and_log(
.push_front(LoggedNotification::DataThresholdExceeded(
DataThresholdExceeded {
id: logged_notifications.1,
- chart_type,
+ data_repr,
threshold: notifications.data_notification.previous_threshold,
data_info,
timestamp: get_formatted_timestamp(timestamp),
is_expanded: false,
- hosts: hosts_list(info_traffic_msg, chart_type),
- services: services_list(info_traffic_msg, chart_type),
+ hosts: hosts_list(info_traffic_msg, data_repr),
+ services: services_list(info_traffic_msg, data_repr),
},
));
if sound_to_play.eq(&Sound::None) {
@@ -98,7 +98,7 @@ pub fn notify_and_log(
logged_notifications.1 - emitted_notifications_prev
}
-fn hosts_list(info_traffic_msg: &InfoTraffic, chart_type: ChartType) -> Vec<(Host, DataInfoHost)> {
+fn hosts_list(info_traffic_msg: &InfoTraffic, data_repr: DataRepr) -> Vec<(Host, DataInfoHost)> {
let mut hosts: Vec<(Host, DataInfoHost)> = info_traffic_msg
.hosts
.iter()
@@ -106,7 +106,7 @@ fn hosts_list(info_traffic_msg: &InfoTraffic, chart_type: ChartType) -> Vec<(Hos
.collect();
hosts.sort_by(|(_, a), (_, b)| {
a.data_info
- .compare(&b.data_info, SortType::Descending, chart_type)
+ .compare(&b.data_info, SortType::Descending, data_repr)
});
let n_entry = min(hosts.len(), 4);
hosts
@@ -117,17 +117,14 @@ fn hosts_list(info_traffic_msg: &InfoTraffic, chart_type: ChartType) -> Vec<(Hos
.collect()
}
-fn services_list(
- info_traffic_msg: &InfoTraffic,
- chart_type: ChartType,
-) -> Vec<(Service, DataInfo)> {
+fn services_list(info_traffic_msg: &InfoTraffic, data_repr: DataRepr) -> Vec<(Service, DataInfo)> {
let mut services: Vec<(Service, DataInfo)> = info_traffic_msg
.services
.iter()
.filter(|(service, _)| service != &&Service::NotApplicable)
.map(|(s, data)| (*s, *data))
.collect();
- services.sort_by(|(_, a), (_, b)| a.compare(b, SortType::Descending, chart_type));
+ services.sort_by(|(_, a), (_, b)| a.compare(b, SortType::Descending, data_repr));
let n_entry = min(services.len(), 4);
services
.get(..n_entry)
diff --git a/src/notifications/types/logged_notification.rs b/src/notifications/types/logged_notification.rs
index 6a75d2cd..1b354370 100644
--- a/src/notifications/types/logged_notification.rs
+++ b/src/notifications/types/logged_notification.rs
@@ -1,6 +1,6 @@
-use crate::chart::types::chart_type::ChartType;
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::service::Service;
@@ -38,7 +38,7 @@ pub fn expand(&mut self, expand: bool) {
#[derive(Clone)]
pub struct DataThresholdExceeded {
pub(crate) id: usize,
- pub(crate) chart_type: ChartType,
+ pub(crate) data_repr: DataRepr,
pub(crate) threshold: u64,
pub(crate) data_info: DataInfo,
pub(crate) timestamp: String,
diff --git a/src/notifications/types/notifications.rs b/src/notifications/types/notifications.rs
index 8935ad07..7a9c9d05 100644
--- a/src/notifications/types/notifications.rs
+++ b/src/notifications/types/notifications.rs
@@ -1,11 +1,12 @@
use serde::{Deserialize, Serialize};
use crate::ByteMultiple;
-use crate::chart::types::chart_type::ChartType;
+use crate::networking::types::data_representation::DataRepr;
use crate::notifications::types::sound::Sound;
/// 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,9 +33,10 @@ pub enum Notification {
}
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug, Copy)]
+#[serde(default)]
pub struct DataNotification {
/// Data representation
- pub chart_type: ChartType,
+ pub data_repr: DataRepr,
/// Threshold of received + sent bytes; if exceeded a notification is emitted
pub threshold: Option,
/// B, KB, MB or GB
@@ -48,7 +50,7 @@ pub struct DataNotification {
impl Default for DataNotification {
fn default() -> Self {
DataNotification {
- chart_type: ChartType::Bytes,
+ data_repr: DataRepr::Bytes,
threshold: None,
byte_multiple: ByteMultiple::KB,
sound: Sound::Pop,
@@ -101,6 +103,7 @@ pub fn from(value: &str, existing: Option) -> 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,
diff --git a/src/report/get_report_entries.rs b/src/report/get_report_entries.rs
index f7742a7d..1f6646c4 100644
--- a/src/report/get_report_entries.rs
+++ b/src/report/get_report_entries.rs
@@ -4,10 +4,11 @@
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::report::types::sort_type::SortType;
-use crate::{ChartType, InfoTraffic, ReportSortType, Service, Sniffer};
+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,
@@ -46,24 +47,12 @@ pub fn get_searched_entries(
})
.collect();
- all_results.sort_by(|&(_, a), &(_, b)| match sniffer.report_sort_type {
- ReportSortType {
- byte_sort,
- packet_sort: SortType::Neutral,
- } => match byte_sort {
- SortType::Ascending => a.transmitted_bytes.cmp(&b.transmitted_bytes),
- SortType::Descending => b.transmitted_bytes.cmp(&a.transmitted_bytes),
- SortType::Neutral => b.final_timestamp.cmp(&a.final_timestamp),
- },
- ReportSortType {
- byte_sort: SortType::Neutral,
- packet_sort,
- } => match packet_sort {
- SortType::Ascending => a.transmitted_packets.cmp(&b.transmitted_packets),
- SortType::Descending => b.transmitted_packets.cmp(&a.transmitted_packets),
- SortType::Neutral => b.final_timestamp.cmp(&a.final_timestamp),
- },
- _ => b.final_timestamp.cmp(&a.final_timestamp),
+ all_results.sort_by(|&(_, a), &(_, b)| {
+ a.compare(
+ b,
+ sniffer.conf.report_sort_type,
+ sniffer.traffic_chart.data_repr,
+ )
});
let upper_bound = min(sniffer.page_number * 20, all_results.len());
@@ -82,12 +71,12 @@ pub fn get_searched_entries(
pub fn get_host_entries(
info_traffic: &InfoTraffic,
- chart_type: ChartType,
+ 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, chart_type));
+ 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]
@@ -98,7 +87,7 @@ pub fn get_host_entries(
pub fn get_service_entries(
info_traffic: &InfoTraffic,
- chart_type: ChartType,
+ data_repr: DataRepr,
sort_type: SortType,
) -> Vec<(Service, DataInfo)> {
let mut sorted_vec: Vec<(&Service, &DataInfo)> = info_traffic
@@ -107,7 +96,7 @@ pub fn get_service_entries(
.filter(|(service, _)| service != &&Service::NotApplicable)
.collect();
- sorted_vec.sort_by(|&(_, a), &(_, b)| a.compare(b, sort_type, chart_type));
+ 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]
diff --git a/src/report/types/mod.rs b/src/report/types/mod.rs
index ebbc80a9..588104e0 100644
--- a/src/report/types/mod.rs
+++ b/src/report/types/mod.rs
@@ -1,4 +1,3 @@
pub mod report_col;
-pub mod report_sort_type;
pub mod search_parameters;
pub mod sort_type;
diff --git a/src/report/types/report_col.rs b/src/report/types/report_col.rs
index 1ec1877f..2e5db7b1 100644
--- a/src/report/types/report_col.rs
+++ b/src/report/types/report_col.rs
@@ -1,10 +1,8 @@
-use crate::ByteMultiple;
use crate::networking::types::address_port_pair::AddressPortPair;
+use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
use crate::report::types::search_parameters::FilterInputType;
-use crate::translations::translations::{
- address_translation, bytes_translation, packets_translation, protocol_translation,
-};
+use crate::translations::translations::{address_translation, protocol_translation};
use crate::translations::translations_2::{destination_translation, source_translation};
use crate::translations::translations_3::{port_translation, service_translation};
use crate::translations::types::language::Language;
@@ -25,36 +23,30 @@ pub enum ReportCol {
DstPort,
Proto,
Service,
- Bytes,
- Packets,
+ Data,
}
impl ReportCol {
- pub(crate) const ALL: [ReportCol; 8] = [
+ pub(crate) const ALL: [ReportCol; 7] = [
ReportCol::SrcIp,
ReportCol::SrcPort,
ReportCol::DstIp,
ReportCol::DstPort,
ReportCol::Proto,
ReportCol::Service,
- ReportCol::Bytes,
- ReportCol::Packets,
+ ReportCol::Data,
];
pub(crate) const FILTER_COLUMNS_WIDTH: f32 = 4.0 * SMALL_COL_WIDTH + 2.0 * LARGE_COL_WIDTH;
- pub(crate) fn get_title(&self, language: Language) -> String {
+ pub(crate) fn get_title(&self, language: Language, data_repr: DataRepr) -> String {
match self {
ReportCol::SrcIp | ReportCol::DstIp => address_translation(language).to_string(),
ReportCol::SrcPort | ReportCol::DstPort => port_translation(language).to_string(),
ReportCol::Proto => protocol_translation(language).to_string(),
ReportCol::Service => service_translation(language).to_string(),
- ReportCol::Bytes => {
- let mut str = bytes_translation(language).to_string();
- str.remove(0).to_uppercase().to_string() + &str
- }
- ReportCol::Packets => {
- let mut str = packets_translation(language).to_string();
+ ReportCol::Data => {
+ let mut str = data_repr.get_label(language).to_string();
str.remove(0).to_uppercase().to_string() + &str
}
}
@@ -72,7 +64,12 @@ pub(crate) fn get_title_direction_info(&self, language: Language) -> String {
}
}
- pub(crate) fn get_value(&self, key: &AddressPortPair, val: &InfoAddressPortPair) -> String {
+ pub(crate) fn get_value(
+ &self,
+ key: &AddressPortPair,
+ val: &InfoAddressPortPair,
+ data_repr: DataRepr,
+ ) -> String {
match self {
ReportCol::SrcIp => key.address1.to_string(),
ReportCol::SrcPort => {
@@ -92,8 +89,7 @@ pub(crate) fn get_value(&self, key: &AddressPortPair, val: &InfoAddressPortPair)
}
ReportCol::Proto => key.protocol.to_string(),
ReportCol::Service => val.service.to_string(),
- ReportCol::Bytes => ByteMultiple::formatted_string(val.transmitted_bytes),
- ReportCol::Packets => val.transmitted_packets.to_string(),
+ ReportCol::Data => data_repr.formatted_string(val.transmitted_data(data_repr)),
}
}
@@ -126,7 +122,7 @@ pub(crate) fn get_filter_input_type(&self) -> FilterInputType {
ReportCol::DstPort => FilterInputType::PortDst,
ReportCol::Proto => FilterInputType::Proto,
ReportCol::Service => FilterInputType::Service,
- ReportCol::Bytes | ReportCol::Packets => FilterInputType::Country, // just to not panic...
+ ReportCol::Data => FilterInputType::Country, // just to not panic...
}
}
}
diff --git a/src/report/types/report_sort_type.rs b/src/report/types/report_sort_type.rs
deleted file mode 100644
index 161d79de..00000000
--- a/src/report/types/report_sort_type.rs
+++ /dev/null
@@ -1,175 +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::report_col::ReportCol;
-use crate::report::types::sort_type::SortType;
-use crate::utils::types::icon::Icon;
-
-/// Struct representing the possible kinds of sort for displayed relevant connections.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
-pub struct ReportSortType {
- pub byte_sort: SortType,
- pub packet_sort: SortType,
-}
-
-impl ReportSortType {
- pub fn next_sort(self, report_col: &ReportCol) -> Self {
- match report_col {
- ReportCol::Bytes => Self {
- byte_sort: self.byte_sort.next_sort(),
- packet_sort: SortType::Neutral,
- },
- ReportCol::Packets => Self {
- byte_sort: SortType::Neutral,
- packet_sort: self.packet_sort.next_sort(),
- },
- _ => Self::default(),
- }
- }
-
- pub fn icon<'a>(self, report_col: &ReportCol) -> Text<'a, StyleType> {
- match report_col {
- ReportCol::Bytes => self.byte_sort.icon(),
- ReportCol::Packets => self.packet_sort.icon(),
- _ => Icon::SortNeutral.to_text(),
- }
- }
-
- pub fn button_type(self, report_col: &ReportCol) -> ButtonType {
- match report_col {
- ReportCol::Bytes => self.byte_sort.button_type(),
- ReportCol::Packets => self.packet_sort.button_type(),
- _ => ButtonType::SortArrows,
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::report::types::report_col::ReportCol;
- 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 {
- byte_sort: SortType::Neutral,
- packet_sort: SortType::Neutral
- }
- );
-
- sort = sort.next_sort(&ReportCol::Packets);
- assert_eq!(
- sort,
- ReportSortType {
- byte_sort: SortType::Neutral,
- packet_sort: SortType::Descending
- }
- );
-
- sort = sort.next_sort(&ReportCol::Packets);
- assert_eq!(
- sort,
- ReportSortType {
- byte_sort: SortType::Neutral,
- packet_sort: SortType::Ascending
- }
- );
-
- sort = sort.next_sort(&ReportCol::Packets);
- assert_eq!(
- sort,
- ReportSortType {
- byte_sort: SortType::Neutral,
- packet_sort: SortType::Neutral
- }
- );
-
- sort = sort.next_sort(&ReportCol::Packets);
- assert_eq!(
- sort,
- ReportSortType {
- byte_sort: SortType::Neutral,
- packet_sort: SortType::Descending
- }
- );
-
- sort = sort.next_sort(&ReportCol::Bytes);
- assert_eq!(
- sort,
- ReportSortType {
- byte_sort: SortType::Descending,
- packet_sort: SortType::Neutral
- }
- );
-
- sort = sort.next_sort(&ReportCol::Packets);
- assert_eq!(
- sort,
- ReportSortType {
- byte_sort: SortType::Neutral,
- packet_sort: SortType::Descending
- }
- );
-
- sort = sort.next_sort(&ReportCol::Bytes);
- assert_eq!(
- sort,
- ReportSortType {
- byte_sort: SortType::Descending,
- packet_sort: SortType::Neutral
- }
- );
-
- sort = sort.next_sort(&ReportCol::Bytes);
- assert_eq!(
- sort,
- ReportSortType {
- byte_sort: SortType::Ascending,
- packet_sort: SortType::Neutral
- }
- );
-
- sort = sort.next_sort(&ReportCol::Packets);
- assert_eq!(
- sort,
- ReportSortType {
- byte_sort: SortType::Neutral,
- packet_sort: SortType::Descending
- }
- );
-
- sort = sort.next_sort(&ReportCol::Bytes);
- assert_eq!(
- sort,
- ReportSortType {
- byte_sort: SortType::Descending,
- packet_sort: SortType::Neutral
- }
- );
-
- sort = sort.next_sort(&ReportCol::Bytes);
- assert_eq!(
- sort,
- ReportSortType {
- byte_sort: SortType::Ascending,
- packet_sort: SortType::Neutral
- }
- );
-
- sort = sort.next_sort(&ReportCol::Bytes);
- assert_eq!(
- sort,
- ReportSortType {
- byte_sort: SortType::Neutral,
- packet_sort: SortType::Neutral
- }
- );
- }
-}
diff --git a/src/report/types/sort_type.rs b/src/report/types/sort_type.rs
index 2dec0be5..b991a0e2 100644
--- a/src/report/types/sort_type.rs
+++ b/src/report/types/sort_type.rs
@@ -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,
diff --git a/src/translations/mod.rs b/src/translations/mod.rs
index b55b4908..408e867e 100644
--- a/src/translations/mod.rs
+++ b/src/translations/mod.rs
@@ -3,4 +3,5 @@
pub mod translations_2;
pub mod translations_3;
pub mod translations_4;
+pub mod translations_5;
pub mod types;
diff --git a/src/translations/translations.rs b/src/translations/translations.rs
index 010bef21..a07389c2 100644
--- a/src/translations/translations.rs
+++ b/src/translations/translations.rs
@@ -5,33 +5,33 @@
use crate::StyleType;
use crate::translations::types::language::Language;
-pub fn choose_adapters_translation<'a>(language: Language) -> Text<'a, StyleType> {
- Text::new(match language {
- Language::EN => "Select network adapter to inspect",
- Language::IT => "Seleziona la scheda di rete da ispezionare",
- Language::FR => "Sélectionnez une carte réseau à inspecter",
- Language::ES => "Seleccione el adaptador de red que desea inspeccionar",
- Language::PL => "Wybierz adapter sieciowy do inspekcji",
- Language::DE => "Wähle einen Netzwerkadapter zum überwachen aus",
- Language::UK => "Виберіть мережевий адаптер для перевірки",
- Language::ZH => "选择需要监控的网络适配器",
- Language::ZH_TW => "選取要檢視的網路介面卡",
- Language::RO => "Selectați adaptor de rețea pentru a inspecta",
- Language::KO => "검사할 네트워크 어댑터 선택",
- Language::TR => "İncelemek için bir ağ adaptörü seçiniz",
- Language::RU => "Выберите сетевой адаптер для инспекции",
- Language::PT => "Selecione o adaptador de rede a inspecionar",
- Language::EL => "Επίλεξε τον προσαρμογέα δικτύου για επιθεώρηση",
- // Language::FA => "مبدل شبکه را برای بازرسی انتخاب کنید",
- Language::SV => "Välj nätverksadapter att inspektera",
- Language::FI => "Valitse tarkasteltava verkkosovitin",
- Language::JA => "使用するネットワーク アダプターを選択してください",
- Language::UZ => "Tekshirish uchun tarmoq adapterini tanlang",
- Language::VI => "Hãy chọn network adapter để quan sát",
- Language::ID => "Pilih Adapter Jaringan yang ingin dicek",
- Language::NL => "Selecteer netwerkadapter om te inspecteren",
- })
-}
+// pub fn choose_adapters_translation<'a>(language: Language) -> Text<'a, StyleType> {
+// Text::new(match language {
+// Language::EN => "Select network adapter to inspect",
+// Language::IT => "Seleziona la scheda di rete da ispezionare",
+// Language::FR => "Sélectionnez une carte réseau à inspecter",
+// Language::ES => "Seleccione el adaptador de red que desea inspeccionar",
+// Language::PL => "Wybierz adapter sieciowy do inspekcji",
+// Language::DE => "Wähle einen Netzwerkadapter zum überwachen aus",
+// Language::UK => "Виберіть мережевий адаптер для перевірки",
+// Language::ZH => "选择需要监控的网络适配器",
+// Language::ZH_TW => "選取要檢視的網路介面卡",
+// Language::RO => "Selectați adaptor de rețea pentru a inspecta",
+// Language::KO => "검사할 네트워크 어댑터 선택",
+// Language::TR => "İncelemek için bir ağ adaptörü seçiniz",
+// Language::RU => "Выберите сетевой адаптер для инспекции",
+// Language::PT => "Selecione o adaptador de rede a inspecionar",
+// Language::EL => "Επιλέξτε τον προσαρμογέα δικτύου για επιθεώρηση",
+// // Language::FA => "مبدل شبکه را برای بازرسی انتخاب کنید",
+// Language::SV => "Välj nätverksadapter att inspektera",
+// Language::FI => "Valitse tarkasteltava verkkosovitin",
+// Language::JA => "使用するネットワーク アダプターを選択してください",
+// Language::UZ => "Tekshirish uchun tarmoq adapterini tanlang",
+// Language::VI => "Hãy chọn network adapter để quan sát",
+// Language::ID => "Pilih Adapter Jaringan yang ingin dicek",
+// Language::NL => "Selecteer netwerkadapter om te inspecteren",
+// })
+// }
// pub fn application_protocol_translation(language: Language) -> &'static str {
// match language {
@@ -60,33 +60,33 @@ pub fn choose_adapters_translation<'a>(language: Language) -> Text<'a, StyleType
// }
// }
-pub fn select_filters_translation<'a>(language: Language) -> Text<'a, StyleType> {
- Text::new(match language {
- Language::EN => "Select filters to be applied on network traffic",
- Language::IT => "Seleziona i filtri da applicare al traffico di rete",
- Language::FR => "Sélectionnez les filtres à appliquer sur le traffic réseau",
- Language::ES => "Seleccionar los filtros que se aplicarán al tráfico de red",
- Language::PL => "Wybierz filtry, które mają być zastosowane na ruchu sieciowym",
- Language::DE => "Wähle die Filter, die auf den Netzwerkverkehr angewendet werden sollen",
- Language::UK => "Виберіть фільтри, які мають бути застосовані до мережевого руху",
- Language::ZH => "选择需要监控的目标",
- Language::ZH_TW => "選取要套用於網路流量的篩選器",
- Language::RO => "Selectați filtre pentru traficul de rețea",
- Language::KO => "네트워크 트레픽에 적용할 필터 선택",
- Language::TR => "Ağ trafiğine uygulanacak filtreleri seçiniz",
- Language::RU => "Выберите фильтры для применения к сетевому трафику",
- Language::PT => "Selecione os filtros a serem aplicados no tráfego de rede",
- Language::EL => "Επίλεξε τα φίλτρα για εφαρμογή στην κίνηση του δικτύου",
- // Language::FA => "صافی ها را جهت اعمال بر آمد و شد شبکه انتخاب کنید",
- Language::SV => "Välj filtren som ska appliceras på nätverkstrafiken",
- Language::FI => "Valitse suodattimet verkkoliikenteelle",
- Language::JA => "トラフィックに適用するフィルターを選択してください",
- Language::UZ => "Tarmoq trafigiga qo'llaniladigan filtrlarni tanlang",
- Language::VI => "Hãy chọn bộ lọc cho lưu lượng mạng",
- Language::ID => "Pilih filter yang ingin dipasang dilalulintas jaringan",
- Language::NL => "Selecteer filters om toe te passen op netwerkverkeer",
- })
-}
+// pub fn select_filters_translation<'a>(language: Language) -> Text<'a, StyleType> {
+// Text::new(match language {
+// Language::EN => "Select filters to be applied on network traffic",
+// Language::IT => "Seleziona i filtri da applicare al traffico di rete",
+// Language::FR => "Sélectionnez les filtres à appliquer sur le traffic réseau",
+// Language::ES => "Seleccionar los filtros que se aplicarán al tráfico de red",
+// Language::PL => "Wybierz filtry, które mają być zastosowane na ruchu sieciowym",
+// Language::DE => "Wähle die Filter, die auf den Netzwerkverkehr angewendet werden sollen",
+// Language::UK => "Виберіть фільтри, які мають бути застосовані до мережевого руху",
+// Language::ZH => "选择需要监控的目标",
+// Language::ZH_TW => "選取要套用於網路流量的篩選器",
+// Language::RO => "Selectați filtre pentru traficul de rețea",
+// Language::KO => "네트워크 트레픽에 적용할 필터 선택",
+// Language::TR => "Ağ trafiğine uygulanacak filtreleri seçiniz",
+// Language::RU => "Выберите фильтры для применения к сетевому трафику",
+// Language::PT => "Selecione os filtros a serem aplicados no tráfego de rede",
+// Language::EL => "Επιλέξτε τα φίλτρα που θα εφαρμοστούν στην κίνηση του δικτύου",
+// // Language::FA => "صافی ها را جهت اعمال بر آمد و شد شبکه انتخاب کنید",
+// Language::SV => "Välj filtren som ska appliceras på nätverkstrafiken",
+// Language::FI => "Valitse suodattimet verkkoliikenteelle",
+// Language::JA => "トラフィックに適用するフィルターを選択してください",
+// Language::UZ => "Tarmoq trafigiga qo'llaniladigan filtrlarni tanlang",
+// Language::VI => "Hãy chọn bộ lọc cho lưu lượng mạng",
+// Language::ID => "Pilih filter yang ingin dipasang dilalulintas jaringan",
+// Language::NL => "Selecteer filters om toe te passen op netwerkverkeer",
+// })
+// }
pub fn start_translation(language: Language) -> &'static str {
match language {
@@ -163,33 +163,33 @@ pub fn addresses_translation(language: Language) -> &'static str {
}
}
-pub fn ip_version_translation(language: Language) -> &'static str {
- match language {
- Language::EN => "IP version",
- Language::IT => "Versione IP",
- Language::FR => "Version IP",
- Language::ES => "Versión IP",
- Language::PL => "Wersja IP",
- Language::DE => "IP Version",
- Language::UK => "Версія IP",
- Language::ZH => "目标IP协议版本",
- Language::ZH_TW => "IP 版本",
- Language::RO => "Versiune IP",
- Language::KO => "IP 버전",
- Language::TR => "IP versiyonu",
- Language::RU => "Версия IP",
- Language::PT => "Versão de IP",
- Language::EL => "Έκδοση IP",
- // Language::FA => "نسخهٔ IP",
- Language::SV => "IP-version",
- Language::FI => "IP-versio",
- Language::JA => "IP バージョン",
- Language::UZ => "IP versiyasi",
- Language::VI => "Phiên bản IP",
- Language::ID => "Versi IP",
- Language::NL => "IP versie",
- }
-}
+// pub fn ip_version_translation(language: Language) -> &'static str {
+// match language {
+// Language::EN => "IP version",
+// Language::IT => "Versione IP",
+// Language::FR => "Version IP",
+// Language::ES => "Versión IP",
+// Language::PL => "Wersja IP",
+// Language::DE => "IP Version",
+// Language::UK => "Версія IP",
+// Language::ZH => "目标IP协议版本",
+// Language::ZH_TW => "IP 版本",
+// Language::RO => "Versiune IP",
+// Language::KO => "IP 버전",
+// Language::TR => "IP versiyonu",
+// Language::RU => "Версия IP",
+// Language::PT => "Versão de IP",
+// Language::EL => "Έκδοση IP",
+// // Language::FA => "نسخهٔ IP",
+// Language::SV => "IP-version",
+// Language::FI => "IP-versio",
+// Language::JA => "IP バージョン",
+// Language::UZ => "IP versiyasi",
+// Language::VI => "Phiên bản IP",
+// Language::ID => "Versi IP",
+// Language::NL => "IP versie",
+// }
+// }
// pub fn transport_protocol_translation(language: Language) -> &'static str {
// match language {
@@ -361,7 +361,7 @@ pub fn ask_quit_translation<'a>(language: Language) -> Text<'a, StyleType> {
Language::TR => "Bu analizden çıkmak istediğine emin misin?",
Language::RU => "Вы уверены, что хотите выйти из текущего анализа?",
Language::PT => "Tem a certeza que deseja sair desta análise?",
- Language::EL => "Είσαι σίγουρος ότι θες να κλείσεις την ανάλυση;",
+ Language::EL => "Είστε βέβαιοι ότι θέλετε να τερματίσετε την ανάλυση;",
// Language::FA => "آیا مطمئن هستید می خواهید از این تحلیل خارج شوید؟",
Language::SV => "Är du säker på att du vill avsluta analysen?",
Language::FI => "Haluatko varmasti lopettaa analyysin?",
@@ -417,7 +417,7 @@ pub fn ask_clear_all_translation<'a>(language: Language) -> Text<'a, StyleType>
Language::TR => "Bildirimleri temizlemek istediğine emin misin?",
Language::RU => "Вы уверены, что хотите удлить все уведомления?",
Language::PT => "Tem a certeza que deseja eliminar as notificações?",
- Language::EL => "Είσαι σίγουρος ότι θες να κάνεις εκκαθάριση των ειδοποιήσεων;",
+ Language::EL => "Είστε βέβαιοι ότι θέλετε να εκκαθαρίσετε τις ειδοποιήσεις;",
// Language::FA => "آیا مطمئن هستید می خواهید اعلان ها را پاک کنید؟",
Language::SV => "Är du säker på att du vill radera notifikationerna?",
Language::FI => "Haluatko varmasti tyhjentää ilmoitukset?",
@@ -751,124 +751,124 @@ pub fn waiting_translation<'a>(language: Language, adapter: &str) -> Text<'a, St
})
}
-#[allow(clippy::too_many_lines)]
-pub fn some_observed_translation<'a>(language: Language, observed: u128) -> Text<'a, StyleType> {
- Text::new(match language {
- Language::EN => format!(
- "Total intercepted packets: {observed}\n\n\
- Filtered packets: 0\n\n\
- Some packets have been intercepted, but still none has been selected according to the filters you specified..."
- ),
- Language::IT => format!(
- "Totale pacchetti intercettati: {observed}\n\n\
- Pacchetti filtrati: 0\n\n\
- Alcuni pacchetti sono stati intercettati, ma ancora nessuno è stato selezionato secondo i filtri specificati..."
- ),
- Language::FR => format!(
- "Total des paquets interceptés: {observed}\n\n\
- Paquets filtrés: 0\n\n\
- Certains paquets ont été interceptés, mais aucun ne satisfait les critères des filtres sélectionnés..."
- ),
- Language::ES => format!(
- "Total de paquetes interceptados: {observed}\n\n\
- Paquetes filtrados: 0\n\n\
- Se interceptaron algunos paquetes, pero ninguno de ellos cumplía los criterios de los filtros seleccionados..."
- ),
- Language::PL => format!(
- "Suma przechwyconych pakietów: {observed}\n\n\
- Przefiltrowane pakiety: 0\n\n\
- Niektóre pakiety zostały przechwycone, ale żaden nie został wybrany zgodnie z wskazanymi filtrami..."
- ),
- Language::DE => format!(
- "Anzahl der empfangenen Pakete: {observed}\n\n\
- Gefilterte Pakete: 0\n\n\
- Ein Paar Pakete wurden empfangen, aber es entsprechen noch keine den gewählten Filtern..."
- ),
- Language::UK => format!(
- "Сума перехоплених пакетів: {observed}\n\n\
- Відфільтровані пакети: 0\n\n\
- Деякі пакети були перехоплені, але жоден з них не був вибраний відповідно до вказаних фільтрів..."
- ),
- Language::ZH => format!(
- "监测到的数据包总数: {observed}\n\n\
- 目标数据包总数: 0\n\n\
- 当前已监测到一些数据包, 但其中并未包含您的目标数据包......"
- ),
- Language::ZH_TW => format!(
- "攔截到的封包總數: {observed}\n\n\
- 已篩選封包: 0\n\n\
- 已攔截到部分封包,但尚未有任何封包符合您指定的篩選條件..."
- ),
- Language::RO => format!(
- "Total pachete interceptate: {observed}\n\n\
- Pachete filtrate: 0\n\n\
- Unele pachete au fost interceptate, dar încă niciunul nu a fost selectat conform filtrelor pe care le-ați specificat..."
- ),
- Language::KO => format!(
- "감지한 총 패킷: {observed}\n\n\
- 필터링된 패킷: 0\n\n\
- 일부 패킷이 감지되었지만, 지정한 필터에 따라 선택되지 않았습니다..."
- ),
- Language::TR => format!(
- "Toplam yakalanan paketler: {observed}\n\n\
- Filterelenen paketler: 0\n\n\
- Bazı paketler yakalandı, fakat belirttiğiniz filtrelere göre hiç biri seçilmedi..."
- ),
- Language::RU => format!(
- "Всего пакетов перехвачено: {observed}\n\n\
- Фильтровано пакетов: 0\n\n\
- Сетевые пакеты были перехвачены, но ни один из них не соответствует заданным фильтрам..."
- ),
- Language::PT => format!(
- "Total de pacotes interceptados: {observed}\n\n\
- Pacotes filtrados: 0\n\n\
- Alguns pacotes foram interceptados, mas nenhum deles foi selecionado de acordo com os filtros especificados..."
- ),
- Language::EL => format!(
- "Συνολικά αναχαιτισμένα πακέτα: {observed}\n\n\
- Φιλτραρισμένα πακέτα: 0\n\n\
- Κάποια από τα πακέτα έχουν αναχαιτιστεί, αλλά κανένα ακόμη δεν έχει επιλεγεί σύμφωνα με τα φίλτρα που επέλεξες..."
- ),
- // Language::FA => format!("مجموع بسته های رهگیری شده: {observed}\n\n\
- // بسته های صاف شده: 0\n\n\
- // شماری از بسته ها رهگیری شده اند، ولی هنوز هیچ کدام بر اساس صافی تعیین شده شما انتخاب نشده اند..."),
- Language::SV => format!(
- "Antal fångade paket: {observed}\n\n\
- Filtrerade paket: 0\n\n\
- Några paket har fångats, men än har inget valts enligt de angivna filtren ..."
- ),
- Language::FI => format!(
- "Siepattuja paketteja yhteensä: {observed}\n\n\
- Suodatettuja paketteja: 0\n\n\
- Joitakin paketteja on siepattu, mutta yhtäkään ei ole valittu määrittämiesi suodattimien mukaan..."
- ),
- Language::JA => format!(
- "取得したパケット数: {observed}\n\n\
- フィルター後のパケット数: 0\n\n\
- パケットは取得できていますが、設定されたフィルタリングにより表示されません..."
- ),
- Language::UZ => format!(
- "Jami ushlangan paketlar: {observed}\n\n\
- Filtrlangan paketlar: 0\n\n\
- Tarmoq paketlari ushlandi, lekin ularning hech biri belgilangan filtrlarga mos kelmadi..."
- ),
- Language::VI => format!(
- "Tổng số gói tin bị chặn: {observed}\n\n\
- Các gói tin đã lọc: 0\n\n\
- Một số gói đã bị chặn, nhưng vẫn chưa có gói tin nào được bắt theo bộ lọc bạn đã chọn..."
- ),
- Language::ID => format!(
- "Total paket yang dilacak: {observed}\n\n\
- Paket yg difilter: 0\n\n\
- Beberapa paket dilacak, tetapi tidak ada yg terlihat berdasarkan filter yang kamu pilih..."
- ),
- Language::NL => format!(
- "Totaal aantal onderschepte pakketten: {observed}\n\n\
- Gefilterde pakketten: 0\n\n\
- Er zijn enkele pakketten onderschept, maar nog geen enkele is geselecteerd volgens de filters die je hebt opgegeven..."
- ),
- })
-}
+// #[allow(clippy::too_many_lines)]
+// pub fn some_observed_translation<'a>(language: Language, observed: u128) -> Text<'a, StyleType> {
+// Text::new(match language {
+// Language::EN => format!(
+// "Total intercepted packets: {observed}\n\n\
+// Filtered packets: 0\n\n\
+// Some packets have been intercepted, but still none has been selected according to the filters you specified..."
+// ),
+// Language::IT => format!(
+// "Totale pacchetti intercettati: {observed}\n\n\
+// Pacchetti filtrati: 0\n\n\
+// Alcuni pacchetti sono stati intercettati, ma ancora nessuno è stato selezionato secondo i filtri specificati..."
+// ),
+// Language::FR => format!(
+// "Total des paquets interceptés: {observed}\n\n\
+// Paquets filtrés: 0\n\n\
+// Certains paquets ont été interceptés, mais aucun ne satisfait les critères des filtres sélectionnés..."
+// ),
+// Language::ES => format!(
+// "Total de paquetes interceptados: {observed}\n\n\
+// Paquetes filtrados: 0\n\n\
+// Se interceptaron algunos paquetes, pero ninguno de ellos cumplía los criterios de los filtros seleccionados..."
+// ),
+// Language::PL => format!(
+// "Suma przechwyconych pakietów: {observed}\n\n\
+// Przefiltrowane pakiety: 0\n\n\
+// Niektóre pakiety zostały przechwycone, ale żaden nie został wybrany zgodnie z wskazanymi filtrami..."
+// ),
+// Language::DE => format!(
+// "Anzahl der empfangenen Pakete: {observed}\n\n\
+// Gefilterte Pakete: 0\n\n\
+// Ein Paar Pakete wurden empfangen, aber es entsprechen noch keine den gewählten Filtern..."
+// ),
+// Language::UK => format!(
+// "Сума перехоплених пакетів: {observed}\n\n\
+// Відфільтровані пакети: 0\n\n\
+// Деякі пакети були перехоплені, але жоден з них не був вибраний відповідно до вказаних фільтрів..."
+// ),
+// Language::ZH => format!(
+// "监测到的数据包总数: {observed}\n\n\
+// 目标数据包总数: 0\n\n\
+// 当前已监测到一些数据包, 但其中并未包含您的目标数据包......"
+// ),
+// Language::ZH_TW => format!(
+// "攔截到的封包總數: {observed}\n\n\
+// 已篩選封包: 0\n\n\
+// 已攔截到部分封包,但尚未有任何封包符合您指定的篩選條件..."
+// ),
+// Language::RO => format!(
+// "Total pachete interceptate: {observed}\n\n\
+// Pachete filtrate: 0\n\n\
+// Unele pachete au fost interceptate, dar încă niciunul nu a fost selectat conform filtrelor pe care le-ați specificat..."
+// ),
+// Language::KO => format!(
+// "감지한 총 패킷: {observed}\n\n\
+// 필터링된 패킷: 0\n\n\
+// 일부 패킷이 감지되었지만, 지정한 필터에 따라 선택되지 않았습니다..."
+// ),
+// Language::TR => format!(
+// "Toplam yakalanan paketler: {observed}\n\n\
+// Filterelenen paketler: 0\n\n\
+// Bazı paketler yakalandı, fakat belirttiğiniz filtrelere göre hiç biri seçilmedi..."
+// ),
+// Language::RU => format!(
+// "Всего пакетов перехвачено: {observed}\n\n\
+// Фильтровано пакетов: 0\n\n\
+// Сетевые пакеты были перехвачены, но ни один из них не соответствует заданным фильтрам..."
+// ),
+// Language::PT => format!(
+// "Total de pacotes interceptados: {observed}\n\n\
+// Pacotes filtrados: 0\n\n\
+// Alguns pacotes foram interceptados, mas nenhum deles foi selecionado de acordo com os filtros especificados..."
+// ),
+// Language::EL => format!(
+// "Συνολικά αναχαιτισμένα πακέτα: {observed}\n\n\
+// Φιλτραρισμένα πακέτα: 0\n\n\
+// Κάποια από τα πακέτα έχουν αναχαιτιστεί, αλλά κανένα ακόμη δεν έχει επιλεγεί σύμφωνα με τα φίλτρα που επέλεξες..."
+// ),
+// // Language::FA => format!("مجموع بسته های رهگیری شده: {observed}\n\n\
+// // بسته های صاف شده: 0\n\n\
+// // شماری از بسته ها رهگیری شده اند، ولی هنوز هیچ کدام بر اساس صافی تعیین شده شما انتخاب نشده اند..."),
+// Language::SV => format!(
+// "Antal fångade paket: {observed}\n\n\
+// Filtrerade paket: 0\n\n\
+// Några paket har fångats, men än har inget valts enligt de angivna filtren ..."
+// ),
+// Language::FI => format!(
+// "Siepattuja paketteja yhteensä: {observed}\n\n\
+// Suodatettuja paketteja: 0\n\n\
+// Joitakin paketteja on siepattu, mutta yhtäkään ei ole valittu määrittämiesi suodattimien mukaan..."
+// ),
+// Language::JA => format!(
+// "取得したパケット数: {observed}\n\n\
+// フィルター後のパケット数: 0\n\n\
+// パケットは取得できていますが、設定されたフィルタリングにより表示されません..."
+// ),
+// Language::UZ => format!(
+// "Jami ushlangan paketlar: {observed}\n\n\
+// Filtrlangan paketlar: 0\n\n\
+// Tarmoq paketlari ushlandi, lekin ularning hech biri belgilangan filtrlarga mos kelmadi..."
+// ),
+// Language::VI => format!(
+// "Tổng số gói tin bị chặn: {observed}\n\n\
+// Các gói tin đã lọc: 0\n\n\
+// Một số gói đã bị chặn, nhưng vẫn chưa có gói tin nào được bắt theo bộ lọc bạn đã chọn..."
+// ),
+// Language::ID => format!(
+// "Total paket yang dilacak: {observed}\n\n\
+// Paket yg difilter: 0\n\n\
+// Beberapa paket dilacak, tetapi tidak ada yg terlihat berdasarkan filter yang kamu pilih..."
+// ),
+// Language::NL => format!(
+// "Totaal aantal onderschepte pakketten: {observed}\n\n\
+// Gefilterde pakketten: 0\n\n\
+// Er zijn enkele pakketten onderschept, maar nog geen enkele is geselecteerd volgens de filters die je hebt opgegeven..."
+// ),
+// })
+// }
// pub fn filtered_packets_translation(language: Language) -> &'static str {
// match language {
@@ -1038,7 +1038,7 @@ pub fn some_observed_translation<'a>(language: Language, observed: u128) -> Text
// })
// }
-pub fn error_translation(language: Language, error: &str) -> Text {
+pub fn error_translation(language: Language, error: &str) -> Text<'_, StyleType> {
Text::new(match language {
Language::EN => format!(
"An error occurred! \n\n\
@@ -1462,7 +1462,7 @@ pub fn appearance_title_translation<'a>(language: Language) -> Text<'a, StyleTyp
Language::TR => "Favori temanızı seçin",
Language::RU => "Выберите предпочительную тему",
Language::PT => "Escolha o seu tema favorito",
- Language::EL => "Επίλεξε το αγαπημένο σου θέμα",
+ Language::EL => "Επιλέξτε το αγαπημένο σας θέμα",
// Language::FA => "زمینه دلخواه خود را انتخاب کنید",
Language::SV => "Välj ditt favorittema",
Language::FI => "Valitse suosikkiteemasi",
@@ -1787,7 +1787,7 @@ pub fn overview_translation(language: Language) -> &'static str {
Language::TR => "Ön izleme",
Language::RU => "Обзор",
Language::PT => "Visão geral",
- Language::EL => "επισκόπηση",
+ Language::EL => "Επισκόπηση",
// Language::FA => "نمای کلی",
Language::SV => "Översikt",
Language::FI => "Yleiskatsaus",
@@ -2167,7 +2167,7 @@ pub fn favorite_transmitted_translation(language: Language) -> &'static str {
Language::TR => "Favorilerden yeni veri aktarıldı",
Language::RU => "Новый обмен данными в избранных соедиениях",
Language::PT => "Novos dados trocados dos favoritos",
- Language::EL => "Καινούρια δεδομένα έχουν ανταλλαγεί στα αγαπημένα",
+ Language::EL => "Νέα δεδομένα έχουν ανταλλαγεί στα αγαπημένα",
// Language::FA => "مبادله داده جدید از پسندیده ها",
Language::SV => "Ny data utbytt av favoriter",
Language::FI => "Uusia tietoja vaihdettu suosikeista",
@@ -2253,9 +2253,9 @@ pub fn no_notifications_set_translation<'a>(language: Language) -> Text<'a, Styl
Pode ativar as notificações nas definições:"
}
Language::EL => {
- "Δεν έχεις ενεργοποιήσει τις ειδοποιήσεις ακόμη!\n\n\
- Αφότου τις ενεργοποιήσεις, αυτή η σελίδα θα απεικονίσει μια καταγραφή των ειδοποιήσεών σου\n\n\
- Μπορείς να ενεργοποιήσεις τις ειδοποιήσεις από τις ρυθμίσεις:"
+ "Δεν έχετε ενεργοποιήσει τις ειδοποιήσεις ακόμη!\n\n\
+ Αφότου τις ενεργοποιήσετε, αυτή η σελίδα θα εμφανίσει ένα αρχείο καταγραφής των ειδοποιήσεών σας\n\n\
+ Μπορείτε να ενεργοποιήσετε τις ειδοποιήσεις από τις ρυθμίσεις:"
}
// Language::FA => "شما هنوز اعلان ها را فعال نکرده اید!\n\n\
// پس از آنکه آن ها را فعال کنید، این صفحه یک کارنامه از اعلان های شما را نمایش خواهد داد\n\n
@@ -2357,8 +2357,8 @@ pub fn no_notifications_received_translation<'a>(language: Language) -> Text<'a,
Quando receber uma notificação, ela será mostrada aqui"
}
Language::EL => {
- "Δεν υπάρχει κάτι για απεικόνιση αυτή τη στιγμή...\n\n\
- Όταν λάβεις μια ειδοποίηση, αυτή θα εμφανιστεί εδώ"
+ "Δεν υπάρχουν ειδοποιήσεις αυτή τη στιγμή...\n\n\
+ Όταν λάβετε μια ειδοποίηση, θα εμφανιστεί εδώ"
}
// Language::FA => {
// "در حال حاضر هیچ چیزی برای دیدن نیست...\n\n\
diff --git a/src/translations/translations_2.rs b/src/translations/translations_2.rs
index e193426a..684b947b 100644
--- a/src/translations/translations_2.rs
+++ b/src/translations/translations_2.rs
@@ -1,5 +1,3 @@
-#![allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)]
-
use crate::Language;
pub fn new_version_available_translation(language: Language) -> &'static str {
@@ -54,7 +52,7 @@ pub fn inspect_translation(language: Language) -> &'static str {
Language::VI => "Quan sát",
Language::ID => "Memeriksa",
Language::NL => "Inspecteren",
- _ => "Inspect",
+ Language::EL => "Επιθεώρηση",
}
}
@@ -82,7 +80,7 @@ pub fn connection_details_translation(language: Language) -> &'static str {
Language::VI => "Thông tin kết nối",
Language::ID => "Rincian koneksi",
Language::NL => "Verbindingsdetails",
- _ => "Connection details",
+ Language::EL => "Λεπτομέρειες σύνδεσης",
}
}
@@ -94,7 +92,7 @@ pub fn dropped_translation(language: Language) -> &'static str {
Language::RU => "Потеряно",
Language::SV => "Tappade",
Language::FI => "Pudotetut",
- Language::DE => "Verloren",
+ Language::DE | Language::NL => "Verloren",
Language::TR => "Düşen",
// Language::FA => "رها شده",
Language::ES | Language::PT => "Perdidos",
@@ -109,8 +107,7 @@ pub fn dropped_translation(language: Language) -> &'static str {
Language::UZ => "Yig'ilgan",
Language::VI => "Mất",
Language::ID => "Dihapus",
- Language::NL => "Verloren",
- _ => "Dropped",
+ Language::EL => "Απορριμμένα",
}
}
@@ -138,7 +135,7 @@ pub fn data_representation_translation(language: Language) -> &'static str {
Language::VI => "Miêu tả dữ liệu",
Language::ID => "Penyajian ulang data",
Language::NL => "Gegevensweergave",
- _ => "Data representation",
+ Language::EL => "Αναπαράσταση δεδομένων",
}
}
@@ -166,7 +163,7 @@ pub fn host_translation(language: Language) -> &'static str {
Language::VI => "Máy chủ",
Language::ID => "Jaringan asal",
Language::NL => "Netwerk host",
- _ => "Network host",
+ Language::EL => "Κόμβος δικτύου",
}
}
@@ -194,7 +191,7 @@ pub fn only_top_30_items_translation(language: Language) -> &'static str {
Language::VI => "Chỉ có 30 mục gần nhất được hiển thị ở đây",
Language::ID => "Hanya 30 teratas yang ditampilkan disini",
Language::NL => "Alleen de bovenste 30 items worden hier weergegeven",
- _ => "Only the top 30 items are displayed here",
+ Language::EL => "Εμφανίζονται μόνο τα κορυφαία 30 στοιχεία",
}
}
@@ -248,7 +245,7 @@ pub fn local_translation(language: Language) -> &'static str {
Language::VI => "Mạng nội bộ",
Language::ID => "Jaringan lokal",
Language::NL => "Lokaal netwerk",
- _ => "Local network",
+ Language::EL => "Τοπικό δίκτυο",
}
}
@@ -276,7 +273,7 @@ pub fn unknown_translation(language: Language) -> &'static str {
Language::VI => "Không rõ địa điểm",
Language::ID => "Lokasi tidak diketahui",
Language::NL => "Onbekende locatie",
- _ => "Unknown location",
+ Language::EL => "Άγνωστη τοποθεσία",
}
}
@@ -304,7 +301,7 @@ pub fn your_network_adapter_translation(language: Language) -> &'static str {
Language::VI => "Network adapter của bạn",
Language::ID => "Adaptor jaringan kamu",
Language::NL => "Uw netwerkadapter",
- _ => "Your network adapter",
+ Language::EL => "Ο προσαρμογέας δικτύου σας",
}
}
@@ -332,7 +329,7 @@ pub fn socket_address_translation(language: Language) -> &'static str {
Language::VI => "Địa chỉ socket",
Language::ID => "Alamat sambungan",
Language::NL => "Socket adres",
- _ => "Socket address",
+ Language::EL => "Διεύθυνση υποδοχής",
}
}
@@ -360,13 +357,13 @@ pub fn mac_address_translation(language: Language) -> &'static str {
Language::VI => "Địa chỉ MAC",
Language::ID => "Alamat MAC",
Language::NL => "MAC-adres",
- _ => "MAC address",
+ Language::EL => "Διεύθυνση MAC",
}
}
pub fn source_translation(language: Language) -> &'static str {
match language {
- Language::EN => "Source",
+ Language::EN | Language::FR => "Source",
Language::IT => "Sorgente",
Language::RU => "Источник",
Language::SV => "Källa",
@@ -381,20 +378,19 @@ pub fn source_translation(language: Language) -> &'static str {
Language::UK => "Джерело",
Language::RO => "Sursă",
Language::PL => "Źródło",
- Language::FR => "Source",
Language::JA => "送信元",
Language::UZ => "Manba",
Language::PT => "Fonte",
Language::VI => "Nguồn",
Language::ID => "Asal",
Language::NL => "Bron",
- _ => "Source",
+ Language::EL => "Πηγή",
}
}
pub fn destination_translation(language: Language) -> &'static str {
match language {
- Language::EN | Language::SV => "Destination",
+ Language::EN | Language::SV | Language::FR => "Destination",
Language::IT => "Destinazione",
Language::RU => "Получатель",
Language::FI => "Määränpää",
@@ -408,13 +404,12 @@ pub fn destination_translation(language: Language) -> &'static str {
Language::UK => "Призначення",
Language::RO => "Destinație",
Language::PL => "Miejsce docelowe",
- Language::FR => "Destination",
Language::JA => "送信先",
Language::UZ => "Qabul qiluvchi",
Language::VI => "Đích",
Language::ID => "Tujuan",
Language::NL => "Bestemming",
- _ => "Destination",
+ Language::EL => "Προορισμός",
}
}
@@ -430,8 +425,7 @@ pub fn fqdn_translation(language: Language) -> &'static str {
// Language::FA => "نام دامنه جامع الشرایط",
Language::ES => "Nombre de dominio completo",
Language::KO => "절대 도메인 네임",
- Language::ZH | Language::JA => "FQDN",
- Language::ZH_TW => "FQDN",
+ Language::ZH | Language::JA | Language::ZH_TW => "FQDN",
Language::UK => "Повністю визначене доменне ім'я",
Language::RO => "Nume de domeniu complet calificat",
Language::PL => "Pełna nazwa domeny",
@@ -441,7 +435,7 @@ pub fn fqdn_translation(language: Language) -> &'static str {
Language::VI => "Tên miền đầy đủ",
Language::ID => "Nama domain yang memenuhi syarat",
Language::NL => "Volledig gekwalificeerde domeinnaam",
- _ => "Fully qualified domain name",
+ Language::EL => "Πλήρως προσδιορισμένο όνομα τομέα",
}
}
@@ -469,7 +463,7 @@ pub fn administrative_entity_translation(language: Language) -> &'static str {
Language::VI => "Tên Autonomous System",
Language::ID => "Nama System Otomatis",
Language::NL => "Naam van het autonome systeem",
- _ => "Autonomous System name",
+ Language::EL => "Όνομα αυτόνομου συστήματος",
}
}
@@ -497,7 +491,7 @@ pub fn transmitted_data_translation(language: Language) -> &'static str {
Language::VI => "Dữ liệu được truyền",
Language::ID => "Data terkirim",
Language::NL => "Verzonden gegevens",
- _ => "Transmitted data",
+ Language::EL => "Μεταδιδόμενα δεδομένα",
}
}
@@ -506,9 +500,8 @@ pub fn country_translation(language: Language) -> &'static str {
Language::EN => "Country",
Language::IT => "Paese",
Language::RU => "Страна",
- Language::SV => "Land",
+ Language::SV | Language::DE | Language::NL => "Land",
Language::FI => "Maa",
- Language::DE => "Land",
Language::TR => "Ülke",
// Language::FA => "کشور",
Language::ES | Language::PT => "País",
@@ -523,8 +516,7 @@ pub fn country_translation(language: Language) -> &'static str {
Language::UZ => "Davlat",
Language::VI => "Quốc gia",
Language::ID => "Negara",
- Language::NL => "Land",
- _ => "Country",
+ Language::EL => "Χώρα",
}
}
@@ -552,7 +544,7 @@ pub fn domain_name_translation(language: Language) -> &'static str {
Language::VI => "Tên miền",
Language::ID => "Nama Domain",
Language::NL => "Domeinnaam",
- _ => "Domain name",
+ Language::EL => "Όνομα τομέα",
}
}
@@ -580,7 +572,7 @@ pub fn only_show_favorites_translation(language: Language) -> &'static str {
Language::VI => "Chỉ hiển thị mục ưa thích",
Language::ID => "Hanya tunjukkan favorit",
Language::NL => "Toon alleen favorieten",
- _ => "Only show favorites",
+ Language::EL => "Εμφάνιση μόνο αγαπημένων",
}
}
@@ -635,7 +627,9 @@ pub fn no_search_results_translation(language: Language) -> &'static str {
Language::VI => "Không có kết quả nào theo các bộ lọc được chỉ định",
Language::ID => "Tidak ada hasil berdasarkan filter pencarian spesifik",
Language::NL => "Geen resultaten beschikbaar volgens de opgegeven zoekfilters",
- _ => "No result available according to the specified search filters",
+ Language::EL => {
+ "Δεν υπάρχουν διαθέσιμα αποτελέσματα σύμφωνα με τα καθορισμένα φίλτρα αναζήτησης"
+ }
}
}
@@ -670,11 +664,10 @@ pub fn showing_results_translation(
Language::NL => {
format!("{start}-{end} van de {total} totale resultaten worden weergegeven")
}
- _ => format!("Showing {start}-{end} of {total} total results"),
+ Language::EL => format!("Εμφάνιση {start}-{end} από {total} συνολικά αποτελέσματα"),
}
}
-#[allow(dead_code)]
pub fn color_gradients_translation(language: Language) -> &'static str {
match language {
Language::EN => "Apply color gradients",
@@ -699,6 +692,6 @@ pub fn color_gradients_translation(language: Language) -> &'static str {
Language::VI => "Áp dụng color gradients",
Language::ID => "Aplikasikan gradasi warna",
Language::NL => "Kleurverlopen toepassen",
- _ => "Apply color gradients",
+ Language::EL => "Εφαρμογή χρωματικών διαβαθμίσεων",
}
}
diff --git a/src/translations/translations_3.rs b/src/translations/translations_3.rs
index 3fd89175..23a0b1a4 100644
--- a/src/translations/translations_3.rs
+++ b/src/translations/translations_3.rs
@@ -1,4 +1,4 @@
-#![allow(clippy::match_same_arms)]
+#![allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)]
use iced::widget::Text;
@@ -28,6 +28,7 @@ pub fn general_translation(language: Language) -> &'static str {
Language::UK => "Загальні",
Language::ID => "Umum",
Language::NL => "Algemeen",
+ Language::EL => "Γενικά",
_ => "General",
}
}
@@ -55,6 +56,7 @@ pub fn zoom_translation(language: Language) -> &'static str {
Language::TR => "Yakınlaştırma",
Language::UK => "Масштабування",
Language::ID => "Perbesar",
+ Language::EL => "Εστίαση",
_ => "Zoom",
}
}
@@ -82,6 +84,7 @@ pub fn mmdb_files_translation(language: Language) -> &'static str {
Language::UK => "Файли бази даних",
Language::ID => "Berkas database",
Language::NL => "Database bestanden",
+ Language::EL => "Αρχεία βάσης δεδομένων",
_ => "Database files",
}
}
@@ -109,6 +112,9 @@ pub fn params_not_editable_translation(language: Language) -> &'static str {
Language::UK => "Наступні параметри не можна змінювати під час аналізу трафіку",
Language::ID => "Parameter berikut tidak bisa diubah saat dianalisa",
Language::NL => "De volgende parameters kunnen niet worden aangepast tijdens de analyse",
+ Language::EL => {
+ "Οι ακόλουθες παράμετροι δεν μπορούν να τροποποιηθούν κατά τη διάρκεια της ανάλυσης"
+ }
_ => "The following parameters can't be modified during the analysis",
}
}
@@ -135,6 +141,7 @@ pub fn custom_style_translation(language: Language) -> &'static str {
Language::UK => "Власний стиль",
Language::ID => "Ubah Model",
Language::NL => "Aangepaste stijl",
+ Language::EL => "Προσαρμοσμένο στυλ",
_ => "Custom style",
}
}
@@ -160,6 +167,7 @@ pub fn copy_translation(language: Language) -> &'static str {
Language::UK => "Копіювати",
Language::ID => "Salin",
Language::NL => "Kopiëren",
+ Language::EL => "Αντιγραφή",
_ => "Copy",
}
}
@@ -186,35 +194,37 @@ pub fn port_translation(language: Language) -> &'static str {
Language::UK => "Порт",
Language::ID => "Port",
Language::NL => "Poort",
+ Language::EL => "Θύρα",
_ => "Port",
}
}
-pub fn invalid_filters_translation(language: Language) -> &'static str {
- match language {
- Language::EN => "Invalid filters",
- // Language::FA => "صافی نامعتبر",
- Language::ES | Language::PT => "Filtros inválidos",
- Language::IT => "Filtri non validi",
- Language::FR => "Filtres invalides",
- Language::DE => "Ungültige Filter",
- Language::PL => "Nieprawidłowe filtry",
- Language::RU => "Неверный формат фильтров",
- Language::RO => "Filtre invalide",
- Language::JA => "無効なフィルター",
- Language::UZ => "Noto'g'ri filtrlar",
- Language::SV => "Ogiltiga filter",
- Language::VI => "Bộ lọc không khả dụng",
- Language::ZH => "无效的过滤器",
- Language::ZH_TW => "無效的篩選器",
- Language::KO => "잘못된 필터",
- Language::TR => "Geçersiz filtreler",
- Language::UK => "Неправильний формат фільтрів",
- Language::ID => "Filter salah",
- Language::NL => "Ongeldige filters",
- _ => "Invalid filters",
- }
-}
+// pub fn invalid_filters_translation(language: Language) -> &'static str {
+// match language {
+// Language::EN => "Invalid filters",
+// // Language::FA => "صافی نامعتبر",
+// Language::ES | Language::PT => "Filtros inválidos",
+// Language::IT => "Filtri non validi",
+// Language::FR => "Filtres invalides",
+// Language::DE => "Ungültige Filter",
+// Language::PL => "Nieprawidłowe filtry",
+// Language::RU => "Неверный формат фильтров",
+// Language::RO => "Filtre invalide",
+// Language::JA => "無効なフィルター",
+// Language::UZ => "Noto'g'ri filterlar",
+// Language::SV => "Ogiltiga filter",
+// Language::VI => "Bộ lọc không khả dụng",
+// Language::ZH => "无效的过滤器",
+// Language::ZH_TW => "無效的篩選器",
+// Language::KO => "잘못된 필터",
+// Language::TR => "Geçersiz filtreler",
+// Language::UK => "Неправильний формат фільтрів",
+// Language::ID => "Filter salah",
+// Language::NL => "Ongeldige filters",
+// Language::EL => "Μη έγκυρα φίλτρα",
+// _ => "Invalid filters",
+// }
+// }
pub fn messages_translation(language: Language) -> &'static str {
match language {
@@ -238,6 +248,7 @@ pub fn messages_translation(language: Language) -> &'static str {
Language::UK => "Повідомлення",
Language::ID => "Pesan",
Language::NL => "Berichten",
+ Language::EL => "Μηνύματα",
_ => "Messages",
}
}
@@ -264,6 +275,7 @@ pub fn link_type_translation(language: Language) -> &'static str {
Language::PT => "Tipo de conexão",
Language::UK => "Різновид зʼєднання",
Language::ID => "Tipe koneksi",
+ Language::EL => "Τύπος σύνδεσης",
_ => "Link type",
}
}
@@ -324,6 +336,9 @@ pub fn unsupported_link_type_translation<'a>(
Language::NL => {
"Het linktype dat is gekoppeld aan deze adapter wordt nog niet ondersteund door Sniffnet..."
}
+ Language::EL => {
+ "Ο τύπος σύνδεσης που σχετίζεται με αυτόν τον προσαρμογέα δεν υποστηρίζεται ακόμη από το Sniffnet..."
+ }
_ => "The link type associated with this adapter is not supported by Sniffnet yet...",
};
@@ -356,6 +371,7 @@ pub fn style_from_file_translation(language: Language) -> &'static str {
Language::UK => "Виберіть стиль з файлу",
Language::ID => "Pilih model / gaya dari berkas",
Language::NL => "Selecteer stijl vanuit een bestand",
+ Language::EL => "Επιλογή στυλ από αρχείο",
_ => "Select style from a file",
}
}
@@ -383,6 +399,7 @@ pub fn database_from_file_translation(language: Language) -> &'static str {
Language::UK => "Виберіть файл бази даних",
Language::ID => "Pilih berkas database",
Language::NL => "Selecteer database bestand",
+ Language::EL => "Επιλογή αρχείου βάσης δεδομένων",
_ => "Select database file",
}
}
@@ -410,6 +427,7 @@ pub fn filter_by_host_translation(language: Language) -> &'static str {
Language::UK => "Фільтр за хостом мережі",
Language::ID => "Filter berdasarkan jaringan asal",
Language::NL => "Filteren op netwerk host",
+ Language::EL => "Φίλτρο ανά διακομιστή δικτύου",
_ => "Filter by network host",
}
}
@@ -434,6 +452,7 @@ pub fn service_translation(language: Language) -> &'static str {
Language::UK => "Сервіс",
Language::ID => "Layanan",
Language::NL => "Dienst",
+ Language::EL => "Υπηρεσία",
_ => "Service",
}
}
@@ -461,6 +480,7 @@ pub fn export_capture_translation(language: Language) -> &'static str {
Language::ID => "Ekspor data tangkapan",
Language::ES => "Exportar archivo de captura",
Language::NL => "Exporteer capture bestand",
+ Language::EL => "Εξαγωγή αρχείου καταγραφής",
_ => "Export capture file",
}
}
@@ -487,6 +507,7 @@ pub fn directory_translation(language: Language) -> &'static str {
Language::ID => "Direktori",
Language::ES => "Directorio",
Language::NL => "Map",
+ Language::EL => "Κατάλογος",
_ => "Directory",
}
}
@@ -514,6 +535,7 @@ pub fn select_directory_translation(language: Language) -> &'static str {
Language::ID => "Pilih direktori tujuan",
Language::ES => "Selecciona el directorio de destino",
Language::NL => "Selecteer doelmap",
+ Language::EL => "Επιλογή καταλόγου προορισμού",
_ => "Select destination directory",
}
}
@@ -541,6 +563,7 @@ pub fn file_name_translation(language: Language) -> &'static str {
Language::ID => "Nama berkas",
Language::ES => "Nombre del archivo",
Language::NL => "Bestandsnaam",
+ Language::EL => "Όνομα αρχείου",
_ => "File name",
}
}
@@ -567,6 +590,7 @@ pub fn thumbnail_mode_translation(language: Language) -> &'static str {
Language::UK => "Режим мініатюри",
Language::ID => "Mode gambar kecil",
Language::NL => "Miniatuur modus",
+ Language::EL => "Λειτουργία μικρογραφιών",
_ => "Thumbnail mode",
}
}
diff --git a/src/translations/translations_4.rs b/src/translations/translations_4.rs
index 30089fff..fffb656e 100644
--- a/src/translations/translations_4.rs
+++ b/src/translations/translations_4.rs
@@ -9,12 +9,17 @@ pub fn reserved_address_translation(language: Language, info: &str) -> String {
match language {
Language::EN => format!("Reserved address ({info})"),
Language::IT => format!("Indirizzo riservato ({info})"),
+ Language::JA => format!("予約済みアドレス ({info})"),
Language::PT => format!("Endereço reservado ({info})"),
Language::UK => format!("Зарезервована адреса ({info})"),
+ Language::ZH => format!("预留地址 ({info})"),
Language::ZH_TW => format!("保留的網路位址 ({info})"),
+ Language::FR => format!("Adresse réservée ({info})"),
Language::NL => format!("Gereserveerd adres ({info})"),
+ Language::RO => format!("Adresă rezervată ({info})"),
Language::DE => format!("Reservierte Adresse ({info})"),
Language::UZ => format!("Rezervlangan manzil ({info})"),
+ Language::EL => format!("Δεσμευμένη διεύθυνση ({info})"),
_ => format!("Reserved address ({info})"),
}
}
@@ -23,36 +28,51 @@ pub fn share_feedback_translation(language: Language) -> &'static str {
match language {
Language::EN => "Share your feedback",
Language::IT => "Condividi il tuo feedback",
+ Language::JA => "フィードバックを共有",
+ Language::ZH => "分享您的反馈",
Language::ZH_TW => "分享您的意見回饋",
+ Language::FR => "Partagez vos commentaires",
Language::NL => "Deel uw feedback",
+ Language::RO => "Împărtășiți feedback-ul dvs.",
Language::DE => "Feedback geben",
Language::UZ => "Fikr-mulohazalaringizni ulashing",
+ Language::EL => "Μοιραστείτε τα σχόλιά σας",
_ => "Share your feedback",
}
}
// refers to bytes or packets excluded because of the filters
-pub fn excluded_translation(language: Language) -> &'static str {
- match language {
- Language::EN => "Excluded",
- Language::IT => "Esclusi",
- Language::UZ => "Chiqarib tashlangan",
- Language::ZH_TW => "已排除",
- Language::NL => "Uitgesloten",
- Language::DE => "Herausgefiltert",
- _ => "Excluded",
- }
-}
+// pub fn excluded_translation(language: Language) -> &'static str {
+// match language {
+// Language::EN => "Excluded",
+// Language::IT => "Esclusi",
+// Language::JA => "除外",
+// Language::ZH => "已被过滤",
+// Language::UZ => "Chiqarib tashlangan",
+// Language::ZH_TW => "已排除",
+// Language::FR => "Exclus",
+// Language::NL => "Uitgesloten",
+// Language::DE => "Herausgefiltert",
+// Language::EL => "Εξαιρούμενα",
+// Language::RO => "Excluși",
+// _ => "Excluded",
+// }
+// }
-pub fn import_capture_translation(language: Language) -> &'static str {
+pub fn capture_file_translation(language: Language) -> &'static str {
match language {
- Language::EN => "Import capture file",
- Language::IT => "Importa file di cattura",
- Language::NL => "Importeer capture bestand",
- Language::DE => "Aufzeichnungsdatei importieren",
- Language::UZ => "Tahlil faylini import qilish",
+ Language::EN => "Capture file",
+ Language::IT => "File di cattura",
+ Language::FR => "Fichier de capture",
+ Language::JA => "キャプチャファイル",
+ Language::ZH => "捕获文件",
+ Language::NL => "Capture bestand",
+ Language::DE => "Aufzeichnungsdatei",
+ Language::UZ => "Tahlil faylini",
+ Language::EL => "Αρχείου καταγραφής",
+ Language::RO => "Importă fișierul de captură",
Language::ZH_TW => "導入擷取文件",
- _ => "Import capture file",
+ _ => "Capture file",
}
}
@@ -60,10 +80,15 @@ pub fn select_capture_translation(language: Language) -> &'static str {
match language {
Language::EN => "Select capture file",
Language::IT => "Seleziona file di cattura",
+ Language::FR => "Sélectionner un fichier de capture",
+ Language::JA => "キャプチャファイルを選択",
+ Language::ZH => "选择捕获文件",
Language::NL => "Selecteer capture bestand",
+ Language::RO => "Selectează fișierul de captură",
Language::DE => "Aufzeichnungsdatei auswählen",
Language::UZ => "Tahlil faylini tanlang",
Language::ZH_TW => "選擇擷取文件",
+ Language::EL => "Επιλογή αρχείου καταγραφής",
_ => "Select capture file",
}
}
@@ -81,11 +106,31 @@ pub fn reading_from_pcap_translation<'a>(language: Language, file: &str) -> Text
{file_name_translation}: {file}\n\n\
Sei sicuro che il file che hai selezionato non sia vuoto?"
),
+ Language::FR => format!(
+ "Lecture des paquets depuis le fichier...\n\n\
+ {file_name_translation}: {file}\n\n\
+ Êtes-vous sûr que le fichier sélectionné n'est pas vide?"
+ ),
+ Language::JA => format!(
+ "ファイルからパケットを読み込み中...\n\n\
+ {file_name_translation}: {file}\n\n\
+ 選択したファイルが空でないことを確認しましたか?"
+ ),
+ Language::ZH => format!(
+ "从文件中读取数据包...\n\n\
+ {file_name_translation}: {file}\n\n\
+ 您确定选中的文件不是空的吗?"
+ ),
Language::NL => format!(
"Pakketten lezen uit bestand...\n\n\
{file_name_translation}: {file}\n\n\
Weet je zeker dat het geselecteerde bestand niet leeg is?"
),
+ Language::RO => format!(
+ "Citirea pachetelor din fișier...\n\n\
+ {file_name_translation}: {file}\n\n\
+ Ești sigur că fișierul selectat nu este gol?"
+ ),
Language::DE => format!(
"Pakete aus Datei laden... \n\n\
{file_name_translation}: {file}\n\n\
@@ -101,10 +146,15 @@ pub fn reading_from_pcap_translation<'a>(language: Language, file: &str) -> Text
{file_name_translation}: {file}\n\n\
您確定您選擇的檔案不是空的嗎?"
),
+ Language::EL => format!(
+ "Ανάγνωση πακέτων από αρχείο...\n\n\
+ {file_name_translation}: {file}\n\n\
+ Είστε βέβαιοι ότι το επιλεγμένο αρχείο δεν είναι κενό;"
+ ),
_ => format!(
"Reading packets from file...\n\n\
- {file_name_translation}: {file}\n\n\
- Are you sure the file you selected isn't empty?"
+ {file_name_translation}: {file}\n\n\
+ Are you sure the file you selected isn't empty?"
),
})
}
@@ -113,45 +163,61 @@ pub fn data_exceeded_translation(language: Language) -> &'static str {
match language {
Language::EN => "Data threshold exceeded",
Language::IT => "Soglia di dati superata",
+ Language::FR => "Seuil de données dépassé",
+ Language::JA => "データの閾値を超えました",
+ Language::ZH => "已超出数据阈值",
Language::NL => "Gegevenslimiet overschreden",
+ Language::RO => "Limita de date depășită",
Language::DE => "Datenschwelle überschritten",
Language::UZ => "Ma'lumotlar chegarasidan oshib ketdi",
Language::ZH_TW => "已排除",
+ Language::EL => "Υπέρβαση ορίου δεδομένων",
_ => "Data threshold exceeded",
}
}
-#[allow(dead_code)]
pub fn bits_exceeded_translation(language: Language) -> &'static str {
match language {
Language::EN => "Bits threshold exceeded",
Language::IT => "Soglia di bit superata",
+ Language::FR => "Seuil de bits dépassé",
+ Language::JA => "ビットの閾値を超えました",
+ Language::ZH => "已超出比特阈值",
Language::NL => "Bits limiet overschreden",
+ Language::RO => "Limita de biți depășită",
Language::DE => "Bitschwelle überschritten",
Language::UZ => "Bitlar chegarasidan oshib ketdi",
Language::ZH_TW => "超出數據界限",
+ Language::EL => "Υπέρβαση ορίου δυφίων",
_ => "Bits threshold exceeded",
}
}
-#[allow(dead_code)]
pub fn bits_translation(language: Language) -> &'static str {
match language {
- Language::EN | Language::IT | Language::NL | Language::DE => "Bits",
+ Language::EN | Language::IT | Language::NL | Language::DE | Language::FR => "bits",
+ Language::JA => "ビット",
+ Language::ZH => "比特",
+ Language::UZ => "bitlar",
+ Language::EL => "Δυφία",
+ Language::RO => "Biți",
Language::ZH_TW => "位元",
- Language::UZ => "Bitlar",
- _ => "Bits",
+ _ => "bits",
}
}
#[allow(dead_code)]
pub fn pause_translation(language: Language) -> &'static str {
match language {
- Language::EN | Language::DE => "Pause",
+ Language::EN | Language::DE | Language::FR => "Pause",
Language::IT => "Pausa",
+ Language::JA => "一時停止",
+ Language::ZH => "暂停",
Language::NL => "Pauzeren",
+ Language::RO => "Pauză",
Language::UZ => "To'xtatish",
Language::ZH_TW => "暫停",
+ Language::EL => "Παύση",
_ => "Pause",
}
}
@@ -161,10 +227,15 @@ pub fn resume_translation(language: Language) -> &'static str {
match language {
Language::EN => "Resume",
Language::IT => "Riprendi",
+ Language::FR => "Reprendre",
+ Language::JA => "再開",
+ Language::ZH => "恢复",
Language::NL => "Hervatten",
+ Language::RO => "Continuă",
Language::DE => "Fortsetzen",
Language::UZ => "Davom ettirish",
Language::ZH_TW => "繼續",
+ Language::EL => "Συνέχεια",
_ => "Resume",
}
}
diff --git a/src/translations/translations_5.rs b/src/translations/translations_5.rs
new file mode 100644
index 00000000..cfe4516e
--- /dev/null
+++ b/src/translations/translations_5.rs
@@ -0,0 +1,21 @@
+#![allow(clippy::match_same_arms)]
+
+use crate::translations::types::language::Language;
+
+pub fn filter_traffic_translation(language: Language) -> String {
+ match language {
+ Language::EN => "Filter traffic",
+ Language::IT => "Filtra il traffico",
+ _ => "Filter traffic",
+ }
+ .to_string()
+}
+
+// the source from which Sniffnet reads network traffic (i.e., a capture file or a network adapter)
+pub fn traffic_source_translation(language: Language) -> &'static str {
+ match language {
+ Language::EN => "Traffic source",
+ Language::IT => "Fonte del traffico",
+ _ => "Traffic source",
+ }
+}
diff --git a/src/translations/types/language.rs b/src/translations/types/language.rs
index 6092f077..2c7aba79 100644
--- a/src/translations/types/language.rs
+++ b/src/translations/types/language.rs
@@ -121,7 +121,15 @@ pub fn get_flag<'a>(self) -> Svg<'a, StyleType> {
pub fn is_up_to_date(self) -> bool {
matches!(
self,
- Language::EN | Language::IT | Language::NL | Language::DE | Language::UZ
+ Language::EN
+ | Language::IT
+ | Language::NL
+ | Language::DE
+ | Language::UZ
+ | Language::ZH
+ | Language::JA
+ | Language::FR
+ | Language::EL
)
}
}
diff --git a/src/utils/formatted_strings.rs b/src/utils/formatted_strings.rs
index da4f9d05..8dcabc23 100644
--- a/src/utils/formatted_strings.rs
+++ b/src/utils/formatted_strings.rs
@@ -1,15 +1,8 @@
use std::cmp::min;
use std::net::IpAddr;
-use crate::Language;
-use crate::networking::types::filters::Filters;
-use crate::translations::translations::{
- address_translation, ip_version_translation, protocol_translation,
-};
-use crate::translations::translations_3::{invalid_filters_translation, port_translation};
use crate::utils::types::timestamp::Timestamp;
use chrono::{Local, TimeZone};
-use std::fmt::Write;
/// Application version number (to be displayed in gui footer)
pub const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
@@ -27,61 +20,6 @@
// }
// }
-pub fn get_invalid_filters_string(filters: &Filters, language: Language) -> String {
- let mut ret_val = format!("{}:", invalid_filters_translation(language));
- if !filters.ip_version_valid() {
- let _ = write!(ret_val, "\n • {}", ip_version_translation(language));
- }
- if !filters.protocol_valid() {
- let _ = write!(ret_val, "\n • {}", protocol_translation(language));
- }
- if !filters.address_valid() {
- let _ = write!(ret_val, "\n • {}", address_translation(language));
- }
- if !filters.port_valid() {
- let _ = write!(ret_val, "\n • {}", port_translation(language));
- }
- ret_val
-}
-
-/// Computes the string representing the active filters
-pub fn get_active_filters_string(filters: &Filters, language: Language) -> String {
- let mut filters_string = String::new();
- if filters.ip_version_active() {
- let _ = writeln!(
- filters_string,
- "• {}: {}",
- ip_version_translation(language),
- filters.pretty_print_ip()
- );
- }
- if filters.protocol_active() {
- let _ = writeln!(
- filters_string,
- "• {}: {}",
- protocol_translation(language),
- filters.pretty_print_protocol()
- );
- }
- if filters.address_active() {
- let _ = writeln!(
- filters_string,
- "• {}: {}",
- address_translation(language),
- filters.address_str
- );
- }
- if filters.port_active() {
- let _ = writeln!(
- filters_string,
- "• {}: {}",
- port_translation(language),
- filters.port_str
- );
- }
- filters_string
-}
-
pub fn print_cli_welcome_message() {
let ver = APP_VERSION;
print!(
diff --git a/src/utils/types/icon.rs b/src/utils/types/icon.rs
index 87f16a39..5208aa6a 100644
--- a/src/utils/types/icon.rs
+++ b/src/utils/types/icon.rs
@@ -39,7 +39,7 @@ pub enum Icon {
PacketsThreshold,
// Restore,
Roadmap,
- Rocket,
+ // Rocket,
Settings,
Sniffnet,
SortAscending,
@@ -85,7 +85,7 @@ pub fn codepoint(&self) -> char {
Icon::Overview => 'd',
Icon::PacketsThreshold => '\\',
// Icon::Restore => 'k',
- Icon::Rocket => 'S',
+ // Icon::Rocket => 'S',
Icon::Settings => 'a',
Icon::Sniffnet => 'A',
Icon::Star => 'g',