diff options
author | David Orchard <if_coding@fastmail.com> | 2021-07-28 22:52:15 -0700 |
---|---|---|
committer | David Orchard <if_coding@fastmail.com> | 2021-08-15 10:31:18 -0700 |
commit | fc8cf0ed674cbb495df1baff34d4b832ca5fad3c (patch) | |
tree | a83c82ff11399ebd2da8d7ae58da31b7f295998c | |
parent | 62c84297e5340d717d89c5c0b36ed86dd5ad9d2f (diff) |
Use LinkedHashMap in place of HashMap
39 files changed, 144 insertions, 137 deletions
@@ -35,6 +35,7 @@ serde-hjson = { version = "0.9", default-features = false, optional = true } rust-ini = { version = "0.17", optional = true } ron = { version = "0.6", optional = true } json5_rs = { version = "0.3", optional = true, package = "json5" } +linked-hash-map = { version = "0.5.4", features = ["serde_impl"] } [dev-dependencies] serde_derive = "1.0.8" diff --git a/examples/async_source/main.rs b/examples/async_source/main.rs index 10befe0..6c836a3 100644 --- a/examples/async_source/main.rs +++ b/examples/async_source/main.rs @@ -1,4 +1,5 @@ -use std::{collections::HashMap, error::Error}; +use linked_hash_map::LinkedHashMap; +use std::error::Error; use config::{builder::AsyncState, AsyncSource, ConfigBuilder, ConfigError, FileFormat}; @@ -56,7 +57,7 @@ struct HttpSource { #[async_trait] impl AsyncSource for HttpSource { - async fn collect(&self) -> Result<HashMap<String, config::Value>, ConfigError> { + async fn collect(&self) -> Result<LinkedHashMap<String, config::Value>, ConfigError> { reqwest::get(&self.uri) .await .map_err(|e| ConfigError::Foreign(Box::new(e)))? // error conversion is possible from custom AsyncSource impls diff --git a/examples/glob/src/main.rs b/examples/glob/src/main.rs index b3183ef..c85f86e 100644 --- a/examples/glob/src/main.rs +++ b/examples/glob/src/main.rs @@ -1,5 +1,5 @@ use std::path::Path; -use std::collections::HashMap; +use std::collections::LinkedHashMap; use config::*; use glob::glob; @@ -14,9 +14,9 @@ fn main() { .merge(File::from(Path::new("conf/05-some.yml"))).unwrap() .merge(File::from(Path::new("conf/99-extra.json"))).unwrap(); - // Print out our settings (as a HashMap) + // Print out our settings (as a LinkedHashMap) println!("\n{:?} \n\n-----------", - settings.try_into::<HashMap<String, String>>().unwrap()); + settings.try_into::<LinkedHashMap<String, String>>().unwrap()); // Option 2 // -------- @@ -28,9 +28,9 @@ fn main() { File::from(Path::new("conf/99-extra.json"))]) .unwrap(); - // Print out our settings (as a HashMap) + // Print out our settings (as a LinkedHashMap) println!("\n{:?} \n\n-----------", - settings.try_into::<HashMap<String, String>>().unwrap()); + settings.try_into::<LinkedHashMap<String, String>>().unwrap()); // Option 3 // -------- @@ -43,7 +43,7 @@ fn main() { .collect::<Vec<_>>()) .unwrap(); - // Print out our settings (as a HashMap) + // Print out our settings (as a LinkedHashMap) println!("\n{:?} \n\n-----------", - settings.try_into::<HashMap<String, String>>().unwrap()); + settings.try_into::<LinkedHashMap<String, String>>().unwrap()); } diff --git a/examples/simple/src/main.rs b/examples/simple/src/main.rs index 1c7ddb7..698401a 100644 --- a/examples/simple/src/main.rs +++ b/examples/simple/src/main.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::LinkedHashMap; fn main() { let mut settings = config::Config::default(); @@ -9,7 +9,7 @@ fn main() { // Eg.. `APP_DEBUG=1 ./target/app` would set the `debug` key .merge(config::Environment::with_prefix("APP")).unwrap(); - // Print out our settings (as a HashMap) + // Print out our settings (as a LinkedHashMap) println!("{:?}", - settings.try_into::<HashMap<String, String>>().unwrap()); + settings.try_into::<LinkedHashMap<String, String>>().unwrap()); } diff --git a/examples/watch/src/main.rs b/examples/watch/src/main.rs index a197390..9e676ad 100644 --- a/examples/watch/src/main.rs +++ b/examples/watch/src/main.rs @@ -1,5 +1,5 @@ use config::*; -use std::collections::HashMap; +use std::collections::LinkedHashMap; use std::sync::RwLock; use notify::{RecommendedWatcher, DebouncedEvent, Watcher, RecursiveMode}; use std::sync::mpsc::channel; @@ -20,7 +20,7 @@ fn show() { .read() .unwrap() .clone() - .try_into::<HashMap<String, String>>() + .try_into::<LinkedHashMap<String, String>>() .unwrap()); } diff --git a/src/builder.rs b/src/builder.rs index bb88f44..65f49ae 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1,5 +1,6 @@ +use linked_hash_map::LinkedHashMap; +use std::iter::IntoIterator; use std::str::FromStr; -use std::{collections::HashMap, iter::IntoIterator}; use crate::error::Result; use crate::source::AsyncSource; @@ -87,8 +88,8 @@ use crate::{config::Config, path::Expression, source::Source, value::Value}; /// ``` #[derive(Debug, Clone, Default)] pub struct ConfigBuilder<St: BuilderState> { - defaults: HashMap<Expression, Value>, - overrides: HashMap<Expression, Value>, + defaults: LinkedHashMap<Expression, Value>, + overrides: LinkedHashMap<Expression, Value>, state: St, } @@ -120,8 +121,8 @@ pub struct DefaultState { /// Refer to [`ConfigBuilder`] for similar API sample usage or to the examples folder of the crate, where such a source is implemented. #[derive(Debug, Clone, Default)] pub struct AsyncConfigBuilder { - defaults: HashMap<Expression, Value>, - overrides: HashMap<Expression, Value>, + defaults: LinkedHashMap<Expression, Value>, + overrides: LinkedHashMap<Expression, Value>, sources: Vec<SourceType>, } @@ -244,11 +245,11 @@ impl ConfigBuilder<DefaultState> { } fn build_internal( - defaults: HashMap<Expression, Value>, - overrides: HashMap<Expression, Value>, + defaults: LinkedHashMap<Expression, Value>, + overrides: LinkedHashMap<Expression, Value>, sources: &[Box<dyn Source + Send + Sync>], ) -> Result<Config> { - let mut cache: Value = HashMap::<String, Value>::new().into(); + let mut cache: Value = LinkedHashMap::<String, Value>::new().into(); // Add defaults for (key, val) in defaults.into_iter() { @@ -322,11 +323,11 @@ impl ConfigBuilder<AsyncState> { } async fn build_internal( - defaults: HashMap<Expression, Value>, - overrides: HashMap<Expression, Value>, + defaults: LinkedHashMap<Expression, Value>, + overrides: LinkedHashMap<Expression, Value>, sources: &[SourceType], ) -> Result<Config> { - let mut cache: Value = HashMap::<String, Value>::new().into(); + let mut cache: Value = LinkedHashMap::<String, Value>::new().into(); // Add defaults for (key, val) in defaults.into_iter() { diff --git a/src/config.rs b/src/config.rs index b9c64b4..6302c45 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use linked_hash_map::LinkedHashMap; use std::fmt::Debug; use crate::builder::{ConfigBuilder, DefaultState}; @@ -16,8 +16,8 @@ use crate::value::{Table, Value}; /// them according to the source's priority. #[derive(Clone, Debug)] pub struct Config { - defaults: HashMap<path::Expression, Value>, - overrides: HashMap<path::Expression, Value>, + defaults: LinkedHashMap<path::Expression, Value>, + overrides: LinkedHashMap<path::Expression, Value>, sources: Vec<Box<dyn Source + Send + Sync>>, /// Root of the cached configuration. @@ -83,7 +83,7 @@ impl Config { #[deprecated(since = "0.12.0", note = "please use 'ConfigBuilder' instead")] pub fn refresh(&mut self) -> Result<&mut Config> { self.cache = { - let mut cache: Value = HashMap::<String, Value>::new().into(); + let mut cache: Value = LinkedHashMap::<String, Value>::new().into(); // Add defaults for (key, val) in self.defaults.iter() { @@ -181,7 +181,7 @@ impl Config { self.get(key).and_then(Value::into_bool) } - pub fn get_table(&self, key: &str) -> Result<HashMap<String, Value>> { + pub fn get_table(&self, key: &str) -> Result<LinkedHashMap<String, Value>> { self.get(key).and_then(Value::into_table) } @@ -212,7 +212,7 @@ impl Source for Config { Box::new((*self).clone()) } - fn collect(&self) -> Result<HashMap<String, Value>> { + fn collect(&self) -> Result<LinkedHashMap<String, Value>> { self.cache.clone().into_table() } } @@ -1,4 +1,5 @@ -use std::collections::{HashMap, VecDeque}; +use linked_hash_map::LinkedHashMap; +use std::collections::VecDeque; use std::iter::Enumerate; use serde::de; @@ -199,7 +200,7 @@ struct MapAccess { } impl MapAccess { - fn new(table: HashMap<String, Value>) -> Self { + fn new(table: LinkedHashMap<String, Value>) -> Self { MapAccess { elements: table.into_iter().collect(), } @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use linked_hash_map::LinkedHashMap; use std::env; use crate::error::*; @@ -79,8 +79,8 @@ impl Source for Environment { Box::new((*self).clone()) } - fn collect(&self) -> Result<HashMap<String, Value>> { - let mut m = HashMap::new(); + fn collect(&self) -> Result<LinkedHashMap<String, Value>> { + let mut m = LinkedHashMap::new(); let uri: String = "the environment".into(); let separator = self.separator.as_deref().unwrap_or(""); diff --git a/src/file/format/hjson.rs b/src/file/format/hjson.rs index f94b1d3..a61afef 100644 --- a/src/file/format/hjson.rs +++ b/src/file/format/hjson.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use linked_hash_map::LinkedHashMap; use std::error::Error; use crate::value::{Value, ValueKind}; @@ -6,14 +6,14 @@ use crate::value::{Value, ValueKind}; pub fn parse( uri: Option<&String>, text: &str, -) -> Result<HashMap<String, Value>, Box<dyn Error + Send + Sync>> { +) -> Result<LinkedHashMap<String, Value>, Box<dyn Error + Send + Sync>> { // Parse a JSON object value from the text // TODO: Have a proper error fire if the root of a file is ever not a Table let value = from_hjson_value(uri, &serde_hjson::from_str(text)?); match value.kind { ValueKind::Table(map) => Ok(map), - _ => Ok(HashMap::new()), + _ => Ok(LinkedHashMap::new()), } } @@ -30,7 +30,7 @@ fn from_hjson_value(uri: Option<&String>, value: &serde_hjson::Value) -> Value { serde_hjson::Value::Bool(value) => Value::new(uri, ValueKind::Boolean(value)), serde_hjson::Value::Object(ref table) => { - let mut m = HashMap::new(); + let mut m = LinkedHashMap::new(); for (key, value) in table { m.insert(key.clone(), from_hjson_value(uri, value)); diff --git a/src/file/format/ini.rs b/src/file/format/ini.rs index b45695a..47f3499 100644 --- a/src/file/format/ini.rs +++ b/src/file/format/ini.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use linked_hash_map::LinkedHashMap; use std::error::Error; use ini::Ini; @@ -8,13 +8,13 @@ use crate::value::{Value, ValueKind}; pub fn parse( uri: Option<&String>, text: &str, -) -> Result<HashMap<String, Value>, Box<dyn Error + Send + Sync>> { - let mut map: HashMap<String, Value> = HashMap::new(); +) -> Result<LinkedHashMap<String, Value>, Box<dyn Error + Send + Sync>> { + let mut map: LinkedHashMap<String, Value> = LinkedHashMap::new(); let i = Ini::load_from_str(text)?; for (sec, prop) in i.iter() { match sec { Some(sec) => { - let mut sec_map: HashMap<String, Value> = HashMap::new(); + let mut sec_map: LinkedHashMap<String, Value> = LinkedHashMap::new(); for (k, v) in prop.iter() { sec_map.insert( k.to_owned(), diff --git a/src/file/format/json.rs b/src/file/format/json.rs index 87a6e61..a6a9443 100644 --- a/src/file/format/json.rs +++ b/src/file/format/json.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use linked_hash_map::LinkedHashMap; use std::error::Error; use crate::value::{Value, ValueKind}; @@ -6,14 +6,14 @@ use crate::value::{Value, ValueKind}; pub fn parse( uri: Option<&String>, text: &str, -) -> Result<HashMap<String, Value>, Box<dyn Error + Send + Sync>> { +) -> Result<LinkedHashMap<String, Value>, Box<dyn Error + Send + Sync>> { // Parse a JSON object value from the text // TODO: Have a proper error fire if the root of a file is ever not a Table let value = from_json_value(uri, &serde_json::from_str(text)?); match value.kind { ValueKind::Table(map) => Ok(map), - _ => Ok(HashMap::new()), + _ => Ok(LinkedHashMap::new()), } } @@ -34,7 +34,7 @@ fn from_json_value(uri: Option<&String>, value: &serde_json::Value) -> Value { serde_json::Value::Bool(value) => Value::new(uri, ValueKind::Boolean(value)), serde_json::Value::Object(ref table) => { - let mut m = HashMap::new(); + let mut m = LinkedHashMap::new(); for (key, value) in table { m.insert(key.clone(), from_json_value(uri, value)); diff --git a/src/file/format/json5.rs b/src/file/format/json5.rs index ea3390f..952b265 100644 --- a/src/file/format/json5.rs +++ b/src/file/format/json5.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use linked_hash_map::LinkedHashMap; use std::error::Error; use crate::error::{ConfigError, Unexpected}; @@ -13,13 +13,13 @@ pub enum Val { Float(f64), String(String), Array(Vec<Val>), - Object(HashMap<String, Val>), + Object(LinkedHashMap<String, Val>), } pub fn parse( uri: Option<&String>, text: &str, -) -> Result<HashMap<String, Value>, Box<dyn Error + Send + Sync>> { +) -> Result<LinkedHashMap<String, Value>, Box<dyn Error + Send + Sync>> { match json5_rs::from_str::<Val>(text)? { Val::String(ref value) => Err(Unexpected::Str(value.clone())), Val::Integer(value) => Err(Unexpected::Integer(value)), @@ -29,7 +29,7 @@ pub fn parse( Val::Null => Err(Unexpected::Unit), Val::Object(o) => match from_json5_value(uri, Val::Object(o)).kind { ValueKind::Table(map) => Ok(map), - _ => Ok(HashMap::new()), + _ => Ok(LinkedHashMap::new()), }, } .map_err(|err| ConfigError::invalid_root(uri, err)) diff --git a/src/file/format/mod.rs b/src/file/format/mod.rs index 53bacf6..55d1c7a 100644 --- a/src/file/format/mod.rs +++ b/src/file/format/mod.rs @@ -2,7 +2,7 @@ // BUG: ? For some reason this doesn't do anything if I try and function scope this #![allow(unused_mut)] -use std::collections::HashMap; +use linked_hash_map::LinkedHashMap; use std::error::Error; use crate::value::Value; @@ -62,8 +62,8 @@ pub enum FileFormat { lazy_static! { #[doc(hidden)] // #[allow(unused_mut)] ? - pub static ref ALL_EXTENSIONS: HashMap<FileFormat, Vec<&'static str>> = { - let mut formats: HashMap<FileFormat, Vec<_>> = HashMap::new(); + pub static ref ALL_EXTENSIONS: LinkedHashMap<FileFormat, Vec<&'static str>> = { + let mut formats: LinkedHashMap<FileFormat, Vec<_>> = LinkedHashMap::new(); #[cfg(feature = "toml")] formats.insert(FileFormat::Toml, vec!["toml"]); @@ -107,7 +107,7 @@ impl FileFormat { self, uri: Option<&String>, text: &str, - ) -> Result<HashMap<String, Value>, Box<dyn Error + Send + Sync>> { + ) -> Result<LinkedHashMap<String, Value>, Box<dyn Error + Send + Sync>> { match self { #[cfg(feature = "toml")] FileFormat::Toml => toml::parse(uri, text), diff --git a/src/file/format/ron.rs b/src/file/format/ron.rs index f7dbce3..a527d6f 100644 --- a/src/file/format/ron.rs +++ b/src/file/format/ron.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use linked_hash_map::LinkedHashMap; use std::error::Error; use crate::value::{Value, ValueKind}; @@ -6,12 +6,12 @@ use crate::value::{Value, ValueKind}; pub fn parse( uri: Option<&String>, text: &str, -) -> Result<HashMap<String, Value>, Box<dyn Error + Send + Sync>> { +) -> Result<LinkedHashMap<String, Value>, Box<dyn Error + Send + Sync>> { let value = from_ron_value(uri, ron::from_str(text)?)?; match value.kind { ValueKind::Table(map) => Ok(map), - _ => Ok(HashMap::new()), + _ => Ok(LinkedHashMap::new()), } } @@ -56,7 +56,7 @@ fn from_ron_value( Ok((key, value)) }) - .collect::<Result<HashMap<_, _>, _>>()?; + .collect::<Result<LinkedHashMap<_, _>, _>>()?; ValueKind::Table(map) } diff --git a/src/file/format/toml.rs b/src/file/format/toml.rs index c7c5972..5468d97 100644 --- a/src/file/format/toml.rs +++ b/src/file/format/toml.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use linked_hash_map::LinkedHashMap; use std::error::Error; use crate::value::{Value, ValueKind}; @@ -6,14 +6,14 @@ use crate::value::{Value, ValueKind}; pub fn parse( uri: Option<&String>, text: &str, -) -> Result<HashMap<String, Value>, Box<dyn Error + Send + Sync>> { +) -> Result<LinkedHashMap<String, Value>, Box<dyn Error + Send + Sync>> { // Parse a TOML value from the provided text // TODO: Have a proper error fire if the root of a file is ever not a Table let value = from_toml_value(uri, &toml::from_str(text)?); match value.kind { ValueKind::Table(map) => Ok(map), - _ => Ok(HashMap::new()), + _ => Ok(LinkedHashMap::new()), } } @@ -25,7 +25,7 @@ fn from_toml_value(uri: Option<&String>, value: &toml::Value) -> Value { toml::Value::Boolean(value) => Value::new(uri, value), toml::Value::Table(ref table) => { - let mut m = HashMap::new(); + let mut m = LinkedHashMap::new(); for (key, value) in table { m.insert(key.clone(), from_toml_value(uri, value)); diff --git a/src/file/format/yaml.rs b/src/file/format/yaml.rs index 2526395..246758a 100644 --- a/src/file/format/yaml.rs +++ b/src/file/format/yaml.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use linked_hash_map::LinkedHashMap; use std::error::Error; use std::fmt; use std::mem; @@ -10,7 +10,7 @@ use crate::value::{Value, ValueKind}; pub fn parse( uri: Option<&String>, text: &str, -) -> Result<HashMap<String, Value>, Box<dyn Error + Send + Sync>> { +) -> Result<LinkedHashMap<String, Value>, Box<dyn Error + Send + Sync>> { // Parse a YAML object from file let mut docs = yaml::YamlLoader::load_from_str(text)?; let root = match docs.len() { @@ -26,7 +26,7 @@ pub fn parse( match value.kind { ValueKind::Table(map) => Ok(map), - _ => Ok(HashMap::new()), + _ => Ok(LinkedHashMap::new()), } } @@ -40,7 +40,7 @@ fn from_yaml_value(uri: Option<&String>, value: &yaml::Yaml) -> Value { yaml::Yaml::Integer(value) => Value::new(uri, ValueKind::Integer(value)), yaml::Yaml::Boolean(value) => Value::new(uri, ValueKind::Boolean(value)), yaml::Yaml::Hash(ref table) => { - let mut m = HashMap::new(); + let mut m = LinkedHashMap::new(); for (key, value) in table { if let Some(k) = key.as_str() { m.insert(k.to_owned(), from_yaml_value(uri, value)); diff --git a/src/file/mod.rs b/src/file/mod.rs index b00a271..02fd51f 100644 --- a/src/file/mod.rs +++ b/src/file/mod.rs @@ -1,7 +1,7 @@ mod format; pub mod source; -use std::collections::HashMap; +use linked_hash_map::LinkedHashMap; use std::path::{Path, PathBuf}; use crate::error::*; @@ -99,7 +99,7 @@ where Box::new((*self).clone()) } - fn collect(&self) -> Result<HashMap<String, Value>> { + fn collect(&self) -> Result<LinkedHashMap<String, Value>> { // Coerce the file contents to a string let (uri, contents, format) = match self .source @@ -110,7 +110,7 @@ where Err(error) => { if !self.required { - return Ok(HashMap::new()); + return Ok(LinkedHashMap::new()); } return Err(error); diff --git a/src/path/mod.rs b/src/path/mod.rs index d58a6f2..04c424c 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use linked_hash_map::LinkedHashMap; use std::str::FromStr; use crate::error::*; @@ -135,7 +135,7 @@ impl Expression { ), _ => { - *value = HashMap::<String, Value>::new().into(); + *value = LinkedHashMap::<String, Value>::new().into(); if let ValueKind::Table(ref mut map) = value.kind { Some( @@ -186,7 +186,7 @@ impl Expression { ValueKind::Table(_) => {} _ => { - *root = HashMap::<String, Value>::new().into(); + *root = LinkedHashMap::<String, Value>::new().into(); } } @@ -195,7 +195,7 @@ impl Expression { // Pull out another table let mut target = if let ValueKind::Table(ref mut map) = root.kind { map.entry(id.clone()) - .or_insert_with(|| HashMap::<String, Value>::new().into()) + .or_insert_with(|| LinkedHashMap::<String, Value>::new().into()) } else { unreachable!(); }; @@ -224,7 +224,7 @@ impl Expression { _ => { // Didn't find a table. Oh well. Make a table and do this anyway - *parent = HashMap::<String, Value>::new().into(); + *parent = LinkedHashMap::<String, Value>::new().into(); Expression::Identifier(key.clone()).set(parent, value); } diff --git a/src/source.rs b/src/source.rs index 831e4c4..7d257d0 100644 --- a/src/source.rs +++ b/src/source.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use linked_hash_map::LinkedHashMap; use std::fmt::Debug; use std::str::FromStr; @@ -13,8 +13,8 @@ pub trait Source: Debug { fn clone_into_box(&self) -> Box<dyn Source + Send + Sync>; /// Collect all configuration properties available from this source and return - /// a HashMap. - fn collect(&self) -> Result<HashMap<String, Value>>; + /// a LinkedHashMap. + fn collect(&self) -> Result<LinkedHashMap<String, Value>>; /// Collects all configuration properties to a provided cache. fn collect_to(&self, cache: &mut Value) -> Result<()> { @@ -55,8 +55,8 @@ pub trait AsyncSource: Debug + Sync { // Sync is supertrait due to https://docs.rs/async-trait/0.1.50/async_trait/index.html#dyn-traits /// Collects all configuration properties available from this source and return - /// a HashMap as an async operations. - async fn collect(&self) -> Result<HashMap<String, Value>>; + /// a LinkedHashMap as an async operations. + async fn collect(&self) -> Result<LinkedHashMap<String, Value>>; /// Collects all configuration properties to a provided cache. async fn collect_to(&self, cache: &mut Value) -> Result<()> { @@ -86,8 +86,8 @@ impl Source for Vec<Box<dyn Source + Send + Sync>> { Box::new((*self).clone()) } - fn collect(&self) -> Result<HashMap<String, Value>> { - let mut cache: Value = HashMap::<String, Value>::new().into(); + fn collect(&self) -> Result<LinkedHashMap<String, Value>> { + let mut cache: Value = LinkedHashMap::<String, Value>::new().into(); for source in self { source.collect_to(&mut cache)?; @@ -106,8 +106,8 @@ impl Source for [Box<dyn Source + Send + Sync>] { Box::new(self.to_owned()) } |