use std::{
fmt::{self, Display, Formatter},
path::PathBuf,
};
use serde::{de, ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer};
use crate::{Extensions, ListOrMap, Resource};
impl From<Config> for Resource<Config> {
fn from(value: Config) -> Self {
Self::Compose(value)
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Config {
#[serde(flatten)]
pub source: Source,
#[serde(default, skip_serializing_if = "ListOrMap::is_empty")]
pub labels: ListOrMap,
#[serde(flatten)]
pub extensions: Extensions,
}
impl From<Source> for Config {
fn from(source: Source) -> Self {
Self {
source,
labels: ListOrMap::default(),
extensions: Extensions::default(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Source {
File(PathBuf),
Environment(String),
Content(String),
}
impl Source {
const NAME: &'static str = "Source";
}
impl From<PathBuf> for Source {
fn from(value: PathBuf) -> Self {
Self::File(value)
}
}
#[derive(Debug, Clone, Copy)]
enum SourceField {
File,
Environment,
Content,
}
impl SourceField {
const fn as_str(self) -> &'static str {
match self {
Self::File => "file",
Self::Environment => "environment",
Self::Content => "content",
}
}
}
impl Display for SourceField {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str(self.as_str())
}
}
macro_rules! format_fields {
($args:literal) => {
format_args!(
$args,
SourceField::File,
SourceField::Environment,
SourceField::Content,
)
};
}
impl Serialize for Source {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut state = serializer.serialize_struct(Self::NAME, 1)?;
match self {
Self::File(source) => state.serialize_field(SourceField::File.as_str(), source)?,
Self::Environment(source) => {
state.serialize_field(SourceField::Environment.as_str(), source)?;
}
Self::Content(source) => {
state.serialize_field(SourceField::Content.as_str(), source)?;
}
}
state.end()
}
}
impl<'de> Deserialize<'de> for Source {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let SourceFlat {
file,
environment,
content,
} = SourceFlat::deserialize(deserializer)?;
match (file, environment, content) {
(Some(file), None, None) => Ok(file.into()),
(None, Some(environment), None) => Ok(Self::Environment(environment)),
(None, None, Some(content)) => Ok(Self::Content(content)),
(None, None, None) => Err(de::Error::custom(format_fields!(
"missing required field `{}`, `{}`, or `{}`"
))),
_ => Err(de::Error::custom(format_fields!(
"can only set one of `{}`, `{}`, or `{}`"
))),
}
}
}
#[derive(Deserialize)]
#[serde(
rename = "Source",
expecting = "a struct with a `file`, `environment`, or `content` field"
)]
struct SourceFlat {
#[serde(default)]
file: Option<PathBuf>,
#[serde(default)]
environment: Option<String>,
#[serde(default)]
content: Option<String>,
}