1use std::any;
53
54use serde::de::Error as DeError;
55
56use self::deserializer::ValueDeserializer;
57pub use self::{
58 deserializer::DeserializerOptions,
59 macros::Serde,
60 param::{
61 CustomKnownOption, DeserializeParam, Optional, OrString, Qualified, Serde, WellKnown,
62 WellKnownOption, WithDefault,
63 },
64 repeated::{Delimited, Entries, NamedEntries, Repeated, ToEntries},
65 secret::{FromSecretString, Secret},
66 units::WithUnit,
67};
68use crate::{
69 DescribeConfig, DeserializeConfigError, ParseError, ParseErrorCategory, ParseErrors,
70 error::{ErrorWithOrigin, LocationInConfig, LowLevelError},
71 metadata::{BasicTypes, ConfigMetadata, ParamMetadata},
72 value::{Pointer, StrValue, Value, ValueOrigin, WithOrigin},
73};
74
75#[doc(hidden)]
76pub mod _private;
77#[cfg(feature = "alloy")]
78mod alloy_impl;
79mod deserializer;
80mod macros;
81mod param;
82#[cfg(feature = "primitive-types")]
83mod primitive_types_impl;
84mod repeated;
85mod secret;
86#[cfg(test)]
87mod tests;
88mod units;
89
90#[derive(Debug)]
92pub struct DeserializeContext<'a> {
93 de_options: &'a DeserializerOptions,
94 root_value: &'a WithOrigin,
95 path: String,
96 patched_current_value: Option<&'a WithOrigin>,
97 current_config: &'static ConfigMetadata,
98 location_in_config: Option<LocationInConfig>,
99 errors: &'a mut ParseErrors,
100}
101
102impl<'a> DeserializeContext<'a> {
103 pub(crate) fn new(
104 de_options: &'a DeserializerOptions,
105 root_value: &'a WithOrigin,
106 path: String,
107 current_config: &'static ConfigMetadata,
108 errors: &'a mut ParseErrors,
109 ) -> Self {
110 Self {
111 de_options,
112 root_value,
113 path,
114 patched_current_value: None,
115 current_config,
116 location_in_config: None,
117 errors,
118 }
119 }
120
121 fn child(
122 &mut self,
123 path: &str,
124 location_in_config: Option<LocationInConfig>,
125 ) -> DeserializeContext<'_> {
126 DeserializeContext {
127 de_options: self.de_options,
128 root_value: self.root_value,
129 path: Pointer(&self.path).join(path),
130 patched_current_value: self.patched_current_value.and_then(|val| {
131 if path.is_empty() {
132 Some(val)
133 } else if let Value::Object(object) = &val.inner {
134 object.get(path)
135 } else {
136 None
137 }
138 }),
139 current_config: self.current_config,
140 location_in_config,
141 errors: self.errors,
142 }
143 }
144
145 pub fn borrow(&mut self) -> DeserializeContext<'_> {
147 DeserializeContext {
148 de_options: self.de_options,
149 root_value: self.root_value,
150 path: self.path.clone(),
151 patched_current_value: self.patched_current_value,
152 current_config: self.current_config,
153 location_in_config: self.location_in_config,
154 errors: self.errors,
155 }
156 }
157
158 fn patched<'s>(&'s mut self, current_value: &'s WithOrigin) -> DeserializeContext<'s> {
160 DeserializeContext {
161 de_options: self.de_options,
162 root_value: self.root_value,
163 path: self.path.clone(),
164 patched_current_value: Some(current_value),
165 current_config: self.current_config,
166 location_in_config: self.location_in_config,
167 errors: self.errors,
168 }
169 }
170
171 pub(crate) fn current_value(&self) -> Option<&'a WithOrigin> {
172 self.patched_current_value
173 .or_else(|| self.root_value.get(Pointer(&self.path)))
174 }
175
176 pub fn current_value_deserializer(
182 &self,
183 name: &'static str,
184 ) -> Result<ValueDeserializer<'a>, ErrorWithOrigin> {
185 if let Some(value) = self.current_value() {
186 Ok(ValueDeserializer::new(value, self.de_options))
187 } else {
188 Err(DeError::missing_field(name))
189 }
190 }
191
192 fn for_nested_config(&mut self, index: usize) -> DeserializeContext<'_> {
194 let nested_meta = self
195 .current_config
196 .nested_configs
197 .get(index)
198 .unwrap_or_else(|| {
199 panic!(
200 "Internal error: called `for_nested_config()` with missing config index {index}"
201 )
202 });
203 let path = nested_meta.name;
204 DeserializeContext {
205 current_config: nested_meta.meta,
206 ..self.child(path, None)
207 }
208 }
209
210 fn for_param(&mut self, index: usize) -> (DeserializeContext<'_>, &'static ParamMetadata) {
211 let param = self.current_config.params.get(index).unwrap_or_else(|| {
212 panic!("Internal error: called `for_param()` with missing param index {index}")
213 });
214 (
215 self.child(param.name, Some(LocationInConfig::Param(index))),
216 param,
217 )
218 }
219
220 pub fn push_error(&mut self, err: ErrorWithOrigin) {
222 self.push_generic_error(err, None);
223 }
224
225 #[cold]
226 fn push_generic_error(&mut self, err: ErrorWithOrigin, validation: Option<String>) {
227 let (inner, category) = match err.inner {
228 LowLevelError::Json { err, category } => (err, category),
229 LowLevelError::InvalidArray
230 | LowLevelError::InvalidObject
231 | LowLevelError::Validation => return,
232 };
233
234 let mut origin = err.origin;
235 if matches!(origin.as_ref(), ValueOrigin::Unknown) {
236 if let Some(val) = self.current_value() {
237 origin = val.origin.clone();
238 }
239 }
240
241 self.errors.push(ParseError {
242 inner,
243 category,
244 path: self.path.clone(),
245 origin,
246 config: self.current_config,
247 location_in_config: self.location_in_config,
248 validation,
249 });
250 }
251
252 #[tracing::instrument(
253 level = "trace",
254 skip_all,
255 fields(path = self.path, config = ?self.current_config.ty)
256 )]
257 pub(crate) fn deserialize_any_config(
258 mut self,
259 ) -> Result<Box<dyn any::Any>, DeserializeConfigError> {
260 if let Some(val) = self.current_value() {
265 if !matches!(&val.inner, Value::Object(_)) {
266 self.push_error(val.invalid_type("config object"));
267 return Err(DeserializeConfigError::new());
268 }
269 }
270 let config = (self.current_config.deserializer)(self.borrow())?;
271
272 let mut has_errors = false;
273 for &validation in self.current_config.validations {
274 let _span = tracing::trace_span!("validation", %validation).entered();
275 if let Err(err) = validation.validate(config.as_ref()) {
276 tracing::info!(%validation, origin = %err.origin, "config validation failed: {}", err.inner);
277 self.push_generic_error(err, Some(validation.to_string()));
278 has_errors = true;
279 }
280 }
281
282 if has_errors {
283 Err(DeserializeConfigError::new())
284 } else {
285 Ok(config)
286 }
287 }
288
289 pub(crate) fn deserialize_config<C: 'static>(self) -> Result<C, DeserializeConfigError> {
291 Ok(*self
292 .deserialize_any_config()?
293 .downcast::<C>()
294 .expect("Internal error: config deserializer output has wrong type"))
295 }
296
297 pub(crate) fn deserialize_any_config_opt(
298 mut self,
299 ) -> Result<Option<Box<dyn any::Any>>, DeserializeConfigError> {
300 if self.current_value().is_none() {
301 return Ok(None);
302 }
303
304 let error_count = self.errors.len();
305 self.borrow()
306 .deserialize_any_config()
307 .map(Some)
308 .or_else(|err| {
309 let only_missing_field_errors = self
310 .errors
311 .iter()
312 .skip(error_count)
313 .all(|err| matches!(err.category, ParseErrorCategory::MissingField));
314 if only_missing_field_errors {
315 tracing::trace!(
316 "optional config misses required params and no other errors; coercing it to `None`"
317 );
318 self.errors.truncate(error_count);
319 Ok(None)
320 } else {
321 Err(err)
322 }
323 })
324 }
325
326 pub(crate) fn deserialize_config_opt<C: 'static>(
327 self,
328 ) -> Result<Option<C>, DeserializeConfigError> {
329 let config = self.deserialize_any_config_opt()?.map(|boxed| {
330 *boxed
331 .downcast::<C>()
332 .expect("Internal error: config deserializer output has wrong type")
333 });
334 Ok(config)
335 }
336}
337
338#[doc(hidden)]
340impl DeserializeContext<'_> {
341 pub fn deserialize_nested_config<C: DeserializeConfig>(
342 &mut self,
343 index: usize,
344 default_fn: Option<fn() -> C>,
345 ) -> Result<C, DeserializeConfigError> {
346 let child_ctx = self.for_nested_config(index);
347 if child_ctx.current_value().is_none() {
348 if let Some(default) = default_fn {
349 return Ok(default());
350 }
351 }
352 child_ctx.deserialize_config()
353 }
354
355 pub fn deserialize_nested_config_opt<C: DeserializeConfig>(
356 &mut self,
357 index: usize,
358 ) -> Result<Option<C>, DeserializeConfigError> {
359 self.for_nested_config(index).deserialize_config_opt()
360 }
361
362 #[tracing::instrument(
363 level = "trace",
364 name = "deserialize_param",
365 skip_all,
366 fields(path = self.path, config = ?self.current_config.ty, param)
367 )]
368 pub(crate) fn deserialize_any_param(
369 &mut self,
370 index: usize,
371 ) -> Result<Box<dyn any::Any>, DeserializeConfigError> {
372 let (mut child_ctx, param) = self.for_param(index);
373 tracing::Span::current().record("param", param.rust_field_name);
374
375 let maybe_coerced = child_ctx
377 .current_value()
378 .and_then(|val| val.coerce_value_type(param.expecting));
379 let mut child_ctx = if let Some(coerced) = &maybe_coerced {
380 child_ctx.patched(coerced)
381 } else {
382 child_ctx
383 };
384 tracing::trace!(
385 deserializer = ?param.deserializer,
386 value = ?child_ctx.current_value(),
387 "deserializing param"
388 );
389
390 match param
391 .deserializer
392 .deserialize_param(child_ctx.borrow(), param)
393 {
394 Ok(param) => Ok(param),
395 Err(err) => {
396 tracing::info!(origin = %err.origin, "deserialization failed: {}", err.inner);
397 child_ctx.push_error(err);
398 Err(DeserializeConfigError::new())
399 }
400 }
401 }
402
403 pub fn deserialize_param<T: 'static>(
404 &mut self,
405 index: usize,
406 ) -> Result<T, DeserializeConfigError> {
407 self.deserialize_any_param(index).map(|val| {
408 *val.downcast()
409 .expect("Internal error: deserializer output has wrong type")
410 })
411 }
412}
413
414impl WithOrigin {
415 #[tracing::instrument(level = "trace", skip(self))]
416 fn coerce_value_type(&self, expecting: BasicTypes) -> Option<Self> {
417 let Value::String(StrValue::Plain(str)) = &self.inner else {
418 return None; };
420
421 if !expecting.contains(BasicTypes::STRING) && (str.is_empty() || str == "null") {
424 return Some(Self::new(Value::Null, self.origin.clone()));
425 }
426
427 match expecting {
429 BasicTypes::BOOL => match str.parse::<bool>() {
432 Ok(bool_value) => {
433 return Some(Self::new(bool_value.into(), self.origin.clone()));
434 }
435 Err(err) => {
436 tracing::info!(%expecting, "failed coercing value: {err}");
437 }
438 },
439 BasicTypes::INTEGER | BasicTypes::FLOAT => match str.parse::<serde_json::Number>() {
440 Ok(number) => {
441 return Some(Self::new(number.into(), self.origin.clone()));
442 }
443 Err(err) => {
444 tracing::info!(%expecting, "failed coercing value: {err}");
445 }
446 },
447 _ => { }
448 }
449 None
450 }
451}
452
453pub trait DeserializeConfig: DescribeConfig + Sized {
455 fn deserialize_config(ctx: DeserializeContext<'_>) -> Result<Self, DeserializeConfigError>;
462}