mod common;
pub mod config;
pub mod duration;
mod include;
mod name;
pub mod network;
pub mod secret;
mod serde;
pub mod service;
mod volume;
use std::path::PathBuf;
use ::serde::{Deserialize, Serialize};
use indexmap::IndexMap;
pub use self::{
common::{
AsShort, AsShortIter, ExtensionKey, Extensions, Identifier, InvalidExtensionKeyError,
InvalidIdentifierError, InvalidMapKeyError, ItemOrList, ListOrMap, Map, MapKey, Number,
ParseNumberError, Resource, ShortOrLong, StringOrNumber, TryFromNumberError,
TryFromValueError, Value, YamlValue,
},
config::Config,
include::Include,
name::{InvalidNameError, Name},
network::Network,
secret::Secret,
service::Service,
volume::Volume,
};
pub type Networks = IndexMap<Identifier, Option<Resource<Network>>>;
pub type Volumes = IndexMap<Identifier, Option<Resource<Volume>>>;
pub type Configs = IndexMap<Identifier, Resource<Config>>;
pub type Secrets = IndexMap<Identifier, Resource<Secret>>;
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct Compose {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub include: Vec<ShortOrLong<PathBuf, Include>>,
pub services: IndexMap<Identifier, Service>,
#[serde(default, skip_serializing_if = "Networks::is_empty")]
pub networks: Networks,
#[serde(default, skip_serializing_if = "Volumes::is_empty")]
pub volumes: Volumes,
#[serde(default, skip_serializing_if = "Configs::is_empty")]
pub configs: Configs,
#[serde(default, skip_serializing_if = "Secrets::is_empty")]
pub secrets: Secrets,
#[serde(flatten)]
pub extensions: Extensions,
}
macro_rules! impl_from {
($Ty:ident::$f:ident, $($From:ty),+ $(,)?) => {
$(
impl From<$From> for $Ty {
fn from(value: $From) -> Self {
Self::$f(value)
}
}
)+
};
}
use impl_from;
macro_rules! impl_try_from {
($Ty:ident::$f:ident -> $Error:ty, $($From:ty),+ $(,)?) => {
$(
impl TryFrom<$From> for $Ty {
type Error = $Error;
fn try_from(value: $From) -> Result<Self, Self::Error> {
Self::$f(value)
}
}
)+
};
}
use impl_try_from;
macro_rules! impl_from_str {
($($Ty:ident => $Error:ty),* $(,)?) => {
$(
impl ::std::str::FromStr for $Ty {
type Err = $Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
crate::impl_try_from! {
$Ty::parse -> $Error,
&str,
String,
Box<str>,
::std::borrow::Cow<'_, str>,
}
)*
};
($($Ty:ident),* $(,)?) => {
$(
impl ::std::str::FromStr for $Ty {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::parse(s))
}
}
crate::impl_from!($Ty::parse, &str, String, Box<str>, ::std::borrow::Cow<'_, str>);
)*
};
}
use impl_from_str;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn full_round_trip() -> serde_yaml::Result<()> {
let yaml = include_str!("test-full.yaml");
let compose: Compose = serde_yaml::from_str(yaml)?;
assert_eq!(
serde_yaml::from_str::<serde_yaml::Value>(yaml)?,
serde_yaml::to_value(compose)?,
);
Ok(())
}
}