doido_controller/params.rs
1//! Strong parameters: a Rails-style allowlist over untyped request params.
2//!
3//! Deserializing straight into a typed struct is the common path in doido, but
4//! when you hold an untyped params object (e.g. a nested form) [`Params`] lets
5//! you `require` a key and `permit` an explicit set of fields before use, so
6//! unexpected keys can never be mass-assigned.
7
8use doido_core::Result;
9use serde::de::DeserializeOwned;
10use serde_json::{Map, Value};
11
12/// A wrapper around a JSON params object supporting `require`/`permit`.
13#[derive(Debug, Clone)]
14pub struct Params {
15 value: Value,
16}
17
18impl Params {
19 /// Wrap a params value (typically a JSON object).
20 pub fn new(value: Value) -> Self {
21 Self { value }
22 }
23
24 /// Return the nested params under `key`, erroring if it is absent (Rails
25 /// `params.require(:key)`).
26 pub fn require(&self, key: &str) -> Result<Params> {
27 match self.value.get(key) {
28 Some(v) => Ok(Params::new(v.clone())),
29 None => Err(doido_core::anyhow::anyhow!(
30 "param `{key}` is required but missing"
31 )),
32 }
33 }
34
35 /// Keep only the listed top-level keys, dropping everything else (Rails
36 /// `params.permit(:a, :b)`). Non-object params become an empty object.
37 pub fn permit(&self, allowed: &[&str]) -> Params {
38 let mut out = Map::new();
39 if let Value::Object(map) = &self.value {
40 for &key in allowed {
41 if let Some(v) = map.get(key) {
42 out.insert(key.to_string(), v.clone());
43 }
44 }
45 }
46 Params::new(Value::Object(out))
47 }
48
49 /// Borrow a raw value by key.
50 pub fn get(&self, key: &str) -> Option<&Value> {
51 self.value.get(key)
52 }
53
54 /// Deserialize the (typically permitted) params into a typed value.
55 pub fn deserialize<T: DeserializeOwned>(&self) -> Result<T> {
56 serde_json::from_value(self.value.clone())
57 .map_err(|e| doido_core::anyhow::anyhow!("params deserialization failed: {e}"))
58 }
59
60 /// Consume into the underlying JSON value.
61 pub fn into_value(self) -> Value {
62 self.value
63 }
64}