use std::collections::HashMap; use anyhow::Result; use askama::Template; use derive_builder::Builder; use interim_models::{base::Base, client::AppDbClient, lens::Lens}; use interim_pgtypes::{ client::BaseClient, pg_class::{PgClass, PgRelKind}, }; use sqlx::postgres::types::Oid; use uuid::Uuid; #[derive(Builder, Clone, Template)] #[template(path = "navbar.html")] pub struct Navbar { pub base: Base, pub namespaces: Vec, #[builder(setter(strip_option))] pub current: Option, pub root_path: String, } impl Navbar { pub fn builder() -> NavbarBuilder { NavbarBuilder::default() } } #[derive(Clone, Debug)] pub struct NamespaceItem { pub name: String, pub rels: Vec, } #[derive(Clone, Debug)] pub struct RelItem { pub name: String, pub class_oid: Oid, pub lenses: Vec, } #[derive(Clone, Debug)] pub struct LensItem { pub name: String, pub id: Uuid, } #[derive(Clone, Debug, PartialEq)] pub enum NavLocation { Rel(Oid, Option), } #[derive(Clone, Debug, PartialEq)] pub enum RelLocation { Lens(Uuid), Rbac, } impl NavbarBuilder { /// Helper function to populate relations and lenses automatically. pub async fn populate_rels( &mut self, app_db: &mut AppDbClient, base_client: &mut BaseClient, ) -> Result<&mut Self> { let rels = PgClass::with_kind_in([PgRelKind::OrdinaryTable]) .fetch_all(base_client) .await?; let mut namespaces: HashMap> = HashMap::new(); for rel in rels { if rel.regnamespace.as_str() != "pg_catalog" && rel.regnamespace.as_str() != "information_schema" { let lenses = Lens::belonging_to_base( self.base .as_ref() .ok_or(NavbarBuilderError::UninitializedField("base"))? .id, ) .belonging_to_rel(rel.oid) .fetch_all(app_db) .await?; let rel_items = namespaces.entry(rel.regnamespace).or_default(); rel_items.push(RelItem { name: rel.relname, class_oid: rel.oid, lenses: lenses .into_iter() .map(|lens| LensItem { name: lens.name, id: lens.id, }) .collect(), }); } } Ok(self.namespaces( namespaces .into_iter() .map(|(name, rel_items)| NamespaceItem { name, rels: rel_items, }) .collect(), )) } }