riverbase_form
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
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
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
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.