// repository/networks.rs — CRUD for the `networks` table use sqlx::{AnyPool, Row}; use crate::models::Network; use crate::server::db::DbError; // ─── Read ───────────────────────────────────────────────────────────────────── /// Returns every network ordered by id. pub async fn list_networks(pool: &AnyPool) -> Result, DbError> { // `fetch_all` runs the query and collects every row into a Vec. // It returns an error if the query fails; an empty table returns Ok(vec![]). let rows = sqlx::query("SELECT id, cidr FROM networks ORDER BY id") .fetch_all(pool) .await?; // `.iter().map(...).collect()` transforms each raw DB row into a Network struct. Ok(rows.iter().map(row_to_network).collect()) } /// Returns a single network by id, or `None` if it does not exist. pub async fn find_network(pool: &AnyPool, id: i64) -> Result, DbError> { // `fetch_optional` returns `Ok(None)` when no row matches — unlike // `fetch_one`, which returns an error when nothing is found. let row = sqlx::query("SELECT id, cidr FROM networks WHERE id = $1") .bind(id) // `$1` is replaced with the value of `id` at runtime .fetch_optional(pool) .await?; // `Option::map` applies the conversion only if the row is Some. Ok(row.as_ref().map(row_to_network)) } // ─── Write ──────────────────────────────────────────────────────────────────── /// Inserts a new network and returns the created record (with its auto-generated id). /// /// Fails with `DbError::Connection` if the CIDR is already registered /// (the `cidr` column has a UNIQUE constraint). /// /// `RETURNING id, cidr` reads back the inserted row in a single round-trip, /// avoiding a separate SELECT after the INSERT. /// Requires SQLite ≥ 3.35 (2021) and any PostgreSQL version. pub async fn create_network(pool: &AnyPool, cidr: &str) -> Result { let row = sqlx::query("INSERT INTO networks (cidr) VALUES ($1) RETURNING id, cidr") .bind(cidr) .fetch_one(pool) // exactly one row is returned by RETURNING .await?; Ok(row_to_network(&row)) } /// Deletes a network by id and all its hosts (via `ON DELETE CASCADE`). /// /// Returns `true` if a row was deleted, `false` if the id did not exist. pub async fn delete_network(pool: &AnyPool, id: i64) -> Result { // `execute` runs the query without fetching rows. // `rows_affected()` tells us how many rows were actually deleted. let result = sqlx::query("DELETE FROM networks WHERE id = $1") .bind(id) .execute(pool) .await?; Ok(result.rows_affected() > 0) } // ─── Row mapping ────────────────────────────────────────────────────────────── /// Converts a raw database row into a `Network` struct. /// /// `row.get("col")` extracts a typed value by column name. /// The type must implement `sqlx::Decode` for the `Any` backend. fn row_to_network(row: &sqlx::any::AnyRow) -> Network { Network { id: row.get("id"), cidr: row.get("cidr"), } }