1use std::fmt;
4
5use serde::de::{
6 self, Deserializer, IntoDeserializer, MapAccess, SeqAccess, Visitor,
7 value::{MapAccessDeserializer, SeqAccessDeserializer},
8};
9use serde::{Deserialize, Serialize};
10
11use crate::validation::{
12 ProtocolLimits, Validate, ValidationError, add_to_total, check_count, check_finite,
13 check_finite_slice, check_opt_string, check_string,
14};
15
16use super::de::{
17 de_config_map, de_config_string, de_finite_f32, de_float_array, de_opt_session_id, prefix_path,
18};
19
20#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
22#[serde(try_from = "ConfigPayloadWire")]
23pub struct ConfigPayload {
24 pub session_id: Option<String>,
26 pub config: std::collections::HashMap<String, ConfigValue>,
28}
29
30#[derive(Deserialize)]
31struct ConfigPayloadWire {
32 #[serde(deserialize_with = "de_opt_session_id")]
33 session_id: Option<String>,
34 #[serde(deserialize_with = "de_config_map")]
35 config: std::collections::HashMap<String, ConfigValue>,
36}
37
38impl TryFrom<ConfigPayloadWire> for ConfigPayload {
39 type Error = ValidationError;
40
41 fn try_from(wire: ConfigPayloadWire) -> Result<Self, Self::Error> {
42 let payload = ConfigPayload {
43 session_id: wire.session_id,
44 config: wire.config,
45 };
46 payload.validate()?;
47 Ok(payload)
48 }
49}
50
51impl Validate for ConfigPayload {
52 fn validate_with(&self, limits: ProtocolLimits) -> Result<(), ValidationError> {
53 check_opt_string("session_id", self.session_id.as_deref(), limits)?;
54 check_count("config", self.config.len(), limits.max_metadata_entries)?;
55 let mut total = 0;
56 add_to_total(&mut total, self.config.len(), limits, "aggregate")?;
57 validate_config_entries(&self.config, limits, &mut total)
58 }
59}
60
61fn validate_config_entries(
62 config: &std::collections::HashMap<String, ConfigValue>,
63 limits: ProtocolLimits,
64 total: &mut usize,
65) -> Result<(), ValidationError> {
66 for (key, value) in config {
67 if key.is_empty() {
68 return Err(ValidationError::nested_metadata("config.<empty>"));
69 }
70 check_string("config.<key>", key, limits)?;
71 value
72 .validate_with(limits)
73 .map_err(|err| prefix_path(err, &format!("config.{key}")))?;
74 if let ConfigValue::FloatArray(values) = value {
75 add_to_total(total, values.len(), limits, "aggregate")?;
76 }
77 }
78 Ok(())
79}
80
81#[derive(Serialize, Debug, Clone, PartialEq)]
105#[serde(untagged)]
106pub enum ConfigValue {
107 Float(f32),
111
112 Integer(u64),
117
118 String(String),
122
123 Boolean(bool),
125 FloatArray(Vec<f32>),
126}
127
128impl<'de> Deserialize<'de> for ConfigValue {
129 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
130 struct ConfigValueVisitor;
131
132 impl<'de> Visitor<'de> for ConfigValueVisitor {
133 type Value = ConfigValue;
134
135 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 f.write_str("a JSON number, string, boolean, or array of finite floats")
137 }
138
139 fn visit_bool<E: de::Error>(self, value: bool) -> Result<Self::Value, E> {
140 Ok(ConfigValue::Boolean(value))
141 }
142
143 fn visit_i64<E: de::Error>(self, value: i64) -> Result<Self::Value, E> {
144 de_finite_f32(value.into_deserializer()).map(ConfigValue::Float)
145 }
146
147 fn visit_u64<E: de::Error>(self, value: u64) -> Result<Self::Value, E> {
148 de_finite_f32(value.into_deserializer()).map(ConfigValue::Float)
149 }
150
151 fn visit_f32<E: de::Error>(self, value: f32) -> Result<Self::Value, E> {
152 de_finite_f32(value.into_deserializer()).map(ConfigValue::Float)
153 }
154
155 fn visit_f64<E: de::Error>(self, value: f64) -> Result<Self::Value, E> {
156 de_finite_f32(value.into_deserializer()).map(ConfigValue::Float)
157 }
158
159 fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
160 de_config_string(value.into_deserializer()).map(ConfigValue::String)
161 }
162
163 fn visit_string<E: de::Error>(self, value: String) -> Result<Self::Value, E> {
164 de_config_string(value.into_deserializer()).map(ConfigValue::String)
165 }
166
167 fn visit_seq<A: SeqAccess<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {
168 de_float_array(SeqAccessDeserializer::new(seq)).map(ConfigValue::FloatArray)
169 }
170
171 fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
172 match serde_json::Value::deserialize(MapAccessDeserializer::new(map))? {
200 serde_json::Value::Number(number) => {
201 let Ok(parsed) = number.to_string().parse::<f32>() else {
202 return Err(de::Error::custom(ValidationError::non_finite(
203 "value",
204 f32::NAN,
205 )));
206 };
207 de_finite_f32(parsed.into_deserializer()).map(ConfigValue::Float)
208 }
209 _ => Err(de::Error::custom(ValidationError::nested_metadata("value"))),
210 }
211 }
212 }
213
214 deserializer.deserialize_any(ConfigValueVisitor)
215 }
216}
217
218impl Validate for ConfigValue {
219 fn validate_with(&self, limits: ProtocolLimits) -> Result<(), ValidationError> {
220 match self {
221 Self::Float(value) => check_finite("value", *value),
222 Self::Integer(_) | Self::Boolean(_) => Ok(()),
223 Self::String(value) => check_string("value", value, limits),
224 Self::FloatArray(values) => {
225 check_count("value", values.len(), limits.max_channel_values)?;
226 check_finite_slice("value", values)
227 }
228 }
229 }
230}