use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use url::Url;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ServiceType {
ZeroKms,
Secrets,
}
impl ServiceType {
pub fn as_str(&self) -> &'static str {
match self {
ServiceType::ZeroKms => "zerokms",
ServiceType::Secrets => "secrets",
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Services(BTreeMap<ServiceType, Url>);
impl Services {
pub fn new() -> Self {
Self(BTreeMap::new())
}
pub fn insert(&mut self, service_type: ServiceType, url: Url) {
self.0.insert(service_type, url);
}
pub fn get(&self, service_type: ServiceType) -> Option<&Url> {
self.0.get(&service_type)
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (&ServiceType, &Url)> {
self.0.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_services_roundtrip() {
let mut services = Services::new();
services.insert(
ServiceType::ZeroKms,
Url::parse("https://us-east-1.aws.viturhosted.net/").unwrap(),
);
let json = serde_json::to_string(&services).unwrap();
assert_eq!(
json,
r#"{"zerokms":"https://us-east-1.aws.viturhosted.net/"}"#
);
let deserialized: Services = serde_json::from_str(&json).unwrap();
assert_eq!(
deserialized.get(ServiceType::ZeroKms).map(|u| u.as_str()),
Some("https://us-east-1.aws.viturhosted.net/")
);
}
#[test]
fn test_empty_services_not_serialized_as_null() {
let services = Services::new();
let json = serde_json::to_string(&services).unwrap();
assert_eq!(json, "{}");
}
#[test]
fn test_default_is_empty() {
let services = Services::default();
assert!(services.is_empty());
}
#[test]
fn test_missing_key_returns_none() {
let services = Services::new();
assert_eq!(services.get(ServiceType::ZeroKms), None);
}
#[test]
fn test_service_type_as_str() {
assert_eq!(ServiceType::ZeroKms.as_str(), "zerokms");
assert_eq!(ServiceType::Secrets.as_str(), "secrets");
}
#[test]
fn test_services_roundtrip_with_multiple_services() {
let mut services = Services::new();
services.insert(
ServiceType::Secrets,
Url::parse("https://dashboard.cipherstash.com/").unwrap(),
);
services.insert(
ServiceType::ZeroKms,
Url::parse("https://us-east-1.aws.viturhosted.net/").unwrap(),
);
let json = serde_json::to_string(&services).unwrap();
let deserialized: Services = serde_json::from_str(&json).unwrap();
assert_eq!(
deserialized.get(ServiceType::Secrets).map(|u| u.as_str()),
Some("https://dashboard.cipherstash.com/"),
"secrets endpoint should roundtrip through serde"
);
assert_eq!(
deserialized.get(ServiceType::ZeroKms).map(|u| u.as_str()),
Some("https://us-east-1.aws.viturhosted.net/"),
"zerokms endpoint should roundtrip through serde"
);
}
#[test]
fn test_iter_populated_services() {
let mut services = Services::new();
let url = Url::parse("https://zerokms.example.com/").unwrap();
services.insert(ServiceType::ZeroKms, url.clone());
let entries: Vec<_> = services.iter().collect();
assert_eq!(entries.len(), 1);
assert_eq!(entries[0], (&ServiceType::ZeroKms, &url));
}
#[test]
fn test_iter_empty_services() {
let services = Services::new();
assert_eq!(services.iter().count(), 0);
}
}