use std::collections::BTreeMap;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::de::{Deserializer, Error as DeError};
use serde::ser::{Serialize, SerializeMap, Serializer};
use super::{
container::ContainerConfig, dockerfile::DockerfileConfig, healthcheck::Healthcheck,
postgres::PostgresConfig, redis::RedisConfig,
};
#[derive(Debug, Clone, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ResourceKind {
Postgres(PostgresConfig),
Redis(RedisConfig),
Container(ContainerConfig),
Dockerfile(DockerfileConfig),
}
impl ResourceKind {
#[must_use]
pub fn depends_on(&self) -> &[String] {
match self {
Self::Postgres(c) => &c.depends_on,
Self::Redis(c) => &c.depends_on,
Self::Container(c) => &c.depends_on,
Self::Dockerfile(c) => &c.depends_on,
}
}
#[must_use]
pub fn healthcheck(&self) -> Option<&Healthcheck> {
match self {
Self::Postgres(c) => c.healthcheck.as_ref(),
Self::Redis(c) => c.healthcheck.as_ref(),
Self::Container(c) => c.healthcheck.as_ref(),
Self::Dockerfile(c) => c.healthcheck.as_ref(),
}
}
#[must_use]
pub fn kind_name(&self) -> &'static str {
match self {
Self::Postgres(_) => "postgres",
Self::Redis(_) => "redis",
Self::Container(_) => "container",
Self::Dockerfile(_) => "dockerfile",
}
}
}
impl Serialize for ResourceKind {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(1))?;
match self {
Self::Postgres(c) => map.serialize_entry("postgres", c)?,
Self::Redis(c) => map.serialize_entry("redis", c)?,
Self::Container(c) => map.serialize_entry("container", c)?,
Self::Dockerfile(c) => map.serialize_entry("dockerfile", c)?,
}
map.end()
}
}
impl<'de> Deserialize<'de> for ResourceKind {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let entries: BTreeMap<String, serde_norway::Value> = BTreeMap::deserialize(deserializer)?;
let mut iter = entries.into_iter();
let (kind, value) = iter
.next()
.ok_or_else(|| DeError::custom("resource entry must contain exactly one kind"))?;
if iter.next().is_some() {
return Err(DeError::custom(
"resource entry must contain exactly one kind",
));
}
match kind.as_str() {
"postgres" => serde_norway::from_value(value)
.map(Self::Postgres)
.map_err(|e| DeError::custom(e.to_string())),
"redis" => serde_norway::from_value(value)
.map(Self::Redis)
.map_err(|e| DeError::custom(e.to_string())),
"container" => serde_norway::from_value(value)
.map(Self::Container)
.map_err(|e| DeError::custom(e.to_string())),
"dockerfile" => serde_norway::from_value(value)
.map(Self::Dockerfile)
.map_err(|e| DeError::custom(e.to_string())),
other => Err(DeError::custom(format!("unknown resource kind `{other}`"))),
}
}
}