Skip to content

Commit

Permalink
feat: better error paths
Browse files Browse the repository at this point in the history
  • Loading branch information
TroyKomodo committed Jul 8, 2023
1 parent 7885d5c commit fb65738
Show file tree
Hide file tree
Showing 10 changed files with 324 additions and 435 deletions.
22 changes: 22 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions config/config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ serde_ignored = "0"
serde-value = "0"
serde_path_to_error = "0"
humantime = "2"
num-order = "1"

# Derive macro
config_derive = { path = "../config_derive" }
Expand Down
38 changes: 4 additions & 34 deletions config/config/example/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,23 @@ type TypeAlias = bool;
struct AppConfig {
enabled: TypeAlias,
logging: LoggingConfig,
#[config(cli(skip), env(skip))]
count: Vec<Vec<u8>>,
}

#[derive(config::Config, Debug, PartialEq, serde::Deserialize)]
#[serde(default)]
struct LoggingConfig {
level: String,
json: bool,
manual: Manual,
manual2: Manual,
}

#[derive(config::Config, Default, Debug, PartialEq, serde::Deserialize)]
struct Manual {
#[config(cli(skip), env(skip))]
cycle: Box<Option<LoggingConfig>>,
}

impl Default for LoggingConfig {
fn default() -> Self {
Self {
level: "INFO".to_string(),
json: false,
manual: Manual::default(),
manual2: Manual::default(),
}
}
}
Expand All @@ -55,33 +48,10 @@ fn parse() -> Result<AppConfig, ConfigError> {
br#"
{
"enabled": "on",
"count": [[2], [123]],
"logging": {
"level": "DEBUG",
"json": true,
"manual": {
"cycle": {
"level": "INFO",
"json": false,
"manual": {
"cycle": null
},
"manual2": {
"cycle": null
}
}
},
"manual2": {
"cycle": {
"level": "INFO",
"json": false,
"manual": {
"cycle": null
},
"manual2": {
"cycle": null
}
}
}
"json": true
}
}
"#
Expand Down
4 changes: 3 additions & 1 deletion config/config/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub enum ConfigErrorEnum {
Multiple(MultiError),
#[error("subkey on non-map: {0:?}")]
SubkeyOnNonMap(Value),
#[error("subkey on non-seq: {0:?}")]
SubIndexOnNonSeq(Value),
#[error("validation error: {0}")]
ValidationError(String),
#[error("referenced not found: {0}")]
Expand Down Expand Up @@ -152,7 +154,7 @@ impl std::fmt::Display for ConfigError {
self.error().fmt(f)?;

if let Some(path) = &self.path {
write!(f, " (path .{})", path)?;
write!(f, " (path {})", path)?;
}

Ok(())
Expand Down
178 changes: 139 additions & 39 deletions config/config/src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,73 +5,173 @@ use std::{
sync::{Arc, Mutex, Weak},
};

use crate::Value;

#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
pub struct KeyPath(Vec<String>);
pub struct KeyPath {
segments: Vec<KeyPathSegment>,
}

impl KeyPath {
pub fn new() -> Self {
Self::default()
impl From<&str> for KeyPath {
fn from(s: &str) -> Self {
Self {
segments: s
.split('.')
.map(|s| {
KeyPathSegment::Struct {
field: s.to_string(),
}
})
.collect(),
}
}
}

pub fn child(&self) -> Option<&str> {
self.0.last().map(|s| s.as_str())
impl From<String> for KeyPath {
fn from(s: String) -> Self {
Self {
segments: s
.split('.')
.map(|s| {
KeyPathSegment::Struct {
field: s.to_string(),
}
})
.collect(),
}
}
}

pub fn push_child(&self, name: &str) -> Self {
let mut path = self.clone();
path.0.push(name.to_string());
path
impl IntoIterator for KeyPath {
type Item = KeyPathSegment;
type IntoIter = std::vec::IntoIter<KeyPathSegment>;

fn into_iter(self) -> Self::IntoIter {
self.segments.into_iter()
}
}

impl<'a> IntoIterator for &'a KeyPath {
type Item = &'a KeyPathSegment;
type IntoIter = std::slice::Iter<'a, KeyPathSegment>;

pub fn root(&self) -> Option<&str> {
self.0.first().map(|s| s.as_str())
fn into_iter(self) -> Self::IntoIter {
self.segments.iter()
}
}

pub fn drop_root(mut self) -> Self {
self.0.remove(0);
self
impl KeyPath {
pub fn iter(&self) -> std::slice::Iter<'_, KeyPathSegment> {
self.segments.iter()
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum KeyPathSegment {
Map {
key: Value,
},
Seq {
index: usize,
},
Struct {
field: String,
},
}

pub fn take_child(&mut self) -> Option<String> {
self.0.pop()
impl std::fmt::Display for KeyPathSegment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Map { key } => write!(f, ".{}", match key {
Value::String(s) => s.to_string(),
Value::I8(i) => i.to_string(),
Value::I16(i) => i.to_string(),
Value::I32(i) => i.to_string(),
Value::I64(i) => i.to_string(),
Value::U8(i) => i.to_string(),
Value::U16(i) => i.to_string(),
Value::U32(i) => i.to_string(),
Value::U64(i) => i.to_string(),
Value::F32(i) => i.to_string(),
Value::F64(i) => i.to_string(),
Value::Bool(i) => i.to_string(),
Value::Char(i) => i.to_string(),
Value::Unit => "<unit>".to_string(),
Value::Option(_) => "<option>".to_string(),
Value::Map(_) => "<map>".to_string(),
Value::Seq(_) => "<seq>".to_string(),
Value::Bytes(_) => "<bytes>".to_string(),
Value::Newtype(_) => "<newtype>".to_string(),
}),
Self::Seq { index } => write!(f, "[{}]", index),
Self::Struct { field } => write!(f, ".{}", field),
}
}
}

pub fn push_root(&mut self, root: &str) {
self.0.insert(0, root.to_string());
impl KeyPath {
pub fn root() -> Self {
Self {
segments: vec![],
}
}

pub fn get_inner(&self) -> &[String] {
&self.0
pub fn drop_root(&self) -> Self {
Self {
segments: self.segments[1..].to_vec(),
}
}

pub fn new() -> Self {
Self {
segments: vec![],
}
}
}

impl IntoIterator for KeyPath {
type Item = String;
type IntoIter = std::vec::IntoIter<Self::Item>;
pub fn get_inner(&self) -> &[KeyPathSegment] {
&self.segments
}

fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
pub fn push_map(&self, key: &Value) -> Self {
Self {
segments: {
let mut segments = self.segments.clone();
segments.push(KeyPathSegment::Map { key: key.clone() });
segments
},
}
}
}

impl<'a> IntoIterator for &'a KeyPath {
type Item = &'a String;
type IntoIter = std::slice::Iter<'a, String>;
pub fn push_seq(&self, index: usize) -> Self {
Self {
segments: {
let mut segments = self.segments.clone();
segments.push(KeyPathSegment::Seq { index });
segments
},
}
}

fn into_iter(self) -> Self::IntoIter {
self.0.iter()
pub fn push_struct(&self, field: &str) -> Self {
Self {
segments: {
let mut segments = self.segments.clone();
segments.push(KeyPathSegment::Struct {
field: field.to_string(),
});
segments
},
}
}
}

impl Display for KeyPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.join("."))
}
}
for segment in &self.segments {
write!(f, "{}", segment)?;
}

impl From<&str> for KeyPath {
fn from(value: &str) -> Self {
Self(value.split('.').map(|s| s.to_string()).collect())
Ok(())
}
}

Expand Down
Loading

0 comments on commit fb65738

Please sign in to comment.