#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
mod builder;
mod cfg;
pub(crate) mod context;
mod error;
mod file;
mod merge;
mod provider;
mod reloadable;
mod section;
mod settings;
#[cfg(feature = "chained")]
pub mod chained;
#[cfg(feature = "cmd")]
pub mod cmd;
pub mod de;
#[cfg(feature = "env")]
pub mod env;
#[cfg(feature = "ini")]
pub mod ini;
#[cfg(feature = "json")]
pub mod json;
#[cfg(feature = "mem")]
pub mod mem;
pub mod ser;
#[cfg(feature = "typed")]
pub mod typed;
pub mod path;
pub mod prelude;
#[cfg(feature = "xml")]
pub mod xml;
#[cfg(feature = "yaml")]
pub mod yaml;
pub use builder::Builder;
pub use cfg::{Configuration, ReloadableConfiguration};
pub use error::Error;
pub use file::{FileSource, FileSourceBuilder};
pub use merge::Merge;
pub use provider::Provider;
pub use reloadable::Reloadable;
pub use section::{OwnedSection, Section};
pub use settings::Settings;
#[cfg(feature = "derive")]
#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
pub use config_derive::Deserialize;
pub type Result<T = ()> = std::result::Result<T, Error>;
#[inline]
pub fn builder() -> Builder {
Builder::default()
}
pub fn pascal_case(text: &str) -> String {
let mut converted = String::with_capacity(text.len());
let mut next_is_upper = true;
let mut last_was_lower = false;
for ch in text.chars() {
if ch == ' ' || ch == '_' || ch == '-' || ch == ':' {
next_is_upper = true;
last_was_lower = false;
if ch == ':' {
converted.push(ch);
}
} else if ch.is_alphabetic() && (next_is_upper || (last_was_lower && ch.is_ascii_uppercase())) {
converted.push(ch.to_ascii_uppercase());
next_is_upper = false;
last_was_lower = ch.is_ascii_lowercase();
} else if ch.is_alphabetic() {
converted.push(ch.to_ascii_lowercase());
last_was_lower = ch.is_ascii_lowercase();
} else {
converted.push(ch);
next_is_upper = true;
last_was_lower = false;
}
}
converted
}
#[inline(never)]
fn overridden(mut id: u8, names: &[String], providers: u8, key: &str, old: &str, new: &str) {
use tracing::trace;
const UNKNOWN: &str = "Unknown";
let mut i = (id as u32).saturating_sub(1);
let current = if i < u8::BITS && (i as usize) < names.len() {
&names[i as usize]
} else {
UNKNOWN
};
let last = loop {
if id > 0 {
id >>= 1;
i = (id as u32).saturating_sub(1);
if providers & id != 0 {
if i < u8::BITS && (i as usize) < names.len() {
break names[i as usize].as_str();
} else {
break UNKNOWN;
}
}
} else {
break UNKNOWN;
}
};
trace!("key '{key}' with value '{old}' ({last}) has been overridden with value '{new}' ({current})");
}
#[cfg(test)]
mod tests {
use super::*;
use test_case::test_case;
#[test_case(""; "if empty")]
#[test_case("HelloWorld"; "in pascal case")]
#[test_case("Hello.World"; "with a period")]
#[test_case("Hello:World"; "with a colon")]
fn pascal_case_should_not_change_text(expected: &str) {
let actual = pascal_case(expected);
assert_eq!(actual, expected);
}
#[test_case("hello world"; "from lower title case")]
#[test_case("Hello World"; "from upper title case")]
#[test_case("helloWorld"; "from camel case")]
#[test_case("hello_world"; "from snake case")]
#[test_case("HELLO_WORLD"; "from screaming snake case")]
#[test_case("hello-world"; "from kebab case")]
#[test_case("HELLO-WORLD"; "from screaming kebab case")]
fn pascal_case_should_convert_text(text: &str) {
let actual = pascal_case(text);
assert_eq!(actual, "HelloWorld");
}
#[test]
fn pascal_case_should_convert_capitalized_with_colon() {
let expected = "Hello:World";
let actual = pascal_case("HELLO:WORLD");
assert_eq!(actual, expected);
}
}