runique 3.0.2

A Django-inspired web framework for Rust with ORM, templates, and comprehensive security middleware
Documentation
//! Runique built-in admin resources: rights and groups.
//!
//! Automatically injected into the admin registry without declaration in `admin!{}`.

mod droit;
mod groupe;
mod user;

use crate::admin::helper::resource_entry::ResourceEntry;

/// Checks for a unique constraint violation, on every engine.
///
/// The structured check asks sqlx (`is_unique_violation`), which knows each
/// engine's code; it needs `sea_orm::sqlx`, available as soon as a real backend
/// (`postgres`/`mysql`/`sqlite`) is active — `orm`-only builds lack it. Errors
/// that never reached the driver fall back to their wording, case-insensitively:
/// the old check only matched Postgres, SQLite's `UNIQUE constraint failed` and
/// MySQL's `Duplicate entry` slipped through.
pub(crate) fn is_unique_violation(err: &sea_orm::DbErr) -> bool {
    #[cfg(any(feature = "postgres", feature = "mysql", feature = "sqlite"))]
    {
        use sea_orm::{RuntimeErr, sqlx};
        // sqlx knows each engine's code (Postgres 23505, MySQL 1062, SQLite 2067).
        if let sea_orm::DbErr::Exec(RuntimeErr::SqlxError(arc_err))
        | sea_orm::DbErr::Query(RuntimeErr::SqlxError(arc_err)) = err
            && let sqlx::Error::Database(db_err) = arc_err.as_ref()
        {
            return db_err.is_unique_violation();
        }
    }
    // Errors that never reached the driver: their wording, whatever the case
    // (SQLite says "UNIQUE constraint failed", MySQL "Duplicate entry").
    let s = err.to_string().to_lowercase();
    s.contains("23505")
        || s.contains("duplicate key")
        || s.contains("duplicate entry")
        || s.contains("unique constraint")
        || s.contains("duplicated")
}

/// Returns the Runique built-in `ResourceEntry` (users, rights, groups).
/// Called automatically in the admin registry generated by the daemon.
pub fn builtin_resources() -> Vec<ResourceEntry> {
    vec![
        user::user_entry(),
        droit::droit_entry(),
        groupe::groupe_entry(),
    ]
}

/// Written from cargo-mutants survivors (2026-10-02).
#[cfg(test)]
mod unique_violation_tests {
    use super::is_unique_violation;
    use sea_orm::DbErr;

    #[test]
    fn each_engine_wording_is_a_unique_violation() {
        for msg in [
            "SQLSTATE 23505",
            "duplicate key value violates unique constraint \"users_email_key\"",
            "UNIQUE constraint failed: users.email (unique constraint)",
            "Duplicate entry: duplicated value",
        ] {
            assert!(is_unique_violation(&DbErr::Custom(msg.into())), "{msg}");
        }
        assert!(!is_unique_violation(&DbErr::Custom(
            "connection reset".into()
        )));
    }

    /// One message per wording, so that dropping any single check is caught.
    #[test]
    fn each_wording_counts_on_its_own() {
        for msg in [
            "SQLSTATE 23505",
            "duplicate key value",
            "Duplicate entry 'a@b.c' for key 'email'",
            "UNIQUE constraint failed: users.email",
            "value duplicated",
        ] {
            assert!(is_unique_violation(&DbErr::Custom(msg.into())), "{msg}");
        }
    }

    /// A real driver error goes through the SQLSTATE check first: only a
    /// unique violation counts, not any database error.
    #[cfg(feature = "sqlite")]
    #[tokio::test]
    async fn a_real_not_null_error_is_not_a_unique_violation() {
        use sea_orm::{ConnectionTrait, Database};
        let db = Database::connect("sqlite::memory:").await.unwrap();
        db.execute_unprepared("CREATE TABLE t (v TEXT NOT NULL UNIQUE)")
            .await
            .unwrap();
        let not_null = db
            .execute_unprepared("INSERT INTO t (v) VALUES (NULL)")
            .await
            .unwrap_err();
        assert!(!is_unique_violation(&not_null), "{not_null}");
        db.execute_unprepared("INSERT INTO t (v) VALUES ('a')")
            .await
            .unwrap();
        let dup = db
            .execute_unprepared("INSERT INTO t (v) VALUES ('a')")
            .await
            .unwrap_err();
        assert!(is_unique_violation(&dup), "{dup}");
    }
}