use anyhow::Result;
use async_trait::async_trait;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ServiceEndpoint {
pub uri: String,
}
impl ServiceEndpoint {
pub fn new(uri: impl Into<String>) -> Self {
Self { uri: uri.into() }
}
#[must_use]
pub fn http(host: &str, port: u16) -> Self {
Self {
uri: format!("{}://{}:{}", "http", host, port),
}
}
#[must_use]
pub fn https(host: &str, port: u16) -> Self {
Self {
uri: format!("https://{host}:{port}"),
}
}
pub fn uds(path: impl AsRef<std::path::Path>) -> Self {
Self {
uri: format!("unix://{}", path.as_ref().display()),
}
}
}
#[derive(Debug, Clone)]
pub struct ServiceInstanceInfo {
pub module: String,
pub instance_id: String,
pub endpoint: ServiceEndpoint,
pub version: Option<String>,
}
#[derive(Debug, Clone)]
pub struct RegisterInstanceInfo {
pub module: String,
pub instance_id: String,
pub grpc_services: Vec<(String, ServiceEndpoint)>,
pub version: Option<String>,
}
#[async_trait]
pub trait DirectoryClient: Send + Sync {
async fn resolve_grpc_service(&self, service_name: &str) -> Result<ServiceEndpoint>;
async fn list_instances(&self, module: &str) -> Result<Vec<ServiceInstanceInfo>>;
async fn register_instance(&self, info: RegisterInstanceInfo) -> Result<()>;
async fn deregister_instance(&self, module: &str, instance_id: &str) -> Result<()>;
async fn send_heartbeat(&self, module: &str, instance_id: &str) -> Result<()>;
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn test_service_endpoint_creation() {
let http_ep = ServiceEndpoint::http("localhost", 8080);
assert_eq!(http_ep.uri, concat!("http", "://localhost:8080"));
let https_endpoint = ServiceEndpoint::https("localhost", 8443);
assert_eq!(https_endpoint.uri, "https://localhost:8443");
let uds_ep = ServiceEndpoint::uds("/tmp/socket.sock");
assert!(uds_ep.uri.starts_with("unix://"));
assert!(uds_ep.uri.contains("socket.sock"));
let custom_ep = ServiceEndpoint::new(concat!("http", "://example.com"));
assert_eq!(custom_ep.uri, concat!("http", "://example.com"));
}
#[test]
fn test_register_instance_info() {
let info = RegisterInstanceInfo {
module: "test_module".to_owned(),
instance_id: "instance1".to_owned(),
grpc_services: vec![(
"test.Service".to_owned(),
ServiceEndpoint::http("127.0.0.1", 8001),
)],
version: Some("1.0.0".to_owned()),
};
assert_eq!(info.module, "test_module");
assert_eq!(info.instance_id, "instance1");
assert_eq!(info.grpc_services.len(), 1);
}
}