use std::ops::Deref;
use std::path::{PathBuf, Path};
use serde::{Deserialize, de};
use crate::{Error, value::{ConfiguredValueDe, MapDe, Tag}};
pub trait Magic: for<'de> Deserialize<'de> {
#[doc(hidden)] const NAME: &'static str;
#[doc(hidden)] const FIELDS: &'static [&'static str];
#[doc(hidden)] fn deserialize_from<'de: 'c, 'c, V: de::Visitor<'de>>(
de: ConfiguredValueDe<'c>,
visitor: V
) -> Result<V::Value, Error>;
}
#[derive(Debug, Clone)]
pub struct RelativePathBuf {
metadata_path: Option<PathBuf>,
path: PathBuf,
}
impl PartialEq for RelativePathBuf {
fn eq(&self, other: &Self) -> bool {
self.relative() == other.relative()
}
}
impl<P: AsRef<Path>> From<P> for RelativePathBuf {
fn from(path: P) -> RelativePathBuf {
Self { metadata_path: None, path: path.as_ref().into() }
}
}
impl Magic for RelativePathBuf {
const NAME: &'static str = "___figment_relative_path_buf";
const FIELDS: &'static [&'static str] = &[
"___figment_relative_metadata_path",
"___figment_relative_path"
];
fn deserialize_from<'de: 'c, 'c, V: de::Visitor<'de>>(
de: ConfiguredValueDe<'c>,
visitor: V
) -> Result<V::Value, Error> {
let config = de.config;
let metadata_path = config.get_metadata(de.value.tag())
.and_then(|metadata| metadata.source.as_ref()
.and_then(|s| s.file_path())
.map(|path| path.display().to_string()));
let mut map = crate::value::Map::new();
if let Some(path) = metadata_path {
map.insert(Self::FIELDS[0].into(), path.into());
}
map.insert(Self::FIELDS[1].into(), de.value.clone());
visitor.visit_map(MapDe::new(&map, |v| ConfiguredValueDe::from(config, v)))
}
}
impl RelativePathBuf {
pub fn original(&self) -> &Path {
&self.path
}
pub fn relative(&self) -> PathBuf {
if self.original().has_root() {
return self.original().into();
}
self.metadata_path()
.and_then(|root| match root.is_dir() {
true => Some(root),
false => root.parent(),
})
.map(|root| root.join(self.original()))
.unwrap_or_else(|| self.original().into())
}
pub fn metadata_path(&self) -> Option<&Path> {
self.metadata_path.as_ref().map(|p| p.as_ref())
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Either<A, B> {
Left(A),
Right(B),
}
impl<'de: 'b, 'b, A, B> Deserialize<'de> for Either<A, B>
where A: Magic, B: Deserialize<'b>
{
fn deserialize<D>(de: D) -> Result<Self, D::Error>
where D: de::Deserializer<'de>
{
use crate::value::ValueVisitor;
let value = de.deserialize_struct(A::NAME, A::FIELDS, ValueVisitor)?;
match A::deserialize(&value) {
Ok(value) => Ok(Either::Left(value)),
Err(a_err) => {
let value = value.as_dict()
.and_then(|d| d.get(A::FIELDS[A::FIELDS.len() - 1]))
.unwrap_or(&value);
match B::deserialize(value) {
Ok(value) => Ok(Either::Right(value)),
Err(b_err) => Err(de::Error::custom(format!("{}; {}", a_err, b_err)))
}
}
}
}
}
#[derive(Debug, Clone)]
pub struct Tagged<T> {
tag: Tag,
value: T,
}
impl<T: PartialEq> PartialEq for Tagged<T> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<T: for<'de> Deserialize<'de>> Magic for Tagged<T> {
const NAME: &'static str = "___figment_tagged_item";
const FIELDS: &'static [&'static str] = &[
"___figment_tagged_tag" , "___figment_tagged_value"
];
fn deserialize_from<'de: 'c, 'c, V: de::Visitor<'de>>(
de: ConfiguredValueDe<'c>,
visitor: V
) -> Result<V::Value, Error>{
let mut map = crate::value::Map::new();
map.insert(Self::FIELDS[0].into(), de.value.tag().into());
map.insert(Self::FIELDS[1].into(), de.value.clone());
visitor.visit_map(MapDe::new(&map, |v| ConfiguredValueDe::from(de.config, v)))
}
}
impl<T> Tagged<T> {
pub fn tag(&self) -> Tag {
self.tag
}
pub fn into_inner(self) -> T {
self.value
}
}
impl<T> Deref for Tagged<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T> From<T> for Tagged<T> {
fn from(value: T) -> Self {
Tagged { tag: Tag::Default, value, }
}
}
mod _serde {
use super::*;
#[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _: () = {
#[allow(rust_2018_idioms, clippy::useless_attribute)]
extern crate serde as _serde;
#[automatically_derived]
impl<'de> _serde::Deserialize<'de> for RelativePathBuf {
fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result<Self, __D::Error>
where
__D: _serde::Deserializer<'de>,
{
#[allow(non_camel_case_types)]
enum __Field {
__field0,
__field1,
__ignore,
}
struct __FieldVisitor;
impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
type Value = __Field;
fn expecting(
&self,
__formatter: &mut _serde::export::Formatter,
) -> _serde::export::fmt::Result {
_serde::export::Formatter::write_str(__formatter, "field identifier")
}
fn visit_u64<__E>(
self,
__value: u64,
) -> _serde::export::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
0u64 => _serde::export::Ok(__Field::__field0),
1u64 => _serde::export::Ok(__Field::__field1),
_ => _serde::export::Err(_serde::de::Error::invalid_value(
_serde::de::Unexpected::Unsigned(__value),
&"field index 0 <= i < 2",
)),
}
}
fn visit_str<__E>(
self,
__value: &str,
) -> _serde::export::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
"___figment_relative_metadata_path" => {
_serde::export::Ok(__Field::__field0)
}
"___figment_relative_path" => _serde::export::Ok(__Field::__field1),
_ => _serde::export::Ok(__Field::__ignore),
}
}
fn visit_bytes<__E>(
self,
__value: &[u8],
) -> _serde::export::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
b"___figment_relative_metadata_path" => {
_serde::export::Ok(__Field::__field0)
}
b"___figment_relative_path" => {
_serde::export::Ok(__Field::__field1)
}
_ => _serde::export::Ok(__Field::__ignore),
}
}
}
impl<'de> _serde::Deserialize<'de> for __Field {
#[inline]
fn deserialize<__D>(
__deserializer: __D,
) -> _serde::export::Result<Self, __D::Error>
where
__D: _serde::Deserializer<'de>,
{
_serde::Deserializer::deserialize_identifier(
__deserializer,
__FieldVisitor,
)
}
}
struct __Visitor<'de> {
marker: _serde::export::PhantomData<RelativePathBuf>,
lifetime: _serde::export::PhantomData<&'de ()>,
}
impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> {
type Value = RelativePathBuf;
fn expecting(
&self,
__formatter: &mut _serde::export::Formatter,
) -> _serde::export::fmt::Result {
_serde::export::Formatter::write_str(
__formatter,
"struct RelativePathBuf",
)
}
#[inline]
fn visit_seq<__A>(
self,
mut __seq: __A,
) -> _serde::export::Result<Self::Value, __A::Error>
where
__A: _serde::de::SeqAccess<'de>,
{
let __field0 = match match _serde::de::SeqAccess::next_element::<
Option<PathBuf>,
>(&mut __seq)
{
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
} {
_serde::export::Some(__value) => __value,
_serde::export::None => {
return _serde::export::Err(_serde::de::Error::invalid_length(
0usize,
&"struct RelativePathBuf with 2 elements",
));
}
};
let __field1 = match match _serde::de::SeqAccess::next_element::<PathBuf>(
&mut __seq,
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
} {
_serde::export::Some(__value) => __value,
_serde::export::None => {
return _serde::export::Err(_serde::de::Error::invalid_length(
1usize,
&"struct RelativePathBuf with 2 elements",
));
}
};
_serde::export::Ok(RelativePathBuf {
metadata_path: __field0,
path: __field1,
})
}
#[inline]
fn visit_map<__A>(
self,
mut __map: __A,
) -> _serde::export::Result<Self::Value, __A::Error>
where
__A: _serde::de::MapAccess<'de>,
{
let mut __field0: _serde::export::Option<Option<PathBuf>> =
_serde::export::None;
let mut __field1: _serde::export::Option<PathBuf> =
_serde::export::None;
while let _serde::export::Some(__key) =
match _serde::de::MapAccess::next_key::<__Field>(&mut __map) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
}
{
match __key {
__Field::__field0 => {
if _serde::export::Option::is_some(&__field0) {
return _serde::export::Err(
<__A::Error as _serde::de::Error>::duplicate_field(
"___figment_relative_metadata_path",
),
);
}
__field0 = _serde::export::Some(
match _serde::de::MapAccess::next_value::<Option<PathBuf>>(
&mut __map,
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
},
);
}
__Field::__field1 => {
if _serde::export::Option::is_some(&__field1) {
return _serde::export::Err(
<__A::Error as _serde::de::Error>::duplicate_field(
"___figment_relative_path",
),
);
}
__field1 = _serde::export::Some(
match _serde::de::MapAccess::next_value::<PathBuf>(
&mut __map,
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
},
);
}
_ => {
let _ = match _serde::de::MapAccess::next_value::<
_serde::de::IgnoredAny,
>(
&mut __map
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
};
}
}
}
let __field0 = match __field0 {
_serde::export::Some(__field0) => __field0,
_serde::export::None => match _serde::private::de::missing_field(
"___figment_relative_metadata_path",
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
},
};
let __field1 = match __field1 {
_serde::export::Some(__field1) => __field1,
_serde::export::None => match _serde::private::de::missing_field(
"___figment_relative_path",
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
},
};
_serde::export::Ok(RelativePathBuf {
metadata_path: __field0,
path: __field1,
})
}
}
const FIELDS: &'static [&'static str] = &[
"___figment_relative_metadata_path",
"___figment_relative_path",
];
_serde::Deserializer::deserialize_struct(
__deserializer,
"___figment_relative_path_buf",
FIELDS,
__Visitor {
marker: _serde::export::PhantomData::<RelativePathBuf>,
lifetime: _serde::export::PhantomData,
},
)
}
}
};
#[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _: () = {
#[allow(rust_2018_idioms, clippy::useless_attribute)]
extern crate serde as _serde;
#[automatically_derived]
impl _serde::Serialize for RelativePathBuf {
fn serialize<__S>(
&self,
__serializer: __S,
) -> _serde::export::Result<__S::Ok, __S::Error>
where
__S: _serde::Serializer,
{
let mut __serde_state = match _serde::Serializer::serialize_struct(
__serializer,
"___figment_relative_path_buf",
false as usize + 1 + 1,
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
};
match _serde::ser::SerializeStruct::serialize_field(
&mut __serde_state,
"___figment_relative_metadata_path",
&self.metadata_path,
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
};
match _serde::ser::SerializeStruct::serialize_field(
&mut __serde_state,
"___figment_relative_path",
&self.path,
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
};
_serde::ser::SerializeStruct::end(__serde_state)
}
}
};
#[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _: () = {
#[allow(rust_2018_idioms, clippy::useless_attribute)]
extern crate serde as _serde;
#[automatically_derived]
impl<A, B> _serde::Serialize for Either<A, B>
where
A: _serde::Serialize,
B: _serde::Serialize,
{
fn serialize<__S>(
&self,
__serializer: __S,
) -> _serde::export::Result<__S::Ok, __S::Error>
where
__S: _serde::Serializer,
{
match *self {
Either::Left(ref __field0) => {
_serde::Serializer::serialize_newtype_variant(
__serializer,
"Either",
0u32,
"Left",
__field0,
)
}
Either::Right(ref __field0) => {
_serde::Serializer::serialize_newtype_variant(
__serializer,
"Either",
1u32,
"Right",
__field0,
)
}
}
}
}
};
#[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _: () = {
#[allow(rust_2018_idioms, clippy::useless_attribute)]
extern crate serde as _serde;
#[automatically_derived]
impl<'de, T> _serde::Deserialize<'de> for Tagged<T>
where
T: _serde::Deserialize<'de>,
{
fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result<Self, __D::Error>
where
__D: _serde::Deserializer<'de>,
{
#[allow(non_camel_case_types)]
enum __Field {
__field0,
__field1,
__ignore,
}
struct __FieldVisitor;
impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
type Value = __Field;
fn expecting(
&self,
__formatter: &mut _serde::export::Formatter,
) -> _serde::export::fmt::Result {
_serde::export::Formatter::write_str(__formatter, "field identifier")
}
fn visit_u64<__E>(
self,
__value: u64,
) -> _serde::export::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
0u64 => _serde::export::Ok(__Field::__field0),
1u64 => _serde::export::Ok(__Field::__field1),
_ => _serde::export::Err(_serde::de::Error::invalid_value(
_serde::de::Unexpected::Unsigned(__value),
&"field index 0 <= i < 2",
)),
}
}
fn visit_str<__E>(
self,
__value: &str,
) -> _serde::export::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
"___figment_tagged_tag" => _serde::export::Ok(__Field::__field0),
"___figment_tagged_value" => _serde::export::Ok(__Field::__field1),
_ => _serde::export::Ok(__Field::__ignore),
}
}
fn visit_bytes<__E>(
self,
__value: &[u8],
) -> _serde::export::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
b"___figment_tagged_tag" => _serde::export::Ok(__Field::__field0),
b"___figment_tagged_value" => _serde::export::Ok(__Field::__field1),
_ => _serde::export::Ok(__Field::__ignore),
}
}
}
impl<'de> _serde::Deserialize<'de> for __Field {
#[inline]
fn deserialize<__D>(
__deserializer: __D,
) -> _serde::export::Result<Self, __D::Error>
where
__D: _serde::Deserializer<'de>,
{
_serde::Deserializer::deserialize_identifier(
__deserializer,
__FieldVisitor,
)
}
}
struct __Visitor<'de, T>
where
T: _serde::Deserialize<'de>,
{
marker: _serde::export::PhantomData<Tagged<T>>,
lifetime: _serde::export::PhantomData<&'de ()>,
}
impl<'de, T> _serde::de::Visitor<'de> for __Visitor<'de, T>
where
T: _serde::Deserialize<'de>,
{
type Value = Tagged<T>;
fn expecting(
&self,
__formatter: &mut _serde::export::Formatter,
) -> _serde::export::fmt::Result {
_serde::export::Formatter::write_str(__formatter, "struct Tagged")
}
#[inline]
fn visit_seq<__A>(
self,
mut __seq: __A,
) -> _serde::export::Result<Self::Value, __A::Error>
where
__A: _serde::de::SeqAccess<'de>,
{
let __field0 = match match _serde::de::SeqAccess::next_element::<Tag>(
&mut __seq,
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
} {
_serde::export::Some(__value) => __value,
_serde::export::None => {
return _serde::export::Err(_serde::de::Error::invalid_length(
0usize,
&"struct Tagged with 2 elements",
));
}
};
let __field1 =
match match _serde::de::SeqAccess::next_element::<T>(&mut __seq) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
} {
_serde::export::Some(__value) => __value,
_serde::export::None => {
return _serde::export::Err(
_serde::de::Error::invalid_length(
1usize,
&"struct Tagged with 2 elements",
),
);
}
};
_serde::export::Ok(Tagged {
tag: __field0,
value: __field1,
})
}
#[inline]
fn visit_map<__A>(
self,
mut __map: __A,
) -> _serde::export::Result<Self::Value, __A::Error>
where
__A: _serde::de::MapAccess<'de>,
{
let mut __field0: _serde::export::Option<Tag> = _serde::export::None;
let mut __field1: _serde::export::Option<T> = _serde::export::None;
while let _serde::export::Some(__key) =
match _serde::de::MapAccess::next_key::<__Field>(&mut __map) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
}
{
match __key {
__Field::__field0 => {
if _serde::export::Option::is_some(&__field0) {
return _serde::export::Err(
<__A::Error as _serde::de::Error>::duplicate_field(
"___figment_tagged_tag",
),
);
}
__field0 = _serde::export::Some(
match _serde::de::MapAccess::next_value::<Tag>(
&mut __map,
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
},
);
}
__Field::__field1 => {
if _serde::export::Option::is_some(&__field1) {
return _serde::export::Err(
<__A::Error as _serde::de::Error>::duplicate_field(
"___figment_tagged_value",
),
);
}
__field1 = _serde::export::Some(
match _serde::de::MapAccess::next_value::<T>(&mut __map)
{
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
},
);
}
_ => {
let _ = match _serde::de::MapAccess::next_value::<
_serde::de::IgnoredAny,
>(
&mut __map
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
};
}
}
}
let __field0 = match __field0 {
_serde::export::Some(__field0) => __field0,
_serde::export::None => match _serde::private::de::missing_field(
"___figment_tagged_tag",
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
},
};
let __field1 = match __field1 {
_serde::export::Some(__field1) => __field1,
_serde::export::None => match _serde::private::de::missing_field(
"___figment_tagged_value",
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
},
};
_serde::export::Ok(Tagged {
tag: __field0,
value: __field1,
})
}
}
const FIELDS: &'static [&'static str] =
&["___figment_tagged_tag", "___figment_tagged_value"];
_serde::Deserializer::deserialize_struct(
__deserializer,
"___figment_tagged_item",
FIELDS,
__Visitor {
marker: _serde::export::PhantomData::<Tagged<T>>,
lifetime: _serde::export::PhantomData,
},
)
}
}
};
#[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _: () = {
#[allow(rust_2018_idioms, clippy::useless_attribute)]
extern crate serde as _serde;
#[automatically_derived]
impl<T> _serde::Serialize for Tagged<T>
where
T: _serde::Serialize,
{
fn serialize<__S>(
&self,
__serializer: __S,
) -> _serde::export::Result<__S::Ok, __S::Error>
where
__S: _serde::Serializer,
{
let mut __serde_state = match _serde::Serializer::serialize_struct(
__serializer,
"___figment_tagged_item",
false as usize + 1 + 1,
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
};
match _serde::ser::SerializeStruct::serialize_field(
&mut __serde_state,
"___figment_tagged_tag",
&self.tag,
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
};
match _serde::ser::SerializeStruct::serialize_field(
&mut __serde_state,
"___figment_tagged_value",
&self.value,
) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
};
_serde::ser::SerializeStruct::end(__serde_state)
}
}
};
}
#[cfg(test)]
mod tests {
use crate::Figment;
#[test]
fn test_relative_path_buf() {
use super::RelativePathBuf;
use crate::providers::{Format, Toml};
use std::path::Path;
crate::Jail::expect_with(|jail| {
jail.set_env("foo", "bar");
jail.create_file("Config.toml", r###"
[debug]
file_path = "hello.js"
another = "whoops/hi/there"
absolute = "/tmp/foo"
"###)?;
let path: RelativePathBuf = Figment::new()
.merge(Toml::file("Config.toml").nested())
.select("debug")
.extract_inner("file_path")?;
assert_eq!(path.original(), Path::new("hello.js"));
assert_eq!(path.metadata_path().unwrap(), jail.directory().join("Config.toml"));
assert_eq!(path.relative(), jail.directory().join("hello.js"));
let path: RelativePathBuf = Figment::new()
.merge(Toml::file("Config.toml").nested())
.select("debug")
.extract_inner("another")?;
assert_eq!(path.original(), Path::new("whoops/hi/there"));
assert_eq!(path.metadata_path().unwrap(), jail.directory().join("Config.toml"));
assert_eq!(path.relative(), jail.directory().join("whoops/hi/there"));
let path: RelativePathBuf = Figment::new()
.merge(Toml::file("Config.toml").nested())
.select("debug")
.extract_inner("absolute")?;
assert_eq!(path.original(), Path::new("/tmp/foo"));
assert_eq!(path.metadata_path().unwrap(), jail.directory().join("Config.toml"));
assert_eq!(path.relative(), Path::new("/tmp/foo"));
jail.create_file("Config.toml", r###"
[debug.inner.container]
inside = "inside_path/a.html"
"###)?;
#[derive(serde::Deserialize)]
struct Testing { inner: Container, }
#[derive(serde::Deserialize)]
struct Container { container: Inside, }
#[derive(serde::Deserialize)]
struct Inside { inside: RelativePathBuf, }
let testing: Testing = Figment::new()
.merge(Toml::file("Config.toml").nested())
.select("debug")
.extract()?;
let path = testing.inner.container.inside;
assert_eq!(path.original(), Path::new("inside_path/a.html"));
assert_eq!(path.metadata_path().unwrap(), jail.directory().join("Config.toml"));
assert_eq!(path.relative(), jail.directory().join("inside_path/a.html"));
Ok(())
})
}
#[test]
fn test_tagged() {
use super::Tagged;
let val = Figment::from(("foo", "hello"))
.extract_inner::<Tagged<String>>("foo")
.expect("extraction");
let first_tag = val.tag();
assert_eq!(val.value, "hello");
let val = Figment::from(("bar", "hi"))
.extract_inner::<Tagged<String>>("bar")
.expect("extraction");
let second_tag = val.tag();
assert_eq!(val.value, "hi");
assert!(second_tag != first_tag);
#[derive(serde::Deserialize)]
struct TwoVals {
foo: Tagged<String>,
bar: Tagged<u16>,
}
let two = Figment::new()
.merge(("foo", "hey"))
.merge(("bar", 10))
.extract::<TwoVals>()
.expect("extraction");
let tag3 = two.foo.tag();
assert_eq!(two.foo.value, "hey");
assert!(tag3 != second_tag);
let tag4 = two.bar.tag();
assert_eq!(two.bar.value, 10);
assert!(tag4 != tag3);
let val = Figment::new()
.merge(("foo", "hey"))
.merge(("bar", 10))
.extract::<Tagged<TwoVals>>()
.expect("extraction");
assert!(val.tag().is_default());
let tag5 = val.value.foo.tag();
assert_eq!(val.value.foo.value, "hey");
assert!(tag4 != tag5);
let tag6 = val.value.bar.tag();
assert_eq!(val.value.bar.value, 10);
assert!(tag6 != tag5)
}
}