//! [`UserProvider`] — the trait that backs the `login!` and `register!` macros.
//!
//! Implement this for your `User` model so the auth macros can look up users
//! by email and retrieve password hashes and role lists.
//!
//! # Example (with rok-orm PgModel)
//!
//! ```rust,ignore
//! use rok_auth::{AuthError, UserProvider};
//! use rok_orm::PgModel;
//!
//! #[derive(rok_orm::Model, sqlx::FromRow)]
//! pub struct User {
//!     pub id:            i64,
//!     pub email:         String,
//!     pub password_hash: String,
//!     pub roles:         Vec<String>,
//! }
//!
//! impl UserProvider for User {
//!     type Id = i64;
//!
//!     fn user_id(&self) -> i64 { self.id }
//!     fn password_hash(&self) -> &str { &self.password_hash }
//!     fn roles(&self) -> Vec<String> { self.roles.clone() }
//!
//!     async fn find_by_email(
//!         pool: &sqlx::PgPool,
//!         email: &str,
//!     ) -> Result<Option<Self>, AuthError> {
//!         User::find_where(pool, User::query().where_eq("email", email))
//!             .await
//!             .map(|mut v| v.pop())
//!             .map_err(|e| AuthError::Internal(e.to_string()))
//!     }
//! }
//! ```

use std::future::Future;

/// Trait that bridges rok-auth macros to your application's user model.
///
/// Implement this for the struct that represents a user row in your database.
/// The `login!` and `register!` macros then work with any conforming type.
pub trait UserProvider: Sized + Send + Unpin {
    /// The type of the user's primary key (e.g. `i64`, `uuid::Uuid`).
    type Id: ToString + Send;

    /// Return the user's primary key value.
    fn user_id(&self) -> Self::Id;

    /// Return the stored PHC-encoded password hash (produced by `password::hash`).
    fn password_hash(&self) -> &str;

    /// Return the list of role strings assigned to this user.
    fn roles(&self) -> Vec<String>;

    /// Look up a user by email address, returning `None` if not found.
    ///
    /// Called by the `login!` macro to retrieve the stored hash and roles.
    fn find_by_email(
        pool: &sqlx::PgPool,
        email: &str,
    ) -> impl Future<Output = Result<Option<Self>, crate::AuthError>> + Send;
}
