From 18804e740cff474877d1a499529e2538929ca353 Mon Sep 17 00:00:00 2001 From: mathieu Date: Fri, 15 May 2026 19:56:47 +0200 Subject: [PATCH] style(i18n): translate all code and comments to English Rename French identifiers to English across all source files: - validate_cidr / validate_ip_in_network (was valider_*) - known_protocol (was protocole_connu) - counter / doubled (was compteur / double) - InvalidCidr / InvalidIp / IpOutsideNetwork (was *Invalide / *HorsReseau) - test names and error messages All comments, doc strings, .expect() messages, and tracing logs converted. Co-Authored-By: Claude Sonnet 4.6 --- src/app.rs | 97 ++++++++++++++-------------- src/client/home.rs | 94 +++++++++++++-------------- src/client/mod.rs | 16 ++--- src/lib.rs | 50 +++++++-------- src/main.rs | 77 +++++++++++----------- src/models.rs | 135 +++++++++++++++++++-------------------- src/server/config.rs | 75 +++++++++++----------- src/server/mod.rs | 12 ++-- src/server/routes.rs | 30 ++++----- src/server/validation.rs | 114 ++++++++++++++++----------------- 10 files changed, 346 insertions(+), 354 deletions(-) diff --git a/src/app.rs b/src/app.rs index 3484308..9dd2044 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,8 +1,8 @@ -// app.rs — Composants racine de l'application Leptos +// app.rs — Root Leptos components // -// Ce fichier contient deux composants : -// - `Shell` : le document HTML complet (head + body) — SSR uniquement -// - `App` : le contenu de la page avec le routeur — partagé SSR + WASM +// This file contains two components: +// - `Shell` : full HTML document (head + body) — SSR only +// - `App` : page content and router — shared between SSR and WASM use leptos::prelude::*; use leptos_meta::*; @@ -13,89 +13,88 @@ use leptos_router::{ use crate::client::home::HomePage; -// Shell — document HTML complet rendu par le serveur Axum +// Shell — full HTML document rendered by the Axum server. // -// Ce composant n'existe qu'en mode SSR (`#[cfg(feature = "ssr")]`). -// Il fournit la structure HTML de base que leptos_meta ne peut pas créer seul : -// un et un valides. Sans ça, les composants , <Stylesheet> -// de leptos_meta n'ont nulle part où s'injecter. +// This component only exists in SSR mode (`#[cfg(feature = "ssr")]`). +// It provides the HTML skeleton that leptos_meta requires: +// a valid <head> and <body>. Without it, <Title> and <Stylesheet> +// components have nowhere to inject their output. // -// Flux de rendu SSR : -// 1. Axum appelle Shell() pour chaque requête -// 2. Shell rend le <head> avec MetaTags (placeholder rempli par App) -// 3. Shell rend le <body> contenant App() -// 4. App() appelle provide_meta_context() et définit les métadonnées -// 5. Leptos collecte les métadonnées et les injecte dans MetaTags rétroactivement -// 6. HydrationScripts génère les <script> pour charger le bundle WASM +// SSR rendering flow: +// 1. Axum calls Shell() for each incoming request +// 2. Shell renders <head> with MetaTags (a placeholder filled by App) +// 3. Shell renders <body> containing App() +// 4. App() calls provide_meta_context() and registers metadata +// 5. Leptos retroactively injects that metadata into MetaTags +// 6. HydrationScripts generates <script> tags to load the WASM bundle #[cfg(feature = "ssr")] #[component] pub fn Shell( - // LeptosOptions contient la config du projet (chemins, noms de fichiers, ports...) - // Utilisée par HydrationScripts pour construire les URLs du bundle WASM. + // LeptosOptions holds project configuration (paths, file names, ports...). + // Used by HydrationScripts to build the WASM bundle URLs. options: leptos::config::LeptosOptions, ) -> impl IntoView { view! { <!DOCTYPE html> - <html lang="fr"> + <html lang="en"> <head> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> - // MetaTags : placeholder où leptos_meta injecte les balises collectées - // depuis les composants <Title>, <Stylesheet>, <Meta>... définis dans App(). + // MetaTags: placeholder where leptos_meta injects tags collected + // from <Title>, <Stylesheet>, <Meta>... defined inside App(). <MetaTags/> - // HydrationScripts : génère les balises <link> et <script> qui chargent - // le bundle WASM compilé par trunk et appellent la fonction hydrate() de lib.rs. + // HydrationScripts: generates <link> and <script> tags that load + // the trunk-compiled WASM bundle and call hydrate() from lib.rs. <HydrationScripts options=options.clone()/> - // AutoReload : hot-reload en développement (no-op en production). - // S'active uniquement si la variable d'env LEPTOS_WATCH est définie. + // AutoReload: hot-reload during development (no-op in production). + // Only activates when the LEPTOS_WATCH environment variable is set. <AutoReload options/> </head> <body> - // App() s'exécute ici, fournit le contexte meta et rend le contenu de la page + // App() runs here, provides the meta context, and renders page content. <App/> </body> </html> } } -// App — composant racine partagé entre le serveur (SSR) et le navigateur (WASM) +// App — root component shared between the server (SSR) and the browser (WASM). // -// Ce composant est rendu : -// - côté serveur : dans le <body> du Shell, pour générer le HTML -// - côté navigateur : via hydrate() dans lib.rs, pour attacher la réactivité +// This component is rendered: +// - server-side : inside the Shell <body>, to generate HTML +// - client-side : via hydrate() in lib.rs, to attach reactivity // -// `-> impl IntoView` : retourne "quelque chose affichable". Le type exact est opaque -// car le compilateur Leptos génère un type complexe à partir de la macro `view!`. +// `-> impl IntoView` : returns "something displayable". The concrete type is +// opaque because Leptos's `view!` macro generates a complex internal type. #[component] pub fn App() -> impl IntoView { - // Initialise le système de métadonnées Leptos. - // Sans cet appel, <Title>, <Stylesheet>, <Meta> dans les composants enfants - // n'auraient pas de contexte où stocker les métadonnées. + // Initialize the Leptos metadata context. + // Without this call, <Title>, <Stylesheet>, and <Meta> in child components + // would have no context to store their metadata in. provide_meta_context(); view! { - // Définit le titre de l'onglet navigateur - <Title text="Rust IPAM — Gestionnaire d'adresses IP"/> + <Title text="Rust IPAM — IP Address Manager"/> - // Charge le CSS global depuis /pkg/rust-ipam.css - // Ce fichier est généré par trunk à partir de style.css (si ajouté plus tard) + // Load the global CSS from /pkg/rust-ipam.css. + // This file is generated by trunk from style.css (to be added later). <Stylesheet id="main" href="/pkg/rust-ipam.css"/> - // Le Router gère la navigation sans rechargement de page. - // Côté serveur, il détermine quel composant rendre selon l'URL. + // Router handles client-side navigation without full page reloads. + // On the server, it determines which component to render for the requested URL. <Router> <main> - // <Routes> est le conteneur pour toutes les définitions de routes. - // `fallback` est affiché si aucune route ne correspond. + // <Routes> is the container for all route definitions. + // `fallback` is displayed when no route matches the current URL. <Routes fallback=|| view! { - <div class="page-erreur"> - <h1>"404 — Page introuvable"</h1> - <a href="/">"← Retour à l'accueil"</a> + <div class="not-found"> + <h1>"404 — Page not found"</h1> + <a href="/">"← Back to home"</a> </div> }> - // path!(/) correspond à l'URL racine "/" - // Ajouter de nouvelles pages ici : - // <Route path=path!("/reseaux") view=ReseauxPage/> + // path!(/) matches the root URL "/" + // Add new pages here, e.g.: + // <Route path=path!("/networks") view=NetworksPage/> <Route path=path!("/") view=HomePage/> </Routes> </main> diff --git a/src/client/home.rs b/src/client/home.rs index 10f2219..a2dfdb9 100644 --- a/src/client/home.rs +++ b/src/client/home.rs @@ -1,75 +1,75 @@ -// client/home.rs — Page d'accueil +// client/home.rs — Home page // -// Ce fichier illustre les concepts fondamentaux de Leptos : -// - Composants (`#[component]`) -// - Signals (valeurs réactives) -// - Memos (valeurs dérivées) -// - Gestion d'événements (`on:click`) +// Demonstrates the core Leptos concepts: +// - Components (`#[component]`) +// - Signals (reactive values) +// - Memos (derived values) +// - Event handling (`on:click`) use leptos::prelude::*; -// Composant de la page d'accueil. +// Home page component. // -// En Leptos, un composant est une simple fonction Rust avec l'attribut `#[component]`. -// Elle est appelée une seule fois pour construire le graphe réactif — -// ce n'est pas comme React où le composant se "ré-exécute" à chaque mise à jour. +// In Leptos, a component is a plain Rust function annotated with `#[component]`. +// It is called once to build the reactive graph — +// unlike React, it does not re-execute on every update. #[component] pub fn HomePage() -> impl IntoView { - // `RwSignal<T>` (Read-Write Signal) est une valeur réactive mutable. + // `RwSignal<T>` (Read-Write Signal) is a mutable reactive value. // - // Quand on appelle `.set()` ou `.update()`, Leptos identifie automatiquement - // tous les éléments du DOM qui dépendent de ce signal et les met à jour — - // sans Virtual DOM, sans diff complet : seulement ce qui change. + // When `.set()` or `.update()` is called, Leptos automatically identifies + // all DOM nodes that depend on this signal and updates only those — + // no Virtual DOM, no full diff: surgical updates only. // - // `i32` = entier signé 32 bits (le type entier par défaut en Rust) - let compteur = RwSignal::new(0i32); + // `i32` = signed 32-bit integer (Rust's default integer type) + let counter = RwSignal::new(0i32); - // `Memo<T>` est une valeur calculée à partir d'un ou plusieurs signals. - // Elle se recalcule automatiquement quand `compteur` change, mais - // ne notifie ses dépendants que si sa valeur a effectivement changé. + // `Memo<T>` is a value derived from one or more signals. + // It recomputes automatically when `counter` changes, but only + // notifies its dependents when the result actually differs. // - // `move |_|` : une closure qui capture `compteur` par déplacement (ownership). - // - `move` : la closure prend possession de `compteur` - // - `|_|` : elle ignore son argument (la valeur précédente du memo) - let double = Memo::new(move |_| compteur.get() * 2); + // `move |_|`: a closure that captures `counter` by move (takes ownership). + // - `move` : the closure owns `counter` + // - `|_|` : ignores the argument (the previous memo value) + let doubled = Memo::new(move |_| counter.get() * 2); view! { - <div class="page-accueil"> + <div class="home-page"> <h1>"Rust IPAM"</h1> - <p class="sous-titre">"Gestionnaire d'adresses IP"</p> + <p class="subtitle">"IP Address Manager"</p> - // --- Démonstration de la réactivité --- - // Dans un vrai projet IPAM, on afficherait ici la liste des sous-réseaux, - // les adresses disponibles, les statistiques d'utilisation... - <section class="demo-reactive"> - <h2>"Réactivité Leptos"</h2> + // --- Reactivity demo --- + // In the real IPAM app, this section will show the network list, + // available addresses, usage statistics, etc. + <section class="reactivity-demo"> + <h2>"Leptos Reactivity Demo"</h2> - // `{compteur}` insère la valeur du signal directement dans le DOM. - // Leptos met à jour UNIQUEMENT ce nœud texte quand compteur change. - // Pas de re-render du composant entier : c'est granulaire et efficace. - <p>"Compteur : " {compteur}</p> + // `{counter}` inserts the signal value directly into the DOM. + // Leptos updates ONLY this text node when counter changes — + // the entire component does not re-render. + <p>"Counter: " {counter}</p> - // `{double}` : idem, mais pour le memo (valeur dérivée) - <p>"Double : " {double}</p> + // `{doubled}`: same, but for the memo (derived value) + <p>"Doubled: " {doubled}</p> - <div class="boutons"> - // `on:click` attache un event listener au bouton. + <div class="buttons"> + // `on:click` attaches a JavaScript event listener. // - // `.update(|n| *n += 1)` : - // - prend une closure qui reçoit une référence mutable `&mut i32` - // - `*n` déréférence le pointeur pour modifier la valeur pointée - // - `+= 1` incrémente la valeur en place - <button on:click=move |_| compteur.update(|n| *n += 1)> + // `.update(|n| *n += 1)`: + // - takes a closure receiving a `&mut i32` + // - `*n` dereferences the pointer to modify the value + // - `+= 1` increments in place + <button on:click=move |_| counter.update(|n| *n += 1)> "+" </button> - <button on:click=move |_| compteur.update(|n| *n -= 1)> + <button on:click=move |_| counter.update(|n| *n -= 1)> "-" </button> - // `.set(0)` remplace directement la valeur (plus simple qu'update ici) - <button on:click=move |_| compteur.set(0)> - "Réinitialiser" + // `.set(0)` replaces the value directly (simpler than update here) + <button on:click=move |_| counter.set(0)> + "Reset" </button> </div> </section> diff --git a/src/client/mod.rs b/src/client/mod.rs index c7e9e20..d2b6bbe 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,12 +1,12 @@ -// client/mod.rs — Module client (composants UI) +// client/mod.rs — Client UI module // -// Contient les pages et composants Leptos de l'application. +// Contains Leptos pages and components. // -// Important : malgré le nom "client", ce code s'exécute des DEUX côtés : -// - Côté serveur : pour générer le HTML initial (SSR) -// - Côté navigateur : compilé en WASM pour rendre l'interface interactive +// Despite the name "client", this code runs on BOTH sides: +// - Server-side : to generate the initial HTML (SSR) +// - Browser : compiled to WASM to make the interface interactive // -// Ne pas mettre ici de code qui nécessite des APIs navigateur (window, document...) -// sans le protéger avec `#[cfg(target_arch = "wasm32")]`. +// Do not place code here that requires browser-only APIs (window, document...) +// without guarding it with `#[cfg(target_arch = "wasm32")]`. -pub mod home; // Page d'accueil +pub mod home; // Home page diff --git a/src/lib.rs b/src/lib.rs index 28ae695..28a7de4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,41 +1,41 @@ -// lib.rs — Racine de la bibliothèque partagée +// lib.rs — Shared library root // -// Ce fichier est compilé dans les DEUX modes : -// "ssr" → le serveur Axum l'utilise pour rendre du HTML -// "hydrate" → trunk le compile en WebAssembly pour le navigateur +// This file is compiled in BOTH modes: +// "ssr" → used by the Axum server to render HTML +// "hydrate" → compiled by trunk into WebAssembly for the browser // -// C'est ce partage de code qui rend Leptos "full-stack" : -// on écrit les composants une fois, ils s'exécutent des deux côtés. +// This code sharing is what makes Leptos "full-stack": +// components are written once and run on both sides. -// Déclaration des sous-modules de cette bibliothèque. -// `pub` les rend accessibles depuis main.rs et d'autres crates. -pub mod app; // Composant racine App() et configuration du routeur -pub mod client; // Pages et composants de l'interface utilisateur -pub mod models; // Structs de données partagés server + client (Network, Host, Port, Application) -pub mod server; // Handlers HTTP et logique métier côté serveur +// Declare the sub-modules of this library. +// `pub` makes them accessible from main.rs and other crates. +pub mod app; // Root App() component and router configuration +pub mod client; // UI pages and Leptos components +pub mod models; // Shared data structs: Network, Host, Port, Application +pub mod server; // HTTP handlers and server-side business logic -// Point d'entrée WebAssembly — exécuté par le navigateur au chargement du bundle .wasm +// WebAssembly entry point — called by the browser when the .wasm bundle loads. // -// `#[cfg(feature = "hydrate")]` : ce code n'existe que dans le bundle WASM. -// `#[wasm_bindgen(start)]` : demande à wasm-bindgen d'appeler cette fonction -// automatiquement sans intervention JavaScript. +// `#[cfg(feature = "hydrate")]` : this code only exists in the WASM bundle. +// `#[wasm_bindgen(start)]` : instructs wasm-bindgen to call this function +// automatically, without any JavaScript glue. #[cfg(feature = "hydrate")] #[wasm_bindgen::prelude::wasm_bindgen(start)] pub fn hydrate() { use crate::app::App; - // Active les messages d'erreur Rust dans la console du navigateur. - // Sans ça, un panic Rust en WASM affiche juste "unreachable executed" — inutile. - // `set_once()` garantit qu'on ne l'initialise pas plusieurs fois si hydrate() est appelé plusieurs fois. + // Enable Rust panic messages in the browser console. + // Without this, a Rust panic in WASM only shows "unreachable executed" — useless. + // `set_once()` ensures the hook is registered at most once. console_error_panic_hook::set_once(); - // Monte l'application Leptos dans le <body> de la page HTML. + // Mount the Leptos application into the <body> of the HTML page. // - // En mode "hydration" (SSR + WASM), Leptos ne recrée pas le DOM depuis zéro. - // Il trouve le HTML déjà rendu par le serveur et y attache les event listeners - // pour rendre l'interface interactive. C'est plus rapide qu'un SPA classique - // qui construit tout le DOM côté client. + // In "hydration" mode (SSR + WASM), Leptos does not rebuild the DOM from scratch. + // It finds the HTML already rendered by the server and attaches event listeners + // to make the interface interactive. This is faster than a classic SPA + // that builds the entire DOM on the client side. // - // `hydrate_body` (Leptos 0.7) = mode SSR + hydration (≠ `mount_to_body` qui repart de zéro) + // `hydrate_body` (Leptos 0.7) = SSR + hydration mode (≠ `mount_to_body` which starts fresh) leptos::mount::hydrate_body(App); } diff --git a/src/main.rs b/src/main.rs index 30b8437..2780f0b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,92 +1,91 @@ -// main.rs — Point d'entrée du serveur Axum +// main.rs — Axum server entry point // -// Ce fichier est compilé UNIQUEMENT en mode "ssr" (Server-Side Rendering). -// `#[cfg(feature = "ssr")]` est l'équivalent d'un `#ifdef` en C : -// le code qu'il protège n'existe pas dans le bundle WASM. +// This file is compiled ONLY when the "ssr" feature is enabled. +// `#[cfg(feature = "ssr")]` works like `#ifdef` in C: +// the guarded code does not exist in the WASM bundle. // -// Pour lancer le serveur : +// Run the server: // cargo run --features ssr // -// Pour lancer avec des logs détaillés : +// Run with verbose logs: // RUST_LOG=debug cargo run --features ssr #[cfg(feature = "ssr")] #[tokio::main] -// `#[tokio::main]` transforme `fn main()` synchrone en une fonction asynchrone, -// gérée par le runtime Tokio. Sans ça, Rust ne sait pas exécuter du code `async`. +// `#[tokio::main]` turns the synchronous `fn main()` into an async function +// managed by the Tokio runtime. Without it, Rust cannot execute `async` code. async fn main() { use axum::Router; use leptos::config::get_configuration; - use leptos_axum::{generate_route_list, LeptosRoutes}; use leptos::view; + use leptos_axum::{generate_route_list, LeptosRoutes}; use rust_ipam::{ app::{App, Shell}, server::{config::AppConfig, routes::not_found_handler}, }; use tower_http::services::ServeDir; - // Initialise les logs structurés. - // tracing::info!(), tracing::warn!(), etc. n'affichent rien sans cet initialisateur. - // RUST_LOG=debug cargo run --features ssr → active les logs debug + // Initialize structured logging. + // tracing::info!(), tracing::warn!(), etc. produce no output without this. + // RUST_LOG=debug cargo run --features ssr → enables debug-level logs tracing_subscriber::fmt() .with_env_filter( std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string()), ) .init(); - tracing::info!("Démarrage du serveur Rust IPAM..."); + tracing::info!("Starting Rust IPAM server..."); - // Charge la configuration depuis .env / variables d'environnement. - // On arrête le serveur immédiatement si la config est invalide — il ne peut - // pas fonctionner sans savoir à quelle base de données se connecter. + // Load configuration from environment variables / .env file. + // The server cannot start without knowing which database to connect to, + // so we abort immediately on any configuration error. let app_config = AppConfig::from_env() - .expect("Erreur de configuration — vérifier le fichier .env"); + .expect("Configuration error — check your .env file"); - tracing::info!("Base de données : {} ({})", app_config.backend, app_config.database_url); + tracing::info!("Database: {} ({})", app_config.backend, app_config.database_url); - // `Some("Cargo.toml")` indique à Leptos de lire la section - // [package.metadata.leptos] du Cargo.toml pour la configuration - // (noms de fichiers, chemins, adresse serveur...). + // `Some("Cargo.toml")` tells Leptos to read the [package.metadata.leptos] + // section from Cargo.toml (file paths, output names, server address...). let conf = get_configuration(Some("Cargo.toml")) - .expect("Impossible de charger la configuration Leptos depuis Cargo.toml"); + .expect("Failed to load Leptos configuration from Cargo.toml"); let leptos_options = conf.leptos_options; let addr = leptos_options.site_addr; - // Analyse les composants `<Route>` dans `App` pour construire - // la liste des URLs que Leptos SSR doit gérer. + // Walk all `<Route>` components inside `App` to build the list of URLs + // that Leptos SSR must handle. let routes = generate_route_list(App); - // Construit le routeur Axum avec le pattern builder (chaînage de méthodes). + // Build the Axum router using the builder pattern (method chaining). let app = Router::new() - // Sert les fichiers statiques compilés par trunk (WASM, JS...). - // Trunk les place dans target/site/pkg/ (configuré dans [package.metadata.leptos]). + // Serve static files compiled by trunk (WASM, JS...). + // Trunk places them in target/site/pkg/ as configured in [package.metadata.leptos]. .nest_service("/pkg", ServeDir::new("target/site/pkg")) - // Branche les routes Leptos dans Axum. - // Pour chaque URL, Axum rend Shell() en HTML et le renvoie au navigateur. - // Shell() contient App() qui fournit le contenu de la page. + // Mount all Leptos routes into Axum. + // For each URL, Axum renders Shell() to HTML and sends it to the browser. + // Shell() contains App(), which provides the page content. .leptos_routes(&leptos_options, routes, { - // On clone les options pour les capturer dans la closure. - // Le `move` transfère la propriété de `leptos_options` dans la closure. + // Clone options so the closure can capture them. + // `move` transfers ownership of `leptos_options` into the closure. let leptos_options = leptos_options.clone(); move || view! { <Shell options=leptos_options.clone()/> } }) .fallback(not_found_handler) - // Partage les options Leptos avec tous les handlers via le système d'état Axum. + // Share Leptos options with all handlers via Axum's state system. .with_state(leptos_options); let listener = tokio::net::TcpListener::bind(&addr) .await - .expect(&format!("Impossible d'écouter sur l'adresse {}", addr)); + .expect(&format!("Failed to bind to address {}", addr)); - tracing::info!("Serveur disponible sur http://{}", addr); + tracing::info!("Server listening on http://{}", addr); axum::serve(listener, app) .await - .expect("Erreur critique du serveur"); + .expect("Fatal server error"); } -// Ce bloc vide est obligatoire pour que le compilateur trouve un `fn main()` -// en mode WASM (où la feature "ssr" n'est pas activée). -// En WASM, le vrai point d'entrée est la fonction `hydrate()` dans lib.rs. +// This empty block is required so the compiler finds a `fn main()` +// when building in WASM mode (where the "ssr" feature is not enabled). +// In WASM, the real entry point is `hydrate()` in lib.rs. #[cfg(not(feature = "ssr"))] fn main() {} diff --git a/src/models.rs b/src/models.rs index 6de5eb3..29e3ae8 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,132 +1,131 @@ -// models.rs — Modèles de données partagés (server + client) +// models.rs — Shared data models (server + client) // -// Ce module définit les structs qui représentent les entités métier du projet IPAM. -// Ils sont compilés côté serveur ET côté WASM car Leptos en a besoin des deux côtés : -// - Serveur : pour lire/écrire en BDD et rendre le HTML -// - Client : pour afficher les données dans les composants Leptos +// This module defines the structs that represent the IPAM domain entities. +// They are compiled for both the server and WASM, because Leptos needs them +// on both sides: +// - Server : to read/write the database and render HTML +// - Client : to display data inside Leptos components // -// Chaque struct dérive `Serialize` et `Deserialize` de serde. -// C'est obligatoire pour que Leptos puisse transférer les données entre -// le serveur et le navigateur via les "server functions" (#[server]). +// Each struct derives `Serialize` and `Deserialize` from serde. +// This is required for Leptos to transfer data between the server and the +// browser through server functions (#[server]). use serde::{Deserialize, Serialize}; -// ─── Réseau ─────────────────────────────────────────────────────────────────── +// ─── Network ────────────────────────────────────────────────────────────────── -/// Un réseau IP défini par sa plage CIDR. +/// An IP network defined by its CIDR range. /// -/// Exemple : { id: 1, cidr: "192.168.1.0/24" } -/// → plage de 192.168.1.0 à 192.168.1.255 (254 hôtes utilisables) +/// Example: { id: 1, cidr: "192.168.1.0/24" } +/// → covers 192.168.1.0 to 192.168.1.255 (254 usable hosts) /// -/// La notation CIDR (Classless Inter-Domain Routing) combine l'adresse réseau -/// et le masque en un seul champ : <adresse>/<longueur du préfixe>. -/// /24 = 24 bits de masque = 255.255.255.0 +/// CIDR (Classless Inter-Domain Routing) combines the network address and +/// the subnet mask into a single field: <address>/<prefix length>. +/// /24 = 24-bit mask = 255.255.255.0 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Network { - /// Identifiant unique auto-incrémenté par la base de données. - /// `i64` est le type entier signé 64 bits — correspond à `BIGINT` en SQL. + /// Unique identifier, auto-incremented by the database. + /// `i64` is a signed 64-bit integer — maps to `BIGINT` in SQL. pub id: i64, - /// Plage d'adresses en notation CIDR. - /// Ex: "10.0.0.0/8", "172.16.0.0/12", "192.168.1.0/24" + /// Address range in CIDR notation. + /// Examples: "10.0.0.0/8", "172.16.0.0/12", "192.168.1.0/24" pub cidr: String, } -// ─── Hôte ───────────────────────────────────────────────────────────────────── +// ─── Host ───────────────────────────────────────────────────────────────────── -/// Un hôte (machine, serveur, équipement réseau) appartenant à un réseau. +/// A host (server, workstation, network device) belonging to a network. /// -/// Contrainte : l'IP doit appartenir à la plage CIDR du réseau référencé -/// par `network_id`. Cette contrainte est vérifiée à la création/modification. +/// Constraint: the IP address must fall within the CIDR range of the network +/// referenced by `network_id`. This is enforced on creation and update. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Host { pub id: i64, - /// Nom descriptif de l'hôte. Ex: "serveur-web-01", "routeur-principal" + /// Human-readable name. Examples: "web-server-01", "main-router" pub name: String, - /// Adresse IPv4 de l'hôte, stockée en texte. - /// Ex: "192.168.1.10" - /// On utilise String plutôt que IpAddr pour simplifier la sérialisation - /// et le stockage en base de données. + /// IPv4 address stored as text. Example: "192.168.1.10" + /// We use String instead of IpAddr to simplify serialization + /// and database storage. pub ip: String, - /// Référence vers le réseau auquel appartient cet hôte. - /// C'est une "clé étrangère" (Foreign Key) vers la table `networks`. + /// Foreign key referencing the network this host belongs to. pub network_id: i64, } // ─── Port ───────────────────────────────────────────────────────────────────── -/// Un port réseau ouvert sur un hôte, avec sa description probable. +/// A network port open on a host, with its likely protocol description. /// -/// Les numéros de ports standards (0–1023) sont des "well-known ports". -/// Un port peut être associé à plusieurs applications (association non-stricte). +/// Well-known ports (0–1023) have standardized protocol assignments. +/// A port can be associated with multiple applications (non-strict relation). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Port { - /// Numéro de port TCP/UDP. - /// `u16` : entier non signé 16 bits → plage 0 à 65535. - /// C'est exactement la plage valide pour les ports réseau. + /// TCP/UDP port number. + /// `u16` is an unsigned 16-bit integer → range 0 to 65535, + /// which exactly matches the valid range for network ports. pub number: u16, - /// Description du protocole probable sur ce port. - /// `Option<String>` : peut être absent (None) si le protocole est inconnu. - /// Ex: Some("SSH"), Some("HTTPS"), None + /// Description of the likely protocol on this port. + /// `Option<String>`: may be absent (None) when the protocol is unknown. + /// Examples: Some("SSH"), Some("HTTPS"), None pub description: Option<String>, - /// Hôte sur lequel ce port est ouvert. + /// The host on which this port is open. pub host_id: i64, } impl Port { - /// Retourne la description standard pour les ports les plus courants. - /// Utilisé pour pré-remplir la description lors de l'ajout d'un port. + /// Returns the standard description for common well-known ports. + /// Used to pre-fill the description field when adding a port. /// - /// `match` est l'équivalent Rust d'un switch/case, mais exhaustif : - /// le compilateur oblige à gérer tous les cas possibles. - pub fn protocole_connu(numero: u16) -> Option<&'static str> { - // `&'static str` : référence vers une chaîne qui vit toute la durée du programme - // (les littéraux de chaînes comme "SSH" sont stockés dans le binaire compilé) - match numero { - 21 => Some("FTP"), - 22 => Some("SSH"), - 23 => Some("Telnet"), - 25 => Some("SMTP"), - 53 => Some("DNS"), - 80 => Some("HTTP"), - 110 => Some("POP3"), - 143 => Some("IMAP"), - 443 => Some("HTTPS"), + /// `match` is Rust's exhaustive pattern-matching construct (like switch/case, + /// but the compiler enforces that all cases are handled). + pub fn known_protocol(number: u16) -> Option<&'static str> { + // `&'static str`: a reference to a string that lives for the entire + // program lifetime (string literals are stored in the compiled binary). + match number { + 21 => Some("FTP"), + 22 => Some("SSH"), + 23 => Some("Telnet"), + 25 => Some("SMTP"), + 53 => Some("DNS"), + 80 => Some("HTTP"), + 110 => Some("POP3"), + 143 => Some("IMAP"), + 443 => Some("HTTPS"), 3306 => Some("MySQL"), 5432 => Some("PostgreSQL"), 6379 => Some("Redis"), - 8080 => Some("HTTP alternatif"), - _ => None, // `_` est le pattern "tout le reste" (wildcard) + 8080 => Some("HTTP (alternate)"), + _ => None, // `_` is the wildcard pattern — matches everything else } } } // ─── Application ────────────────────────────────────────────────────────────── -/// Une application qui utilise un ou plusieurs ports. +/// An application that uses one or more ports. /// -/// L'association entre application et port est non-stricte : -/// un même port peut être partagé par plusieurs applications. -/// Ex: le port 80 peut être utilisé par Nginx ET par un proxy applicatif. +/// The association between an application and a port is non-strict: +/// the same port can be shared by multiple applications. +/// Example: port 80 might be used by both Nginx and an application proxy. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Application { pub id: i64, - /// Nom de l'application. Ex: "Nginx", "PostgreSQL", "Prometheus" + /// Application name. Examples: "Nginx", "PostgreSQL", "Prometheus" pub name: String, } -// ─── Association Application ↔ Port ────────────────────────────────────────── +// ─── ApplicationPort ────────────────────────────────────────────────────────── -/// Lien entre une application et un port (relation many-to-many). +/// Join record linking an application to a port (many-to-many relationship). /// -/// On utilise un struct dédié plutôt qu'un Vec<Port> dans Application -/// pour correspondre directement à la table de jointure en base de données. +/// A dedicated struct is used instead of Vec<Port> inside Application +/// so it maps directly to the join table in the database. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ApplicationPort { pub application_id: i64, diff --git a/src/server/config.rs b/src/server/config.rs index e2d9de3..518394e 100644 --- a/src/server/config.rs +++ b/src/server/config.rs @@ -1,44 +1,44 @@ -// server/config.rs — Configuration de l'application +// server/config.rs — Application configuration // -// Ce module charge et valide la configuration au démarrage du serveur. -// Toute la configuration passe par des variables d'environnement, -// elles-mêmes chargées depuis un fichier `.env` via dotenvy. +// This module loads and validates configuration at server startup. +// All configuration is read from environment variables, +// which can be populated from a `.env` file via dotenvy. // -// Principe : une seule variable suffit pour choisir la base de données. -// DATABASE_URL=sqlite://data/ipam.db → SQLite (dev, pas de serveur nécessaire) +// A single variable is enough to select the database backend: +// DATABASE_URL=sqlite://data/ipam.db → SQLite (dev, no server needed) // DATABASE_URL=postgresql://user:pw@host/db → PostgreSQL (production) use thiserror::Error; -// ─── Erreurs de configuration ───────────────────────────────────────────────── +// ─── Configuration errors ───────────────────────────────────────────────────── -// `#[derive(Error)]` de la crate thiserror génère automatiquement l'impl -// du trait standard `std::error::Error` — pas besoin de l'écrire à la main. +// `#[derive(Error)]` from thiserror automatically generates the impl for the +// standard `std::error::Error` trait — no need to write it by hand. // -// `#[error("...")]` définit le message affiché par Display / println!("{}", err). +// `#[error("...")]` defines the message shown by Display / println!("{}", err). #[derive(Debug, Error)] pub enum ConfigError { - // `#[from]` permet de convertir automatiquement un VarError en ConfigError - // via l'opérateur `?`. Ex: std::env::var("X")? dans une fn -> Result<_, ConfigError> - #[error("Variable d'environnement manquante : {0}")] + // `#[from]` auto-converts a VarError into ConfigError via the `?` operator. + // Example: std::env::var("X")? in a fn -> Result<_, ConfigError> + #[error("Missing environment variable: {0}")] MissingVar(#[from] std::env::VarError), - #[error("URL de base de données non reconnue : '{0}' — doit commencer par sqlite:// ou postgresql://")] + #[error("Unknown database URL '{0}' — must start with sqlite:// or postgresql://")] UnknownBackend(String), } -// ─── Backend de base de données ─────────────────────────────────────────────── +// ─── Database backend ───────────────────────────────────────────────────────── -// `#[derive(Debug, Clone)]` génère automatiquement : -// - Debug : permet d'afficher la valeur avec {:?} dans les logs -// - Clone : permet de copier la valeur (nécessaire pour l'état Axum) +// `#[derive(Debug, Clone)]` automatically generates: +// - Debug : allows printing the value with {:?} in logs +// - Clone : allows copying the value (required for Axum state) #[derive(Debug, Clone, PartialEq)] pub enum DatabaseBackend { Postgres, Sqlite, } -// Impl Display pour pouvoir écrire tracing::info!("Backend : {}", backend) +// Display impl allows writing: tracing::info!("Backend: {}", backend) impl std::fmt::Display for DatabaseBackend { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -48,35 +48,35 @@ impl std::fmt::Display for DatabaseBackend { } } -// ─── Configuration principale ───────────────────────────────────────────────── +// ─── Application configuration ──────────────────────────────────────────────── #[derive(Debug, Clone)] pub struct AppConfig { - /// URL complète de connexion à la base de données. - /// Ex: "sqlite://data/ipam.db" ou "postgresql://user:pw@localhost/ipam" + /// Full database connection URL. + /// Examples: "sqlite://data/ipam.db" or "postgresql://user:pw@localhost/ipam" pub database_url: String, - /// Backend détecté automatiquement depuis le préfixe de DATABASE_URL. + /// Backend detected automatically from the DATABASE_URL prefix. pub backend: DatabaseBackend, } impl AppConfig { - /// Charge la configuration depuis les variables d'environnement. + /// Loads configuration from environment variables. /// - /// Ordre de priorité pour les variables : - /// 1. Variables déjà définies dans le shell (ex: export DATABASE_URL=...) - /// 2. Fichier `.env` à la racine du projet + /// Variable priority order: + /// 1. Variables already set in the shell (e.g. export DATABASE_URL=...) + /// 2. `.env` file at the project root /// - /// Retourne une `ConfigError` si DATABASE_URL est absente ou invalide. + /// Returns `ConfigError` if DATABASE_URL is missing or unrecognized. pub fn from_env() -> Result<Self, ConfigError> { - // Charge le fichier .env s'il existe. - // `let _ =` ignore silencieusement l'erreur si .env est absent — - // c'est voulu : en production les variables sont injectées directement. + // Load the .env file if it exists. + // `let _ =` silently ignores the error when .env is absent — + // this is intentional: in production, variables are injected directly. let _ = dotenvy::dotenv(); - // `std::env::var` retourne Result<String, VarError>. - // Le `?` propage l'erreur VarError, convertie en ConfigError::MissingVar - // grâce au `#[from]` défini plus haut. + // `std::env::var` returns Result<String, VarError>. + // The `?` propagates VarError, converted to ConfigError::MissingVar + // thanks to the `#[from]` attribute above. let database_url = std::env::var("DATABASE_URL")?; let backend = Self::detect_backend(&database_url)?; @@ -84,17 +84,16 @@ impl AppConfig { Ok(Self { database_url, backend }) } - /// Déduit le backend depuis le préfixe de l'URL. + /// Infers the database backend from the URL prefix. /// - /// `&str` : référence vers une chaîne — pas de copie, juste un emprunt. + /// `&str`: a borrowed string reference — no copy, just a borrow. fn detect_backend(url: &str) -> Result<DatabaseBackend, ConfigError> { - // `starts_with` est une méthode de &str qui vérifie le préfixe if url.starts_with("postgresql://") || url.starts_with("postgres://") { Ok(DatabaseBackend::Postgres) } else if url.starts_with("sqlite://") { Ok(DatabaseBackend::Sqlite) } else { - // `to_string()` crée un String owned depuis un &str + // `to_string()` creates an owned String from a &str Err(ConfigError::UnknownBackend(url.to_string())) } } diff --git a/src/server/mod.rs b/src/server/mod.rs index 76a1037..b7e95a0 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,15 +1,13 @@ -// server/mod.rs — Module serveur +// server/mod.rs — Server-side module // -// Contient tout le code qui s'exécute uniquement côté serveur. +// Contains all code that runs on the server only. // -// Certains sous-modules utilisent des crates SSR-only (dotenvy, ipnetwork...) -// et sont donc protégés par `#[cfg(feature = "ssr")]` pour ne pas être -// compilés dans le bundle WASM. +// Some sub-modules depend on SSR-only crates (dotenvy, ipnetwork...) +// and are therefore gated with `#[cfg(feature = "ssr")]` to prevent +// them from being compiled into the WASM bundle. -// Accessible des deux côtés (handlers HTTP sont SSR mais le module est déclaré shared) pub mod routes; -// Modules SSR uniquement — utilisent des crates non disponibles en WASM #[cfg(feature = "ssr")] pub mod config; diff --git a/src/server/routes.rs b/src/server/routes.rs index e4109bb..344fccd 100644 --- a/src/server/routes.rs +++ b/src/server/routes.rs @@ -1,26 +1,26 @@ -// server/routes.rs — Handlers HTTP Axum additionnels +// server/routes.rs — Additional Axum HTTP handlers // -// Ces handlers complètent les routes gérées par Leptos. -// Exemples d'usages futurs : -// - Endpoints API REST (/api/...) -// - Exports de fichiers (CSV, PDF...) -// - Webhooks entrants -// - Health check pour le monitoring (/health) +// These handlers complement the routes managed by Leptos. +// Intended for future use: +// - REST API endpoints (/api/...) +// - File exports (CSV, PDF...) +// - Incoming webhooks +// - Health check endpoint (/health) // -// `#[cfg(feature = "ssr")]` protège tout ce fichier : -// Axum n'existe pas dans le bundle WASM, donc on ne compile ce code qu'en mode serveur. +// The entire file is guarded by `#[cfg(feature = "ssr")]`: +// Axum does not exist in the WASM bundle, so this code is server-only. #[cfg(feature = "ssr")] use axum::{http::StatusCode, response::IntoResponse}; -// Handler 404 — utilisé comme fallback dans main.rs pour toute URL non reconnue. +// Fallback 404 handler — used in main.rs for any URL not matched by Leptos or Axum. // -// `impl IntoResponse` : Axum accepte n'importe quel type qui implémente ce trait. -// Un tuple `(StatusCode, &str)` l'implémente automatiquement : -// Axum en fait une réponse HTTP 404 avec le corps "Page introuvable". +// `impl IntoResponse`: Axum accepts any type that implements this trait. +// A `(StatusCode, &str)` tuple implements it automatically: +// Axum turns it into an HTTP 404 response with the given body. // -// `async fn` est obligatoire pour les handlers Axum, même sans opération asynchrone. +// `async fn` is required for Axum handlers, even without any async operations. #[cfg(feature = "ssr")] pub async fn not_found_handler() -> impl IntoResponse { - (StatusCode::NOT_FOUND, "Page introuvable") + (StatusCode::NOT_FOUND, "Not found") } diff --git a/src/server/validation.rs b/src/server/validation.rs index 11ebde6..24573a9 100644 --- a/src/server/validation.rs +++ b/src/server/validation.rs @@ -1,64 +1,62 @@ -// server/validation.rs — Validation des données métier +// server/validation.rs — Business rule validation // -// Ce module vérifie les règles métier qui ne peuvent pas être exprimées -// uniquement par les types Rust. Il s'exécute côté serveur uniquement -// car il utilise `ipnetwork` pour les calculs CIDR. +// This module enforces rules that cannot be expressed through Rust's type system +// alone. It runs server-side only because it uses `ipnetwork` for CIDR arithmetic. use ipnetwork::IpNetwork; use std::net::IpAddr; use thiserror::Error; -// ─── Erreurs de validation ──────────────────────────────────────────────────── +// ─── Validation errors ──────────────────────────────────────────────────────── #[derive(Debug, Error)] pub enum ValidationError { - /// Le format CIDR est invalide (ex: "192.168.1/24" au lieu de "192.168.1.0/24") - #[error("CIDR invalide '{0}' : {1}")] - CidrInvalide(String, ipnetwork::IpNetworkError), + /// The CIDR string is malformed (e.g. "192.168.1/24" instead of "192.168.1.0/24") + #[error("Invalid CIDR '{0}': {1}")] + InvalidCidr(String, ipnetwork::IpNetworkError), - /// L'adresse IP n'a pas le bon format - #[error("Adresse IP invalide '{0}' : {1}")] - IpInvalide(String, std::net::AddrParseError), + /// The IP address string is malformed + #[error("Invalid IP address '{0}': {1}")] + InvalidIp(String, std::net::AddrParseError), - /// L'IP de l'hôte n'appartient pas à la plage du réseau - #[error("L'adresse IP {ip} n'appartient pas au réseau {cidr}")] - IpHorsReseau { ip: String, cidr: String }, + /// The host IP does not fall within the network's CIDR range + #[error("IP address {ip} does not belong to network {cidr}")] + IpOutsideNetwork { ip: String, cidr: String }, } -// ─── Validation du CIDR ─────────────────────────────────────────────────────── +// ─── CIDR validation ────────────────────────────────────────────────────────── -/// Vérifie qu'une chaîne est un CIDR valide. -/// Retourne le réseau parsé si valide. +/// Validates that a string is a well-formed CIDR block. +/// Returns the parsed network on success. /// -/// `&str` : on emprunte la chaîne sans en prendre possession (pas de copie). -/// `Result<T, E>` : soit une valeur T (succès), soit une erreur E (échec). -pub fn valider_cidr(cidr: &str) -> Result<IpNetwork, ValidationError> { +/// `&str`: borrowed string reference — no copy, just a borrow. +/// `Result<T, E>`: either a value T (success) or an error E (failure). +pub fn validate_cidr(cidr: &str) -> Result<IpNetwork, ValidationError> { cidr.parse::<IpNetwork>() - // `.map_err` transforme l'erreur si `parse` échoue - // `|e|` est une closure : une fonction anonyme qui prend `e` en paramètre - .map_err(|e| ValidationError::CidrInvalide(cidr.to_string(), e)) + // `.map_err` transforms the error if `parse` fails. + // `|e|` is a closure: an anonymous function that takes `e` as a parameter. + .map_err(|e| ValidationError::InvalidCidr(cidr.to_string(), e)) } -// ─── Validation de l'appartenance IP ↔ Réseau ──────────────────────────────── +// ─── IP-in-network validation ───────────────────────────────────────────────── -/// Vérifie qu'une adresse IP appartient à la plage d'un réseau CIDR. +/// Verifies that an IP address belongs to a given CIDR network range. /// -/// Règle métier clé : un hôte doit toujours être dans le réseau auquel il appartient. -/// Ex : 192.168.1.10 ✓ dans 192.168.1.0/24 -/// 10.0.0.1 ✗ dans 192.168.1.0/24 -pub fn valider_ip_dans_reseau(ip: &str, cidr: &str) -> Result<(), ValidationError> { - // On parse le CIDR et l'IP, propageant les erreurs avec `?` - let reseau = valider_cidr(cidr)?; +/// Key business rule: a host must always reside within its assigned network. +/// Example: 192.168.1.10 ✓ in 192.168.1.0/24 +/// 10.0.0.1 ✗ in 192.168.1.0/24 +pub fn validate_ip_in_network(ip: &str, cidr: &str) -> Result<(), ValidationError> { + let network = validate_cidr(cidr)?; - let adresse: IpAddr = ip + let address: IpAddr = ip .parse() - .map_err(|e| ValidationError::IpInvalide(ip.to_string(), e))?; + .map_err(|e| ValidationError::InvalidIp(ip.to_string(), e))?; - // `IpNetwork::contains` retourne true si l'IP est dans la plage - if reseau.contains(adresse) { + // `IpNetwork::contains` returns true if the address falls within the range. + if network.contains(address) { Ok(()) } else { - Err(ValidationError::IpHorsReseau { + Err(ValidationError::IpOutsideNetwork { ip: ip.to_string(), cidr: cidr.to_string(), }) @@ -67,44 +65,44 @@ pub fn valider_ip_dans_reseau(ip: &str, cidr: &str) -> Result<(), ValidationErro // ─── Tests ──────────────────────────────────────────────────────────────────── -// `#[cfg(test)]` : ce bloc n'est compilé que lors de `cargo test` -// Écrire les tests directement dans le même fichier est idiomatique en Rust. +// `#[cfg(test)]`: this block is only compiled when running `cargo test`. +// Writing tests in the same file as the code being tested is idiomatic Rust. #[cfg(test)] mod tests { - // `super::*` importe tout depuis le module parent (ce fichier) + // `super::*` imports everything from the parent module (this file). use super::*; #[test] - fn ip_dans_reseau_valide() { - // `.unwrap()` est acceptable dans les tests — si ça échoue, le test échoue - assert!(valider_ip_dans_reseau("192.168.1.10", "192.168.1.0/24").is_ok()); - assert!(valider_ip_dans_reseau("10.0.0.1", "10.0.0.0/8").is_ok()); - assert!(valider_ip_dans_reseau("172.16.5.100", "172.16.0.0/12").is_ok()); + fn ip_within_valid_network() { + // `.unwrap()` is acceptable in tests — a failure here fails the test. + assert!(validate_ip_in_network("192.168.1.10", "192.168.1.0/24").is_ok()); + assert!(validate_ip_in_network("10.0.0.1", "10.0.0.0/8").is_ok()); + assert!(validate_ip_in_network("172.16.5.100", "172.16.0.0/12").is_ok()); } #[test] - fn ip_hors_reseau() { - assert!(valider_ip_dans_reseau("10.0.0.1", "192.168.1.0/24").is_err()); - assert!(valider_ip_dans_reseau("192.168.2.1", "192.168.1.0/24").is_err()); + fn ip_outside_network() { + assert!(validate_ip_in_network("10.0.0.1", "192.168.1.0/24").is_err()); + assert!(validate_ip_in_network("192.168.2.1", "192.168.1.0/24").is_err()); } #[test] - fn cidr_invalide() { - assert!(valider_cidr("pas-un-cidr").is_err()); - assert!(valider_cidr("192.168.1/24").is_err()); // adresse tronquée - assert!(valider_cidr("192.168.1.0/33").is_err()); // préfixe > 32 + fn invalid_cidr() { + assert!(validate_cidr("not-a-cidr").is_err()); + assert!(validate_cidr("192.168.1/24").is_err()); // truncated address + assert!(validate_cidr("192.168.1.0/33").is_err()); // prefix > 32 } #[test] - fn ip_invalide() { - assert!(valider_ip_dans_reseau("999.0.0.1", "192.168.1.0/24").is_err()); - assert!(valider_ip_dans_reseau("pas-une-ip", "192.168.1.0/24").is_err()); + fn invalid_ip() { + assert!(validate_ip_in_network("999.0.0.1", "192.168.1.0/24").is_err()); + assert!(validate_ip_in_network("not-an-ip", "192.168.1.0/24").is_err()); } #[test] - fn protocole_connu() { - assert_eq!(crate::models::Port::protocole_connu(22), Some("SSH")); - assert_eq!(crate::models::Port::protocole_connu(443), Some("HTTPS")); - assert_eq!(crate::models::Port::protocole_connu(9999), None); + fn known_protocol() { + assert_eq!(crate::models::Port::known_protocol(22), Some("SSH")); + assert_eq!(crate::models::Port::known_protocol(443), Some("HTTPS")); + assert_eq!(crate::models::Port::known_protocol(9999), None); } }