1use crate::field::FieldDefinition;
9use once_cell::sync::Lazy;
10use regex::Regex;
11use serde::{Deserialize, Serialize};
12use std::collections::HashSet;
13use validator::{Validate, ValidationError};
14
15static ID_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[a-zA-Z0-9_-]+$").unwrap());
17
18#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
22#[serde(try_from = "FormBuilder")]
23pub struct FormDefinition {
24 #[validate(length(min = 1, max = 64), regex(path = *ID_REGEX))]
27 id: String,
28
29 #[validate(length(min = 1, max = 100))]
31 name: String,
32
33 version: u32,
36
37 #[validate(nested, custom(function = "validate_unique_field_ids"))]
43 fields: Vec<FieldDefinition>,
44}
45
46fn validate_unique_field_ids(fields: &[FieldDefinition]) -> Result<(), ValidationError> {
48 let mut seen = HashSet::new();
49 for field in fields {
50 if !seen.insert(field.id()) {
52 let mut err = ValidationError::new("duplicate_field_id");
53 err.add_param(std::borrow::Cow::from("duplicate_id"), &field.id());
54 return Err(err);
55 }
56 }
57 Ok(())
58}
59
60impl FormDefinition {
61 pub fn id(&self) -> &str {
63 &self.id
64 }
65 pub fn name(&self) -> &str {
67 &self.name
68 }
69 pub fn version(&self) -> u32 {
71 self.version
72 }
73 pub fn fields(&self) -> &[FieldDefinition] {
75 &self.fields
76 }
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct FormBuilder {
82 pub id: String,
84 pub name: String,
86 #[serde(default = "default_version")]
87 pub version: u32,
89 #[serde(default)]
90 pub fields: Vec<FieldDefinition>,
92}
93
94fn default_version() -> u32 {
96 1
97}
98
99impl FormBuilder {
100 pub fn new(id: &str, name: &str) -> Self {
103 Self {
104 id: id.to_string(),
105 name: name.to_string(),
106 version: 1,
107 fields: Vec::new(),
108 }
109 }
110
111 pub fn version(mut self, version: u32) -> Self {
113 self.version = version;
114 self
115 }
116
117 pub fn add_field(mut self, field: FieldDefinition) -> Self {
119 self.fields.push(field);
120 self
121 }
122
123 pub fn with_fields(mut self, fields: Vec<FieldDefinition>) -> Self {
125 self.fields = fields;
126 self
127 }
128
129 pub fn build(self) -> Result<FormDefinition, validator::ValidationErrors> {
135 FormDefinition::try_from(self)
136 }
137}
138
139impl TryFrom<FormBuilder> for FormDefinition {
140 type Error = validator::ValidationErrors;
141
142 fn try_from(builder: FormBuilder) -> Result<Self, Self::Error> {
143 let form = FormDefinition {
144 id: builder.id,
145 name: builder.name,
146 version: builder.version,
147 fields: builder.fields,
148 };
149
150 form.validate()?;
151 Ok(form)
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use crate::field::{FieldBuilder, FieldType};
159 use serde_json::json;
160
161 fn create_field(id: &str) -> FieldDefinition {
162 FieldBuilder::new(id, "Label", FieldType::Text)
163 .build()
164 .unwrap()
165 }
166
167 #[test]
168 fn test_form_builder_valid() {
169 let first_name = create_field("first_name");
170 let last_name = create_field("last_name");
171
172 let form = FormBuilder::new("user_profile", "User Profile")
174 .add_field(first_name.clone())
175 .add_field(last_name.clone())
176 .build();
177
178 assert!(form.is_ok());
179 let form = form.unwrap();
180 assert_eq!(form.fields.len(), 2);
181
182 let form = FormBuilder::new("user_profile", "User Profile")
184 .with_fields(vec![first_name, last_name])
185 .build();
186
187 assert!(form.is_ok());
188 let form = form.unwrap();
189 assert_eq!(form.fields.len(), 2);
190 }
191
192 #[test]
193 fn test_form_duplicate_fields() {
194 let form_res = FormBuilder::new("signup", "Sign Up")
196 .add_field(create_field("email"))
197 .add_field(create_field("email"))
198 .build();
199
200 assert!(form_res.is_err());
201
202 let err = form_res.unwrap_err();
203 assert!(err.to_string().contains("duplicate_field_id"));
204 }
205
206 #[test]
207 fn test_serde_integration() {
208 let json_input = json!({
209 "id": "bug_report",
210 "name": "Bug Report",
211 "fields": [
212 {
213 "id": "title",
214 "label": "Title",
215 "field_type": {
216 "kind": "text"
217 }
218 },
219 {
220 "id": "severity",
221 "label": "Severity",
222 "field_type": {
223 "kind": "number",
224 "config": { "min": 1.0, "max": 5.0 }
225 }
226 }
227 ]
228 });
229
230 let form: FormDefinition = serde_json::from_value(json_input).expect("Should deserialize");
231 assert_eq!(form.id, "bug_report");
232 assert_eq!(form.fields.len(), 2);
233 }
234}