use derive_builder::Builder; use sqlx::{query_as, types::Json}; use uuid::Uuid; use crate::{client::AppDbClient, expression::PgExpressionAny}; /// A form transition directionally connects two portals within the same /// workspace, representing a potential navigation of a user between two forms. /// If the user submits a form, form transitions with `source_id` corresponding /// to that portal will be evaluated one by one (in order by ID---that is, by /// creation time), and the first with a condition evaluating to true will be /// used to direct the user to the form corresponding to portal `dest_id`. #[derive(Clone, Debug)] pub struct FormTransition { /// Primary key (defaults to UUIDv7). pub id: Uuid, /// When a user is filling out a sequence of forms, this is the ID of the /// portal for which they have just submitted a form for. /// /// **Source portal is expected to belong to the same workspace as the /// destination portal.** pub source_id: Uuid, /// When a user is filling out a sequence of forms, this is the ID of the /// portal for which they will be directed to if the condition evaluates to /// true. /// /// **Destination portal is expected to belong to the same workspace as the /// source portal.** pub dest_id: Uuid, /// Represents a semi-arbitrary Postgres expression which will permit this /// transition to be followed, only if the expression evaluates to true at /// the time of the source form's submission. pub condition: Json>, } impl FormTransition { /// Build an insert statement to create a new transtition. pub fn insert() -> InsertableBuilder { InsertableBuilder::default() } /// Build a single-field query by source portal ID. pub fn with_source(id: Uuid) -> WithSourceQuery { WithSourceQuery { id } } } #[derive(Clone, Copy, Debug)] pub struct WithSourceQuery { id: Uuid, } impl WithSourceQuery { pub async fn fetch_all( self, app_db: &mut AppDbClient, ) -> Result, sqlx::Error> { query_as!( FormTransition, r#" select id, source_id, dest_id, condition as "condition: Json>" from form_transitions where source_id = $1 "#, self.id, ) .fetch_all(app_db.get_conn()) .await } } #[derive(Builder, Clone, Debug)] pub struct Insertable { source_id: Uuid, dest_id: Uuid, condition: Option, } impl Insertable { pub async fn execute(self, app_db: &mut AppDbClient) -> Result { query_as!( FormTransition, r#" insert into form_transitions (source_id, dest_id, condition) values ($1, $2, $3) returning id, source_id, dest_id, condition as "condition: Json>" "#, self.source_id, self.dest_id, Json(self.condition) as Json>, ) .fetch_one(app_db.get_conn()) .await } }