GithubHelp home page GithubHelp logo

n4d1k-lgtm / confgr Goto Github PK

View Code? Open in Web Editor NEW
2.0 1.0 0.0 52 KB

A simple, powerful and easy to use derive macro for rust application configuration management.

Home Page: https://crates.io/crates/confgr

License: MIT License

Rust 100.00%
config config-file crate derive-macro development environment-variables rust settings toml

confgr's Introduction

Confgr

A lightweight rust application configuration derive macro.


Built with ❤️ by Ki

Overview

The Config derive macro simplifies application configuration by automatically loading settings from sources in the following order:

  1. Environment Variables.
  2. Configuration File (e.g., toml, json, yaml, ini, ron, json5).
  3. Default Values.

Key Features

  • Simplicity: Minimal boilerplate. Define your configuration struct, customize the macro, and you're good to go.
  • Flexibility: Supports a variety of configuration file formats including toml, json, yaml, ini, ron, and json5.
  • Integration: Synergy with other crates, such as smart_default.

There are also several useful helper attributes for customizing the behavior of the derive macro.

Attribute Functionality
prefix Sets a prefix for environment variables. Can be applied at the struct or field level.
path Specifies the static path to a configuration file. The file extension may (though probably shouldn't) be omitted.
env_path Resolves an environment variable at runtime to determine the configuration file path.
default_path Specifies a fallback path used if the path determined by env_path does not exist.
key Overrides the default environment variable name. This ignores the prefix and uses the provided key directly.
name forwards to #[serde(rename = "_")] to rename fields during serialization/deserialization. It does not affect environment variable names.
nest Required for non-standard types which must also derive Config, used for nesting configuration structs.
skip Skips loading the attribute from an environment variable. Necessary for types that don't implement FromStr but are present in the configuration file.
separator Specifies a character to separate the prefix and the field name. The default separator is "_".

Path Attribute Behavior

  • env_path: Resolves the provided environment variable into a config filepath. This takes precedence over path and default_path, but will not panic if the file or environment does not exist.
  • path: Directly sets the path to the configuration file. When set, default_path may not be used. Panics if the file does not exist.
  • default_path: Identical to path, but does not panic if the file does not exist.

Usage

serde is a required dependency.

 [dependencies]
 confgr = "0.2.0"
 serde = { version = "1.0", features = ["derive"] }

Then define your configuration like so:

use confgr::prelude::*;

#[derive(Config)]
#[config(path = "docs.toml", prefix = "APP")]
pub struct AppConfig {
  port: u32,
  address: String,
  #[config(key = "DEBUG_MODE")]
  debug: bool,
}

// Default implementation is required.
impl Default for AppConfig {
  fn default() -> Self {
    Self {
      port: 3000,
      address: "127.0.0.1".to_string(),
      debug: false
    }
  }
}

std::env::set_var("APP_PORT", "4000");
std::env::set_var("DEBUG_MODE", "true");

let settings = AppConfig::load_config();

assert_eq!(settings.port, 4000);
assert_eq!(settings.address, "127.0.0.1");
assert!(settings.debug)

Check out the examples directory for more.

Warnings/Pitfalls

  • Nested structs do not load separate files based on their own path attributes. If you would like multiple files to be loaded, you must use multiple structs with multiple load_config() calls. This may change in a future version.
  • Types that do not implement FromStr must use #[config(skip)] or #[config(nest)].
  • The separator character is only inserted between the prefix and the field name, not in any part of the parsed field name.
  • The prefix is applied per field or for the entire struct, but is ignored if #[config(key = "_")] is used.
  • All configuration structs must implement Default.
  • Custom types used in configuration struct fields must implement Deserialize, Clone, and Debug.
  • Option is not currently compatible with #[config(nest)] on types that implement Confgr.

Debugging

When encountering issues using the macro, the following methods may be of use.

Verifying Environment Variables

The get_env_keys() method can be used to retrieve the environment variable key names built by the derive macro.

use std::collections::HashMap;
use confgr::prelude::*;

#[derive(Config, Default)]
#[config(prefix = "APP")]
pub struct AppConfig {
    port: u32,
    #[config(separator = "__")]
    address: String,
    #[config(key = "DEBUG_MODE")]
    debug: bool,
}

let keys: HashMap<String, String> = AppConfig::get_env_keys();

assert_eq!(keys["port"], "APP_PORT");
assert_eq!(keys["address"], "APP__ADDRESS");
assert_eq!(keys["debug"], "DEBUG_MODE");

Verifying Configuration File Path

You can use check_file() to ensure that the configuration file is accessible using the provided path, path_env or default_path attributes.

use confgr::prelude::*;

#[derive(Config, Default)]
#[config(path = "docs.toml", env_path = "APP_CONFIG_FILE")]
pub struct AppConfig {
    port: u32,
    debug: bool,
}

std::env::set_var("APP_CONFIG_FILE", "env_config.toml");
AppConfig::check_file().expect("Failed to open configuration file.");

std::env::remove_var("APP_CONFIG_FILE");
AppConfig::check_file().expect("Failed to open configuration file.");

Test Deserialization

The deserialize_from_file() method can be used to manually test the config deserialization step. Returns a Result<Self::Layer, ConfgrError>.

use confgr::prelude::\*;

#[derive(Config, Default)] #[config(path = "docs.toml")]
pub struct AppConfig {
port: u32,
debug: bool,
}

let config = AppConfig::deserialize_from_file().expect("Failed to deserialize configuration.");
println!("Deserialized configuration: {:?}", config);

Considerations

  • Version Instability: As of now, this crate is in an unstable development phase and I reserve the right to make breaking changes in future versions without obligation to maintain backwards compatibility.
  • Production Use Caution: This is my first published Rust crate, while it is fully functional and useful for me, it's advisable not to rely heavily on this library in critical production environments without thorough testing, especially where guarantees of stability and performance are required.
  • Contribution: Contributions are welcome! Whether it's feature requests, bug reports, or pull requests, I'd love some constructive feedback!

I highly recommend checking out the config crate as it is a feature complete non-proc-macro alternative. This crate actually relies on config for file parsing.

confgr's People

Contributors

n4d1k-lgtm avatar

Stargazers

Nathanael Frisch avatar  avatar

Watchers

 avatar

confgr's Issues

`from_env_keys()` is not properly implemented for nested config structs.

Example:

#[derive(Config, Serialize, Deserialize, Debug, Default)]
pub struct AppConfig {
    #[config(key = "DEBUG_MODE")]
    pub debug: bool,
    #[config(nest)]
    pub mailbox: MailboxConfig,
    #[config(nest)]
    pub web: WebConfig,
    #[config(nest)]
    pub notifications: NotificationConfig,
    #[config(nest)]
    pub cobbler: CobblerConfig,
    #[config(nest)]
    pub ipa: IPAConfig,
    #[config(nest)]
    pub cli: CliConfig,
    #[config(nest)]
    pub metrics: MetricsConfig,
}

println!("Environment Variables: {:?}", AppConfig::get_env_keys());

This should traverse each nested config struct and get their env keys. However currently it just outputs the field names.

Environment Variables: 
{
  "mailbox": "MAILBOX",  
  "cli": "CLI", 
  "ipa": "IPA", 
  "metrics": "METRICS", 
  "debug": "DEBUG_MODE", 
  "cobbler": "COBBLER", 
  "notifications": "NOTIFICATIONS", 
  "web": "WEB"
}

`prefix` config attribute is not compatible with `config(nest)`

Using #[config(prefix = "your_prefix")] on a field in combination with #[config(nest)] should pass the provided prefix to allow reusing config structs. A simple example:

#[derive(Config, Default)]
struct Nested {
    #[config(prefix = "inner_override")]
    name: String,
    detail: String,
}

#[derive(Config, Default)]
#[config(prefix = "TEST")]
struct NestedTest {
    id: i32,
    #[config(nest, prefix = "NESTED")]
    nested: Nested,
}

This should result in the env variable for Nested.detail to be NESTED_DETAIL

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.