// 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,
}