serde_envfile/
prefixed.rs

1use std::path::Path;
2
3use super::{
4    de::{from_env_inner, from_file_inner, from_reader_inner, from_str_inner},
5    error::Result,
6    ser::{to_file_inner, to_string_inner, to_writer_inner},
7};
8
9/// Instantiates [`Prefixed`] from which values can be both serialized and deserialized with a prefix.
10///
11/// The prefix is added to all keys during serialization and is expected to be present during deserialization.
12/// This is useful for namespacing environment variables to avoid conflicts.
13///
14/// # Examples
15///
16/// ## Serializing with a prefix
17///
18/// ```
19/// use serde::{Serialize};
20/// use serde_envfile::{prefixed, Error};
21///
22/// #[derive(Serialize)]
23/// struct Config {
24///     database_url: String,
25///     port: u16,
26/// }
27///
28/// fn main() -> Result<(), Error> {
29///     let config = Config {
30///         database_url: "postgres://localhost/mydb".to_string(),
31///         port: 8080,
32///     };
33///
34///     // Serialize with "APP_" prefix
35///     let env_string = prefixed("APP_").to_string(&config)?;
36///     // Results in: APP_DATABASE_URL="postgres://localhost/mydb"\nAPP_PORT="8080"
37///
38///     println!("{}", env_string);
39///     Ok(())
40/// }
41/// ```
42///
43/// ## Deserializing with a prefix
44///
45/// ```
46/// use serde::{Deserialize};
47/// use serde_envfile::{prefixed, Error};
48///
49/// #[derive(Deserialize, Debug)]
50/// struct Config {
51///     database_url: String,
52///     port: u16,
53/// }
54///
55/// fn main() -> Result<(), Error> {
56///     let env_string = "APP_DATABASE_URL=\"postgres://localhost/mydb\"\nAPP_PORT=\"8080\"";
57///
58///     // Deserialize with "APP_" prefix
59///     let config: Config = prefixed("APP_").from_str(env_string)?;
60///
61///     assert_eq!(config.database_url, "postgres://localhost/mydb");
62///     assert_eq!(config.port, 8080);
63///     
64///     Ok(())
65/// }
66/// ```
67pub fn prefixed(prefix: &str) -> Prefixed {
68    Prefixed(prefix)
69}
70
71/// Helper structure to work with prefixed environment variables more efficiently.
72///
73/// This struct provides methods for serializing and deserializing data with a consistent prefix.
74/// Use the [`prefixed`] function to create an instance of this struct.
75pub struct Prefixed<'a>(&'a str);
76
77impl<'a> Prefixed<'a> {
78    pub fn from_env<T>(&self) -> Result<T>
79    where
80        T: serde::de::DeserializeOwned,
81    {
82        from_env_inner::<T>(Some(self.0))
83    }
84
85    pub fn from_str<T>(&self, input: &'a str) -> Result<T>
86    where
87        T: serde::de::DeserializeOwned,
88    {
89        from_str_inner::<T>(Some(self.0), input)
90    }
91
92    pub fn from_reader<R, T>(&self, reader: R) -> Result<T>
93    where
94        R: std::io::Read,
95        T: serde::de::DeserializeOwned,
96    {
97        from_reader_inner::<R, T>(Some(self.0), reader)
98    }
99
100    pub fn from_file<T>(&self, path: &Path) -> Result<T>
101    where
102        T: serde::de::DeserializeOwned,
103    {
104        from_file_inner::<T>(Some(self.0), path)
105    }
106
107    pub fn to_string<T>(&self, v: &T) -> Result<String>
108    where
109        T: serde::ser::Serialize,
110    {
111        to_string_inner(Some(self.0), v)
112    }
113
114    pub fn to_writer<W, T>(&self, writer: W, v: &T) -> Result<()>
115    where
116        W: std::io::Write,
117        T: serde::ser::Serialize,
118    {
119        to_writer_inner(Some(self.0), writer, v)
120    }
121
122    pub fn to_file<P, T>(&self, path: P, v: &T) -> Result<()>
123    where
124        P: AsRef<Path>,
125        T: serde::ser::Serialize,
126    {
127        to_file_inner(Some(self.0), path, v)
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use std::io::Cursor;
134
135    use super::prefixed;
136    use crate::Value;
137
138    #[test]
139    fn serialize_to_string_with_prefix() {
140        //* Given
141        let value = Value::from_iter([("hello", "world")]);
142
143        //* When
144        let output = prefixed("serde_envfile_")
145            .to_string(&value)
146            .expect("Failed to serialize");
147
148        //* Then
149        let expected_output = "SERDE_ENVFILE_HELLO=\"world\"";
150        assert_eq!(output, expected_output);
151    }
152
153    #[test]
154    fn deserilize_from_str_with_prefix() {
155        //* Given
156        #[derive(Debug, PartialEq, serde::Deserialize)]
157        struct Config {
158            hello: String,
159        }
160
161        let env = "SERDE_ENVFILE_HELLO=\"world\"";
162
163        //* When
164        let output = prefixed("serde_envfile_")
165            .from_str::<Config>(env)
166            .expect("Failed to deserialize");
167
168        //* Then
169        let expected_output = Config {
170            hello: String::from("world"),
171        };
172        assert_eq!(output, expected_output);
173    }
174
175    #[test]
176    fn serialize_to_writer_with_prefix() {
177        //* Given
178        let value = Value::from_iter([("hello", "world")]);
179
180        let mut buffer = Vec::new();
181
182        //* When
183        prefixed("serde_envfile_")
184            .to_writer(&mut buffer, &value)
185            .expect("Failed to serialize to writer");
186
187        //* Then
188        let expected_output = "SERDE_ENVFILE_HELLO=\"world\"";
189        let output = String::from_utf8(buffer).expect("Invalid UTF-8 sequence");
190        assert_eq!(output, expected_output);
191    }
192
193    #[test]
194    fn deserialize_from_reader_with_prefix() {
195        //* Given
196        #[derive(Debug, PartialEq, serde::Deserialize)]
197        struct Config {
198            hello: String,
199        }
200
201        let env = "SERDE_ENVFILE_HELLO=\"world\"";
202
203        let reader = Cursor::new(env);
204
205        //* When
206        let output = prefixed("serde_envfile_")
207            .from_reader::<_, Config>(reader)
208            .expect("Failed to deserialize from reader");
209
210        //* Then
211        let expected_output = Config {
212            hello: String::from("world"),
213        };
214        assert_eq!(output, expected_output);
215    }
216}