// models.rs — Shared data models (server + client) // // 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 // // 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}; // ─── Network ────────────────────────────────────────────────────────────────── /// An IP network defined by its CIDR range. /// /// Example: { id: 1, cidr: "192.168.1.0/24" } /// → covers 192.168.1.0 to 192.168.1.255 (254 usable hosts) /// /// CIDR (Classless Inter-Domain Routing) combines the network address and /// the subnet mask into a single field:
/. /// /24 = 24-bit mask = 255.255.255.0 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Network { /// Unique identifier, auto-incremented by the database. /// `i64` is a signed 64-bit integer — maps to `BIGINT` in SQL. pub id: i64, /// Address range in CIDR notation. /// Examples: "10.0.0.0/8", "172.16.0.0/12", "192.168.1.0/24" pub cidr: String, } // ─── Host ───────────────────────────────────────────────────────────────────── /// A host (server, workstation, network device) belonging to a network. /// /// 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, /// Human-readable name. Examples: "web-server-01", "main-router" pub name: String, /// 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, /// Foreign key referencing the network this host belongs to. pub network_id: i64, } // ─── Port ───────────────────────────────────────────────────────────────────── /// A network port entry in the global port catalog. /// /// Ports are defined once here; host_ports and application_ports link them /// to hosts and applications through separate join tables. /// Well-known ports (0–1023) have standardized protocol assignments. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Port { /// TCP/UDP port number (0–65535). /// `u16` is an unsigned 16-bit integer — the exact range for port numbers. pub number: u16, /// Description of the protocol typically running on this port. /// `Option`: absent (None) when the protocol is unknown. /// Examples: Some("SSH"), Some("HTTPS"), None pub description: Option, } // ─── HostPort ───────────────────────────────────────────────────────────────── /// Join record representing a port open on a specific host. /// /// Maps to the `host_ports` table (many-to-many between hosts and ports). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HostPort { pub host_id: i64, pub port_number: u16, } impl Port { /// Returns the standard description for common well-known ports. /// Used to pre-fill the description field when adding a port. /// /// `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 (alternate)"), _ => None, // `_` is the wildcard pattern — matches everything else } } } // ─── Application ────────────────────────────────────────────────────────────── /// An application that uses one or more ports. /// /// 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, /// Application name. Examples: "Nginx", "PostgreSQL", "Prometheus" pub name: String, } // ─── ApplicationPort ────────────────────────────────────────────────────────── /// Join record linking an application to a port (many-to-many relationship). /// /// A dedicated struct is used instead of Vec 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, pub port_number: u16, }