use super::BindingValue;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "service", rename_all = "lowercase")]
pub enum ContainerBinding {
Horizon(HorizonContainerBinding),
Kubernetes(KubernetesContainerBinding),
Local(LocalContainerBinding),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct HorizonContainerBinding {
pub container_name: BindingValue<String>,
pub internal_url: BindingValue<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub public_url: Option<BindingValue<String>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct KubernetesContainerBinding {
pub name: BindingValue<String>,
pub namespace: BindingValue<String>,
pub service_name: BindingValue<String>,
pub service_port: BindingValue<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub public_url: Option<BindingValue<String>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LocalContainerBinding {
pub container_name: BindingValue<String>,
pub internal_url: BindingValue<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub public_url: Option<BindingValue<String>>,
}
impl ContainerBinding {
pub fn horizon(
container_name: impl Into<BindingValue<String>>,
internal_url: impl Into<BindingValue<String>>,
) -> Self {
Self::Horizon(HorizonContainerBinding {
container_name: container_name.into(),
internal_url: internal_url.into(),
public_url: None,
})
}
pub fn horizon_with_public_url(
container_name: impl Into<BindingValue<String>>,
internal_url: impl Into<BindingValue<String>>,
public_url: impl Into<BindingValue<String>>,
) -> Self {
Self::Horizon(HorizonContainerBinding {
container_name: container_name.into(),
internal_url: internal_url.into(),
public_url: Some(public_url.into()),
})
}
pub fn local(
container_name: impl Into<BindingValue<String>>,
internal_url: impl Into<BindingValue<String>>,
) -> Self {
Self::Local(LocalContainerBinding {
container_name: container_name.into(),
internal_url: internal_url.into(),
public_url: None,
})
}
pub fn local_with_public_url(
container_name: impl Into<BindingValue<String>>,
internal_url: impl Into<BindingValue<String>>,
public_url: impl Into<BindingValue<String>>,
) -> Self {
Self::Local(LocalContainerBinding {
container_name: container_name.into(),
internal_url: internal_url.into(),
public_url: Some(public_url.into()),
})
}
pub fn kubernetes(
name: impl Into<BindingValue<String>>,
namespace: impl Into<BindingValue<String>>,
service_name: impl Into<BindingValue<String>>,
service_port: impl Into<BindingValue<u16>>,
) -> Self {
Self::Kubernetes(KubernetesContainerBinding {
name: name.into(),
namespace: namespace.into(),
service_name: service_name.into(),
service_port: service_port.into(),
public_url: None,
})
}
pub fn kubernetes_with_public_url(
name: impl Into<BindingValue<String>>,
namespace: impl Into<BindingValue<String>>,
service_name: impl Into<BindingValue<String>>,
service_port: impl Into<BindingValue<u16>>,
public_url: impl Into<BindingValue<String>>,
) -> Self {
Self::Kubernetes(KubernetesContainerBinding {
name: name.into(),
namespace: namespace.into(),
service_name: service_name.into(),
service_port: service_port.into(),
public_url: Some(public_url.into()),
})
}
pub fn get_internal_url(&self) -> Option<String> {
match self {
ContainerBinding::Horizon(binding) => {
if let BindingValue::Value(url) = &binding.internal_url {
Some(url.clone())
} else {
None
}
}
ContainerBinding::Kubernetes(binding) => {
if let (
BindingValue::Value(service_name),
BindingValue::Value(namespace),
BindingValue::Value(port),
) = (
&binding.service_name,
&binding.namespace,
&binding.service_port,
) {
Some(format!(
"http://{}.{}.svc.cluster.local:{}",
service_name, namespace, port
))
} else {
None
}
}
ContainerBinding::Local(binding) => {
if let BindingValue::Value(url) = &binding.internal_url {
Some(url.clone())
} else {
None
}
}
}
}
pub fn get_public_url(&self) -> Option<&BindingValue<String>> {
match self {
ContainerBinding::Horizon(binding) => binding.public_url.as_ref(),
ContainerBinding::Kubernetes(binding) => binding.public_url.as_ref(),
ContainerBinding::Local(binding) => binding.public_url.as_ref(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_horizon_binding() {
let binding = ContainerBinding::horizon("api", "http://api.svc:8080");
if let ContainerBinding::Horizon(horizon_binding) = binding {
assert_eq!(
horizon_binding.container_name,
BindingValue::Value("api".to_string())
);
assert_eq!(
horizon_binding.internal_url,
BindingValue::Value("http://api.svc:8080".to_string())
);
assert!(horizon_binding.public_url.is_none());
} else {
panic!("Expected Horizon binding");
}
}
#[test]
fn test_horizon_binding_with_public_url() {
let binding = ContainerBinding::horizon_with_public_url(
"api",
"http://api.svc:8080",
"https://api.example.com",
);
if let ContainerBinding::Horizon(horizon_binding) = binding {
assert_eq!(
horizon_binding.public_url,
Some(BindingValue::Value("https://api.example.com".to_string()))
);
} else {
panic!("Expected Horizon binding");
}
}
#[test]
fn test_local_binding() {
let binding = ContainerBinding::local("my-container", "http://my-container.svc:8080");
if let ContainerBinding::Local(local_binding) = binding {
assert_eq!(
local_binding.container_name,
BindingValue::Value("my-container".to_string())
);
assert_eq!(
local_binding.internal_url,
BindingValue::Value("http://my-container.svc:8080".to_string())
);
assert!(local_binding.public_url.is_none());
} else {
panic!("Expected Local binding");
}
}
#[test]
fn test_local_binding_with_public_url() {
let binding = ContainerBinding::local_with_public_url(
"my-container",
"http://my-container.svc:8080",
"http://localhost:62844",
);
if let ContainerBinding::Local(local_binding) = binding {
assert_eq!(
local_binding.public_url,
Some(BindingValue::Value("http://localhost:62844".to_string()))
);
} else {
panic!("Expected Local binding");
}
}
#[test]
fn test_get_internal_url() {
let horizon = ContainerBinding::horizon("api", "http://api.svc:8080");
assert_eq!(
horizon.get_internal_url(),
Some("http://api.svc:8080".to_string())
);
let local = ContainerBinding::local("api", "http://api.svc:3000");
assert_eq!(
local.get_internal_url(),
Some("http://api.svc:3000".to_string())
);
}
#[test]
fn test_serialization_roundtrip() {
let binding = ContainerBinding::horizon_with_public_url(
"api",
"http://api.svc:8080",
"https://api.example.com",
);
let serialized = serde_json::to_string(&binding).unwrap();
let deserialized: ContainerBinding = serde_json::from_str(&serialized).unwrap();
assert_eq!(binding, deserialized);
}
}