Skip to content

Commit

Permalink
ResolvedService: a new plain struct for attributes of a service (#302)
Browse files Browse the repository at this point in the history
  • Loading branch information
keepsimple1 authored Feb 15, 2025
1 parent 349de66 commit 4b671d4
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 1 deletion.
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,9 @@ pub use service_daemon::{
ServiceDaemon, ServiceEvent, UnregisterStatus, SERVICE_NAME_LEN_MAX_DEFAULT,
VERIFY_TIMEOUT_DEFAULT,
};
pub use service_info::{AsIpAddrs, IntoTxtProperties, ServiceInfo, TxtProperties, TxtProperty};
pub use service_info::{
AsIpAddrs, IntoTxtProperties, ResolvedService, ServiceInfo, TxtProperties, TxtProperty,
};

/// A handler to receive messages from [ServiceDaemon]. Re-export from `flume` crate.
pub use flume::Receiver;
54 changes: 54 additions & 0 deletions src/service_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,19 @@ impl ServiceInfo {
.cloned()
.unwrap_or(ServiceStatus::Unknown)
}

/// Consumes self and returns a resolved service, i.e. a lite version of `ServiceInfo`.
pub fn as_resolved_service(self) -> ResolvedService {
ResolvedService {
ty_domain: self.ty_domain,
sub_ty_domain: self.sub_domain,
fullname: self.fullname,
host: self.server,
port: self.port,
addresses: self.addresses,
txt_properties: self.txt_properties,
}
}
}

/// Removes potentially duplicated ".local." at the end of "hostname".
Expand Down Expand Up @@ -1098,6 +1111,47 @@ pub(crate) fn split_sub_domain(domain: &str) -> (&str, Option<&str>) {
}
}

/// Represents a resolved service as a plain data struct.
/// This is from a client (i.e. querier) point of view.
#[non_exhaustive]
pub struct ResolvedService {
/// Service type and domain. For example, "_http._tcp.local."
pub ty_domain: String,

/// Optional service subtype and domain.
///
/// See RFC6763 section 7.1 about "Subtypes":
/// <https://datatracker.ietf.org/doc/html/rfc6763#section-7.1>
/// For example, "_printer._sub._http._tcp.local."
pub sub_ty_domain: Option<String>,

/// Full name of the service. For example, "my-service._http._tcp.local."
pub fullname: String,

/// Host name of the service. For example, "my-server1.local."
pub host: String,

/// Port of the service. I.e. TCP or UDP port.
pub port: u16,

/// Addresses of the service. IPv4 or IPv6 addresses.
pub addresses: HashSet<IpAddr>,

/// Properties of the service, decoded from TXT record.
pub txt_properties: TxtProperties,
}

impl ResolvedService {
/// Returns true if the service data is valid, i.e. ready to be used.
pub fn is_valid(&self) -> bool {
let some_missing = self.ty_domain.is_empty()
|| self.fullname.is_empty()
|| self.host.is_empty()
|| self.addresses.is_empty();
!some_missing
}
}

#[cfg(test)]
mod tests {
use super::{
Expand Down
31 changes: 31 additions & 0 deletions tests/mdns_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,37 @@ fn test_into_txt_properties() {
assert_eq!(txt_props.get_property_val_str("key2").unwrap(), "val2");
}

#[test]
fn test_info_as_resolved_service() {
let sub_ty_domain = "_printer._sub._test._tcp.local.";
let service_info = ServiceInfo::new(
sub_ty_domain,
"my_instance",
"my_host.local.",
"192.168.0.1",
5200,
None,
)
.unwrap();
let resolved_service = service_info.as_resolved_service();
assert!(resolved_service.is_valid());
assert_eq!(resolved_service.sub_ty_domain.unwrap(), sub_ty_domain);
assert_eq!(resolved_service.ty_domain, "_test._tcp.local.");

let info_missing_addr = ServiceInfo::new(
"_test._tcp.local.",
"my_instance",
"my_host.local.",
"",
5200,
None,
)
.unwrap();
let invalid_service = info_missing_addr.as_resolved_service();
assert!(!invalid_service.is_valid());
assert!(invalid_service.sub_ty_domain.is_none());
}

/// Test enabling an interface using its name, for example "en0".
/// Also tests an instance name with Upper Case.
#[test]
Expand Down

0 comments on commit 4b671d4

Please sign in to comment.