Add ServiceType
Signed-off-by: Walker Crouse <Walker.Crouse@bjss.com>
This commit is contained in:
parent
3c14883ac9
commit
9935ce280d
|
@ -1 +1,2 @@
|
||||||
**/target
|
**/target
|
||||||
|
.vscode
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.13"
|
version = "0.7.13"
|
||||||
|
@ -500,7 +502,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zeroconf"
|
name = "zeroconf"
|
||||||
version = "0.7.0"
|
version = "0.7.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"avahi-sys",
|
"avahi-sys",
|
||||||
"bonjour-sys",
|
"bonjour-sys",
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use zeroconf::{MdnsService, ServiceRegistration, TxtRecord};
|
|
||||||
use zeroconf::prelude::*;
|
use zeroconf::prelude::*;
|
||||||
|
use zeroconf::{MdnsService, ServiceRegistration, ServiceType, TxtRecord};
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
|
@ -10,7 +10,7 @@ pub struct Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut service = MdnsService::new("_http._tcp", 8080);
|
let mut service = MdnsService::new(ServiceType::new("http", "tcp").unwrap(), 8080);
|
||||||
let mut txt_record = TxtRecord::new();
|
let mut txt_record = TxtRecord::new();
|
||||||
let context: Arc<Mutex<Context>> = Arc::default();
|
let context: Arc<Mutex<Context>> = Arc::default();
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
//! use std::any::Any;
|
//! use std::any::Any;
|
||||||
//! use std::sync::{Arc, Mutex};
|
//! use std::sync::{Arc, Mutex};
|
||||||
//! use std::time::Duration;
|
//! use std::time::Duration;
|
||||||
//! use zeroconf::{MdnsService, ServiceRegistration, TxtRecord};
|
|
||||||
//! use zeroconf::prelude::*;
|
//! use zeroconf::prelude::*;
|
||||||
|
//! use zeroconf::{MdnsService, ServiceRegistration, ServiceType, TxtRecord};
|
||||||
//!
|
//!
|
||||||
//! #[derive(Default, Debug)]
|
//! #[derive(Default, Debug)]
|
||||||
//! pub struct Context {
|
//! pub struct Context {
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
//! let mut service = MdnsService::new("_http._tcp", 8080);
|
//! let mut service = MdnsService::new(ServiceType::new("http", "tcp").unwrap(), 8080);
|
||||||
//! let mut txt_record = TxtRecord::new();
|
//! let mut txt_record = TxtRecord::new();
|
||||||
//! let context: Arc<Mutex<Context>> = Arc::default();
|
//! let context: Arc<Mutex<Context>> = Arc::default();
|
||||||
//!
|
//!
|
||||||
|
@ -143,6 +143,7 @@ pub mod event_loop;
|
||||||
pub mod ffi;
|
pub mod ffi;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod service;
|
pub mod service;
|
||||||
|
pub mod service_type;
|
||||||
pub mod txt_record;
|
pub mod txt_record;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
@ -153,6 +154,7 @@ pub mod macos;
|
||||||
pub use browser::{ServiceDiscoveredCallback, ServiceDiscovery};
|
pub use browser::{ServiceDiscoveredCallback, ServiceDiscovery};
|
||||||
pub use interface::*;
|
pub use interface::*;
|
||||||
pub use service::{ServiceRegisteredCallback, ServiceRegistration};
|
pub use service::{ServiceRegisteredCallback, ServiceRegistration};
|
||||||
|
pub use service_type::*;
|
||||||
|
|
||||||
/// Type alias for the platform-specific mDNS browser implementation
|
/// Type alias for the platform-specific mDNS browser implementation
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
|
|
@ -8,7 +8,8 @@ use super::poll::ManagedAvahiSimplePoll;
|
||||||
use crate::ffi::{c_str, AsRaw, FromRaw, UnwrapOrNull};
|
use crate::ffi::{c_str, AsRaw, FromRaw, UnwrapOrNull};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
EventLoop, NetworkInterface, Result, ServiceRegisteredCallback, ServiceRegistration, TxtRecord,
|
EventLoop, NetworkInterface, Result, ServiceRegisteredCallback, ServiceRegistration,
|
||||||
|
ServiceType, TxtRecord,
|
||||||
};
|
};
|
||||||
use avahi_sys::{
|
use avahi_sys::{
|
||||||
AvahiClient, AvahiClientFlags, AvahiClientState, AvahiEntryGroup, AvahiEntryGroupState,
|
AvahiClient, AvahiClientFlags, AvahiClientState, AvahiEntryGroup, AvahiEntryGroupState,
|
||||||
|
@ -18,6 +19,7 @@ use libc::c_void;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::fmt::{self, Formatter};
|
use std::fmt::{self, Formatter};
|
||||||
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -28,11 +30,14 @@ pub struct AvahiMdnsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TMdnsService for AvahiMdnsService {
|
impl TMdnsService for AvahiMdnsService {
|
||||||
fn new(kind: &str, port: u16) -> Self {
|
fn new(service_type: ServiceType, port: u16) -> Self {
|
||||||
Self {
|
Self {
|
||||||
client: None,
|
client: None,
|
||||||
poll: None,
|
poll: None,
|
||||||
context: Box::into_raw(Box::new(AvahiServiceContext::new(kind, port))),
|
context: Box::into_raw(Box::new(AvahiServiceContext::new(
|
||||||
|
&service_type.to_string(),
|
||||||
|
port,
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +236,9 @@ unsafe fn handle_group_established(context: &AvahiServiceContext) -> Result<()>
|
||||||
|
|
||||||
let result = ServiceRegistration::builder()
|
let result = ServiceRegistration::builder()
|
||||||
.name(c_str::copy_raw(context.name.as_ref().unwrap().as_ptr()))
|
.name(c_str::copy_raw(context.name.as_ref().unwrap().as_ptr()))
|
||||||
.kind(c_str::copy_raw(context.kind.as_ptr()))
|
.service_type(ServiceType::from_str(&c_str::copy_raw(
|
||||||
|
context.kind.as_ptr(),
|
||||||
|
))?)
|
||||||
.domain("local".to_string())
|
.domain("local".to_string())
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
//! Trait definition for cross-platform service.
|
//! Trait definition for cross-platform service.
|
||||||
|
|
||||||
use crate::{EventLoop, NetworkInterface, Result, TxtRecord};
|
use crate::{EventLoop, NetworkInterface, Result, ServiceType, TxtRecord};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Interface for interacting with underlying mDNS service implementation registration
|
/// Interface for interacting with underlying mDNS service implementation registration
|
||||||
/// capabilities.
|
/// capabilities.
|
||||||
pub trait TMdnsService {
|
pub trait TMdnsService {
|
||||||
/// Creates a new `MdnsService` with the specified `kind` (e.g. `_http._tcp`) and `port`.
|
/// Creates a new `MdnsService` with the specified `ServiceType` (e.g. `_http._tcp`) and `port`.
|
||||||
fn new(kind: &str, port: u16) -> Self;
|
fn new(service_type: ServiceType, port: u16) -> Self;
|
||||||
|
|
||||||
/// Sets the name to register this service under.
|
/// Sets the name to register this service under.
|
||||||
fn set_name(&mut self, name: &str);
|
fn set_name(&mut self, name: &str);
|
||||||
|
@ -64,6 +64,6 @@ pub type ServiceRegisteredCallback = dyn Fn(Result<ServiceRegistration>, Option<
|
||||||
#[derive(Builder, BuilderDelegate, Debug, Getters, Clone, Default, PartialEq, Eq)]
|
#[derive(Builder, BuilderDelegate, Debug, Getters, Clone, Default, PartialEq, Eq)]
|
||||||
pub struct ServiceRegistration {
|
pub struct ServiceRegistration {
|
||||||
name: String,
|
name: String,
|
||||||
kind: String,
|
service_type: ServiceType,
|
||||||
domain: String,
|
domain: String,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
//! Data type for constructing a service type
|
||||||
|
|
||||||
|
use crate::Result;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
/// Data type for constructing a service type to register as an mDNS service.
|
||||||
|
#[derive(Default, Debug, Getters, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
|
pub struct ServiceType {
|
||||||
|
name: String,
|
||||||
|
protocol: String,
|
||||||
|
sub_types: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServiceType {
|
||||||
|
/// Creates a new `ServiceType` with the specified name (e.g. `http`) and protocol (e.g. `tcp`)
|
||||||
|
pub fn new(name: &str, protocol: &str) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
name: Self::check_part(name)?.to_string(),
|
||||||
|
protocol: Self::check_part(protocol)?.to_string(),
|
||||||
|
sub_types: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `ServiceType` with the specified name (e.g. `http`) and protocol (e.g. `tcp`)
|
||||||
|
/// and sub-types.
|
||||||
|
pub fn with_sub_types(name: &str, protocol: &str, sub_types: Vec<&str>) -> Result<Self> {
|
||||||
|
for sub_type in &sub_types {
|
||||||
|
Self::check_part(sub_type)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
protocol: protocol.to_string(),
|
||||||
|
sub_types: sub_types.iter().map(|s| s.to_string()).collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_part(part: &str) -> Result<&str> {
|
||||||
|
if part.contains(".") {
|
||||||
|
Err("invalid character: .".into())
|
||||||
|
} else if part.contains(",") {
|
||||||
|
Err("invalid character: ,".into())
|
||||||
|
} else {
|
||||||
|
Ok(part)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for ServiceType {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
format!("_{}._{}{}", self.name, self.protocol, {
|
||||||
|
if !self.sub_types.is_empty() {
|
||||||
|
format!(",_{}", self.sub_types.join(",_"))
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ServiceType {
|
||||||
|
type Err = crate::error::Error;
|
||||||
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
|
let parts: Vec<&str> = s.split(",").collect();
|
||||||
|
if parts.is_empty() {
|
||||||
|
return Err("could not parse ServiceType from string".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let head: Vec<&str> = parts[0].split(".").collect();
|
||||||
|
let mut name = head[0];
|
||||||
|
if name.starts_with("_") {
|
||||||
|
name = &name[1..];
|
||||||
|
}
|
||||||
|
let mut protocol = head[1];
|
||||||
|
if protocol.starts_with("_") {
|
||||||
|
protocol = &protocol[1..];
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sub_types: Vec<&str> = vec![];
|
||||||
|
if parts.len() > 1 {
|
||||||
|
for i in 1..parts.len() {
|
||||||
|
let mut sub_type = parts[i];
|
||||||
|
if sub_type.starts_with("_") {
|
||||||
|
sub_type = &sub_type[1..];
|
||||||
|
}
|
||||||
|
sub_types.push(sub_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ServiceType::with_sub_types(name, protocol, sub_types)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_invalid() {
|
||||||
|
ServiceType::new(".http", "tcp").expect_err("invalid character: .".into());
|
||||||
|
ServiceType::new("http", ".tcp").expect_err("invalid character: .".into());
|
||||||
|
ServiceType::new(",http", "tcp").expect_err("invalid character: ,".into());
|
||||||
|
ServiceType::new("http", ",tcp").expect_err("invalid character: ,".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn to_string_success() {
|
||||||
|
assert_eq!(
|
||||||
|
ServiceType::new("http", "tcp").unwrap().to_string(),
|
||||||
|
"_http._tcp"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn to_string_with_sub_types_success() {
|
||||||
|
assert_eq!(
|
||||||
|
ServiceType::with_sub_types("http", "tcp", vec!["api-v1", "api-v2"])
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
"_http._tcp,_api-v1,_api-v2"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_str_success() {
|
||||||
|
assert_eq!(
|
||||||
|
ServiceType::from_str("_http._tcp").unwrap(),
|
||||||
|
ServiceType::new("http", "tcp").unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_str_with_sub_types_success() {
|
||||||
|
assert_eq!(
|
||||||
|
ServiceType::from_str("_http._tcp,api-v1,api-v2").unwrap(),
|
||||||
|
ServiceType::with_sub_types("http", "tcp", vec!["api-v1", "api-v2"]).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{MdnsBrowser, MdnsService, TxtRecord};
|
use crate::{MdnsBrowser, MdnsService, ServiceType, TxtRecord};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ fn service_register_is_browsable() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static SERVICE_NAME: &str = "service_register_is_browsable";
|
static SERVICE_NAME: &str = "service_register_is_browsable";
|
||||||
let mut service = MdnsService::new("_http._tcp", 8080);
|
let mut service = MdnsService::new(ServiceType::new("http", "tcp").unwrap(), 8080);
|
||||||
let context: Arc<Mutex<Context>> = Arc::default();
|
let context: Arc<Mutex<Context>> = Arc::default();
|
||||||
|
|
||||||
let mut txt = TxtRecord::new();
|
let mut txt = TxtRecord::new();
|
||||||
|
|
Loading…
Reference in New Issue