//! The three levels settings live at, site, organisation and repository, //! and how a repository sees the levels above it. Shared by AI assistants //! (§13.4) and Flow variables (§13.2). use crate::db::Pool; /// Which level a setting belongs to. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Scope { Site, Org(i64), Repo(i64), } impl Scope { pub fn kind(self) -> &'static str { match self { Scope::Site => "site", Scope::Org(_) => "org", Scope::Repo(_) => "repo", } } pub fn id(self) -> i64 { match self { Scope::Site => 0, Scope::Org(id) | Scope::Repo(id) => id, } } } /// A level as a settings page addresses it. pub struct At { pub scope: Scope, /// The owner name, so a repository finds its organisation. pub owner: String, /// What the audit log names. pub label: String, } impl At { pub fn site() -> At { At { scope: Scope::Site, owner: String::new(), label: "site".into() } } pub fn org(org: &crate::db::accounts::Organisation) -> At { At { scope: Scope::Org(org.id), owner: org.name.clone(), label: org.name.clone() } } pub fn repo(row: &crate::db::repos::RepoRow) -> At { At { scope: Scope::Repo(row.id), owner: row.owner.clone(), label: format!("{}/{}", row.owner, row.name) } } } /// The levels above a scope, outermost first: the site, then the /// organisation when the owner is one. pub async fn above(pool: &Pool, scope: Scope, owner: &str) -> sqlx::Result> { let mut out = vec![]; match scope { Scope::Site => {} Scope::Org(_) => out.push((Scope::Site, "the site".to_string())), Scope::Repo(_) => { out.push((Scope::Site, "the site".to_string())); if let Some(org) = crate::db::accounts::org_by_name(pool, owner).await? { out.push((Scope::Org(org.id), format!("the {} organisation", org.name))); } } } Ok(out) }