figment/value/
magic.rs

1//! (De)serializable values that "magically" use information from the extracing
2//! [`Figment`](crate::Figment).
3
4use std::ops::Deref;
5use std::path::{PathBuf, Path};
6
7use serde::{Deserialize, Serialize, de};
8
9use crate::{Error, value::{ConfiguredValueDe, Interpreter, MapDe, Tag}};
10
11/// Marker trait for "magic" values. Primarily for use with [`Either`].
12pub trait Magic: for<'de> Deserialize<'de> {
13    /// The name of the deserialization pseudo-strucure.
14    #[doc(hidden)] const NAME: &'static str;
15
16    /// The fields of the pseudo-structure. The last one should be the value.
17    #[doc(hidden)] const FIELDS: &'static [&'static str];
18
19    #[doc(hidden)] fn deserialize_from<'de: 'c, 'c, V: de::Visitor<'de>, I: Interpreter>(
20        de: ConfiguredValueDe<'c, I>,
21        visitor: V
22    ) -> Result<V::Value, Error>;
23}
24
25/// A [`PathBuf`] that knows the path of the file it was configured in, if any.
26///
27/// Paths in configuration files are often desired to be relative to the
28/// configuration file itself. For example, a path of `a/b.html` configured in a
29/// file `/var/config.toml` might be desired to resolve as `/var/a/b.html`. This
30/// type makes this possible by simply delcaring the configuration value's type
31/// as [`RelativePathBuf`].
32///
33/// # Example
34///
35/// ```rust
36/// use std::path::Path;
37///
38/// use serde::{Deserialize, Serialize};
39/// use figment::{Figment, value::magic::RelativePathBuf, Jail};
40/// use figment::providers::{Env, Format, Toml, Serialized};
41///
42/// #[derive(Debug, PartialEq, Deserialize, Serialize)]
43/// struct Config {
44///     path: RelativePathBuf,
45/// }
46///
47/// Jail::expect_with(|jail| {
48///     // Note that `jail.directory()` is some non-empty path:
49///     assert_ne!(jail.directory(), Path::new("/"));
50///
51///     // When a path is declared in a file and deserialized as
52///     // `RelativePathBuf`, `relative()` will be relative to the file.
53///     jail.create_file("Config.toml", r#"path = "a/b/c.html""#)?;
54///     let c: Config = Figment::from(Toml::file("Config.toml")).extract()?;
55///     assert_eq!(c.path.original(), Path::new("a/b/c.html"));
56///     assert_eq!(c.path.relative(), jail.directory().join("a/b/c.html"));
57///     assert_ne!(c.path.relative(), Path::new("a/b/c.html"));
58///
59///     // Round-tripping a `RelativePathBuf` preserves path-relativity.
60///     let c: Config = Figment::from(Serialized::defaults(&c)).extract()?;
61///     assert_eq!(c.path.original(), Path::new("a/b/c.html"));
62///     assert_eq!(c.path.relative(), jail.directory().join("a/b/c.html"));
63///     assert_ne!(c.path.relative(), Path::new("a/b/c.html"));
64///
65///     // If a path is declared elsewhere, the "relative" path is the original.
66///     jail.set_env("PATH", "a/b/c.html");
67///     let c: Config = Figment::from(Toml::file("Config.toml"))
68///         .merge(Env::raw().only(&["PATH"]))
69///         .extract()?;
70///
71///     assert_eq!(c.path.original(), Path::new("a/b/c.html"));
72///     assert_eq!(c.path.relative(), Path::new("a/b/c.html"));
73///
74///     // Absolute paths remain unchanged.
75///     jail.create_file("Config.toml", r#"path = "/var/c.html""#);
76///     let c: Config = Figment::from(Toml::file("Config.toml")).extract()?;
77///     assert_eq!(c.path.original(), Path::new("/var/c.html"));
78///     assert_eq!(c.path.relative(), Path::new("/var/c.html"));
79///
80///     // You can use the `From<P: AsRef<Path>>` impl to set defaults:
81///     let figment = Figment::from(Serialized::defaults(Config {
82///         path: "some/default/path".into()
83///     }));
84///
85///     let default: Config = figment.extract()?;
86///     assert_eq!(default.path.original(), Path::new("some/default/path"));
87///     assert_eq!(default.path.relative(), Path::new("some/default/path"));
88///
89///     jail.create_file("Config.toml", r#"path = "an/override""#)?;
90///     let overriden: Config = figment.merge(Toml::file("Config.toml")).extract()?;
91///     assert_eq!(overriden.path.original(), Path::new("an/override"));
92///     assert_eq!(overriden.path.relative(), jail.directory().join("an/override"));
93///
94///     Ok(())
95/// });
96/// ```
97///
98/// # Serialization
99///
100/// By default, a `RelativePathBuf` serializes into a structure that can only
101/// deserialize as a `RelativePathBuf`. In particular, a `RelativePathBuf` does
102/// not serialize into a value compatible with `PathBuf`. To serialize into a
103/// `Path`, use [`RelativePathBuf::serialize_original()`] or
104/// [`RelativePathBuf::serialize_relative()`] together with serde's
105/// `serialize_with` field attribute:
106///
107/// ```rust
108/// use std::path::PathBuf;
109///
110/// use serde::{Deserialize, Serialize};
111/// use figment::{Figment, value::magic::RelativePathBuf, Jail};
112/// use figment::providers::{Format, Toml, Serialized};
113///
114/// #[derive(Deserialize, Serialize)]
115/// struct Config {
116///     relative: RelativePathBuf,
117///     #[serde(serialize_with = "RelativePathBuf::serialize_original")]
118///     root: RelativePathBuf,
119///     #[serde(serialize_with = "RelativePathBuf::serialize_relative")]
120///     temp: RelativePathBuf,
121/// }
122///
123/// Jail::expect_with(|jail| {
124///     jail.create_file("Config.toml", r#"
125///         relative = "relative/path"
126///         root = "root/path"
127///         temp = "temp/path"
128///     "#)?;
129///
130///     // Create a figment with a serialize `Config`.
131///     let figment = Figment::from(Toml::file("Config.toml"));
132///     let config = figment.extract::<Config>()?;
133///     let figment = Figment::from(Serialized::defaults(config));
134///
135///     // This fails, as expected.
136///     let relative = figment.extract_inner::<PathBuf>("relative");
137///     assert!(relative.is_err());
138///
139///     // These succeed. This one uses the originally written path.
140///     let root = figment.extract_inner::<PathBuf>("root")?;
141///     assert_eq!(root, PathBuf::from("root/path"));
142///
143///     // This one the magic relative path.
144///     let temp = figment.extract_inner::<PathBuf>("temp")?;
145///     assert_eq!(temp, jail.directory().join("temp/path"));
146///
147///     Ok(())
148/// })
149/// ```
150#[derive(Debug, Clone)]
151// #[derive(Deserialize, Serialize)]
152// #[serde(rename = "___figment_relative_path_buf")]
153pub struct RelativePathBuf {
154    // #[serde(rename = "___figment_relative_metadata_path")]
155    metadata_path: Option<PathBuf>,
156    // #[serde(rename = "___figment_relative_path")]
157    path: PathBuf,
158}
159
160impl PartialEq for RelativePathBuf {
161    fn eq(&self, other: &Self) -> bool {
162        self.relative() == other.relative()
163    }
164}
165
166impl<P: AsRef<Path>> From<P> for RelativePathBuf {
167    fn from(path: P) -> RelativePathBuf {
168        Self { metadata_path: None, path: path.as_ref().into() }
169    }
170}
171
172impl Magic for RelativePathBuf {
173    const NAME: &'static str = "___figment_relative_path_buf";
174
175    const FIELDS: &'static [&'static str] = &[
176        "___figment_relative_metadata_path",
177        "___figment_relative_path"
178    ];
179
180    fn deserialize_from<'de: 'c, 'c, V: de::Visitor<'de>, I: Interpreter>(
181        de: ConfiguredValueDe<'c, I>,
182        visitor: V
183    ) -> Result<V::Value, Error> {
184        // If we have this struct with a non-empty metadata_path, use it.
185        let config = de.config;
186        if let Some(d) = de.value.as_dict() {
187            if let Some(mpv) = d.get(Self::FIELDS[0]) {
188                if mpv.to_empty().is_none() {
189                    let map_de = MapDe::new(d, |v| ConfiguredValueDe::<I>::from(config, v));
190                    return visitor.visit_map(map_de);
191                }
192            }
193        }
194
195        let metadata_path = config.get_metadata(de.value.tag())
196            .and_then(|metadata| metadata.source.as_ref()
197                .and_then(|s| s.file_path())
198                .map(|path| path.display().to_string()));
199
200        let mut map = crate::value::Map::new();
201        if let Some(path) = metadata_path {
202            map.insert(Self::FIELDS[0].into(), path.into());
203        }
204
205        // If we have this struct with no metadata_path, still use the value.
206        let value = de.value.find_ref(Self::FIELDS[1]).unwrap_or(&de.value);
207        map.insert(Self::FIELDS[1].into(), value.clone());
208        visitor.visit_map(MapDe::new(&map, |v| ConfiguredValueDe::<I>::from(config, v)))
209    }
210}
211
212impl RelativePathBuf {
213    /// Returns the path as it was declared, without modification.
214    ///
215    /// # Example
216    ///
217    /// ```rust
218    /// use std::path::Path;
219    ///
220    /// use figment::{Figment, value::magic::RelativePathBuf, Jail};
221    /// use figment::providers::{Format, Toml};
222    ///
223    /// #[derive(Debug, PartialEq, serde::Deserialize)]
224    /// struct Config {
225    ///     path: RelativePathBuf,
226    /// }
227    ///
228    /// Jail::expect_with(|jail| {
229    ///     jail.create_file("Config.toml", r#"path = "hello.html""#)?;
230    ///     let c: Config = Figment::from(Toml::file("Config.toml")).extract()?;
231    ///     assert_eq!(c.path.original(), Path::new("hello.html"));
232    ///
233    ///     Ok(())
234    /// });
235    /// ```
236    pub fn original(&self) -> &Path {
237        &self.path
238    }
239
240    /// Returns this path resolved relative to the file it was declared in, if any.
241    ///
242    /// If the configured path was relative and it was configured from a file,
243    /// this function returns that path prefixed with that file's parent
244    /// directory. Otherwise it returns the original path. Where
245    /// `config_file_path` is the location of the configuration file, this
246    /// corresponds to:
247    ///
248    /// ```rust
249    /// # use figment::{Figment, value::magic::RelativePathBuf, Jail};
250    /// # use figment::providers::{Format, Toml};
251    /// # use serde::Deserialize;
252    /// #
253    /// # #[derive(Debug, PartialEq, Deserialize)]
254    /// # struct Config {
255    /// #     path: RelativePathBuf,
256    /// # }
257    /// # Jail::expect_with(|jail| {
258    /// # let config_file_path = jail.directory().join("Config.toml");
259    /// # let config_file = jail.create_file("Config.toml", r#"path = "hello.html""#)?;
260    /// # let config: Config = Figment::from(Toml::file("Config.toml")).extract()?;
261    /// # let relative_path_buf = config.path;
262    /// let relative = config_file_path
263    ///     .parent()
264    ///     .unwrap()
265    ///     .join(relative_path_buf.original());
266    /// # assert_eq!(relative_path_buf.relative(), relative);
267    /// # Ok(())
268    /// # });
269    /// ```
270    ///
271    /// # Example
272    ///
273    /// ```rust
274    /// use std::path::Path;
275    ///
276    /// use figment::{Figment, value::magic::RelativePathBuf, Jail};
277    /// use figment::providers::{Env, Format, Toml};
278    ///
279    /// #[derive(Debug, PartialEq, serde::Deserialize)]
280    /// struct Config {
281    ///     path: RelativePathBuf,
282    /// }
283    ///
284    /// Jail::expect_with(|jail| {
285    ///     jail.create_file("Config.toml", r#"path = "hello.html""#)?;
286    ///     let c: Config = Figment::from(Toml::file("Config.toml")).extract()?;
287    ///     assert_eq!(c.path.relative(), jail.directory().join("hello.html"));
288    ///
289    ///     jail.set_env("PATH", r#"hello.html"#);
290    ///     let c: Config = Figment::from(Env::raw().only(&["PATH"])).extract()?;
291    ///     assert_eq!(c.path.relative(), Path::new("hello.html"));
292    ///
293    ///     Ok(())
294    /// });
295    /// ```
296    pub fn relative(&self) -> PathBuf {
297        if self.original().has_root() {
298            return self.original().into();
299        }
300
301        self.metadata_path()
302            .and_then(|root| match root.is_dir() {
303                true => Some(root),
304                false => root.parent(),
305            })
306            .map(|root| root.join(self.original()))
307            .unwrap_or_else(|| self.original().into())
308    }
309
310    /// Returns the path to the file this path was declared in, if any.
311    ///
312    /// # Example
313    ///
314    /// ```rust
315    /// use std::path::Path;
316    ///
317    /// use figment::{Figment, value::magic::RelativePathBuf, Jail};
318    /// use figment::providers::{Env, Format, Toml};
319    ///
320    /// #[derive(Debug, PartialEq, serde::Deserialize)]
321    /// struct Config {
322    ///     path: RelativePathBuf,
323    /// }
324    ///
325    /// Jail::expect_with(|jail| {
326    ///     jail.create_file("Config.toml", r#"path = "hello.html""#)?;
327    ///     let c: Config = Figment::from(Toml::file("Config.toml")).extract()?;
328    ///     assert_eq!(c.path.metadata_path().unwrap(), jail.directory().join("Config.toml"));
329    ///
330    ///     jail.set_env("PATH", r#"hello.html"#);
331    ///     let c: Config = Figment::from(Env::raw().only(&["PATH"])).extract()?;
332    ///     assert_eq!(c.path.metadata_path(), None);
333    ///
334    ///     Ok(())
335    /// });
336    /// ```
337    pub fn metadata_path(&self) -> Option<&Path> {
338        self.metadata_path.as_ref().map(|p| p.as_ref())
339    }
340
341    /// Serialize `self` as the [`original`](Self::original()) path.
342    ///
343    /// See [serialization](Self#serialization) for more.
344    ///
345    /// # Example
346    ///
347    /// ```rust
348    /// use std::path::PathBuf;
349    /// use figment::value::magic::RelativePathBuf;
350    /// use serde::Serialize;
351    ///
352    /// #[derive(Serialize)]
353    /// struct Config {
354    ///     #[serde(serialize_with = "RelativePathBuf::serialize_original")]
355    ///     path: RelativePathBuf,
356    /// }
357    /// ```
358    pub fn serialize_original<S>(&self, ser: S) -> Result<S::Ok, S::Error>
359        where S: serde::Serializer
360    {
361        self.original().serialize(ser)
362    }
363
364    /// Serialize `self` as the [`relative`](Self::relative()) path.
365    ///
366    /// See [serialization](Self#serialization) for more.
367    ///
368    /// # Example
369    ///
370    /// ```rust
371    /// use std::path::PathBuf;
372    /// use figment::value::magic::RelativePathBuf;
373    /// use serde::Serialize;
374    ///
375    /// #[derive(Serialize)]
376    /// struct Config {
377    ///     #[serde(serialize_with = "RelativePathBuf::serialize_relative")]
378    ///     path: RelativePathBuf,
379    /// }
380    /// ```
381    // FIXME: Make this the default? We need a breaking change for this.
382    pub fn serialize_relative<S>(&self, ser: S) -> Result<S::Ok, S::Error>
383        where S: serde::Serializer
384    {
385        self.relative().serialize(ser)
386    }
387}
388
389// /// MAGIC
390// #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
391// #[serde(rename = "___figment_selected_profile")]
392// pub struct SelectedProfile {
393//     profile: crate::Profile,
394// }
395//
396// /// TODO: This doesn't work when it's in a map and the config doesn't contain a
397// /// value for the corresponding field; we never get to call `deserialize` on the
398// /// field's value. We can't fabricate this from no value. We either need to fake
399// /// the field name, somehow, or just not have this.
400// impl Magic for SelectedProfile {
401//     const NAME: &'static str = "___figment_selected_profile";
402//     const FIELDS: &'static [&'static str] = &["profile"];
403//
404//     fn deserialize_from<'de: 'c, 'c, V: de::Visitor<'de>>(
405//         de: ConfiguredValueDe<'c>,
406//         visitor: V
407//     ) -> Result<V::Value, Error>{
408//         let mut map = crate::value::Map::new();
409//         map.insert(Self::FIELDS[0].into(), de.config.profile().to_string().into());
410//         visitor.visit_map(MapDe::new(&map, |v| ConfiguredValueDe::<I>::from(de.config, v)))
411//     }
412// }
413//
414// impl Deref for SelectedProfile {
415//     type Target = crate::Profile;
416//
417//     fn deref(&self) -> &Self::Target {
418//         &self.profile
419//     }
420// }
421
422/// (De)serializes as either a magic value `A` or any other deserializable value
423/// `B`.
424///
425/// An `Either<A, B>` deserializes as either an `A` or `B`, whichever succeeds
426/// first.
427///
428/// The usual `Either` implementation or an "untagged" enum does not allow its
429/// internal values to provide hints to the deserializer. These hints are
430/// required for magic values to work. By contrast, this `Either` _does_ provide
431/// the appropriate hints.
432///
433/// # Example
434///
435/// ```
436/// use serde::{Serialize, Deserialize};
437/// use figment::{Figment, value::magic::{Either, RelativePathBuf, Tagged}};
438///
439/// #[derive(Debug, PartialEq, Deserialize, Serialize)]
440/// struct Config {
441///     int_or_str: Either<Tagged<usize>, String>,
442///     path_or_bytes: Either<RelativePathBuf, Vec<u8>>,
443/// }
444///
445/// fn figment<A: Serialize, B: Serialize>(a: A, b: B) -> Figment {
446///     Figment::from(("int_or_str", a)).merge(("path_or_bytes", b))
447/// }
448///
449/// let config: Config = figment(10, "/a/b").extract().unwrap();
450/// assert_eq!(config.int_or_str, Either::Left(10.into()));
451/// assert_eq!(config.path_or_bytes, Either::Left("/a/b".into()));
452///
453/// let config: Config = figment("hi", "c/d").extract().unwrap();
454/// assert_eq!(config.int_or_str, Either::Right("hi".into()));
455/// assert_eq!(config.path_or_bytes, Either::Left("c/d".into()));
456///
457/// let config: Config = figment(123, &[1, 2, 3]).extract().unwrap();
458/// assert_eq!(config.int_or_str, Either::Left(123.into()));
459/// assert_eq!(config.path_or_bytes, Either::Right(vec![1, 2, 3].into()));
460///
461/// let config: Config = figment("boo!", &[4, 5, 6]).extract().unwrap();
462/// assert_eq!(config.int_or_str, Either::Right("boo!".into()));
463/// assert_eq!(config.path_or_bytes, Either::Right(vec![4, 5, 6].into()));
464///
465/// let config: Config = Figment::from(figment::providers::Serialized::defaults(Config {
466///     int_or_str: Either::Left(10.into()),
467///     path_or_bytes: Either::Left("a/b/c".into()),
468/// })).extract().unwrap();
469///
470/// assert_eq!(config.int_or_str, Either::Left(10.into()));
471/// assert_eq!(config.path_or_bytes, Either::Left("a/b/c".into()));
472///
473/// let config: Config = Figment::from(figment::providers::Serialized::defaults(Config {
474///     int_or_str: Either::Right("hi".into()),
475///     path_or_bytes: Either::Right(vec![3, 7, 13]),
476/// })).extract().unwrap();
477///
478/// assert_eq!(config.int_or_str, Either::Right("hi".into()));
479/// assert_eq!(config.path_or_bytes, Either::Right(vec![3, 7, 13]));
480/// ```
481#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
482// #[serde(untagged)]
483// #[derive(Serialize)]
484pub enum Either<A, B> {
485    /// The "left" variant.
486    Left(A),
487    /// The "right" variant.
488    Right(B),
489}
490
491impl<'de: 'b, 'b, A, B> Deserialize<'de> for Either<A, B>
492    where A: Magic, B: Deserialize<'b>
493{
494    fn deserialize<D>(de: D) -> Result<Self, D::Error>
495        where D: de::Deserializer<'de>
496    {
497        use crate::value::ValueVisitor;
498
499        // FIXME: propogate the error properly
500        let value = de.deserialize_struct(A::NAME, A::FIELDS, ValueVisitor)?;
501        match A::deserialize(&value) {
502            Ok(value) => Ok(Either::Left(value)),
503            Err(a_err) => {
504                let value = value.as_dict()
505                    .and_then(|d| d.get(A::FIELDS[A::FIELDS.len() - 1]))
506                    .unwrap_or(&value);
507
508                match B::deserialize(value) {
509                    Ok(value) => Ok(Either::Right(value)),
510                    Err(b_err) => Err(de::Error::custom(format!("{}; {}", a_err, b_err)))
511                }
512            }
513        }
514
515        // use crate::error::Kind::*;
516        // result.map_err(|e| match e.kind {
517        //     InvalidType(Actual, String) => de::Error::invalid_type()
518        //     InvalidValue(Actual, String),
519        //     UnknownField(String, &'static [&'static str]),
520        //     MissingField(Cow<'static, str>),
521        //     DuplicateField(&'static str),
522        //     InvalidLength(usize, String),
523        //     UnknownVariant(String, &'static [&'static str]),
524        //     kind => de::Error::custom(kind.to_string()),
525        // })
526    }
527}
528
529/// A wrapper around any value of type `T` and its [`Tag`].
530///
531/// ```rust
532/// use figment::{Figment, value::magic::Tagged, Jail};
533/// use figment::providers::{Format, Toml};
534///
535/// #[derive(Debug, PartialEq, serde::Deserialize)]
536/// struct Config {
537///     number: Tagged<usize>,
538/// }
539///
540/// Jail::expect_with(|jail| {
541///     jail.create_file("Config.toml", r#"number = 10"#)?;
542///     let figment = Figment::from(Toml::file("Config.toml"));
543///     let c: Config = figment.extract()?;
544///     assert_eq!(*c.number, 10);
545///
546///     let tag = c.number.tag();
547///     let metadata = figment.get_metadata(tag).expect("number has tag");
548///
549///     assert!(!tag.is_default());
550///     assert_eq!(metadata.name, "TOML file");
551///     Ok(())
552/// });
553/// ```
554#[derive(Debug, Clone)]
555// #[derive(Deserialize, Serialize)]
556// #[serde(rename = "___figment_tagged_item")]
557pub struct Tagged<T> {
558    // #[serde(rename = "___figment_tagged_tag")]
559    tag: Tag,
560    // #[serde(rename = "___figment_tagged_value")]
561    value: T,
562}
563
564impl<T: PartialEq> PartialEq for Tagged<T> {
565    fn eq(&self, other: &Self) -> bool {
566        self.value == other.value
567    }
568}
569
570impl<T: for<'de> Deserialize<'de>> Magic for Tagged<T> {
571    const NAME: &'static str = "___figment_tagged_item";
572    const FIELDS: &'static [&'static str] = &[
573        "___figment_tagged_tag" , "___figment_tagged_value"
574    ];
575
576    fn deserialize_from<'de: 'c, 'c, V: de::Visitor<'de>, I: Interpreter>(
577        de: ConfiguredValueDe<'c, I>,
578        visitor: V
579    ) -> Result<V::Value, Error>{
580        let config = de.config;
581        let mut map = crate::value::Map::new();
582
583        // If we have this struct with a non-default tag, use it.
584        if let Some(dict) = de.value.as_dict() {
585            if let Some(tagv) = dict.get(Self::FIELDS[0]) {
586                if let Ok(false) = tagv.deserialize::<Tag>().map(|t| t.is_default()) {
587                    return visitor.visit_map(MapDe::new(dict, |v| {
588                        ConfiguredValueDe::<I>::from(config, v)
589                    }));
590                }
591            }
592        }
593
594        // If we have this struct with default tag, use the value.
595        let value = de.value.find_ref(Self::FIELDS[1]).unwrap_or(&de.value);
596        map.insert(Self::FIELDS[0].into(), de.value.tag().into());
597        map.insert(Self::FIELDS[1].into(), value.clone());
598        visitor.visit_map(MapDe::new(&map, |v| ConfiguredValueDe::<I>::from(config, v)))
599    }
600}
601
602impl<T> Tagged<T> {
603    /// Returns the tag of the inner value if it is known. As long `self` is a
604    /// leaf and was extracted from a [`Figment`](crate::Figment), the returned
605    /// value is expected to be `Some`.
606    ///
607    /// # Example
608    ///
609    /// ```rust
610    /// use figment::{Figment, Profile, value::magic::Tagged};
611    ///
612    /// let figment = Figment::from(("key", "value"));
613    /// let tagged = figment.extract_inner::<Tagged<String>>("key").unwrap();
614    ///
615    /// assert!(!tagged.tag().is_default());
616    /// assert_eq!(tagged.tag().profile(), Some(Profile::Global));
617    /// ```
618    pub fn tag(&self) -> Tag {
619        self.tag
620    }
621
622    /// Consumes `self` and returns the inner value.
623    ///
624    /// # Example
625    ///
626    /// ```rust
627    /// use figment::{Figment, value::magic::Tagged};
628    ///
629    /// let tagged = Figment::from(("key", "value"))
630    ///     .extract_inner::<Tagged<String>>("key")
631    ///     .unwrap();
632    ///
633    /// let value = tagged.into_inner();
634    /// assert_eq!(value, "value");
635    /// ```
636    pub fn into_inner(self) -> T {
637        self.value
638    }
639}
640
641impl<T> Deref for Tagged<T> {
642    type Target = T;
643
644    fn deref(&self) -> &Self::Target {
645        &self.value
646    }
647}
648
649impl<T> From<T> for Tagged<T> {
650    fn from(value: T) -> Self {
651        Tagged { tag: Tag::Default, value, }
652    }
653}
654
655/// These were generated by serde's derive. We don't want to depend on the
656/// 'derive' feature, so we simply expand it and copy the impls here.
657mod _serde {
658    use super::*;
659
660    #[allow(unused_imports)]
661    pub mod export {
662        // These are re-reexports used by serde's codegen.
663        pub use std::clone::Clone;
664        pub use std::convert::{From, Into};
665        pub use std::default::Default;
666        pub use std::fmt::{self, Formatter};
667        pub use std::marker::PhantomData;
668        pub use std::option::Option::{self, None, Some};
669        pub use std::result::Result::{self, Err, Ok};
670
671        pub fn missing_field<'de, V, E>(field: &'static str) -> Result<V, E>
672            where V: serde::de::Deserialize<'de>,
673                  E: serde::de::Error,
674        {
675            struct MissingFieldDeserializer<E>(&'static str, PhantomData<E>);
676
677            impl<'de, E> serde::de::Deserializer<'de> for MissingFieldDeserializer<E>
678                where E: serde::de::Error
679            {
680                    type Error = E;
681
682                    fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value, E>
683                        where V: serde::de::Visitor<'de>,
684                    {
685                        Err(serde::de::Error::missing_field(self.0))
686                    }
687
688                    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, E>
689                        where V: serde::de::Visitor<'de>,
690                    {
691                        visitor.visit_none()
692                    }
693
694                    serde::forward_to_deserialize_any! {
695                        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str
696                        string bytes byte_buf unit unit_struct newtype_struct seq tuple
697                        tuple_struct map struct enum identifier ignored_any
698                    }
699                }
700
701            let deserializer = MissingFieldDeserializer(field, PhantomData);
702            serde::de::Deserialize::deserialize(deserializer)
703        }
704    }
705
706    #[doc(hidden)]
707    #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
708    const _: () = {
709        #[allow(rust_2018_idioms, clippy::useless_attribute)]
710        extern crate serde as _serde;
711
712        #[automatically_derived]
713        impl<'de> _serde::Deserialize<'de> for RelativePathBuf {
714            fn deserialize<__D>(__deserializer: __D) -> export::Result<Self, __D::Error>
715            where
716                __D: _serde::Deserializer<'de>,
717            {
718                #[allow(non_camel_case_types)]
719                enum __Field {
720                    __field0,
721                    __field1,
722                    __ignore,
723                }
724                struct __FieldVisitor;
725                impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
726                    type Value = __Field;
727                    fn expecting(
728                        &self,
729                        __formatter: &mut export::Formatter,
730                    ) -> export::fmt::Result {
731                        export::Formatter::write_str(__formatter, "field identifier")
732                    }
733                    fn visit_u64<__E>(
734                        self,
735                        __value: u64,
736                    ) -> export::Result<Self::Value, __E>
737                    where
738                        __E: _serde::de::Error,
739                    {
740                        match __value {
741                            0u64 => export::Ok(__Field::__field0),
742                            1u64 => export::Ok(__Field::__field1),
743                            _ => export::Err(_serde::de::Error::invalid_value(
744                                _serde::de::Unexpected::Unsigned(__value),
745                                &"field index 0 <= i < 2",
746                            )),
747                        }
748                    }
749                    fn visit_str<__E>(
750                        self,
751                        __value: &str,
752                    ) -> export::Result<Self::Value, __E>
753                    where
754                        __E: _serde::de::Error,
755                    {
756                        match __value {
757                            "___figment_relative_metadata_path" => {
758                                export::Ok(__Field::__field0)
759                            }
760                            "___figment_relative_path" => export::Ok(__Field::__field1),
761                            _ => export::Ok(__Field::__ignore),
762                        }
763                    }
764                    fn visit_bytes<__E>(
765                        self,
766                        __value: &[u8],
767                    ) -> export::Result<Self::Value, __E>
768                    where
769                        __E: _serde::de::Error,
770                    {
771                        match __value {
772                            b"___figment_relative_metadata_path" => {
773                                export::Ok(__Field::__field0)
774                            }
775                            b"___figment_relative_path" => {
776                                export::Ok(__Field::__field1)
777                            }
778                            _ => export::Ok(__Field::__ignore),
779                        }
780                    }
781                }
782                impl<'de> _serde::Deserialize<'de> for __Field {
783                    #[inline]
784                    fn deserialize<__D>(
785                        __deserializer: __D,
786                    ) -> export::Result<Self, __D::Error>
787                    where
788                        __D: _serde::Deserializer<'de>,
789                    {
790                        _serde::Deserializer::deserialize_identifier(
791                            __deserializer,
792                            __FieldVisitor,
793                        )
794                    }
795                }
796                struct __Visitor<'de> {
797                    marker: export::PhantomData<RelativePathBuf>,
798                    lifetime: export::PhantomData<&'de ()>,
799                }
800                impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> {
801                    type Value = RelativePathBuf;
802                    fn expecting(
803                        &self,
804                        __formatter: &mut export::Formatter,
805                    ) -> export::fmt::Result {
806                        export::Formatter::write_str(
807                            __formatter,
808                            "struct RelativePathBuf",
809                        )
810                    }
811                    #[inline]
812                    fn visit_seq<__A>(
813                        self,
814                        mut __seq: __A,
815                    ) -> export::Result<Self::Value, __A::Error>
816                    where
817                        __A: _serde::de::SeqAccess<'de>,
818                    {
819                        let __field0 = match match _serde::de::SeqAccess::next_element::<
820                            Option<PathBuf>,
821                        >(&mut __seq)
822                        {
823                            export::Ok(__val) => __val,
824                            export::Err(__err) => {
825                                return export::Err(__err);
826                            }
827                        } {
828                            export::Some(__value) => __value,
829                            export::None => {
830                                return export::Err(_serde::de::Error::invalid_length(
831                                    0usize,
832                                    &"struct RelativePathBuf with 2 elements",
833                                ));
834                            }
835                        };
836                        let __field1 = match match _serde::de::SeqAccess::next_element::<PathBuf>(
837                            &mut __seq,
838                        ) {
839                            export::Ok(__val) => __val,
840                            export::Err(__err) => {
841                                return export::Err(__err);
842                            }
843                        } {
844                            export::Some(__value) => __value,
845                            export::None => {
846                                return export::Err(_serde::de::Error::invalid_length(
847                                    1usize,
848                                    &"struct RelativePathBuf with 2 elements",
849                                ));
850                            }
851                        };
852                        export::Ok(RelativePathBuf {
853                            metadata_path: __field0,
854                            path: __field1,
855                        })
856                    }
857                    #[inline]
858                    fn visit_map<__A>(
859                        self,
860                        mut __map: __A,
861                    ) -> export::Result<Self::Value, __A::Error>
862                    where
863                        __A: _serde::de::MapAccess<'de>,
864                    {
865                        let mut __field0: export::Option<Option<PathBuf>> =
866                            export::None;
867                        let mut __field1: export::Option<PathBuf> =
868                            export::None;
869                        while let export::Some(__key) =
870                            match _serde::de::MapAccess::next_key::<__Field>(&mut __map) {
871                                export::Ok(__val) => __val,
872                                export::Err(__err) => {
873                                    return export::Err(__err);
874                                }
875                            }
876                        {
877                            match __key {
878                                __Field::__field0 => {
879                                    if export::Option::is_some(&__field0) {
880                                        return export::Err(
881                                            <__A::Error as _serde::de::Error>::duplicate_field(
882                                                "___figment_relative_metadata_path",
883                                            ),
884                                        );
885                                    }
886                                    __field0 = export::Some(
887                                        match _serde::de::MapAccess::next_value::<Option<PathBuf>>(
888                                            &mut __map,
889                                        ) {
890                                            export::Ok(__val) => __val,
891                                            export::Err(__err) => {
892                                                return export::Err(__err);
893                                            }
894                                        },
895                                    );
896                                }
897                                __Field::__field1 => {
898                                    if export::Option::is_some(&__field1) {
899                                        return export::Err(
900                                            <__A::Error as _serde::de::Error>::duplicate_field(
901                                                "___figment_relative_path",
902                                            ),
903                                        );
904                                    }
905                                    __field1 = export::Some(
906                                        match _serde::de::MapAccess::next_value::<PathBuf>(
907                                            &mut __map,
908                                        ) {
909                                            export::Ok(__val) => __val,
910                                            export::Err(__err) => {
911                                                return export::Err(__err);
912                                            }
913                                        },
914                                    );
915                                }
916                                _ => {
917                                    let _ = match _serde::de::MapAccess::next_value::<
918                                        _serde::de::IgnoredAny,
919                                    >(
920                                        &mut __map
921                                    ) {
922                                        export::Ok(__val) => __val,
923                                        export::Err(__err) => {
924                                            return export::Err(__err);
925                                        }
926                                    };
927                                }
928                            }
929                        }
930                        let __field0 = match __field0 {
931                            export::Some(__field0) => __field0,
932                            export::None => match export::missing_field(
933                                "___figment_relative_metadata_path",
934                            ) {
935                                export::Ok(__val) => __val,
936                                export::Err(__err) => {
937                                    return export::Err(__err);
938                                }
939                            },
940                        };
941                        let __field1 = match __field1 {
942                            export::Some(__field1) => __field1,
943                            export::None => match export::missing_field(
944                                "___figment_relative_path",
945                            ) {
946                                export::Ok(__val) => __val,
947                                export::Err(__err) => {
948                                    return export::Err(__err);
949                                }
950                            },
951                        };
952                        export::Ok(RelativePathBuf {
953                            metadata_path: __field0,
954                            path: __field1,
955                        })
956                    }
957                }
958                const FIELDS: &[&str] = &[
959                    "___figment_relative_metadata_path",
960                    "___figment_relative_path",
961                ];
962                _serde::Deserializer::deserialize_struct(
963                    __deserializer,
964                    "___figment_relative_path_buf",
965                    FIELDS,
966                    __Visitor {
967                        marker: export::PhantomData::<RelativePathBuf>,
968                        lifetime: export::PhantomData,
969                    },
970                )
971            }
972        }
973    };
974
975    #[doc(hidden)]
976    #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
977    const _: () = {
978        #[allow(rust_2018_idioms, clippy::useless_attribute)]
979        extern crate serde as _serde;
980        #[automatically_derived]
981        impl _serde::Serialize for RelativePathBuf {
982            fn serialize<__S>(
983                &self,
984                __serializer: __S,
985            ) -> export::Result<__S::Ok, __S::Error>
986            where
987                __S: _serde::Serializer,
988            {
989                let mut __serde_state = match _serde::Serializer::serialize_struct(
990                    __serializer,
991                    "___figment_relative_path_buf",
992                    false as usize + 1 + 1,
993                ) {
994                    export::Ok(__val) => __val,
995                    export::Err(__err) => {
996                        return export::Err(__err);
997                    }
998                };
999                match _serde::ser::SerializeStruct::serialize_field(
1000                    &mut __serde_state,
1001                    "___figment_relative_metadata_path",
1002                    &self.metadata_path,
1003                ) {
1004                    export::Ok(__val) => __val,
1005                    export::Err(__err) => {
1006                        return export::Err(__err);
1007                    }
1008                };
1009                match _serde::ser::SerializeStruct::serialize_field(
1010                    &mut __serde_state,
1011                    "___figment_relative_path",
1012                    &self.path,
1013                ) {
1014                    export::Ok(__val) => __val,
1015                    export::Err(__err) => {
1016                        return export::Err(__err);
1017                    }
1018                };
1019                _serde::ser::SerializeStruct::end(__serde_state)
1020            }
1021        }
1022    };
1023
1024    #[doc(hidden)]
1025    #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
1026    const _: () = {
1027        #[allow(rust_2018_idioms, clippy::useless_attribute)]
1028        extern crate serde as _serde;
1029        #[automatically_derived]
1030        impl<A, B> _serde::Serialize for Either<A, B>
1031        where
1032            A: _serde::Serialize,
1033            B: _serde::Serialize,
1034        {
1035            fn serialize<__S>(
1036                &self,
1037                __serializer: __S,
1038            ) -> export::Result<__S::Ok, __S::Error>
1039            where
1040                __S: _serde::Serializer,
1041            {
1042                match *self {
1043                    Either::Left(ref __field0) => {
1044                        _serde::Serialize::serialize(__field0, __serializer)
1045                    }
1046                    Either::Right(ref __field0) => {
1047                        _serde::Serialize::serialize(__field0, __serializer)
1048                    }
1049                }
1050            }
1051        }
1052    };
1053
1054    #[doc(hidden)]
1055    #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
1056    const _: () = {
1057        #[allow(rust_2018_idioms, clippy::useless_attribute)]
1058        extern crate serde as _serde;
1059        #[automatically_derived]
1060        impl<'de, T> _serde::Deserialize<'de> for Tagged<T>
1061        where
1062            T: _serde::Deserialize<'de>,
1063        {
1064            fn deserialize<__D>(__deserializer: __D) -> export::Result<Self, __D::Error>
1065            where
1066                __D: _serde::Deserializer<'de>,
1067            {
1068                #[allow(non_camel_case_types)]
1069                enum __Field {
1070                    __field0,
1071                    __field1,
1072                    __ignore,
1073                }
1074                struct __FieldVisitor;
1075                impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
1076                    type Value = __Field;
1077                    fn expecting(
1078                        &self,
1079                        __formatter: &mut export::Formatter,
1080                    ) -> export::fmt::Result {
1081                        export::Formatter::write_str(__formatter, "field identifier")
1082                    }
1083                    fn visit_u64<__E>(
1084                        self,
1085                        __value: u64,
1086                    ) -> export::Result<Self::Value, __E>
1087                    where
1088                        __E: _serde::de::Error,
1089                    {
1090                        match __value {
1091                            0u64 => export::Ok(__Field::__field0),
1092                            1u64 => export::Ok(__Field::__field1),
1093                            _ => export::Err(_serde::de::Error::invalid_value(
1094                                _serde::de::Unexpected::Unsigned(__value),
1095                                &"field index 0 <= i < 2",
1096                            )),
1097                        }
1098                    }
1099                    fn visit_str<__E>(
1100                        self,
1101                        __value: &str,
1102                    ) -> export::Result<Self::Value, __E>
1103                    where
1104                        __E: _serde::de::Error,
1105                    {
1106                        match __value {
1107                            "___figment_tagged_tag" => export::Ok(__Field::__field0),
1108                            "___figment_tagged_value" => export::Ok(__Field::__field1),
1109                            _ => export::Ok(__Field::__ignore),
1110                        }
1111                    }
1112                    fn visit_bytes<__E>(
1113                        self,
1114                        __value: &[u8],
1115                    ) -> export::Result<Self::Value, __E>
1116                    where
1117                        __E: _serde::de::Error,
1118                    {
1119                        match __value {
1120                            b"___figment_tagged_tag" => export::Ok(__Field::__field0),
1121                            b"___figment_tagged_value" => export::Ok(__Field::__field1),
1122                            _ => export::Ok(__Field::__ignore),
1123                        }
1124                    }
1125                }
1126                impl<'de> _serde::Deserialize<'de> for __Field {
1127                    #[inline]
1128                    fn deserialize<__D>(
1129                        __deserializer: __D,
1130                    ) -> export::Result<Self, __D::Error>
1131                    where
1132                        __D: _serde::Deserializer<'de>,
1133                    {
1134                        _serde::Deserializer::deserialize_identifier(
1135                            __deserializer,
1136                            __FieldVisitor,
1137                        )
1138                    }
1139                }
1140                struct __Visitor<'de, T>
1141                where
1142                    T: _serde::Deserialize<'de>,
1143                {
1144                    marker: export::PhantomData<Tagged<T>>,
1145                    lifetime: export::PhantomData<&'de ()>,
1146                }
1147                impl<'de, T> _serde::de::Visitor<'de> for __Visitor<'de, T>
1148                where
1149                    T: _serde::Deserialize<'de>,
1150                {
1151                    type Value = Tagged<T>;
1152                    fn expecting(
1153                        &self,
1154                        __formatter: &mut export::Formatter,
1155                    ) -> export::fmt::Result {
1156                        export::Formatter::write_str(__formatter, "struct Tagged")
1157                    }
1158                    #[inline]
1159                    fn visit_seq<__A>(
1160                        self,
1161                        mut __seq: __A,
1162                    ) -> export::Result<Self::Value, __A::Error>
1163                    where
1164                        __A: _serde::de::SeqAccess<'de>,
1165                    {
1166                        let __field0 = match match _serde::de::SeqAccess::next_element::<Tag>(
1167                            &mut __seq,
1168                        ) {
1169                            export::Ok(__val) => __val,
1170                            export::Err(__err) => {
1171                                return export::Err(__err);
1172                            }
1173                        } {
1174                            export::Some(__value) => __value,
1175                            export::None => {
1176                                return export::Err(_serde::de::Error::invalid_length(
1177                                    0usize,
1178                                    &"struct Tagged with 2 elements",
1179                                ));
1180                            }
1181                        };
1182                        let __field1 =
1183                            match match _serde::de::SeqAccess::next_element::<T>(&mut __seq) {
1184                                export::Ok(__val) => __val,
1185                                export::Err(__err) => {
1186                                    return export::Err(__err);
1187                                }
1188                            } {
1189                                export::Some(__value) => __value,
1190                                export::None => {
1191                                    return export::Err(
1192                                        _serde::de::Error::invalid_length(
1193                                            1usize,
1194                                            &"struct Tagged with 2 elements",
1195                                        ),
1196                                    );
1197                                }
1198                            };
1199                        export::Ok(Tagged {
1200                            tag: __field0,
1201                            value: __field1,
1202                        })
1203                    }
1204                    #[inline]
1205                    fn visit_map<__A>(
1206                        self,
1207                        mut __map: __A,
1208                    ) -> export::Result<Self::Value, __A::Error>
1209                    where
1210                        __A: _serde::de::MapAccess<'de>,
1211                    {
1212                        let mut __field0: export::Option<Tag> = export::None;
1213                        let mut __field1: export::Option<T> = export::None;
1214                        while let export::Some(__key) =
1215                            match _serde::de::MapAccess::next_key::<__Field>(&mut __map) {
1216                                export::Ok(__val) => __val,
1217                                export::Err(__err) => {
1218                                    return export::Err(__err);
1219                                }
1220                            }
1221                        {
1222                            match __key {
1223                                __Field::__field0 => {
1224                                    if export::Option::is_some(&__field0) {
1225                                        return export::Err(
1226                                            <__A::Error as _serde::de::Error>::duplicate_field(
1227                                                "___figment_tagged_tag",
1228                                            ),
1229                                        );
1230                                    }
1231                                    __field0 = export::Some(
1232                                        match _serde::de::MapAccess::next_value::<Tag>(
1233                                            &mut __map,
1234                                        ) {
1235                                            export::Ok(__val) => __val,
1236                                            export::Err(__err) => {
1237                                                return export::Err(__err);
1238                                            }
1239                                        },
1240                                    );
1241                                }
1242                                __Field::__field1 => {
1243                                    if export::Option::is_some(&__field1) {
1244                                        return export::Err(
1245                                            <__A::Error as _serde::de::Error>::duplicate_field(
1246                                                "___figment_tagged_value",
1247                                            ),
1248                                        );
1249                                    }
1250                                    __field1 = export::Some(
1251                                        match _serde::de::MapAccess::next_value::<T>(&mut __map)
1252                                        {
1253                                            export::Ok(__val) => __val,
1254                                            export::Err(__err) => {
1255                                                return export::Err(__err);
1256                                            }
1257                                        },
1258                                    );
1259                                }
1260                                _ => {
1261                                    let _ = match _serde::de::MapAccess::next_value::<
1262                                        _serde::de::IgnoredAny,
1263                                    >(
1264                                        &mut __map
1265                                    ) {
1266                                        export::Ok(__val) => __val,
1267                                        export::Err(__err) => {
1268                                            return export::Err(__err);
1269                                        }
1270                                    };
1271                                }
1272                            }
1273                        }
1274                        let __field0 = match __field0 {
1275                            export::Some(__field0) => __field0,
1276                            export::None => match export::missing_field(
1277                                "___figment_tagged_tag",
1278                            ) {
1279                                export::Ok(__val) => __val,
1280                                export::Err(__err) => {
1281                                    return export::Err(__err);
1282                                }
1283                            },
1284                        };
1285                        let __field1 = match __field1 {
1286                            export::Some(__field1) => __field1,
1287                            export::None => match export::missing_field(
1288                                "___figment_tagged_value",
1289                            ) {
1290                                export::Ok(__val) => __val,
1291                                export::Err(__err) => {
1292                                    return export::Err(__err);
1293                                }
1294                            },
1295                        };
1296                        export::Ok(Tagged {
1297                            tag: __field0,
1298                            value: __field1,
1299                        })
1300                    }
1301                }
1302                const FIELDS: &[&str] =
1303                    &["___figment_tagged_tag", "___figment_tagged_value"];
1304                _serde::Deserializer::deserialize_struct(
1305                    __deserializer,
1306                    "___figment_tagged_item",
1307                    FIELDS,
1308                    __Visitor {
1309                        marker: export::PhantomData::<Tagged<T>>,
1310                        lifetime: export::PhantomData,
1311                    },
1312                )
1313            }
1314        }
1315    };
1316
1317    #[doc(hidden)]
1318    #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
1319    const _: () = {
1320        #[allow(rust_2018_idioms, clippy::useless_attribute)]
1321        extern crate serde as _serde;
1322        #[automatically_derived]
1323        impl<T> _serde::Serialize for Tagged<T>
1324        where
1325            T: _serde::Serialize,
1326        {
1327            fn serialize<__S>(
1328                &self,
1329                __serializer: __S,
1330            ) -> export::Result<__S::Ok, __S::Error>
1331            where
1332                __S: _serde::Serializer,
1333            {
1334                let mut __serde_state = match _serde::Serializer::serialize_struct(
1335                    __serializer,
1336                    "___figment_tagged_item",
1337                    false as usize + 1 + 1,
1338                ) {
1339                    export::Ok(__val) => __val,
1340                    export::Err(__err) => {
1341                        return export::Err(__err);
1342                    }
1343                };
1344                match _serde::ser::SerializeStruct::serialize_field(
1345                    &mut __serde_state,
1346                    "___figment_tagged_tag",
1347                    &self.tag,
1348                ) {
1349                    export::Ok(__val) => __val,
1350                    export::Err(__err) => {
1351                        return export::Err(__err);
1352                    }
1353                };
1354                match _serde::ser::SerializeStruct::serialize_field(
1355                    &mut __serde_state,
1356                    "___figment_tagged_value",
1357                    &self.value,
1358                ) {
1359                    export::Ok(__val) => __val,
1360                    export::Err(__err) => {
1361                        return export::Err(__err);
1362                    }
1363                };
1364                _serde::ser::SerializeStruct::end(__serde_state)
1365            }
1366        }
1367    };
1368}
1369
1370#[cfg(test)]
1371mod tests {
1372    use crate::Figment;
1373
1374    #[test]
1375    fn test_relative_path_buf() {
1376        use super::RelativePathBuf;
1377        use crate::providers::{Format, Toml};
1378        use std::path::Path;
1379
1380        crate::Jail::expect_with(|jail| {
1381            jail.set_env("foo", "bar");
1382            jail.create_file("Config.toml", r###"
1383                [debug]
1384                file_path = "hello.js"
1385                another = "whoops/hi/there"
1386                absolute = "/tmp/foo"
1387            "###)?;
1388
1389            let path: RelativePathBuf = Figment::new()
1390                .merge(Toml::file("Config.toml").nested())
1391                .select("debug")
1392                .extract_inner("file_path")?;
1393
1394            assert_eq!(path.original(), Path::new("hello.js"));
1395            assert_eq!(path.metadata_path().unwrap(), jail.directory().join("Config.toml"));
1396            assert_eq!(path.relative(), jail.directory().join("hello.js"));
1397
1398            let path: RelativePathBuf = Figment::new()
1399                .merge(Toml::file("Config.toml").nested())
1400                .select("debug")
1401                .extract_inner("another")?;
1402
1403            assert_eq!(path.original(), Path::new("whoops/hi/there"));
1404            assert_eq!(path.metadata_path().unwrap(), jail.directory().join("Config.toml"));
1405            assert_eq!(path.relative(), jail.directory().join("whoops/hi/there"));
1406
1407            let path: RelativePathBuf = Figment::new()
1408                .merge(Toml::file("Config.toml").nested())
1409                .select("debug")
1410                .extract_inner("absolute")?;
1411
1412            assert_eq!(path.original(), Path::new("/tmp/foo"));
1413            assert_eq!(path.metadata_path().unwrap(), jail.directory().join("Config.toml"));
1414            assert_eq!(path.relative(), Path::new("/tmp/foo"));
1415
1416            jail.create_file("Config.toml", r###"
1417                [debug.inner.container]
1418                inside = "inside_path/a.html"
1419            "###)?;
1420
1421            #[derive(serde::Deserialize)]
1422            struct Testing { inner: Container, }
1423
1424            #[derive(serde::Deserialize)]
1425            struct Container { container: Inside, }
1426
1427            #[derive(serde::Deserialize)]
1428            struct Inside { inside: RelativePathBuf, }
1429
1430            let testing: Testing = Figment::new()
1431                .merge(Toml::file("Config.toml").nested())
1432                .select("debug")
1433                .extract()?;
1434
1435            let path = testing.inner.container.inside;
1436            assert_eq!(path.original(), Path::new("inside_path/a.html"));
1437            assert_eq!(path.metadata_path().unwrap(), jail.directory().join("Config.toml"));
1438            assert_eq!(path.relative(), jail.directory().join("inside_path/a.html"));
1439
1440            Ok(())
1441        })
1442    }
1443
1444    // #[test]
1445    // fn test_selected_profile() {
1446    //     use super::SelectedProfile;
1447    //
1448    //     let profile: SelectedProfile = Figment::new().extract().unwrap();
1449    //     assert_eq!(&*profile, crate::Profile::default());
1450    //
1451    //     let profile: SelectedProfile = Figment::new().select("foo").extract().unwrap();
1452    //     assert_eq!(&*profile, "foo");
1453    //
1454    //     let profile: SelectedProfile = Figment::new().select("bar").extract().unwrap();
1455    //     assert_eq!(&*profile, "bar");
1456    //
1457    //     #[derive(serde::Deserialize)]
1458    //     struct Testing {
1459    //         #[serde(alias = "other")]
1460    //         profile: SelectedProfile,
1461    //         value: usize
1462    //     }
1463    //
1464    //     let testing: Testing = Figment::from(("value", 123))
1465    //         .merge(("other", "hi"))
1466    //         .select("with-value").extract().unwrap();
1467    //
1468    //     assert_eq!(&*testing.profile, "with-value");
1469    //     assert_eq!(testing.value, 123);
1470    // }
1471
1472    // #[test]
1473    // fn test_selected_profile_kink() {
1474    //     use super::SelectedProfile;
1475    //
1476    //     #[derive(serde::Deserialize)]
1477    //     struct Base {
1478    //         profile: SelectedProfile,
1479    //     }
1480    //
1481    //     #[derive(serde::Deserialize)]
1482    //     struct Testing {
1483    //         base: Base,
1484    //         value: usize
1485    //     }
1486    //
1487    //     let testing: Testing = Figment::from(("value", 123)).extract().unwrap();
1488    //
1489    //     assert_eq!(&*testing.base.profile, "with-value");
1490    //     assert_eq!(testing.value, 123);
1491    // }
1492
1493    #[test]
1494    fn test_tagged() {
1495        use super::Tagged;
1496
1497        let val = Figment::from(("foo", "hello"))
1498            .extract_inner::<Tagged<String>>("foo")
1499            .expect("extraction");
1500
1501        let first_tag = val.tag();
1502        assert_eq!(val.value, "hello");
1503
1504        let val = Figment::from(("bar", "hi"))
1505            .extract_inner::<Tagged<String>>("bar")
1506            .expect("extraction");
1507
1508        let second_tag = val.tag();
1509        assert_eq!(val.value, "hi");
1510        assert!(second_tag != first_tag);
1511
1512        #[derive(serde::Deserialize)]
1513        struct TwoVals {
1514            foo: Tagged<String>,
1515            bar: Tagged<u16>,
1516        }
1517
1518        let two = Figment::new()
1519            .merge(("foo", "hey"))
1520            .merge(("bar", 10))
1521            .extract::<TwoVals>()
1522            .expect("extraction");
1523
1524        let tag3 = two.foo.tag();
1525        assert_eq!(two.foo.value, "hey");
1526        assert!(tag3 != second_tag);
1527
1528        let tag4 = two.bar.tag();
1529        assert_eq!(two.bar.value, 10);
1530        assert!(tag4 != tag3);
1531
1532        let val = Figment::new()
1533            .merge(("foo", "hey"))
1534            .merge(("bar", 10))
1535            .extract::<Tagged<TwoVals>>()
1536            .expect("extraction");
1537
1538        assert!(val.tag().is_default());
1539
1540        let tag5 = val.value.foo.tag();
1541        assert_eq!(val.value.foo.value, "hey");
1542        assert!(tag4 != tag5);
1543
1544        let tag6 = val.value.bar.tag();
1545        assert_eq!(val.value.bar.value, 10);
1546        assert!(tag6 != tag5)
1547    }
1548}