use super::{ChannelKindSpec, ChannelSpec};
use crate::error::{Error, Result};
use serde_yaml::Value as YamlValue;
pub struct ChannelSpecYamlParser;
impl ChannelSpecYamlParser {
fn validate_required(yaml: &YamlValue) -> Result<()> {
if yaml.get("version").is_none() {
return Err(Error::serialization("missing 'version'"));
}
if let Some(root_map) = yaml.as_mapping() {
for (k, _) in root_map.iter() {
if let Some(key_str) = k.as_str() {
if key_str != "version" && key_str != "channel" {
return Err(Error::serialization(format!(
"unknown root property '{key_str}'"
)));
}
}
}
}
let channel = yaml
.get("channel")
.ok_or_else(|| Error::serialization("missing 'channel' section"))?;
if !channel.is_mapping() {
return Err(Error::serialization("'channel' must be a mapping"));
}
if let Some(ch_map) = channel.as_mapping() {
for (k, _) in ch_map.iter() {
if let Some(key_str) = k.as_str() {
if key_str != "kind" && key_str != "id" {
return Err(Error::serialization(format!(
"unknown channel property '{key_str}'"
)));
}
}
}
}
Ok(())
}
pub fn parse_value(yaml: &YamlValue) -> Result<ChannelSpec> {
Self::validate_required(yaml)?; let version = yaml
.get("version")
.ok_or_else(|| Error::serialization("missing 'version'"))?;
if !version.is_i64() && !version.is_u64() {
return Err(Error::serialization("'version' must be integer"));
}
let v = version
.as_i64()
.unwrap_or(version.as_u64().unwrap_or(0) as i64);
if v != 1 {
return Err(Error::serialization("unsupported version (expected 1)"));
}
let channel = yaml
.get("channel")
.ok_or_else(|| Error::serialization("missing 'channel' section"))?;
if !channel.is_mapping() {
return Err(Error::serialization("'channel' must be a mapping"));
}
let kind_spec = if let Some(kind) = channel.get("kind") {
let kind_str = kind
.as_str()
.ok_or_else(|| Error::serialization("channel.kind must be string"))?;
match kind_str {
"direct" => ChannelKindSpec::Direct,
"queue" => ChannelKindSpec::Queue,
other => {
return Err(Error::serialization(format!(
"unsupported channel.kind '{other}'"
)))
}
}
} else {
tracing::info!("channel.kind missing; defaulting to 'direct'");
ChannelKindSpec::Direct
};
let id_opt = channel
.get("id")
.and_then(|v| v.as_str())
.map(|s| s.to_string());
if let Some(ref id) = id_opt {
if id.is_empty() {
return Err(Error::serialization("channel.id must not be empty"));
}
}
Ok(ChannelSpec {
id: id_opt,
kind: kind_spec,
})
}
pub fn parse_str(raw: &str) -> Result<ChannelSpec> {
let val: YamlValue = serde_yaml::from_str(raw)
.map_err(|e| Error::serialization(format!("yaml parse error: {e}")))?;
Self::parse_value(&val)
}
}