25 snprintf(
e->key,
sizeof(
e->key),
"%s", key ? key :
"(unknown)");
26 snprintf(
e->message,
sizeof(
e->message),
"%s",
msg ?
msg :
"validation failed");
34static bool is_email_like(
const char *s,
size_t len) {
35 if (!s || len < 5)
return false;
37 if (!
at)
return false;
38 if (
at == s)
return false;
40 if (!
dot ||
dot <=
at + 1)
return false;
41 if (
dot + 1 == s + len)
return false;
55 if (!value || len == 0) {
57 rule->error_message ?
rule->error_message :
"field is required");
58 bind_add_error(r, key, buf);
65 snprintf(buf,
sizeof(buf),
"%s: minimum length is %zu",
66 rule->error_message ?
rule->error_message :
"too short",
rule->u.min_len);
67 bind_add_error(r, key, buf);
73 if (value && len >
rule->u.max_len) {
74 snprintf(buf,
sizeof(buf),
"%s: maximum length is %zu",
75 rule->error_message ?
rule->error_message :
"too long",
rule->u.max_len);
76 bind_add_error(r, key, buf);
85 snprintf(buf,
sizeof(buf),
"%s: minimum value is %Lg",
86 rule->error_message ?
rule->error_message :
"value too small",
88 bind_add_error(r, key, buf);
97 if (!value ||
v >
rule->u.max_val) {
98 snprintf(buf,
sizeof(buf),
"%s: maximum value is %Lg",
99 rule->error_message ?
rule->error_message :
"value too large",
101 bind_add_error(r, key, buf);
109 bind_add_error(r, key,
"missing value for regex check");
115 bind_add_error(r, key,
"invalid regex pattern");
122 rule->error_message ?
rule->error_message
123 :
"value does not match pattern");
124 bind_add_error(r, key, buf);
131 if (!value || !is_email_like(value, len)) {
133 rule->error_message ?
rule->error_message :
"invalid email format");
134 bind_add_error(r, key, buf);
140 if (!value || !
rule->u.custom.fn(value, len,
rule->u.custom.ctx)) {
142 rule->error_message ?
rule->error_message :
"custom validation failed");
143 bind_add_error(r, key, buf);
158 if (!
f->rules)
return true;
162 if (!check_rule(&
f->rules[
i], value, len, r,
f->json_key))
all_ok =
false;
172 void *
dest = (
char *)out +
f->target_offset;
174 switch (
f->target_type) {
178 if (
strcmp(value,
"true") == 0 ||
strcmp(value,
"1") == 0)
180 else if (
strcmp(value,
"false") == 0 ||
strcmp(value,
"0") == 0)
183 bind_add_error(r,
f->json_key,
"expected boolean");
197 bind_add_error(r,
f->json_key,
"expected integer");
205 unsigned long long v = 0;
211 bind_add_error(r,
f->json_key,
"expected unsigned integer");
223 if (
end == value || *
end !=
'\0') {
224 bind_add_error(r,
f->json_key,
"expected float");
236 if (
end == value || *
end !=
'\0') {
237 bind_add_error(r,
f->json_key,
"expected double");
245 if (
f->target_size == 0) {
246 bind_add_error(r,
f->json_key,
"bad target size");
250 size_t copy =
strlen(value);
251 if (copy >=
f->target_size) copy =
f->target_size - 1;
253 ((
char *)
dest)[copy] =
'\0';
255 ((
char *)
dest)[0] =
'\0';
262 bind_add_error(r,
f->json_key,
"null sstring target");
274 bind_add_error(r,
f->json_key,
"null cJSON target");
284 bind_add_error(r,
f->json_key,
"invalid nested JSON");
290 default: bind_add_error(r,
f->json_key,
"unsupported bind target type");
return false;
300 if (!schema || !out || !result)
return false;
301 memset(result, 0,
sizeof(*result));
305 bind_add_error(result,
"(root)",
"missing request body");
313 bind_add_error(result,
"(root)",
"JSON parse error");
357 const char *
cursor = payload;
386 if (!run_rules(
f,
raw_value, val_len, result)) {
391 if (!write_value(
f,
raw_value, out, result)) {
429 return cwist_bind_generic(schema, payload,
true, out, result);
449 return cwist_bind_generic(schema, payload,
false, out, result);
464 if (!res || !result)
return;
Memory allocation wrappers backed by libttak.
bool cwist_app_req_bind_json_or_400(cwist_http_request *req, cwist_http_response *res, const cwist_bind_schema_t *schema, void *out)
Bind a JSON request body, writing a 400 error response on failure.
bool cwist_app_req_bind_form(cwist_http_request *req, const cwist_bind_schema_t *schema, void *out, cwist_bind_result_t *result)
Validate and bind a form-encoded (key=value) request body into a struct per the schema.
bool cwist_app_req_bind_json(cwist_http_request *req, const cwist_bind_schema_t *schema, void *out, cwist_bind_result_t *result)
Validate and bind a JSON request body into a struct per the schema.
void cwist_bind_write_error_response(cwist_http_response *res, const cwist_bind_result_t *result)
Write a 400 Bad Request JSON error response from a bind result.
Declarative Schema Validation and Type-Safe Binding for CWIST.
#define CWIST_BIND_MAX_ERRORS
#define CWIST_BIND_MSG_MAX
@ CWIST_BIND_JSON_OBJECT
cJSON * (ownership transferred to caller).
@ CWIST_BIND_STRING
Null-terminated C string (copied).
@ CWIST_BIND_SSTRING
cwist_sstring target.
enum cwist_bind_rule_type cwist_bind_rule_type_t
Supported validation constraint types.
@ CWIST_BIND_RULE_CUSTOM
User-supplied predicate.
@ CWIST_BIND_RULE_MAX_LEN
Maximum string length.
@ CWIST_BIND_RULE_MAX_VAL
Maximum numeric value.
@ CWIST_BIND_RULE_EMAIL
Must conform to RFC 5322 simplified email shape.
@ CWIST_BIND_RULE_REQUIRED
Field must be present and non-empty.
@ CWIST_BIND_RULE_REGEX
Must match POSIX extended regular expression.
@ CWIST_BIND_RULE_MIN_VAL
Minimum numeric value.
@ CWIST_BIND_RULE_MIN_LEN
Minimum string length.
cwist_error_t cwist_sstring_assign(cwist_sstring *str, const char *data)
Assign a C string.
const char * cwist_json_get_raw(cwist_json_builder *b)
Returns the raw string buffer. Invalidated on destroy.
void cwist_json_add_string(cwist_json_builder *b, const char *key, const char *value)
Add a string value.
void cwist_json_begin_object(cwist_json_builder *b)
Begin a JSON object.
void cwist_json_end_object(cwist_json_builder *b)
End a JSON object.
void cwist_json_builder_destroy(cwist_json_builder *b)
Destroy a JSON builder.
void cwist_json_add_bool(cwist_json_builder *b, const char *key, bool value)
Add a boolean value.
cwist_json_builder * cwist_json_builder_create(void)
Create a new JSON builder.
void cwist_json_begin_array(cwist_json_builder *b, const char *key)
Begin a JSON array.
void cwist_json_end_array(cwist_json_builder *b)
End a JSON array.
cwist_error_t cwist_http_header_add(cwist_http_header_node **head, const char *key, const char *value)
Add a header to the list.
A single bind/validation failure.
char key[CWIST_BIND_KEY_MAX]
char message[CWIST_BIND_MSG_MAX]
Per-field binding descriptor.
Result of a bind operation.
cwist_bind_error_t errors[CWIST_BIND_MAX_ERRORS]
Single validation rule attached to a field.
Complete schema descriptor for a struct.
const cwist_bind_field_t * fields
HTTP Response Object. Supports standard string body or Zero-Copy pointer body.
cwist_sstring * status_text
e.g., "OK"
cwist_http_status_t status_code
cwist_http_header_node * headers
Simple JSON string builder utility.
char * data
Access directly only when raw handling is necessary.