use derive_builder::Builder; use serde::{Deserialize, Serialize}; use sqlx::query_as; use uuid::Uuid; use crate::{client::AppDbClient, language::Language}; /// A localized prompt to display above or alongside the form input for the /// given field. /// /// There may be zero or one `field_form_prompt` entries for each /// `(field_id, language)` pair. (This uniqueness should be enforced by the /// database.) #[derive(Clone, Debug, Deserialize, Serialize)] pub struct FieldFormPrompt { /// Primary key (defaults to UUIDv7). pub id: Uuid, /// ID of the field to which this prompt belongs. pub field_id: Uuid, /// [ISO 639-3](https://en.wikipedia.org/wiki/List_of_ISO_639-3_codes) /// language code. pub language: Language, /// Prompt content for this field, in this language. pub content: String, } impl FieldFormPrompt { /// Build an insert statement to create a new prompt. pub fn upsert() -> UpsertBuilder { UpsertBuilder::default() } /// Build an update statement to alter the content of an existing prompt. pub fn update() -> UpdateBuilder { UpdateBuilder::default() } /// Build a single-field query by field ID. pub fn belonging_to_field(id: Uuid) -> BelongingToFieldQuery { BelongingToFieldQuery { id } } } #[derive(Builder, Clone, Debug)] pub struct Upsert { field_id: Uuid, language: Language, content: String, } impl Upsert { pub async fn execute(self, app_db: &mut AppDbClient) -> Result { query_as!( FieldFormPrompt, r#" insert into field_form_prompts (field_id, language, content) values ($1, $2, $3) on conflict (field_id, language) do update set content = excluded.content returning id, field_id, language as "language: Language", content "#, self.field_id, self.language.to_string(), self.content, ) .fetch_one(app_db.get_conn()) .await } } #[derive(Builder, Clone, Debug, Default)] pub struct Update { id: Uuid, content: String, } impl Update { pub async fn execute(self, app_db: &mut AppDbClient) -> Result { query_as!( FieldFormPrompt, r#" update field_form_prompts set content = $1 where id = $2 returning id, field_id, language as "language: Language", content "#, self.content, self.id, ) .fetch_one(app_db.get_conn()) .await } } #[derive(Clone, Debug)] pub struct BelongingToFieldQuery { id: Uuid, } impl BelongingToFieldQuery { pub async fn fetch_all( self, app_db: &mut AppDbClient, ) -> Result, sqlx::Error> { query_as!( FieldFormPrompt, r#" select id, field_id, language as "language: Language", content from field_form_prompts where field_id = $1 "#, self.id, ) .fetch_all(app_db.get_conn()) .await } }