use derive_builder::Builder; use redact::Secret; use sqlx::query_as; use url::Url; use uuid::Uuid; use crate::client::AppDbClient; /// A workspace is 1:1 with a Postgres "database". #[derive(Clone, Debug)] pub struct Workspace { /// Primary key (defaults to UUIDv7). pub id: Uuid, /// Human friendly name for the workspace. pub name: String, /// `postgresql://` URL of the instance and database hosting this workspace. pub url: Secret, /// ID of the user account that created this workspace. pub owner_id: Uuid, } impl Workspace { /// Build an insert statement to create a new workspace. pub fn insert() -> InsertableWorkspaceBuilder { InsertableWorkspaceBuilder::default() } /// Build a single-field query by workspace ID. pub fn with_id(id: Uuid) -> WithIdQuery { WithIdQuery { id } } /// Build a query for workspaces filtered by a user's Phono permissions. pub fn with_permission_in>( perms: I, ) -> WithPermissionInQueryPartial { let perms: Vec = perms.into_iter().map(ToOwned::to_owned).collect(); WithPermissionInQueryPartial { perms } } } pub struct WithPermissionInQueryPartial { perms: Vec, } impl WithPermissionInQueryPartial { pub fn for_user(self, user_id: Uuid) -> WithPermissionInQuery { WithPermissionInQuery { perms: self.perms, user_id, } } } pub struct WithPermissionInQuery { perms: Vec, user_id: Uuid, } impl WithPermissionInQuery { pub async fn fetch_all(self, app_db: &mut AppDbClient) -> Result, sqlx::Error> { query_as!( Workspace, " select workspaces.* from workspaces inner join workspace_user_perms as p on p.workspace_id = workspaces.id where p.user_id = $1 and perm = ANY($2) ", self.user_id, self.perms.as_slice(), ) .fetch_all(&mut *app_db.conn) .await } } pub struct WithIdQuery { id: Uuid, } impl WithIdQuery { pub async fn fetch_optional( self, app_db: &mut AppDbClient, ) -> Result, sqlx::Error> { query_as!( Workspace, "select * from workspaces where id = $1", &self.id ) .fetch_optional(&mut *app_db.conn) .await } pub async fn fetch_one(self, app_db: &mut AppDbClient) -> Result { query_as!( Workspace, "select * from workspaces where id = $1", &self.id ) .fetch_one(&mut *app_db.conn) .await } } #[derive(Builder)] pub struct InsertableWorkspace { url: Url, owner_id: Uuid, } impl InsertableWorkspace { pub async fn insert(self, app_db: &mut AppDbClient) -> Result { query_as!( Workspace, " insert into workspaces (url, owner_id) values ($1, $2) returning * ", self.url.to_string(), self.owner_id ) .fetch_one(&mut *app_db.conn) .await } }