Skip to main content

resuma/flow/
submit.rs

1//! Form submissions — `#[submit]` handlers with progressive enhancement.
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::collections::BTreeMap;
6
7/// Result of a `#[submit]` handler.
8#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(tag = "status", rename_all = "snake_case")]
10pub enum SubmitValue<T> {
11    Ok(T),
12    Err(SubmitError),
13    Running,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct SubmitError {
18    pub message: String,
19    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
20    pub field_errors: BTreeMap<String, String>,
21}
22
23impl SubmitError {
24    pub fn new(message: impl Into<String>) -> Self {
25        Self {
26            message: message.into(),
27            field_errors: Default::default(),
28        }
29    }
30
31    pub fn field(mut self, key: impl Into<String>, message: impl Into<String>) -> Self {
32        self.field_errors.insert(key.into(), message.into());
33        self
34    }
35}
36
37/// Encode a submit handler return value, mapping validation errors to HTTP responses.
38pub fn encode_submit_result<T: serde::Serialize>(
39    res: Result<T, SubmitError>,
40) -> crate::core::Result<serde_json::Value> {
41    match res {
42        Ok(v) => serde_json::to_value(v).map_err(|e| {
43            crate::core::ResumaError::Validation(format!(
44                "Could not encode submit result: {e}. If the return value is your own struct or enum, add #[data] above its definition."
45            ))
46        }),
47        Err(e) => Err(crate::core::ResumaError::Other(
48            serde_json::to_string(&e).unwrap_or_else(|_| e.message.clone()),
49        )),
50    }
51}
52
53/// Type-erased submit dispatch.
54pub type SubmitFn = fn(Value) -> SubmitDispatch;
55
56pub enum SubmitDispatch {
57    Ok(Value),
58    Err(SubmitError),
59}