-
Notifications
You must be signed in to change notification settings - Fork 44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ResolvedService: a new plain struct for attributes of a service #302
Changes from all commits
9f54e37
0b727f0
7659174
6eb78f4
7ca7e06
1a081dd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -366,6 +366,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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another question: what if this service info has some There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With public, I meant accessible via public interface :P There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've updated the diff and added |
||
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". | ||
|
@@ -1086,6 +1099,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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One edge case is: if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we have decided to split among client and server, we can return an |
||
/// 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::{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Do we use
Option<ResolvedService>
for empty addresses?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have thought about this, and wanted to keep returning just
ResolvedService
for a couple of reasons:Option
can complicate many things for the user, in my experience).ResolvedService
struct cannot guarantee its fields are all valid, for example what if.fullname
is empty? So I think we can implement methods inResolvedService
to valid its content, something likeis_valid()
, etc.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with your points, but I'm a bit confused.
Currently, we retrieve a service client side in this way (just an example with timeout):
Now we are going to replace
info
variable, which is aServiceInfo
structure, withResolvedService
. But if there isn't a validfullname
or an empty vector ofaddresses
, can't we block all of this before, when we are evaluating theServiceEvent::ServiceResolved(info)
condition? I mean, we can return a differentevent
variant when the conditions above are not satisfied. Just talking for the client side.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I'm not saying to replace
info
here. Because such change will break the backward compatibility, we said going with option 1 instead: The user will callinfo.as_resolved_service
to consumeinfo
and get aResolvedService
struct.Yes, from this event the
info
is always valid, which meansResolvedService
struct will also be valid in this context. But this is a business logic, we can document it but we cannot enforce it (via the type system or compiler).But for any given
ResolvedService
without such context, we would need something likeis_valid
(oris_ready
to match the current code in service_info.rs).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I see now, thank you! The changes are fine for me, and the
is_valid
method is a simpler solution, so let's stick with thatThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I've updated the diff with a test case and export in
lib.rs
. However, I have a new question regardingsub_domain
below. Any suggestions or comments?