use crate::core::Column as _;
use crate::sql::{CounterPool as _, FetcherPool as _, Pool};
use crate::tenancy::auth::Operator;
#[derive(Debug)]
pub enum OperatorError {
NotFound(String),
SelfDeactivation,
LastActive,
Driver(crate::sql::ExecError),
}
impl std::fmt::Display for OperatorError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NotFound(u) => write!(f, "no operator named `{u}`"),
Self::SelfDeactivation => write!(
f,
"you cannot deactivate yourself — your next request would be rejected. \
Ask another operator to do it."
),
Self::LastActive => write!(
f,
"this is the last active operator. Deactivating it would lock everyone \
out of the console, and only a shell on the registry could undo it."
),
Self::Driver(e) => write!(f, "{e}"),
}
}
}
impl std::error::Error for OperatorError {}
impl From<crate::sql::ExecError> for OperatorError {
fn from(e: crate::sql::ExecError) -> Self {
Self::Driver(e)
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Outcome {
Changed,
AlreadySo,
}
pub async fn by_username(registry: &Pool, username: &str) -> Result<Operator, OperatorError> {
Operator::objects()
.where_(Operator::username.eq(username.to_owned()))
.fetch(registry)
.await?
.into_iter()
.next()
.ok_or_else(|| OperatorError::NotFound(username.to_owned()))
}
pub async fn list(registry: &Pool) -> Result<Vec<Operator>, OperatorError> {
Ok(Operator::objects()
.order_by(&[("username", false)])
.fetch(registry)
.await?)
}
pub async fn active_count(registry: &Pool, except: Option<i64>) -> Result<i64, OperatorError> {
let mut q = Operator::objects().where_(Operator::active.eq(true));
if let Some(id) = except {
q = q.where_(Operator::id.ne(id));
}
Ok(q.count(registry).await?)
}
pub async fn set_active(
registry: &Pool,
target: &mut Operator,
active: bool,
actor: Option<i64>,
) -> Result<Outcome, OperatorError> {
let id = target.id.get().copied().unwrap_or_default();
if target.active == active {
return Ok(Outcome::AlreadySo);
}
if !active {
if actor == Some(id) {
return Err(OperatorError::SelfDeactivation);
}
if active_count(registry, Some(id)).await? == 0 {
return Err(OperatorError::LastActive);
}
}
target.active = active;
target.save_pool(registry).await?;
Ok(Outcome::Changed)
}