use crate::error::ValidationErrors;
use serde::Deserialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kind {
Bool,
Int,
Float,
String,
Object,
}
#[derive(Debug, Clone)]
pub struct FieldSpec {
pub name: &'static str,
pub env: Option<&'static str>,
pub path: &'static str,
pub doc: Option<&'static str>,
pub kind: Kind,
pub default: Option<&'static str>,
pub required: bool,
}
#[derive(Debug, Clone)]
pub struct CliSpec {
pub flag: &'static str,
pub field: &'static str,
pub kind: Kind,
pub path: &'static str,
pub doc: Option<&'static str>,
pub takes_value: bool,
pub default: Option<&'static str>,
pub required: bool,
}
pub trait ConfigMeta: Sized + for<'de> Deserialize<'de> {
fn defaults_json() -> serde_json::Value;
fn field_specs() -> &'static [FieldSpec];
fn cli_specs() -> &'static [CliSpec];
fn required_fields() -> &'static [&'static str];
fn doc() -> Option<&'static str> {
None
}
}
pub trait Validate {
fn validate(&self) -> Result<(), ValidationErrors>;
}
impl FieldSpec {
pub fn with_prefix(&self, prefix: &'static str) -> Self {
let combined_path = crate::util::leak_string(format!("{prefix}.{}", self.path));
Self {
name: self.name,
env: self.env,
path: combined_path,
doc: self.doc,
kind: self.kind,
default: self.default,
required: self.required,
}
}
pub fn segments(&self) -> Vec<&'static str> {
self.path.split('.').collect()
}
}
impl CliSpec {
pub fn with_prefix(&self, prefix: &'static str) -> Self {
let combined_path = crate::util::leak_string(format!("{prefix}.{}", self.path));
let combined_flag = if self.flag.is_empty() {
crate::util::leak_string(prefix.replace('_', "-"))
} else {
crate::util::leak_string(format!("{}-{}", prefix.replace('_', "-"), self.flag))
};
Self {
flag: combined_flag,
field: self.field,
kind: self.kind,
path: combined_path,
doc: self.doc,
takes_value: self.takes_value,
default: self.default,
required: self.required,
}
}
pub fn segments(&self) -> Vec<&'static str> {
self.path.split('.').collect()
}
}