use crate::error::Result;
use crate::model::{BoolOptions, CclObject, ListOptions};
use std::str::FromStr;
#[derive(Debug, Clone)]
pub struct CclReader<'a> {
model: &'a CclObject,
bool_options: BoolOptions,
list_options: ListOptions,
}
impl<'a> CclReader<'a> {
pub fn new(model: &'a CclObject) -> Self {
Self {
model,
bool_options: BoolOptions::new(),
list_options: ListOptions::new(),
}
}
pub fn with_bool_options(mut self, options: BoolOptions) -> Self {
self.bool_options = options;
self
}
pub fn with_list_options(mut self, options: ListOptions) -> Self {
self.list_options = options;
self
}
pub fn model(&self) -> &CclObject {
self.model
}
pub fn get_reader(&self, key: &str) -> Result<CclReader<'a>> {
let child = self.model.get(key)?;
Ok(CclReader {
model: child,
bool_options: self.bool_options,
list_options: self.list_options,
})
}
pub fn get_string(&self, key: &str) -> Result<&str> {
self.model.get_string(key)
}
pub fn get_int(&self, key: &str) -> Result<i64> {
self.model.get_int(key)
}
pub fn get_float(&self, key: &str) -> Result<f64> {
self.model.get_float(key)
}
pub fn get_bool(&self, key: &str) -> Result<bool> {
self.model.get_bool_with_options(key, self.bool_options)
}
pub fn get_list(&self, key: &str) -> Result<Vec<String>> {
self.model.get_list_with_options(key, self.list_options)
}
pub fn get_list_typed<T>(&self, key: &str) -> Result<Vec<T>>
where
T: FromStr,
T::Err: std::fmt::Display,
{
self.model
.get_list_typed_with_options(key, self.list_options)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_model(ccl: &str) -> CclObject {
crate::load(ccl).expect("should parse")
}
#[test]
fn test_reader_get_string() {
let model = make_model("name = Alice");
let reader = CclReader::new(&model);
assert_eq!(reader.get_string("name").unwrap(), "Alice");
}
#[test]
fn test_reader_get_int() {
let model = make_model("port = 8080");
let reader = CclReader::new(&model);
assert_eq!(reader.get_int("port").unwrap(), 8080);
}
#[test]
fn test_reader_get_float() {
let model = make_model("rate = 3.14");
let reader = CclReader::new(&model);
let val = reader.get_float("rate").unwrap();
assert!((val - 3.14).abs() < f64::EPSILON);
}
#[test]
fn test_reader_get_bool_strict() {
let model = make_model("enabled = true");
let reader = CclReader::new(&model);
assert!(reader.get_bool("enabled").unwrap());
}
#[test]
fn test_reader_get_bool_strict_rejects_yes() {
let model = make_model("enabled = yes");
let reader = CclReader::new(&model);
assert!(reader.get_bool("enabled").is_err());
}
#[test]
fn test_reader_get_bool_lenient() {
let model = make_model("enabled = yes");
let reader = CclReader::new(&model).with_bool_options(BoolOptions::new().with_lenient());
assert!(reader.get_bool("enabled").unwrap());
}
#[test]
fn test_reader_get_list() {
let model = make_model("servers =\n = web1\n = web2");
let reader = CclReader::new(&model);
assert_eq!(reader.get_list("servers").unwrap(), vec!["web1", "web2"]);
}
#[test]
fn test_reader_sub_reader() {
let model = make_model("config =\n host = localhost\n port = 8080");
let reader = CclReader::new(&model).with_bool_options(BoolOptions::new().with_lenient());
let config = reader.get_reader("config").unwrap();
assert_eq!(config.get_string("host").unwrap(), "localhost");
assert_eq!(config.get_int("port").unwrap(), 8080);
assert_eq!(config.bool_options.lenient, true);
}
#[test]
fn test_reader_missing_key() {
let model = make_model("name = Alice");
let reader = CclReader::new(&model);
assert!(reader.get_string("missing").is_err());
}
}