1use crate::Source;
6use serde::{
7 Deserialize, Deserializer, Serialize, Serializer,
8 de::{self, Visitor},
9};
10use std::fmt;
11
12impl Serialize for Source {
13 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
14 serializer.serialize_str(&self.to_string())
15 }
16}
17
18impl<'de> Deserialize<'de> for Source {
19 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
20 deserializer.deserialize_str(SourceVisitor)
21 }
22}
23
24struct SourceVisitor;
25
26impl<'de> Visitor<'de> for SourceVisitor {
27 type Value = Source;
28
29 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
30 formatter.write_str("a configuration source string such as `env(prefix=APP)`")
31 }
32
33 fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
34 Source::parse(value).map_err(|error| E::custom(error.to_string()))
35 }
36
37 fn visit_string<E: de::Error>(self, value: String) -> Result<Self::Value, E> {
38 self.visit_str(value.as_str())
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45 use crate::OptionValue;
46 use serde::{Deserialize, Serialize};
47
48 #[derive(Serialize, Deserialize, PartialEq, Debug)]
49 struct App {
50 source: Source,
51 }
52
53 #[test]
54 fn deserializes_config_source_from_string() {
55 let app: App = serde_json::from_str(r#"{ "source": "env(prefix=APP_)" }"#).unwrap();
56 assert_eq!(app.source.source(), "env");
57 assert_eq!(
58 app.source.options().get("prefix"),
59 Some(&OptionValue::String("APP_".into()))
60 );
61 }
62
63 #[test]
64 fn serializes_config_source_as_string() {
65 let app = App {
66 source: Source::parse("file(on_error=(load=skip)):.env").unwrap(),
67 };
68 let json = serde_json::to_string(&app).unwrap();
69 assert_eq!(json, r#"{"source":"file(on_error=(load=skip)):.env"}"#);
70 }
71
72 #[test]
73 fn deserializes_invalid_config_source_with_parse_error() {
74 let error = serde_json::from_str::<App>(r#"{ "source": "env(prefix=)" }"#).unwrap_err();
75 let message = error.to_string();
76 assert!(message.contains("configuration source option value cannot be empty"));
77 assert!(!message.contains('\n'));
78 }
79}