#![cfg_attr(feature = "fail-on-warnings", deny(warnings))]
#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
#![allow(clippy::multiple_crate_versions)]
use mdns_sd::ServiceInfo;
use service::MdnsServiceDaemon;
use thiserror::Error;
pub mod service;
pub const SERVICE_TYPE: &str = "_moosicboxserver._tcp.local.";
#[derive(Debug, Error)]
pub enum RegisterServiceError {
#[error(transparent)]
MdnsSd(#[from] mdns_sd::Error),
#[error(transparent)]
IO(#[from] std::io::Error),
}
#[allow(clippy::unnecessary_wraps)]
fn get_service_daemon() -> Result<Box<dyn MdnsServiceDaemon>, mdns_sd::Error> {
#[cfg(feature = "simulator")]
{
Ok(Box::new(service::simulator::SimulatorServiceDaemon))
}
#[cfg(not(feature = "simulator"))]
{
Ok(Box::new(service::MdnsSdServiceDaemon::new(
mdns_sd::ServiceDaemon::new()?,
)))
}
}
pub async fn register_service(
instance_name: &str,
ip: &str,
port: u16,
) -> Result<(), RegisterServiceError> {
let mdns = get_service_daemon()?;
let host_name = format!(
"{}.local.",
hostname::get()?
.into_string()
.unwrap_or_else(|_| "unknown".to_string())
);
log::debug!(
"register_service: Registering mdns service service_type={SERVICE_TYPE} instance_name={instance_name} host_name={host_name} ip={ip} port={port}"
);
let service_info = ServiceInfo::new(SERVICE_TYPE, instance_name, &host_name, ip, port, None)?;
mdns.register(service_info).await?;
log::debug!(
"register_service: Registered mdns service service_type={SERVICE_TYPE} instance_name={instance_name} host_name={host_name} ip={ip} port={port}"
);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test_log::test(switchy_async::test)]
async fn test_register_service_with_simulator() {
let result = register_service("test-server", "192.168.1.100", 8080).await;
assert!(
result.is_ok(),
"Service registration should succeed with simulator"
);
}
#[test_log::test(switchy_async::test)]
async fn test_register_service_with_different_ports() {
let result1 = register_service("test-server-1", "192.168.1.100", 8080).await;
assert!(
result1.is_ok(),
"Service registration should succeed with port 8080"
);
let result2 = register_service("test-server-2", "192.168.1.100", 9000).await;
assert!(
result2.is_ok(),
"Service registration should succeed with port 9000"
);
let result3 = register_service("test-server-3", "192.168.1.100", 443).await;
assert!(
result3.is_ok(),
"Service registration should succeed with port 443"
);
}
#[test_log::test(switchy_async::test)]
async fn test_register_service_with_different_instance_names() {
let result1 = register_service("my-server", "192.168.1.100", 8080).await;
assert!(
result1.is_ok(),
"Service registration should succeed with simple name"
);
let result2 = register_service("server-with-dashes", "192.168.1.100", 8080).await;
assert!(
result2.is_ok(),
"Service registration should succeed with dashes"
);
let result3 = register_service("server_with_underscores", "192.168.1.100", 8080).await;
assert!(
result3.is_ok(),
"Service registration should succeed with underscores"
);
}
#[test_log::test(switchy_async::test)]
async fn test_register_service_with_different_ips() {
let result1 = register_service("test-server", "192.168.1.100", 8080).await;
assert!(
result1.is_ok(),
"Service registration should succeed with private IP"
);
let result2 = register_service("test-server", "10.0.0.5", 8080).await;
assert!(
result2.is_ok(),
"Service registration should succeed with different private IP"
);
let result3 = register_service("test-server", "127.0.0.1", 8080).await;
assert!(
result3.is_ok(),
"Service registration should succeed with localhost"
);
}
#[test_log::test(switchy_async::test)]
async fn test_register_service_creates_correct_service_type() {
let result = register_service("test-server", "192.168.1.100", 8080).await;
assert!(
result.is_ok(),
"Service registration should succeed with correct service type"
);
}
}