#[cfg(feature = "anchors")]
mod anchors;
mod ext;
pub use ext::*;
use std::{
cmp::Ordering,
collections::BTreeMap,
fmt,
fmt::{Display, Formatter},
ops::Deref,
path::Path,
str::FromStr,
};
use serde::{de, Deserialize, Deserializer, Serialize};
use serde_json::Value;
#[cfg(feature = "openapi")]
use utoipa::ToSchema;
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct DockerCompose {
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub configs: BTreeMap<FieldKey, Config>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub include: Vec<Include>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub secrets: BTreeMap<FieldKey, Secret>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub services: BTreeMap<FieldKey, ComposeService>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub networks: BTreeMap<FieldKey, Network>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub volumes: BTreeMap<FieldKey, Volume>,
}
impl FromStr for DockerCompose {
type Err = anyhow::Error;
fn from_str(yaml: &str) -> Result<Self, Self::Err> {
DockerCompose::from_bytes(yaml.as_bytes())
}
}
impl DockerCompose {
#[cfg(feature = "async")]
pub async fn from_path(path: impl AsRef<Path>) -> anyhow::Result<DockerCompose> {
let yaml = tokio::fs::read_to_string(path).await?;
DockerCompose::from_bytes(yaml.as_bytes())
}
#[cfg(not(feature = "async"))]
pub fn from_path(path: impl AsRef<Path>) -> anyhow::Result<DockerCompose> {
let yaml = std::fs::read_to_string(path)?;
DockerCompose::from_bytes(yaml.as_bytes())
}
#[cfg(not(feature = "anchors"))]
pub fn from_bytes(bytes: &[u8]) -> anyhow::Result<DockerCompose> {
let compose: DockerCompose = serde_yaml::from_slice(bytes)?;
Ok(compose)
}
#[cfg(feature = "anchors")]
pub fn from_bytes(bytes: &[u8]) -> anyhow::Result<DockerCompose> {
let compose: DockerCompose = anchors::from_slice(bytes)?;
Ok(compose)
}
}
impl DockerCompose {
pub fn contains_service(&self, service_name: &str) -> bool {
self.services.contains_key(&service_name.into())
}
pub fn get_service(&self, service_name: &str) -> Option<&ComposeService> {
self.services.get(&service_name.into())
}
pub fn contains_network(&self, network_name: &str) -> bool {
self.networks.contains_key(&network_name.into())
}
pub fn get_network(&self, network_name: &str) -> Option<&Network> {
self.networks.get(&network_name.into())
}
pub fn contains_volume(&self, volume_name: &str) -> bool {
self.volumes.contains_key(&volume_name.into())
}
pub fn get_volume(&self, volume_name: &str) -> Option<&Volume> {
self.volumes.get(&volume_name.into())
}
pub fn contains_secret(&self, secret_name: &str) -> bool {
self.volumes.contains_key(&secret_name.into())
}
pub fn get_secret(&self, secret_name: &str) -> Option<&Secret> {
self.secrets.get(&secret_name.into())
}
pub fn contains_config(&self, config_name: &str) -> bool {
self.volumes.contains_key(&config_name.into())
}
pub fn get_config(&self, config_name: &str) -> Option<&ComposeService> {
self.services.get(&config_name.into())
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct Config {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub environment: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub external: Option<External>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub labels: Option<VecOrMap>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub template_driver: Option<String>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct Secret {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub driver: Option<String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub driver_opts: BTreeMap<OptsKey, NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub environment: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub external: Option<External>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub labels: Option<VecOrMap>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub template_driver: Option<String>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged, deny_unknown_fields)]
pub enum Include {
String(String),
Object {
#[serde(default, skip_serializing_if = "Option::is_none")]
env_file: Option<VecOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
path: Option<VecOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
project_directory: Option<String>,
},
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct Volume(pub Option<VolumeConfig>);
impl Deref for Volume {
type Target = Option<VolumeConfig>;
fn deref(&self) -> &Option<VolumeConfig> {
&self.0
}
}
impl From<Volume> for Option<VolumeConfig> {
fn from(value: Volume) -> Self {
value.0
}
}
impl From<&VolumeConfig> for VolumeConfig {
fn from(value: &VolumeConfig) -> Self {
value.clone()
}
}
impl From<Option<VolumeConfig>> for Volume {
fn from(value: Option<VolumeConfig>) -> Self {
Self(value)
}
}
impl From<VolumeConfig> for Volume {
fn from(value: VolumeConfig) -> Self {
Self(Some(value))
}
}
impl From<&VolumeConfig> for Volume {
fn from(value: &VolumeConfig) -> Self {
Self(Some(value.clone()))
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct VolumeConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub driver: Option<String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub driver_opts: BTreeMap<OptsKey, NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub external: Option<External>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub labels: Option<VecOrMap>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct ComposeService {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub annotations: Option<VecOrMap>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub attach: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub blkio_config: Option<ServiceBlkioConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub build: Option<ServiceBuild>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cap_add: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cap_drop: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cgroup: Option<ServiceCgroup>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cgroup_parent: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub command: Option<Command>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub configs: Option<Vec<ServiceConfigOrSecretItem>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub container_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cpu_count: Option<UIntOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cpu_percent: Option<IntOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cpu_period: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cpu_quota: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cpu_rt_period: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cpu_rt_runtime: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cpu_shares: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cpus: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cpuset: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub credential_spec: Option<ServiceCredentialSpec>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub depends_on: Option<ServiceDependsOn>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub deploy: Option<Deployment>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub develop: Option<Development>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub device_cgroup_rules: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub devices: Vec<ServiceDevicesItem>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dns: Option<VecOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dns_opt: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dns_search: Option<VecOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub domainname: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub entrypoint: Option<Command>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub env_file: Option<EnvFile>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub environment: Option<VecOrMap>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expose: Option<Vec<NumOrStr>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extends: Option<ServiceExtends>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub external_links: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extra_hosts: Option<ExtraHosts>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub group_add: Option<Vec<NumOrStr>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub healthcheck: Option<Healthcheck>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hostname: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub image: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub init: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ipc: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub isolation: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub labels: Option<VecOrMap>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub links: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub logging: Option<ServiceLogging>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mac_address: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mem_limit: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mem_reservation: Option<IntOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mem_swappiness: Option<IntOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memswap_limit: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub network_mode: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub networks: Option<ServiceNetworks>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub oom_kill_disable: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub oom_score_adj: Option<IntOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pid: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pids_limit: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub platform: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ports: Option<Vec<ServicePort>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub privileged: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub profiles: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pull_policy: Option<ServicePullPolicy>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub read_only: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub restart: Option<Restart>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub runtime: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scale: Option<IntOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub secrets: Option<Vec<ServiceConfigOrSecretItem>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub storage_opt: Option<StorageOpt>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub security_opt: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shm_size: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stdin_open: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stop_grace_period: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stop_signal: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sysctls: Option<VecOrMap>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tmpfs: Option<VecOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tty: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ulimits: Option<Ulimits>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub user: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub userns_mode: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub uts: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub volumes: Option<Vec<ServiceVolume>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub volumes_from: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub working_dir: Option<String>,
}
impl ComposeService {
pub fn network(&self, name: impl AsRef<str>) -> Option<&ServiceNetwork> {
match self.networks.as_ref() {
Some(ServiceNetworks::Map(sn)) => sn.get(&name.as_ref().into()),
_ => None,
}
}
pub fn volume(&self, mut p: impl FnMut(&ServiceVolume) -> bool) -> Option<&ServiceVolume> {
match self.volumes.as_ref() {
Some(vs) => vs.iter().find(|v| p(v)),
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct StorageOpt(pub BTreeMap<String, Value>);
impl PartialOrd for StorageOpt {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.len().partial_cmp(&other.0.len())
}
}
impl From<&StorageOpt> for StorageOpt {
fn from(value: &StorageOpt) -> Self {
value.clone()
}
}
impl From<BTreeMap<String, Value>> for StorageOpt {
fn from(value: BTreeMap<String, Value>) -> Self {
Self(value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged, deny_unknown_fields)]
pub enum ServiceExtends {
String(String),
Object {
#[serde(default, skip_serializing_if = "Option::is_none")]
file: Option<String>,
service: String,
},
}
impl From<&ServiceExtends> for ServiceExtends {
fn from(value: &ServiceExtends) -> Self {
value.clone()
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged, deny_unknown_fields)]
pub enum ServiceDevicesItem {
String(String),
Object {
#[serde(default, skip_serializing_if = "Option::is_none")]
permissions: Option<String>,
source: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
target: Option<String>,
},
}
impl From<&ServiceDevicesItem> for ServiceDevicesItem {
fn from(value: &ServiceDevicesItem) -> Self {
value.clone()
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct Development {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub watch: Vec<DevelopmentWatchItem>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct DevelopmentWatchItem {
pub action: DevelopmentWatchItemAction,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub ignore: Vec<String>,
pub path: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target: Option<String>,
}
impl From<&DevelopmentWatchItem> for DevelopmentWatchItem {
fn from(value: &DevelopmentWatchItem) -> Self {
value.clone()
}
}
#[derive(Deserialize, Serialize, Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub enum DevelopmentWatchItemAction {
#[serde(rename = "rebuild")]
Rebuild,
#[serde(rename = "sync")]
Sync,
#[serde(rename = "sync+restart")]
SyncRestart,
}
impl From<&DevelopmentWatchItemAction> for DevelopmentWatchItemAction {
fn from(value: &DevelopmentWatchItemAction) -> Self {
*value
}
}
impl Display for DevelopmentWatchItemAction {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
Self::Rebuild => write!(f, "rebuild"),
Self::Sync => write!(f, "sync"),
Self::SyncRestart => write!(f, "sync+restart"),
}
}
}
impl FromStr for DevelopmentWatchItemAction {
type Err = error::ConversionError;
fn from_str(value: &str) -> Result<Self, error::ConversionError> {
match value {
"rebuild" => Ok(Self::Rebuild),
"sync" => Ok(Self::Sync),
"sync+restart" => Ok(Self::SyncRestart),
_ => Err("invalid value".into()),
}
}
}
#[derive(Deserialize, Serialize, PartialEq, PartialOrd, Clone, Debug)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct Deployment {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub endpoint_mode: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub labels: Option<VecOrMap>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mode: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub placement: Option<DeploymentPlacement>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub replicas: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub resources: Option<DeploymentResources>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub restart_policy: Option<DeploymentRestartPolicy>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rollback_config: Option<DeploymentConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub update_config: Option<DeploymentConfig>,
}
impl From<&Deployment> for Deployment {
fn from(value: &Deployment) -> Self {
value.clone()
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct DeploymentConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub delay: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub failure_action: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_failure_ratio: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub monitor: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub order: Option<DeploymentConfigOrder>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parallelism: Option<NumOrStr>,
}
impl From<&DeploymentConfig> for DeploymentConfig {
fn from(value: &DeploymentConfig) -> Self {
value.clone()
}
}
#[derive(Deserialize, Serialize, Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub enum DeploymentConfigOrder {
#[serde(rename = "start-first")]
StartFirst,
#[serde(rename = "stop-first")]
StopFirst,
}
impl From<&DeploymentConfigOrder> for DeploymentConfigOrder {
fn from(value: &DeploymentConfigOrder) -> Self {
*value
}
}
impl Display for DeploymentConfigOrder {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
Self::StartFirst => write!(f, "start-first"),
Self::StopFirst => write!(f, "stop-first"),
}
}
}
impl FromStr for DeploymentConfigOrder {
type Err = error::ConversionError;
fn from_str(value: &str) -> Result<Self, error::ConversionError> {
match value {
"start-first" => Ok(Self::StartFirst),
"stop-first" => Ok(Self::StopFirst),
_ => Err("invalid value".into()),
}
}
}
#[derive(Deserialize, Serialize, PartialEq, PartialOrd, Clone, Debug)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct DeploymentRestartPolicy {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub condition: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub delay: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_attempts: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub window: Option<String>,
}
impl From<&DeploymentRestartPolicy> for DeploymentRestartPolicy {
fn from(value: &DeploymentRestartPolicy) -> Self {
value.clone()
}
}
#[derive(Deserialize, Serialize, PartialEq, PartialOrd, Clone, Debug)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct DeploymentResources {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub limits: Option<DeploymentResourcesLimits>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reservations: Option<DeploymentResourcesReservations>,
}
#[derive(Deserialize, Serialize, PartialEq, PartialOrd, Clone, Debug)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct DeploymentResourcesReservations {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cpus: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub devices: Option<Vec<DevicesItem>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub generic_resources: Option<Vec<GenericResourcesItem>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memory: Option<String>,
}
#[derive(Deserialize, Serialize, PartialEq, PartialOrd, Clone, Debug)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct GenericResourcesItem {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub discrete_resource_spec: Option<GenericResourcesItemDiscreteResourceSpec>,
}
impl From<&GenericResourcesItem> for GenericResourcesItem {
fn from(value: &GenericResourcesItem) -> Self {
value.clone()
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct GenericResourcesItemDiscreteResourceSpec {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kind: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub value: Option<NumOrStr>,
}
impl From<&GenericResourcesItemDiscreteResourceSpec> for GenericResourcesItemDiscreteResourceSpec {
fn from(value: &GenericResourcesItemDiscreteResourceSpec) -> Self {
value.clone()
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct DevicesItem {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub capabilities: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub count: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub device_ids: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub driver: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub options: Option<VecOrMap>,
}
impl From<&DevicesItem> for DevicesItem {
fn from(value: &DevicesItem) -> Self {
value.clone()
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct DeploymentResourcesLimits {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cpus: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memory: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pids: Option<NumOrStr>,
}
#[derive(Deserialize, Serialize, PartialEq, PartialOrd, Clone, Debug)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct DeploymentPlacement {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub constraints: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_replicas_per_node: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub preferences: Vec<Option<String>>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct ServiceCredentialSpec {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub config: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub registry: Option<String>,
}
#[derive(Deserialize, Serialize, Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub enum ServiceCgroup {
#[serde(rename = "host")]
Host,
#[serde(rename = "private")]
Private,
}
impl From<&ServiceCgroup> for ServiceCgroup {
fn from(value: &ServiceCgroup) -> Self {
*value
}
}
impl Display for ServiceCgroup {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
Self::Host => write!(f, "host"),
Self::Private => write!(f, "private"),
}
}
}
impl FromStr for ServiceCgroup {
type Err = error::ConversionError;
fn from_str(value: &str) -> Result<Self, error::ConversionError> {
match value {
"host" => Ok(Self::Host),
"private" => Ok(Self::Private),
_ => Err("invalid value".into()),
}
}
}
#[derive(Deserialize, Serialize, Clone, PartialEq, PartialOrd, Debug)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct Ulimits(pub BTreeMap<UlimitsKey, UlimitsValue>);
impl Deref for Ulimits {
type Target = BTreeMap<UlimitsKey, UlimitsValue>;
fn deref(&self) -> &BTreeMap<UlimitsKey, UlimitsValue> {
&self.0
}
}
impl From<Ulimits> for BTreeMap<UlimitsKey, UlimitsValue> {
fn from(value: Ulimits) -> Self {
value.0
}
}
impl From<&Ulimits> for Ulimits {
fn from(value: &Ulimits) -> Self {
value.clone()
}
}
impl From<BTreeMap<UlimitsKey, UlimitsValue>> for Ulimits {
fn from(value: BTreeMap<UlimitsKey, UlimitsValue>) -> Self {
Self(value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged, deny_unknown_fields)]
pub enum UlimitsValue {
Val(IntOrStr),
HardSoft { hard: IntOrStr, soft: IntOrStr },
}
impl From<&UlimitsValue> for UlimitsValue {
fn from(value: &UlimitsValue) -> Self {
value.clone()
}
}
impl From<IntOrStr> for UlimitsValue {
fn from(value: IntOrStr) -> Self {
Self::Val(value)
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct UlimitsKey(String);
impl Deref for UlimitsKey {
type Target = String;
fn deref(&self) -> &String {
&self.0
}
}
impl From<UlimitsKey> for String {
fn from(value: UlimitsKey) -> Self {
value.0
}
}
impl FromStr for UlimitsKey {
type Err = error::ConversionError;
fn from_str(value: &str) -> Result<Self, error::ConversionError> {
if regex::Regex::new("^[a-z]+$").unwrap().find(value).is_none() {
return Err("`UlimitsKey` doesn't match pattern \"^[a-z]+$\"".into());
}
Ok(Self(value.to_string()))
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct Healthcheck {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub disable: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub interval: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub retries: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub start_interval: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub start_period: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub test: Option<VecOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeout: Option<String>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged)]
pub enum EnvFile {
String(String),
Vec(Vec<EnvFileItem>),
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged, deny_unknown_fields)]
pub enum EnvFileItem {
String(String),
Object {
path: String,
#[serde(default = "defaults::env_file_item_required")]
required: BoolOrStr,
},
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged, deny_unknown_fields)]
pub enum ServicePort {
Number(f64),
String(String),
Object {
#[serde(default, skip_serializing_if = "Option::is_none")]
app_protocol: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
host_ip: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
mode: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
protocol: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
published: Option<IntOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
target: Option<IntOrStr>,
},
}
#[derive(Deserialize, Serialize, Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub enum ServicePullPolicy {
#[serde(rename = "always")]
Always,
#[serde(rename = "never")]
Never,
#[serde(rename = "if_not_present")]
IfNotPresent,
#[serde(rename = "build")]
Build,
#[serde(rename = "missing")]
Missing,
}
impl Display for ServicePullPolicy {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
Self::Always => f.write_str("always"),
Self::Never => f.write_str("never"),
Self::IfNotPresent => f.write_str("if_not_present"),
Self::Build => f.write_str("build"),
Self::Missing => f.write_str("missing"),
}
}
}
impl FromStr for ServicePullPolicy {
type Err = error::ConversionError;
fn from_str(value: &str) -> Result<Self, error::ConversionError> {
match value {
"always" => Ok(Self::Always),
"never" => Ok(Self::Never),
"if_not_present" => Ok(Self::IfNotPresent),
"build" => Ok(Self::Build),
"missing" => Ok(Self::Missing),
_ => Err("invalid value".into()),
}
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged)]
pub enum ServiceDependsOn {
Vec(Vec<String>),
Map(BTreeMap<FieldKey, ServiceDependsOnVar>),
}
impl From<Vec<String>> for ServiceDependsOn {
fn from(value: Vec<String>) -> Self {
Self::Vec(value)
}
}
impl From<BTreeMap<FieldKey, ServiceDependsOnVar>> for ServiceDependsOn {
fn from(value: BTreeMap<FieldKey, ServiceDependsOnVar>) -> Self {
Self::Map(value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct ServiceDependsOnVar {
pub condition: ServiceDependsOnCondition,
#[serde(default = "defaults::default_bool::<true>")]
pub required: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub restart: Option<BoolOrStr>,
}
#[derive(Deserialize, Serialize, PartialOrd, PartialEq, Clone, Debug)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged, deny_unknown_fields)]
pub enum ServiceBuild {
String(String),
Object(Box<ServiceBuildObject>),
}
#[derive(Deserialize, Serialize, PartialOrd, PartialEq, Clone, Debug)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct ServiceBuildObject {
#[serde(default, skip_serializing_if = "Option::is_none")]
additional_contexts: Option<VecOrMap>,
#[serde(default, skip_serializing_if = "Option::is_none")]
args: Option<VecOrMap>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
cache_from: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
cache_to: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
context: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
dockerfile: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
dockerfile_inline: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
entitlements: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
extra_hosts: Option<ExtraHosts>,
#[serde(default, skip_serializing_if = "Option::is_none")]
isolation: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
labels: Option<VecOrMap>,
#[serde(default, skip_serializing_if = "Option::is_none")]
network: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
no_cache: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
platforms: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
privileged: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pull: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
secrets: Option<Vec<ServiceConfigOrSecretItem>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
shm_size: Option<IntOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
ssh: Option<VecOrMap>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
tags: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
target: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
ulimits: Option<Ulimits>,
}
impl From<&ServiceBuild> for ServiceBuild {
fn from(value: &ServiceBuild) -> Self {
value.clone()
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged, deny_unknown_fields)]
pub enum ServiceConfigOrSecretItem {
String(String),
Object {
#[serde(default, skip_serializing_if = "Option::is_none")]
gid: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
mode: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
source: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
target: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
uid: Option<String>,
},
}
#[derive(Deserialize, Serialize, Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub enum ServiceDependsOnCondition {
#[serde(rename = "service_started")]
Started,
#[serde(rename = "service_healthy")]
Healthy,
#[serde(rename = "service_completed_successfully")]
CompletedSuccessfully,
}
impl Display for ServiceDependsOnCondition {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
Self::Started => write!(f, "service_started"),
Self::Healthy => write!(f, "service_healthy"),
Self::CompletedSuccessfully => {
write!(f, "service_completed_successfully")
}
}
}
}
impl FromStr for ServiceDependsOnCondition {
type Err = error::ConversionError;
fn from_str(value: &str) -> Result<Self, error::ConversionError> {
match value {
"service_started" => Ok(Self::Started),
"service_healthy" => Ok(Self::Healthy),
"service_completed_successfully" => Ok(Self::CompletedSuccessfully),
_ => Err("invalid value".into()),
}
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged, deny_unknown_fields)]
pub enum ServiceVolume {
String(String),
Object {
#[serde(default, skip_serializing_if = "Option::is_none")]
bind: Option<ServiceVolumeBind>,
#[serde(default, skip_serializing_if = "Option::is_none")]
consistency: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
read_only: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
source: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
target: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
tmpfs: Option<ServiceVolumeTmpfs>,
#[serde(rename = "type")]
type_: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
volume: Box<Option<ServiceVolumeValue>>,
},
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct ServiceVolumeValue {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub nocopy: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub subpath: Option<String>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct ServiceVolumeTmpfs {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mode: Option<NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub size: Option<UIntOrStr>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct ServiceVolumeBind {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub create_host_path: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub propagation: Option<String>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged)]
pub enum ServiceNetworks {
Vec(Vec<String>),
Map(BTreeMap<FieldKey, ServiceNetwork>),
}
impl From<Vec<String>> for ServiceNetworks {
fn from(value: Vec<String>) -> Self {
Self::Vec(value)
}
}
impl From<BTreeMap<FieldKey, ServiceNetwork>> for ServiceNetworks {
fn from(value: BTreeMap<FieldKey, ServiceNetwork>) -> Self {
Self::Map(value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct ServiceNetworkOpt(pub Option<ServiceNetwork>);
impl Deref for ServiceNetworkOpt {
type Target = Option<ServiceNetwork>;
fn deref(&self) -> &Option<ServiceNetwork> {
&self.0
}
}
impl From<ServiceNetworkOpt> for Option<ServiceNetwork> {
fn from(value: ServiceNetworkOpt) -> Self {
value.0
}
}
impl From<Option<ServiceNetwork>> for ServiceNetworkOpt {
fn from(value: Option<ServiceNetwork>) -> Self {
Self(value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct ServiceNetwork {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub aliases: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub driver_opts: BTreeMap<OptsKey, NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ipv4_address: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ipv6_address: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub link_local_ips: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mac_address: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub priority: Option<f64>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub enum Restart {
#[serde(rename = "no")]
No,
#[serde(rename = "always")]
Always,
#[serde(rename = "on-failure")]
OnFailure,
#[serde(rename = "unless-stopped")]
UnlessStopped,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct ServiceLogging {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub driver: Option<String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub options: BTreeMap<OptsKey, OptNumOrStr>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged)]
pub enum ExtraHosts {
Map(BTreeMap<NonEmptyKey, ExtraHostsVal>),
Vec(Vec<String>),
}
impl From<BTreeMap<NonEmptyKey, ExtraHostsVal>> for ExtraHosts {
fn from(value: BTreeMap<NonEmptyKey, ExtraHostsVal>) -> Self {
Self::Map(value)
}
}
impl From<Vec<String>> for ExtraHosts {
fn from(value: Vec<String>) -> Self {
Self::Vec(value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged)]
pub enum ExtraHostsVal {
Array(Vec<serde_json::Value>),
String(String),
}
impl PartialOrd for ExtraHostsVal {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(ExtraHostsVal::String(s), ExtraHostsVal::String(ss)) => s.partial_cmp(ss),
(ExtraHostsVal::String(..), ExtraHostsVal::Array(..)) => Some(Ordering::Less),
(ExtraHostsVal::Array(..), ExtraHostsVal::String(..)) => Some(Ordering::Greater),
(ExtraHostsVal::Array(js), ExtraHostsVal::Array(jss)) => {
js.len().partial_cmp(&jss.len())
}
}
}
}
impl From<&ExtraHostsVal> for ExtraHostsVal {
fn from(value: &ExtraHostsVal) -> Self {
value.clone()
}
}
impl From<Vec<serde_json::Value>> for ExtraHostsVal {
fn from(value: Vec<serde_json::Value>) -> Self {
Self::Array(value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged)]
pub enum Command {
Null,
String(String),
Vec(Vec<String>),
}
impl From<Vec<String>> for Command {
fn from(value: Vec<String>) -> Self {
Self::Vec(value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct ServiceBlkioConfig {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub device_read_bps: Vec<BlkioLimit>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub device_read_iops: Vec<BlkioLimit>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub device_write_bps: Vec<BlkioLimit>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub device_write_iops: Vec<BlkioLimit>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub weight: Option<IntOrStr>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub weight_device: Vec<BlkioWeight>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct BlkioWeight {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub weight: Option<IntOrStr>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct BlkioLimit {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rate: Option<IntOrStr>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct Network(pub Option<NetworkConfig>);
impl Deref for Network {
type Target = Option<NetworkConfig>;
fn deref(&self) -> &Option<NetworkConfig> {
&self.0
}
}
impl From<Network> for Option<NetworkConfig> {
fn from(value: Network) -> Self {
value.0
}
}
impl From<&Network> for Network {
fn from(value: &Network) -> Self {
value.clone()
}
}
impl From<Option<NetworkConfig>> for Network {
fn from(value: Option<NetworkConfig>) -> Self {
Self(value)
}
}
impl From<NetworkConfig> for Network {
fn from(value: NetworkConfig) -> Self {
Self(Some(value))
}
}
impl From<&NetworkConfig> for Network {
fn from(value: &NetworkConfig) -> Self {
Self(Some(value.clone()))
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct NetworkConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub attachable: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub driver: Option<String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub driver_opts: BTreeMap<OptsKey, NumOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enable_ipv6: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub external: Option<External>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub internal: Option<BoolOrStr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ipam: Option<Ipam>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub labels: Option<VecOrMap>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct Ipam {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub config: Vec<IpamConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub driver: Option<String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub options: BTreeMap<OptsKey, String>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(deny_unknown_fields)]
pub struct IpamConfig {
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub aux_addresses: BTreeMap<OptsKey, String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub gateway: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ip_range: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub subnet: Option<String>,
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged)]
pub enum External {
Boolean(bool),
Object {
#[serde(default, skip_serializing_if = "Option::is_none")]
name: Option<String>,
},
String(String),
}
impl From<bool> for External {
fn from(value: bool) -> Self {
Self::Boolean(value)
}
}
#[derive(Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct FieldKey(String);
impl Display for FieldKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(self.0.as_str())
}
}
impl Deref for FieldKey {
type Target = String;
fn deref(&self) -> &String {
&self.0
}
}
impl From<FieldKey> for String {
fn from(value: FieldKey) -> Self {
value.0
}
}
impl From<&FieldKey> for String {
fn from(value: &FieldKey) -> Self {
value.0.clone()
}
}
impl From<&str> for FieldKey {
fn from(value: &str) -> Self {
FieldKey(value.to_string())
}
}
impl AsRef<FieldKey> for FieldKey {
fn as_ref(&self) -> &FieldKey {
self
}
}
impl FromStr for FieldKey {
type Err = error::ConversionError;
fn from_str(value: &str) -> Result<Self, error::ConversionError> {
if regex::Regex::new("^[a-zA-Z0-9._-]+$").unwrap().find(value).is_none() {
return Err("`FieldKey` doesn't match pattern \"^[a-zA-Z0-9._-]+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl<'de> Deserialize<'de> for FieldKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
String::deserialize(deserializer)?
.parse()
.map_err(|e: error::ConversionError| <D::Error as de::Error>::custom(e.to_string()))
}
}
#[derive(Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct OptsKey(String);
impl Deref for OptsKey {
type Target = String;
fn deref(&self) -> &String {
&self.0
}
}
impl From<OptsKey> for String {
fn from(value: OptsKey) -> Self {
value.0
}
}
impl From<&OptsKey> for String {
fn from(value: &OptsKey) -> Self {
value.0.clone()
}
}
impl From<&str> for OptsKey {
fn from(value: &str) -> Self {
OptsKey(value.to_string())
}
}
impl AsRef<OptsKey> for OptsKey {
fn as_ref(&self) -> &OptsKey {
self
}
}
impl FromStr for OptsKey {
type Err = error::ConversionError;
fn from_str(value: &str) -> Result<Self, error::ConversionError> {
if regex::Regex::new("^.+$").unwrap().find(value).is_none() {
return Err("`OptsKey` doesn't match pattern \"^.+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl<'de> Deserialize<'de> for OptsKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
String::deserialize(deserializer)?
.parse()
.map_err(|e: error::ConversionError| <D::Error as de::Error>::custom(e.to_string()))
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged)]
pub enum NumOrStr {
Number(f64),
String(String),
}
impl FromStr for NumOrStr {
type Err = error::ConversionError;
fn from_str(value: &str) -> Result<Self, error::ConversionError> {
if let Ok(v @ Self::Number(_)) = value.parse() {
Ok(v)
} else if let Ok(v @ Self::String(_)) = value.parse() {
Ok(v)
} else {
Err("`NumOrStr` string conversion failed for all variants".into())
}
}
}
impl Display for NumOrStr {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Number(x) => x.fmt(f),
Self::String(x) => x.fmt(f),
}
}
}
impl From<f64> for NumOrStr {
fn from(value: f64) -> Self {
Self::Number(value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged)]
pub enum BoolOrStr {
Boolean(bool),
String(String),
}
impl FromStr for BoolOrStr {
type Err = error::ConversionError;
fn from_str(value: &str) -> Result<Self, error::ConversionError> {
if let Ok(v @ Self::Boolean(_)) = value.parse() {
Ok(v)
} else if let Ok(v @ Self::String(_)) = value.parse() {
Ok(v)
} else {
Err("`BoolOrStr` string conversion failed for all variants".into())
}
}
}
impl Display for BoolOrStr {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Boolean(x) => x.fmt(f),
Self::String(x) => x.fmt(f),
}
}
}
impl From<bool> for BoolOrStr {
fn from(value: bool) -> Self {
Self::Boolean(value)
}
}
#[derive(Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct NonEmptyKey(String);
impl Deref for NonEmptyKey {
type Target = String;
fn deref(&self) -> &String {
&self.0
}
}
impl From<NonEmptyKey> for String {
fn from(value: NonEmptyKey) -> Self {
value.0
}
}
impl FromStr for NonEmptyKey {
type Err = error::ConversionError;
fn from_str(value: &str) -> Result<Self, error::ConversionError> {
if regex::Regex::new(".+").unwrap().find(value).is_none() {
return Err("`NonEmptyKey` doesn't match pattern \".+\"".into());
}
Ok(Self(value.to_string()))
}
}
impl From<&str> for NonEmptyKey {
fn from(value: &str) -> Self {
NonEmptyKey(value.to_string())
}
}
impl AsRef<NonEmptyKey> for NonEmptyKey {
fn as_ref(&self) -> &NonEmptyKey {
self
}
}
impl From<&NonEmptyKey> for String {
fn from(value: &NonEmptyKey) -> Self {
value.0.clone()
}
}
impl<'de> Deserialize<'de> for NonEmptyKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
String::deserialize(deserializer)?
.parse()
.map_err(|e: error::ConversionError| <D::Error as de::Error>::custom(e.to_string()))
}
}
impl Display for NonEmptyKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged)]
pub enum VecOrMap {
Map(BTreeMap<NonEmptyKey, VecOrMapVal>),
Vec(Vec<String>),
}
impl From<BTreeMap<NonEmptyKey, VecOrMapVal>> for VecOrMap {
fn from(value: BTreeMap<NonEmptyKey, VecOrMapVal>) -> Self {
Self::Map(value)
}
}
impl From<Vec<String>> for VecOrMap {
fn from(value: Vec<String>) -> Self {
Self::Vec(value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged)]
pub enum VecOrMapVal {
Null,
Boolean(bool),
Number(f64),
String(String),
}
impl From<bool> for VecOrMapVal {
fn from(value: bool) -> Self {
Self::Boolean(value)
}
}
impl From<f64> for VecOrMapVal {
fn from(value: f64) -> Self {
Self::Number(value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged)]
pub enum IntOrStr {
Integer(i64),
String(String),
}
impl FromStr for IntOrStr {
type Err = error::ConversionError;
fn from_str(value: &str) -> Result<Self, error::ConversionError> {
if let Ok(v @ Self::Integer(_)) = value.parse() {
Ok(v)
} else if let Ok(v @ Self::String(_)) = value.parse() {
Ok(v)
} else {
Err("`IntOrStr` string conversion failed for all variants".into())
}
}
}
impl Display for IntOrStr {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::String(x) => x.fmt(f),
Self::Integer(x) => x.fmt(f),
}
}
}
impl From<i64> for IntOrStr {
fn from(value: i64) -> Self {
Self::Integer(value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged)]
pub enum OptNumOrStr {
Null,
Number(f64),
String(String),
}
impl From<f64> for OptNumOrStr {
fn from(value: f64) -> Self {
Self::Number(value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged)]
pub enum UIntOrStr {
UInt(u64),
String(String),
}
impl FromStr for UIntOrStr {
type Err = error::ConversionError;
fn from_str(value: &str) -> Result<Self, error::ConversionError> {
if let Ok(v @ Self::UInt(_)) = value.parse() {
Ok(v)
} else if let Ok(v @ Self::String(_)) = value.parse() {
Ok(v)
} else {
Err("`UIntOrStr` string conversion failed for all variants".into())
}
}
}
impl Display for UIntOrStr {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::UInt(x) => x.fmt(f),
Self::String(x) => x.fmt(f),
}
}
}
impl From<u64> for UIntOrStr {
fn from(value: u64) -> Self {
Self::UInt(value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialOrd, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[serde(untagged)]
pub enum VecOrStr {
Vec(Vec<String>),
String(String),
}
impl From<Vec<String>> for VecOrStr {
fn from(value: Vec<String>) -> Self {
Self::Vec(value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct Constraints(pub Value);
impl Deref for Constraints {
type Target = Value;
fn deref(&self) -> &Value {
&self.0
}
}
impl From<Constraints> for Value {
fn from(value: Constraints) -> Self {
value.0
}
}
impl From<&Constraints> for Constraints {
fn from(value: &Constraints) -> Self {
value.clone()
}
}
impl From<Value> for Constraints {
fn from(value: Value) -> Self {
Self(value)
}
}
pub mod error {
use std::{
borrow::Cow,
error::Error,
fmt,
fmt::{Debug, Display, Formatter},
};
pub struct ConversionError(Cow<'static, str>);
impl Error for ConversionError {}
impl Display for ConversionError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
Display::fmt(&self.0, f)
}
}
impl Debug for ConversionError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
Debug::fmt(&self.0, f)
}
}
impl From<&'static str> for ConversionError {
fn from(value: &'static str) -> Self {
Self(value.into())
}
}
impl From<String> for ConversionError {
fn from(value: String) -> Self {
Self(value.into())
}
}
}
pub mod defaults {
pub(super) fn default_bool<const V: bool>() -> bool {
V
}
pub(super) fn env_file_item_required() -> super::BoolOrStr {
super::BoolOrStr::Boolean(true)
}
}