#[cfg(feature = "derive")]
use confetti_rs::{
from_str, to_string, ConfArgument, ConfDirective, ConfMap, FromConf, MapperError, ToConf,
};
#[cfg(feature = "derive")]
use std::error::Error;
#[cfg(feature = "derive")]
fn main() -> Result<(), Box<dyn Error>> {
println!("Confetti-rs Nested Config Example");
println!("=================================");
#[derive(ConfMap, Debug)]
struct DatabaseConfig {
host: String,
port: i32,
username: String,
password: String,
#[conf_map(name = "max-pool-size")]
max_pool_size: Option<i32>,
}
#[derive(ConfMap, Debug)]
struct ServerConfig {
host: String,
port: i32,
#[conf_map(name = "ssl-enabled")]
ssl_enabled: bool,
}
#[derive(Debug)]
struct ServiceConfig {
name: String,
version: String,
database: DatabaseConfig,
server: ServerConfig,
}
impl FromConf for ServiceConfig {
fn from_directive(directive: &ConfDirective) -> Result<Self, MapperError> {
if directive.name.value != "ServiceConfig" {
return Err(MapperError::ParseError(format!(
"Expected directive name ServiceConfig, found {}",
directive.name.value
)));
}
let name = directive
.children
.iter()
.find(|d| d.name.value == "name")
.and_then(|d| d.arguments.get(0))
.map(|arg| arg.value.clone())
.ok_or_else(|| MapperError::MissingField("name".into()))?;
let version = directive
.children
.iter()
.find(|d| d.name.value == "version")
.and_then(|d| d.arguments.get(0))
.map(|arg| arg.value.clone())
.ok_or_else(|| MapperError::MissingField("version".into()))?;
let database_child = directive
.children
.iter()
.find(|d| d.name.value == "database")
.ok_or_else(|| MapperError::MissingField("database".into()))?;
let database_directive = ConfDirective {
name: ConfArgument {
value: "DatabaseConfig".to_string(),
span: database_child.name.span.clone(),
is_quoted: false,
is_triple_quoted: false,
is_expression: false,
},
arguments: Vec::new(),
children: database_child.children.clone(),
};
let database = DatabaseConfig::from_directive(&database_directive)?;
let server_child = directive
.children
.iter()
.find(|d| d.name.value == "server")
.ok_or_else(|| MapperError::MissingField("server".into()))?;
let server_directive = ConfDirective {
name: ConfArgument {
value: "ServerConfig".to_string(),
span: server_child.name.span.clone(),
is_quoted: false,
is_triple_quoted: false,
is_expression: false,
},
arguments: Vec::new(),
children: server_child.children.clone(),
};
let server = ServerConfig::from_directive(&server_directive)?;
Ok(ServiceConfig {
name,
version,
database,
server,
})
}
}
impl ToConf for ServiceConfig {
fn to_directive(&self) -> Result<ConfDirective, MapperError> {
let mut children = Vec::new();
let name_arg = ConfArgument {
value: self.name.clone(),
span: 0..0,
is_quoted: true,
is_triple_quoted: false,
is_expression: false,
};
let name_directive = ConfDirective {
name: ConfArgument {
value: "name".to_string(),
span: 0..0,
is_quoted: false,
is_triple_quoted: false,
is_expression: false,
},
arguments: vec![name_arg],
children: vec![],
};
children.push(name_directive);
let version_arg = ConfArgument {
value: self.version.clone(),
span: 0..0,
is_quoted: true,
is_triple_quoted: false,
is_expression: false,
};
let version_directive = ConfDirective {
name: ConfArgument {
value: "version".to_string(),
span: 0..0,
is_quoted: false,
is_triple_quoted: false,
is_expression: false,
},
arguments: vec![version_arg],
children: vec![],
};
children.push(version_directive);
let database_directive = self.database.to_directive()?;
children.push(database_directive);
let server_directive = self.server.to_directive()?;
children.push(server_directive);
Ok(ConfDirective {
name: ConfArgument {
value: "ServiceConfig".to_string(),
span: 0..0,
is_quoted: false,
is_triple_quoted: false,
is_expression: false,
},
arguments: vec![],
children,
})
}
}
let config_str = r#"
ServiceConfig {
name "MyService";
version "1.0.0";
database {
host "localhost";
port 5432;
username "user";
password "pass";
max-pool-size 10;
}
server {
host "0.0.0.0";
port 8080;
ssl-enabled false;
}
}
"#;
let service_config = from_str::<ServiceConfig>(config_str)?;
println!("Loaded service configuration:");
println!(" Name: {}", service_config.name);
println!(" Version: {}", service_config.version);
println!(" Database:");
println!(" Host: {}", service_config.database.host);
println!(" Port: {}", service_config.database.port);
println!(" Username: {}", service_config.database.username);
println!(" Password: {}", service_config.database.password);
println!(
" Max Pool Size: {:?}",
service_config.database.max_pool_size
);
println!(" Server:");
println!(" Host: {}", service_config.server.host);
println!(" Port: {}", service_config.server.port);
println!(" SSL Enabled: {}", service_config.server.ssl_enabled);
let modified_config = ServiceConfig {
name: "MyService".to_string(),
version: "1.1.0".to_string(),
database: DatabaseConfig {
host: "db.example.com".to_string(),
port: 5432,
username: "admin".to_string(),
password: "secure_password".to_string(),
max_pool_size: Some(20),
},
server: ServerConfig {
host: "api.example.com".to_string(),
port: 443,
ssl_enabled: true,
},
};
let serialized = to_string(&modified_config)?;
println!("\nSerialized configuration:");
println!("{}", serialized);
Ok(())
}
#[cfg(not(feature = "derive"))]
fn main() {
println!("This example requires the 'derive' feature.");
println!("Run with: cargo run --example nested_config --features derive");
}