Skip to content
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

Preserve map ordering #217

Merged
merged 10 commits into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/msrv.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,23 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
args: --all-features

- name: Run cargo test (nightly)
if: matrix.rust == '1.46.0'
continue-on-error: true
uses: actions-rs/cargo@v1
with:
command: test
args: --tests
args: --tests --all-features

- name: Run cargo test (nightly)
if: matrix.rust == 'nightly'
continue-on-error: true
uses: actions-rs/cargo@v1
with:
command: test
args: --all-features

fmt:
needs: [check]
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ yaml = ["yaml-rust"]
hjson = ["serde-hjson"]
ini = ["rust-ini"]
json5 = ["json5_rs"]
preserve_order = ["indexmap", "toml/preserve_order", "serde_json/preserve_order", "ron/indexmap"]

[dependencies]
async-trait = "0.1.50"
Expand All @@ -35,6 +36,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" }
indexmap = { version = "1.7.0", features = ["serde-1"], optional = true}

[dev-dependencies]
serde_derive = "1.0.8"
Expand Down
6 changes: 3 additions & 3 deletions examples/async_source/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{collections::HashMap, error::Error};
use std::error::Error;

use config::{builder::AsyncState, AsyncSource, ConfigBuilder, ConfigError, FileFormat};
use config::{builder::AsyncState, AsyncSource, ConfigBuilder, ConfigError, FileFormat, Map};

use async_trait::async_trait;
use futures::{select, FutureExt};
Expand Down Expand Up @@ -56,7 +56,7 @@ struct HttpSource {

#[async_trait]
impl AsyncSource for HttpSource {
async fn collect(&self) -> Result<HashMap<String, config::Value>, ConfigError> {
async fn collect(&self) -> Result<Map<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
Expand Down
14 changes: 7 additions & 7 deletions examples/glob/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::path::Path;
use std::collections::HashMap;
use std::collections::Map;
use config::*;
use glob::glob;

Expand All @@ -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 Map)
println!("\n{:?} \n\n-----------",
settings.try_into::<HashMap<String, String>>().unwrap());
settings.try_into::<Map<String, String>>().unwrap());

// Option 2
// --------
Expand All @@ -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 Map)
println!("\n{:?} \n\n-----------",
settings.try_into::<HashMap<String, String>>().unwrap());
settings.try_into::<Map<String, String>>().unwrap());

// Option 3
// --------
Expand All @@ -43,7 +43,7 @@ fn main() {
.collect::<Vec<_>>())
.unwrap();

// Print out our settings (as a HashMap)
// Print out our settings (as a Map)
println!("\n{:?} \n\n-----------",
settings.try_into::<HashMap<String, String>>().unwrap());
settings.try_into::<Map<String, String>>().unwrap());
}
6 changes: 3 additions & 3 deletions examples/simple/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::collections::Map;

fn main() {
let mut settings = config::Config::default();
Expand All @@ -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 Map)
println!("{:?}",
settings.try_into::<HashMap<String, String>>().unwrap());
settings.try_into::<Map<String, String>>().unwrap());
}
4 changes: 2 additions & 2 deletions examples/watch/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use config::*;
use std::collections::HashMap;
use std::collections::Map;
use std::sync::RwLock;
use notify::{RecommendedWatcher, DebouncedEvent, Watcher, RecursiveMode};
use std::sync::mpsc::channel;
Expand All @@ -20,7 +20,7 @@ fn show() {
.read()
.unwrap()
.clone()
.try_into::<HashMap<String, String>>()
.try_into::<Map<String, String>>()
.unwrap());
}

Expand Down
23 changes: 12 additions & 11 deletions src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::iter::IntoIterator;
use std::str::FromStr;
use std::{collections::HashMap, iter::IntoIterator};

use crate::error::Result;
use crate::map::Map;
use crate::source::AsyncSource;
use crate::{config::Config, path::Expression, source::Source, value::Value};

Expand Down Expand Up @@ -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: Map<Expression, Value>,
overrides: Map<Expression, Value>,
state: St,
}

Expand Down Expand Up @@ -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: Map<Expression, Value>,
overrides: Map<Expression, Value>,
sources: Vec<SourceType>,
}

Expand Down Expand Up @@ -244,11 +245,11 @@ impl ConfigBuilder<DefaultState> {
}

fn build_internal(
defaults: HashMap<Expression, Value>,
overrides: HashMap<Expression, Value>,
defaults: Map<Expression, Value>,
overrides: Map<Expression, Value>,
sources: &[Box<dyn Source + Send + Sync>],
) -> Result<Config> {
let mut cache: Value = HashMap::<String, Value>::new().into();
let mut cache: Value = Map::<String, Value>::new().into();

// Add defaults
for (key, val) in defaults.into_iter() {
Expand Down Expand Up @@ -322,11 +323,11 @@ impl ConfigBuilder<AsyncState> {
}

async fn build_internal(
defaults: HashMap<Expression, Value>,
overrides: HashMap<Expression, Value>,
defaults: Map<Expression, Value>,
overrides: Map<Expression, Value>,
sources: &[SourceType],
) -> Result<Config> {
let mut cache: Value = HashMap::<String, Value>::new().into();
let mut cache: Value = Map::<String, Value>::new().into();

// Add defaults
for (key, val) in defaults.into_iter() {
Expand Down
12 changes: 6 additions & 6 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::collections::HashMap;
use std::fmt::Debug;

use crate::builder::{ConfigBuilder, DefaultState};
use serde::de::Deserialize;
use serde::ser::Serialize;

use crate::error::*;
use crate::map::Map;
use crate::path;
use crate::ser::ConfigSerializer;
use crate::source::Source;
Expand All @@ -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: Map<path::Expression, Value>,
overrides: Map<path::Expression, Value>,
sources: Vec<Box<dyn Source + Send + Sync>>,

/// Root of the cached configuration.
Expand Down Expand Up @@ -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 = Map::<String, Value>::new().into();

// Add defaults
for (key, val) in self.defaults.iter() {
Expand Down Expand Up @@ -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<Map<String, Value>> {
self.get(key).and_then(Value::into_table)
}

Expand Down Expand Up @@ -212,7 +212,7 @@ impl Source for Config {
Box::new((*self).clone())
}

fn collect(&self) -> Result<HashMap<String, Value>> {
fn collect(&self) -> Result<Map<String, Value>> {
self.cache.clone().into_table()
}
}
5 changes: 3 additions & 2 deletions src/de.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::collections::{HashMap, VecDeque};
use std::collections::VecDeque;
use std::iter::Enumerate;

use serde::de;

use crate::config::Config;
use crate::error::*;
use crate::map::Map;
use crate::value::{Table, Value, ValueKind};

impl<'de> de::Deserializer<'de> for Value {
Expand Down Expand Up @@ -199,7 +200,7 @@ struct MapAccess {
}

impl MapAccess {
fn new(table: HashMap<String, Value>) -> Self {
fn new(table: Map<String, Value>) -> Self {
MapAccess {
elements: table.into_iter().collect(),
}
Expand Down
6 changes: 3 additions & 3 deletions src/env.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::HashMap;
use std::env;

use crate::error::*;
use crate::map::Map;
use crate::source::Source;
use crate::value::{Value, ValueKind};

Expand Down Expand Up @@ -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<Map<String, Value>> {
let mut m = Map::new();
let uri: String = "the environment".into();

let separator = self.separator.as_deref().unwrap_or("");
Expand Down
8 changes: 4 additions & 4 deletions src/file/format/hjson.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
use std::collections::HashMap;
use std::error::Error;

use crate::map::Map;
use crate::value::{Value, ValueKind};

pub fn parse(
uri: Option<&String>,
text: &str,
) -> Result<HashMap<String, Value>, Box<dyn Error + Send + Sync>> {
) -> Result<Map<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(Map::new()),
}
}

Expand All @@ -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 = Map::new();

for (key, value) in table {
m.insert(key.clone(), from_hjson_value(uri, value));
Expand Down
8 changes: 4 additions & 4 deletions src/file/format/ini.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
use std::collections::HashMap;
use std::error::Error;

use ini::Ini;

use crate::map::Map;
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<Map<String, Value>, Box<dyn Error + Send + Sync>> {
let mut map: Map<String, Value> = Map::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: Map<String, Value> = Map::new();
for (k, v) in prop.iter() {
sec_map.insert(
k.to_owned(),
Expand Down
8 changes: 4 additions & 4 deletions src/file/format/json.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
use std::collections::HashMap;
use std::error::Error;

use crate::map::Map;
use crate::value::{Value, ValueKind};

pub fn parse(
uri: Option<&String>,
text: &str,
) -> Result<HashMap<String, Value>, Box<dyn Error + Send + Sync>> {
) -> Result<Map<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(Map::new()),
}
}

Expand All @@ -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 = Map::new();

for (key, value) in table {
m.insert(key.clone(), from_json_value(uri, value));
Expand Down
Loading