Expand description
§Props-Util
A Rust library for easily loading and parsing properties files into strongly-typed structs.
§Overview
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.
§Features
- Derive macro for automatic properties parsing
- Support for default values
- Type conversion from string to your struct’s field types
- Error handling for missing or malformed properties
- Support for both file-based and default initialization
- Type conversion between different configuration types
§Usage
§Basic Example
use props_util::Properties;
use std::io::Result;
#[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() -> Result<()> {
// Create a temporary file for testing
let temp_file = tempfile::NamedTempFile::new()?;
std::fs::write(&temp_file, "server.host=example.com\nserver.port=9090\ndebug.enabled=true")?;
let config = Config::from_file(temp_file.path().to_str().unwrap())?;
println!("Server: {}:{}", config.host, config.port);
println!("Debug mode: {}", config.debug);
Ok(())
}
§Attribute Parameters
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)
§Field Types
Props-Util supports any type that implements FromStr
. This includes:
String
- Numeric types (
u8
,u16
,u32
,u64
,i8
,i16
,i32
,i64
,f32
,f64
) - Boolean (
bool
) Vec<T>
whereT
implementsFromStr
(values are comma-separated in the properties file)Option<T>
whereT
implementsFromStr
(optional fields that may or may not be present in the properties file)- Custom types that implement
FromStr
§Example of using Vec and Option types:
use props_util::Properties;
use std::io::Result;
#[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>,
}
fn main() -> Result<()> {
// Create a temporary file for testing
let temp_file = tempfile::NamedTempFile::new()?;
std::fs::write(&temp_file, "numbers=4,5,6,7\nstrings=test,vec,parsing\noptional_port=9090")?;
let config = Config::from_file(temp_file.path().to_str().unwrap())?;
println!("Numbers: {:?}", config.numbers);
println!("Strings: {:?}", config.strings);
println!("Optional port: {:?}", config.optional_port);
println!("Optional host: {:?}", config.optional_host);
Ok(())
}
§Converting Between Different Types
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<()> {
let server_config = ServerConfig::default()?;
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
, thekey
attribute values must match between the source and target types. If nokey
is specified, the field names must match. This ensures that the configuration values are correctly mapped between the different types.
§Error Handling
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:- The file couldn’t be opened or read
- A required property is missing (and no default is provided)
- A property value couldn’t be parsed into the expected type
- The properties file is malformed (e.g., missing
=
character)
§Default Initialization
You can also create an instance with default values without reading from a file:
use props_util::Properties;
use std::io::Result;
#[derive(Properties, Debug)]
struct Config {
#[prop(key = "server.host", default = "localhost")]
host: String,
#[prop(key = "server.port", default = "8080")]
port: u16,
}
fn main() -> Result<()> {
let config = Config::default()?;
println!("Host: {}", config.host);
println!("Port: {}", config.port);
Ok(())
}
§Properties File Format
The properties file follows a simple key-value format:
- Each line represents a single property
- The format is
key=value
- Lines starting with
#
or!
are treated as comments and ignored - Empty lines are ignored
- Leading and trailing whitespace around both key and value is trimmed
Example:
# 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
§Limitations
- Only named structs are supported (not tuple structs or enums)
- All fields must have the
#[prop]
attribute - Properties files must use the
key=value
format
Derive Macros§
- Properties
- Derive macro for automatically implementing properties parsing functionality.