1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
use serde::{de::Error, Deserialize, Deserializer}; use std::fmt::Display; use std::str::FromStr; /// Deserializes a field expanding all the environment variables before the conversion /// /// # Example: /// /// ```rust /// use std::env; /// use serde_json; /// use serde::Deserialize; /// use serde_with_expand_env::with_expand_envs; /// /// #[derive(Deserialize, Debug)] /// struct Test { /// #[serde(deserialize_with="with_expand_envs")] /// number: usize, /// #[serde(deserialize_with="with_expand_envs")] /// string: String, /// #[serde(deserialize_with="with_expand_envs")] /// default: usize, /// } /// /// let serialized = r#"{"number": "$NUMBER", /// "string": "my string: $STRING", /// "default": "${DEFAULT:-42}" /// }"#; /// /// // No envs set will fail with enviroment variable not found /// assert_eq!(serde_json::from_str::<Test>(&serialized).is_err(), true); /// /// env::set_var("NUMBER", "42"); /// env::set_var("STRING", "hacker"); /// let deserialized: Test = serde_json::from_str(&serialized).unwrap(); /// /// assert_eq!(deserialized.number, 42); /// assert_eq!(deserialized.string, "my string: hacker"); /// assert_eq!(deserialized.default, 42); /// /// env::set_var("DEFAULT", "4200"); /// let deserialized: Test = serde_json::from_str(&serialized).unwrap(); /// assert_eq!(deserialized.default, 4200); /// /// // Invalid number /// env::set_var("NUMBER", "cuarentaydos"); /// env::set_var("STRING", "42"); /// /// assert_eq!(serde_json::from_str::<Test>(&serialized).is_err(), true); /// ``` pub fn with_expand_envs<'de, T, D>(deserializer: D) -> Result<T, D::Error> where D: Deserializer<'de>, T: FromStr + serde::Deserialize<'de>, <T as FromStr>::Err: Display, { #[derive(Deserialize)] #[serde(untagged)] enum StringOrAnything<T> { String(String), Anything(T), } match StringOrAnything::<T>::deserialize(deserializer)? { StringOrAnything::String(s) => match shellexpand::env(&s) { Ok(value) => value.parse::<T>().map_err(Error::custom), Err(err) => Err(Error::custom(err)), }, StringOrAnything::Anything(anything) => Ok(anything), } }