phonograph/interim-models/src/form_transition.rs

107 lines
3.1 KiB
Rust
Raw Normal View History

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<Option<PgExpressionAny>>,
}
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<Vec<FormTransition>, sqlx::Error> {
query_as!(
FormTransition,
r#"
select
id,
source_id,
dest_id,
condition as "condition: Json<Option<PgExpressionAny>>"
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<PgExpressionAny>,
}
impl Insertable {
pub async fn execute(self, app_db: &mut AppDbClient) -> Result<FormTransition, sqlx::Error> {
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<Option<PgExpressionAny>>"
"#,
self.source_id,
self.dest_id,
Json(self.condition) as Json<Option<PgExpressionAny>>,
)
.fetch_one(app_db.get_conn())
.await
}
}