-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Valueless keys #12
base: master
Are you sure you want to change the base?
Valueless keys #12
Changes from 2 commits
f43de64
b3cae95
fe62450
269d68d
ddc591d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -95,7 +95,7 @@ impl<'de> Deserialize<'de> for ConvertToString { | |
/// | ||
/// ```text | ||
/// struct Example { | ||
/// #[serde(deserialize_with = "deserialize_hash_or_key_value_list")] | ||
/// #[serde(deserialize_with = "deserialize_map_or_key_value_list")] | ||
/// pub args: BTreeMap<String, RawOr<String>>, | ||
/// } | ||
/// ``` | ||
|
@@ -168,6 +168,82 @@ where | |
deserializer.deserialize_map(MapOrKeyValueListVisitor) | ||
} | ||
|
||
/// Like `deserialize_map_or_key_value_list`, but allowing missing values | ||
/// (e.g. for environment variables) | ||
pub fn deserialize_map_or_key_optional_value_list<'de, D>( | ||
deserializer: D, | ||
) -> Result<BTreeMap<String, Option<RawOr<String>>>, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
{ | ||
/// Declare an internal visitor type to handle our input. | ||
struct MapOrKeyOptionalValueListVisitor; | ||
|
||
impl<'de> Visitor<'de> for MapOrKeyOptionalValueListVisitor { | ||
type Value = BTreeMap<String, Option<RawOr<String>>>; | ||
|
||
// We have a real map. | ||
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error> | ||
where | ||
V: MapAccess<'de>, | ||
{ | ||
let mut map: BTreeMap<String, Option<RawOr<String>>> = BTreeMap::new(); | ||
while let Some(key) = visitor.next_key::<String>()? { | ||
if map.contains_key(&key) { | ||
let msg = format!("duplicate map key: {}", &key); | ||
return Err(<V::Error as de::Error>::custom(msg)); | ||
} | ||
let ConvertToString(val) = visitor.next_value::<ConvertToString>()?; | ||
let raw_or_value = raw(val) | ||
.map_err(|e| <V::Error as de::Error>::custom(format!("{}", e)))?; | ||
map.insert(key, Some(raw_or_value)); | ||
} | ||
Ok(map) | ||
} | ||
|
||
// We have a key/value list. Yuck. | ||
fn visit_seq<V>(self, mut visitor: V) -> Result<Self::Value, V::Error> | ||
where | ||
V: SeqAccess<'de>, | ||
{ | ||
lazy_static! { | ||
// Match a key/value pair. | ||
static ref KEY_VALUE: Regex = | ||
Regex::new("^([^=]+)(=(.*))?$").unwrap(); | ||
} | ||
|
||
let mut map: BTreeMap<String, Option<RawOr<String>>> = BTreeMap::new(); | ||
while let Some(key_value) = visitor.next_element::<String>()? { | ||
let caps = KEY_VALUE.captures(&key_value).ok_or_else(|| { | ||
let msg = format!("expected KEY[=value], got: <{}>", &key_value); | ||
<V::Error as de::Error>::custom(msg) | ||
})?; | ||
let key = caps.get(1).unwrap().as_str(); | ||
let value = caps.get(3).map(|v| v.as_str()); | ||
if map.contains_key(key) { | ||
let msg = format!("duplicate map key: {}", key); | ||
return Err(<V::Error as de::Error>::custom(msg)); | ||
} | ||
let optional_raw_or_value = match value.map(|value| { | ||
raw(value.to_owned()) | ||
.map_err(|e| <V::Error as de::Error>::custom(format!("{}", e))) | ||
}) { | ||
None => None, | ||
Some(x) => Some(x?), | ||
}; | ||
map.insert(key.to_owned(), optional_raw_or_value); | ||
} | ||
Ok(map) | ||
} | ||
|
||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | ||
write!(formatter, "a map or a key/[value] list") | ||
} | ||
} | ||
|
||
deserializer.deserialize_map(MapOrKeyOptionalValueListVisitor) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just last night, I discovered in serde-rs/serde#1125 (comment) that there has been a breaking change to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I fixed this, and in the other obvious places too. |
||
} | ||
|
||
/// Given a map, deserialize it normally. But if we have a list of string | ||
/// values, deserialize it as a map keyed with those strings, and with | ||
/// `Default::default()` used as the value. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -72,8 +72,8 @@ pub struct Service { | |
|
||
/// Environment variables and values to supply to the container. | ||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty", | ||
deserialize_with = "deserialize_map_or_key_value_list")] | ||
pub environment: BTreeMap<String, RawOr<String>>, | ||
deserialize_with = "deserialize_map_or_key_optional_value_list")] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have we looked at all the other callers of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tested all of them with This raises some mildly interesting questions in cage, such as what to do if |
||
pub environment: BTreeMap<String, Option<RawOr<String>>>, | ||
|
||
/// Expose a list of ports to any containers that link to us. | ||
#[serde(default, skip_serializing_if = "Vec::is_empty")] | ||
|
@@ -305,7 +305,9 @@ impl Service { | |
let mut new_env = BTreeMap::new(); | ||
for rel_path in &self.env_files { | ||
let env_file = EnvFile::load(&base.join(&rel_path.value()?))?; | ||
new_env.append(&mut env_file.to_environment()?); | ||
new_env.extend(env_file.to_environment()? | ||
.into_iter() | ||
.map(|(k, v)| (k, Some(v)))); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If environments now have type There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Turns out env_files also accept valueless keys, so we should just handle them there and produce the same |
||
} | ||
new_env.append(&mut self.environment.clone()); | ||
self.environment = new_env; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!