/// A directive value - can be a string, number, or boolean
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
String(String),
Number(i64),
Float(f64),
Bool(bool),
Size(u64), // e.g. "1024k", "10m"
Time(u64), // in milliseconds, e.g. "30s", "1m"
}
impl Value {
pub fn as_str(&self) -> &str {
match self {
Value::String(s) => s,
_ => "", // Numbers don't have a string representation; use to_string() instead
}
}
/// Get a string representation of the value (allocates for non-string values).
pub fn to_string_lossy(&self) -> String {
match self {
Value::String(s) => s.clone(),
Value::Number(n) => n.to_string(),
Value::Float(f) => f.to_string(),
Value::Bool(b) => b.to_string(),
Value::Size(s) => s.to_string(),
Value::Time(t) => t.to_string(),
}
}
pub fn as_i64(&self) -> i64 {
match self {
Value::Number(n) => *n,
Value::Float(f) => *f as i64,
Value::Size(s) => *s as i64,
Value::Time(t) => *t as i64,
Value::Bool(b) => *b as i64,
_ => 0,
}
}
pub fn as_u64(&self) -> u64 {
self.as_i64() as u64
}
pub fn as_usize(&self) -> usize {
self.as_i64() as usize
}
pub fn as_bool(&self) -> bool {
match self {
Value::Bool(b) => *b,
Value::String(s) => matches!(s.as_str(), "on" | "true" | "yes" | "1"),
Value::Number(n) => *n != 0,
_ => false,
}
}
pub fn as_time_ms(&self) -> u64 {
match self {
Value::Time(t) => *t,
Value::Number(n) => *n as u64,
_ => 0,
}
}
pub fn as_size_bytes(&self) -> u64 {
match self {
Value::Size(s) => *s,
Value::Number(n) => *n as u64,
_ => 0,
}
}
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::String(s) => write!(f, "{}", s),
Value::Number(n) => write!(f, "{}", n),
Value::Float(fl) => write!(f, "{}", fl),
Value::Bool(b) => write!(f, "{}", b),
Value::Size(s) => write!(f, "{}", s),
Value::Time(t) => write!(f, "{}", t),
}
}
}
/// A directive is a key-value pair with optional block
/// e.g. "worker_processes 4;" or "server { ... }"
#[derive(Debug, Clone)]
pub struct Directive {
pub name: String,
pub args: Vec<Value>,
pub block: Option<Block>,
pub line: usize,
}
impl Directive {
pub fn new(name: String, args: Vec<Value>, line: usize) -> Self {
Self {
name,
args,
block: None,
line,
}
}
pub fn with_block(mut self, block: Block) -> Self {
self.block = Some(block);
self
}
pub fn first_arg(&self) -> Option<&Value> {
self.args.first()
}
pub fn first_arg_str(&self) -> &str {
self.args.first().map(|v| v.as_str()).unwrap_or("")
}
}
/// A block is a collection of directives enclosed in { }
#[derive(Debug, Clone)]
pub struct Block {
pub directives: Vec<Directive>,
}
impl Block {
pub fn new() -> Self {
Self {
directives: Vec::new(),
}
}
pub fn is_empty(&self) -> bool {
self.directives.is_empty()
}
/// Get all directives with a given name
pub fn get_all(&self, name: &str) -> Vec<&Directive> {
self.directives.iter().filter(|d| d.name == name).collect()
}
/// Get the first directive with a given name
pub fn get(&self, name: &str) -> Option<&Directive> {
self.directives.iter().find(|d| d.name == name)
}
/// Get the first directive's first argument as string
pub fn get_str(&self, name: &str) -> Option<&str> {
self.get(name).map(|d| d.first_arg_str())
}
/// Get the first directive's first argument as i64
pub fn get_i64(&self, name: &str) -> Option<i64> {
self.get(name)
.and_then(|d| d.first_arg())
.map(|v| v.as_i64())
}
/// Get the first directive's first argument as u64
pub fn get_u64(&self, name: &str) -> Option<u64> {
self.get(name)
.and_then(|d| d.first_arg())
.map(|v| v.as_u64())
}
/// Get the first directive's first argument as bool
pub fn get_bool(&self, name: &str) -> Option<bool> {
self.get(name)
.and_then(|d| d.first_arg())
.map(|v| v.as_bool())
}
/// Get all blocks from directives with a given name
pub fn get_blocks(&self, name: &str) -> Vec<&Block> {
self.directives
.iter()
.filter(|d| d.name == name)
.filter_map(|d| d.block.as_ref())
.collect()
}
/// Check if a directive exists
pub fn has(&self, name: &str) -> bool {
self.directives.iter().any(|d| d.name == name)
}
}
impl Default for Block {
fn default() -> Self {
Self::new()
}
}
/// The root configuration
pub type Config = Block;