Skip to content

Commit

Permalink
breaking: Rework Cargo features. (#131)
Browse files Browse the repository at this point in the history
  • Loading branch information
milesj authored Jul 28, 2024
1 parent 8418c0f commit 17e8407
Show file tree
Hide file tree
Showing 23 changed files with 242 additions and 123 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@

- Refactored the internals of how validation errors work.
- Removed `Config::META` and `ConfigError::META`. Use `Schematic::schema_name()` instead.
- Removed `url` as a default Cargo feature.
- Renamed `valid_*` Cargo features to `validate_*`.
- Renamed some error enum variants.

#### 🚀 Updates

- Added a `env` Cargo feature for toggling environment variable functionality. Enabled by default.
- Added a `extends` Cargo feature for config extending functionality. Enabled by default.
- Added a `validate` Cargo feature for toggling validation functionality. Enabled by default.
- Reworked how parser and validator errors are rendered in the terminal.

#### ⚙️ Internal
Expand Down
31 changes: 15 additions & 16 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ semver = "1.0.23"
serde = { version = "1.0.204", features = ["derive"] }
serde_json = "1.0.120"
serde_yaml = "0.9.34"
toml = "0.8.15"
toml = "0.8.16"
tracing = "0.1.40"
url = "2.5.2"
2 changes: 2 additions & 0 deletions book/src/config/struct/env.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Environment variables

> Requires the `env` Cargo feature, which is enabled by default.
> Not supported for enums.
Settings can also inherit values from environment variables via the `#[setting(env)]` attribute
Expand Down
2 changes: 2 additions & 0 deletions book/src/config/struct/extend.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Extendable sources

> Requires the `extends` Cargo feature, which is enabled by default.
> Not supported for enums.
Configs can extend other configs, generating an accurate layer chain, via the `#[setting(extend)]`
Expand Down
6 changes: 4 additions & 2 deletions book/src/config/struct/validate.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Validation rules

> Requires the `validate` Cargo feature, which is enabled by default.
What kind of configuration crate would this be without built-in validation? As such, we support it
as a first-class feature, with built-in validation rules provided by the
[garde](https://crates.io/crates/garde) crate.
Expand Down Expand Up @@ -120,6 +122,6 @@ fn using_generics<P, C>(value: &str, partial: &P, context: &C, finalize: bool) -

The following Cargo features can be enabled for more functionality:

- `valid_email` - Enables email validation with the `schematic::validate::email` function.
- `valid_url` - Enables URL validation with the `schematic::validate::url` and `url_secure`
- `validate_email` - Enables email validation with the `schematic::validate::email` function.
- `validate_url` - Enables URL validation with the `schematic::validate::url` and `url_secure`
functions.
3 changes: 3 additions & 0 deletions crates/macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@ syn = { version = "2.0.72", features = ["full"] }
[features]
default = []
config = []
env = []
extends = []
schema = []
tracing = []
validate = []
22 changes: 15 additions & 7 deletions crates/macros/src/common/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,16 @@ pub struct FieldArgs {
// config
#[darling(with = "preserve_str_literal", map = "Some")]
pub default: Option<Expr>,
#[cfg(feature = "env")]
pub env: Option<String>,
#[cfg(feature = "extends")]
pub extend: bool,
pub merge: Option<ExprPath>,
pub nested: bool,
#[cfg(feature = "env")]
pub parse_env: Option<ExprPath>,
pub required: bool,
#[cfg(feature = "validate")]
pub validate: Option<Expr>,

// serde
Expand Down Expand Up @@ -111,6 +115,7 @@ impl<'l> Field<'l> {
self.args.exclude
}

#[cfg(feature = "extends")]
pub fn is_extendable(&self) -> bool {
self.args.extend
}
Expand Down Expand Up @@ -157,14 +162,17 @@ impl<'l> Field<'l> {

pub fn get_env_var(&self) -> Option<String> {
if self.is_nested() {
None
} else if let Some(env_name) = &self.args.env {
Some(env_name.to_owned())
} else {
self.env_prefix
.as_ref()
.map(|env_prefix| format!("{env_prefix}{}", self.get_name(None)).to_uppercase())
return None;
}

#[cfg(feature = "env")]
if let Some(env_name) = &self.args.env {
return Some(env_name.to_owned());
}

self.env_prefix
.as_ref()
.map(|env_prefix| format!("{env_prefix}{}", self.get_name(None)).to_uppercase())
}

pub fn get_serde_meta(&self) -> Option<TokenStream> {
Expand Down
3 changes: 3 additions & 0 deletions crates/macros/src/common/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub struct MacroArgs {
// config
pub allow_unknown_fields: bool,
pub context: Option<ExprPath>,
#[cfg(feature = "env")]
pub env_prefix: Option<String>,
pub file: Option<String>,

Expand Down Expand Up @@ -79,6 +80,7 @@ impl<'l> Macro<'l> {
let mut field = Field::from(f);
field.serde_args.inherit_from_container(&serde_args);
field.casing_format.clone_from(&casing_format);
#[cfg(feature = "env")]
field.env_prefix.clone_from(&args.env_prefix);
field
})
Expand All @@ -100,6 +102,7 @@ impl<'l> Macro<'l> {
field.index = index;
field.serde_args.inherit_from_container(&serde_args);
field.casing_format.clone_from(&casing_format);
#[cfg(feature = "env")]
field.env_prefix.clone_from(&args.env_prefix);
field
})
Expand Down
5 changes: 3 additions & 2 deletions crates/macros/src/common/variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::utils::{extract_common_attrs, format_case};
use darling::FromAttributes;
use proc_macro2::{Ident, TokenStream};
use quote::{quote, ToTokens};
use syn::{Attribute, Expr, ExprPath, Fields, Variant as NativeVariant};
use syn::{Attribute, ExprPath, Fields, Variant as NativeVariant};

#[derive(Clone)]
pub enum TaggedFormat {
Expand All @@ -27,7 +27,8 @@ pub struct VariantArgs {
pub merge: Option<ExprPath>,
pub nested: bool,
pub required: bool,
pub validate: Option<Expr>,
#[cfg(feature = "validate")]
pub validate: Option<syn::Expr>,

// serde
pub rename: Option<String>,
Expand Down
22 changes: 18 additions & 4 deletions crates/macros/src/config/container.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::common::Container;
use proc_macro2::{Ident, TokenStream};
use quote::{quote, ToTokens};
use quote::quote;

impl<'l> Container<'l> {
pub fn generate_default_values(&self) -> TokenStream {
Expand Down Expand Up @@ -89,10 +89,13 @@ impl<'l> Container<'l> {
}

pub fn generate_extends_from(&self) -> TokenStream {
#[cfg(feature = "extends")]
match self {
Self::NamedStruct {
fields: settings, ..
} => {
use quote::ToTokens;

// Validate only 1 setting is using it
let mut names = vec![];

Expand Down Expand Up @@ -159,6 +162,9 @@ impl<'l> Container<'l> {
quote! { None }
}
}

#[cfg(not(feature = "extends"))]
quote! { None }
}

pub fn generate_finalize(&self) -> TokenStream {
Expand All @@ -174,6 +180,16 @@ impl<'l> Container<'l> {
.map(|s| s.generate_finalize_statement())
.collect::<Vec<_>>();

let env_statement = if cfg!(feature = "env") {
quote! {
if let Some(data) = Self::env_values()? {
partial.merge(context, data)?;
}
}
} else {
quote! {}
};

quote! {
let mut partial = Self::default();

Expand All @@ -183,9 +199,7 @@ impl<'l> Container<'l> {

partial.merge(context, self)?;

if let Some(data) = Self::env_values()? {
partial.merge(context, data)?;
}
#env_statement

#(#finalize_stmts)*

Expand Down
36 changes: 24 additions & 12 deletions crates/macros/src/config/field.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::common::{Field, FieldValue};
use proc_macro2::{Literal, TokenStream};
use quote::{quote, ToTokens, TokenStreamExt};
use syn::Expr;

impl<'l> Field<'l> {
pub fn generate_default_value(&self) -> TokenStream {
Expand All @@ -20,26 +19,35 @@ impl<'l> Field<'l> {
let env = self.get_env_var();

if env.is_none() {
#[cfg(feature = "env")]
if self.args.parse_env.is_some() {
panic!("Cannot use `parse_env` without `env` or a parent `env_prefix`.");
}

return None;
};

let value = if let Some(parse_env) = &self.args.parse_env {
quote! {
parse_from_env_var(#env, #parse_env)?
}
} else {
quote! {
default_from_env_var(#env)?
}
};
#[cfg(feature = "env")]
{
let value = if let Some(parse_env) = &self.args.parse_env {
quote! {
parse_from_env_var(#env, #parse_env)?
}
} else {
quote! {
default_from_env_var(#env)?
}
};

let key = self.get_field_key();
let key = self.get_field_key();

Some(quote! { partial.#key = #value; })
Some(quote! { partial.#key = #value; })
}

#[cfg(not(feature = "env"))]
{
None
}
}

pub fn generate_finalize_statement(&self) -> TokenStream {
Expand All @@ -62,6 +70,7 @@ impl<'l> Field<'l> {
#[allow(clippy::collapsible_else_if)]
if matches!(self.value_type, FieldValue::Value { .. }) {
// Reset extendable values since we don't have the entire resolved list
#[cfg(feature = "extends")]
if self.args.extend {
return quote! { Default::default() };
}
Expand Down Expand Up @@ -119,7 +128,10 @@ impl<'l> Field<'l> {
let key_quoted = self.get_field_key_string();
let mut stmts = vec![];

#[cfg(feature = "validate")]
if let Some(expr) = self.args.validate.as_ref() {
use syn::Expr;

let func = match expr {
// func(arg)()
Expr::Call(func) => quote! { #func },
Expand Down
Loading

0 comments on commit 17e8407

Please sign in to comment.