Chuyển tới nội dung chính

riverbase_form

riverbase_formExtensions · v0.2.0

Declarative forms, elements and document templates with validation.

cargo add riverbase_form --git https://github.com/riverbase-io/riverbase --features dgen
  • postgresdefault
  • cli
  • dgen

riverbase_form defines data-entry structures as specs. Elements are reusable fields with validation, forms group elements and add cross-field constraints, and documents arrange forms and content into a template. A document can be generated from input, and with the dgen feature rendered through the separate river_duckweed document engine.

Three levels​

Every spec has a stable key such as TXT-0001. validate_key checks the format.

Element​

elements/text_input.hcl
key        = "TXT-0001"
title = "Text Input"
table_name = "text_input_data"

field "value" {
type = "string"
required = true

validation {
pattern = "^[A-Za-z0-9 ]+$"
minLength = 2
maxLength = 120
message = "Enter 2-120 alphanumeric characters"
}

constraint "not_blank" {
expr = "trim(value) != \"\""
message = "Value cannot be blank after trimming"
}
}

Form​

forms/applicant_info.hcl
key   = "FRM-0001"
title = "Applicant Information"

group "basic_info" {
title = "Basic Information"
element "full_name" { key = "TXT-0001", required = true }
element "annual_income" { key = "NUM-0001", required = false }
}

constraint "name_required_when_income" {
expr = "empty(annual_income.amount) || len(trim(full_name.value)) >= 2"
message = "Full name is required when annual income is provided"
}

Constraints are expressions over element values, or JSON Schema if / then blocks for conditional requirements.

Document​

documents/loan_application.hcl
key   = "DOC-0001"
title = "Loan Application"

content "intro" {
content = "Application for {{applicant_name}}"
ctype = "text"
}

section "applicant_details" {
title = "Applicant Details"
form "applicant" { form_key = "FRM-0001" }
}

Load, generate, render​

use std::path::Path;
use riverbase_form::config::{load_documents_from_dir, load_elements_from_dir, load_forms_from_dir};
use riverbase_form::registry::{document_registry, element_registry, form_registry};
use riverbase_form::template::generate;

for spec in load_elements_from_dir(Path::new("elements"))? {
element_registry().register(spec.key.clone(), spec)?;
}
for spec in load_forms_from_dir(Path::new("forms"))? {
form_registry().register(spec.key.clone(), spec)?;
}
for spec in load_documents_from_dir(Path::new("documents"))? {
document_registry().register(spec.key.clone(), spec)?;
}

let template = document_registry().get("DOC-0001").expect("registered");
let doc = generate(&template, &serde_json::json!({ "applicant_name": "Jane Doe" }))?;

// With the `dgen` feature: render through a registered document pipeline.
let run = riverbase_form::render::render_document(&doc, "loan_doc", None).await?;

Validation runs against the element and form specs. Submitted data persists to each element's table_name with the postgres feature. riverbase_form (feature cli) ships a binary for validating specs and generating Diesel code from them. See CLI.