| Crates.io | props-util |
| lib.rs | props-util |
| version | 0.2.1 |
| created_at | 2025-04-21 16:57:16.769754+00 |
| updated_at | 2025-05-22 10:10:19.465202+00 |
| description | A Rust library for easily loading and parsing properties files into strongly-typed structs. |
| homepage | |
| repository | https://github.com/dineshadhi/props-util.git |
| max_upload_size | |
| id | 1642958 |
| size | 45,048 |
A Rust library for easily loading and parsing properties files into strongly-typed structs.
Props-Util provides a procedural macro that allows you to derive a Properties trait for your structs, enabling automatic parsing of properties files into your struct fields. This makes configuration management in Rust applications more type-safe and convenient.
Add the following to your Cargo.toml:
[dependencies]
props-util = "0.1.0" # Replace with the actual version
Properties derive macro:use props_util::Properties;
#[derive(Properties, Debug)]
struct Config {
#[prop(key = "server.host", default = "localhost")]
host: String,
#[prop(key = "server.port", default = "8080")]
port: u16,
#[prop(key = "debug.enabled", default = "false")]
debug: bool,
}
fn main() -> std::io::Result<()> {
let config = Config::from_file("config.properties")?;
println!("Server: {}:{}", config.host, config.port);
println!("Debug mode: {}", config.debug);
Ok(())
}
config.properties):server.host=example.com
server.port=9090
debug.enabled=true
The #[prop] attribute accepts the following parameters:
key: The property key to look for in the properties file (optional). If not specified, the field name will be used as the key.default: A default value to use if the property is not found in the file (optional)env: The environment variable name to look for (optional). If the environment variable is set, its value will be used instead of the value from the properties file.use props_util::Properties;
use std::io::Result;
#[derive(Properties, Debug)]
struct Config {
#[prop(key = "server.host", env = "SERVER_HOST", default = "localhost")]
host: String,
#[prop(key = "server.port", env = "SERVER_PORT", default = "8080")]
port: u16,
#[prop(key = "api.key", env = "API_KEY")] // No default, must be set in env or props file
api_key: String,
}
fn main() -> Result<()> {
// Set environment variables for testing
std::env::set_var("SERVER_HOST", "env.example.com");
std::env::set_var("SERVER_PORT", "9090");
// Create a properties file with different values
let temp_file = tempfile::NamedTempFile::new()?;
std::fs::write(&temp_file, "server.host=file.example.com\nserver.port=8080\napi.key=test123")?;
let config = Config::from_file(temp_file.path().to_str().unwrap())?;
// Environment variables take precedence over file values
println!("Host: {}", config.host); // Will print "env.example.com"
println!("Port: {}", config.port); // Will print "9090"
println!("API Key: {}", config.api_key); // Will print "test123" (from file)
Ok(())
}
Props-Util supports any type that implements FromStr. This includes:
Stringu8, u16, u32, u64, i8, i16, i32, i64, f32, f64)bool)Vec<T> where T implements FromStr (values are comma-separated in the properties file)Option<T> where T implements FromStr (optional fields that may or may not be present in the properties file)FromStr#[derive(Properties, Debug)]
struct Config {
#[prop(key = "numbers", default = "1,2,3")]
numbers: Vec<i32>,
#[prop(key = "strings", default = "hello,world")]
strings: Vec<String>,
#[prop(key = "optional_port")] // No default needed for Option
optional_port: Option<u16>,
#[prop(key = "optional_host")] // No default needed for Option
optional_host: Option<String>,
}
In the properties file:
numbers=4,5,6,7
strings=test,vec,parsing
optional_port=9090
# optional_host is not set, so it will be None
You can use the from function to convert between different configuration types. This is particularly useful when you have multiple structs that share similar configuration fields but with different types or structures:
use props_util::Properties;
use std::io::Result;
#[derive(Properties, Debug)]
struct ServerConfig {
#[prop(key = "host", default = "localhost")]
host: String,
#[prop(key = "port", default = "8080")]
port: u16,
}
#[derive(Properties, Debug)]
struct ClientConfig {
#[prop(key = "host", default = "localhost")] // Note: using same key as ServerConfig
server_host: String,
#[prop(key = "port", default = "8080")] // Note: using same key as ServerConfig
server_port: u16,
}
fn main() -> Result<()> {
// Create a temporary file for testing
let temp_file = tempfile::NamedTempFile::new()?;
std::fs::write(&temp_file, "host=example.com\nport=9090")?;
// Convert from ServerConfig to ClientConfig using the from function
let server_config = ServerConfig::from_file(temp_file.path().to_str().unwrap())?;
let client_config = ClientConfig::from(server_config)?;
println!("Server host: {}", client_config.server_host);
println!("Server port: {}", client_config.server_port);
Ok(())
}
Important: When converting between types using
from, thekeyattribute values must match between the source and target types. If nokeyis specified, the field names must match. This ensures that the configuration values are correctly mapped between the different types.
This approach is useful when:
The from_file method returns a std::io::Result<T>, which will contain:
Ok(T) if the properties file was successfully parsedErr with an appropriate error message if:
= character)You can also create an instance with default values without reading from a file:
let config = Config::default()?;
This will use the default values specified in the #[prop] attributes.
The properties file follows a simple key-value format:
key=value# or ! are treated as comments and ignoredExample:
# Application settings
app.name=MyAwesomeApp
app.version=2.1.0
# Database configuration
database.url=postgres://user:pass@localhost:5432/mydb
database.pool_size=20
# Logging settings
logging.level=debug
logging.file=debug.log
# Network settings
allowed_ips=10.0.0.1,10.0.0.2,192.168.0.1
ports=80,443,8080,8443
# Features
enabled_features=ssl,compression,caching
# Optional settings
optional_ssl_port=8443