ManagedAvahiStringList
Signed-off-by: Walker Crouse <walker.crouse@coop.co.uk>
This commit is contained in:
parent
2d3f8f0eed
commit
560d33454d
@ -4,7 +4,7 @@ use super::constants;
|
|||||||
use crate::NetworkInterface;
|
use crate::NetworkInterface;
|
||||||
use avahi_sys::{avahi_address_snprint, avahi_strerror, AvahiAddress};
|
use avahi_sys::{avahi_address_snprint, avahi_strerror, AvahiAddress};
|
||||||
use libc::c_char;
|
use libc::c_char;
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::CStr;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
/// Converts the specified `*const AvahiAddress` to a `String`.
|
/// Converts the specified `*const AvahiAddress` to a `String`.
|
||||||
|
@ -17,4 +17,5 @@ pub mod poll;
|
|||||||
pub mod raw_browser;
|
pub mod raw_browser;
|
||||||
pub mod resolver;
|
pub mod resolver;
|
||||||
pub mod service;
|
pub mod service;
|
||||||
|
pub mod string_list;
|
||||||
pub mod txt_record;
|
pub mod txt_record;
|
||||||
|
@ -5,7 +5,9 @@ use super::entry_group::{AddServiceParams, ManagedAvahiEntryGroup, ManagedAvahiE
|
|||||||
use super::poll::ManagedAvahiSimplePoll;
|
use super::poll::ManagedAvahiSimplePoll;
|
||||||
use crate::builder::BuilderDelegate;
|
use crate::builder::BuilderDelegate;
|
||||||
use crate::ffi::{c_str, AsRaw, FromRaw};
|
use crate::ffi::{c_str, AsRaw, FromRaw};
|
||||||
use crate::{EventLoop, NetworkInterface, Result, ServiceRegisteredCallback, ServiceRegistration};
|
use crate::{
|
||||||
|
EventLoop, NetworkInterface, Result, ServiceRegisteredCallback, ServiceRegistration, TxtRecord,
|
||||||
|
};
|
||||||
use avahi_sys::{
|
use avahi_sys::{
|
||||||
AvahiClient, AvahiClientFlags, AvahiClientState, AvahiEntryGroup, AvahiEntryGroupState,
|
AvahiClient, AvahiClientFlags, AvahiClientState, AvahiEntryGroup, AvahiEntryGroupState,
|
||||||
AvahiIfIndex,
|
AvahiIfIndex,
|
||||||
@ -22,6 +24,7 @@ use std::sync::Arc;
|
|||||||
pub struct AvahiMdnsService {
|
pub struct AvahiMdnsService {
|
||||||
client: Option<ManagedAvahiClient>,
|
client: Option<ManagedAvahiClient>,
|
||||||
poll: Option<Arc<ManagedAvahiSimplePoll>>,
|
poll: Option<Arc<ManagedAvahiSimplePoll>>,
|
||||||
|
txt_record: Option<TxtRecord>,
|
||||||
context: *mut AvahiServiceContext,
|
context: *mut AvahiServiceContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,6 +34,7 @@ impl AvahiMdnsService {
|
|||||||
Self {
|
Self {
|
||||||
client: None,
|
client: None,
|
||||||
poll: None,
|
poll: None,
|
||||||
|
txt_record: None,
|
||||||
context: Box::into_raw(Box::new(AvahiServiceContext::new(kind, port))),
|
context: Box::into_raw(Box::new(AvahiServiceContext::new(kind, port))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,6 +57,27 @@ impl AvahiMdnsService {
|
|||||||
unsafe { (*self.context).interface_index = avahi_util::interface_index(interface) };
|
unsafe { (*self.context).interface_index = avahi_util::interface_index(interface) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the domain on which to advertise the service.
|
||||||
|
///
|
||||||
|
/// Most applications will want to use the default value of `ptr::null()` to register to the
|
||||||
|
/// default domain.
|
||||||
|
pub fn set_domain(&mut self, _domain: &str) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the SRV target host name.
|
||||||
|
///
|
||||||
|
/// Most applications will want to use the default value of `ptr::null()` to use the machine's
|
||||||
|
// default host name.
|
||||||
|
pub fn set_host(&mut self, _host: &str) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the optional `TxtRecord` to register this service with.
|
||||||
|
pub fn set_txt_record(&mut self, txt_record: TxtRecord) {
|
||||||
|
self.txt_record = Some(txt_record);
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the [`ServiceRegisteredCallback`] that is invoked when the service has been
|
/// Sets the [`ServiceRegisteredCallback`] that is invoked when the service has been
|
||||||
/// registered.
|
/// registered.
|
||||||
///
|
///
|
||||||
|
236
zeroconf/src/linux/string_list.rs
Normal file
236
zeroconf/src/linux/string_list.rs
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
use crate::ffi::c_str;
|
||||||
|
use avahi_sys::{
|
||||||
|
avahi_free, avahi_string_list_add_pair, avahi_string_list_copy, avahi_string_list_equal,
|
||||||
|
avahi_string_list_find, avahi_string_list_free, avahi_string_list_get_pair,
|
||||||
|
avahi_string_list_length, avahi_string_list_new, avahi_string_list_to_string, AvahiStringList,
|
||||||
|
};
|
||||||
|
use libc::{c_char, c_void};
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ManagedAvahiStringList(*mut AvahiStringList);
|
||||||
|
|
||||||
|
impl ManagedAvahiStringList {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self(unsafe { avahi_string_list_new(ptr::null()) })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn add_pair(&mut self, key: *const c_char, value: *const c_char) {
|
||||||
|
self.0 = avahi_string_list_add_pair(self.0, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn find(&mut self, key: *const c_char) -> Option<AvahiStringListNode> {
|
||||||
|
let node = avahi_string_list_find(self.0, key);
|
||||||
|
if !node.is_null() {
|
||||||
|
Some(AvahiStringListNode::new(node))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length(&self) -> u32 {
|
||||||
|
unsafe { avahi_string_list_length(self.0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(&self) -> AvahiString {
|
||||||
|
unsafe { avahi_string_list_to_string(self.0).into() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for ManagedAvahiStringList {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(unsafe { avahi_string_list_copy(self.0) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for ManagedAvahiStringList {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
unsafe { avahi_string_list_equal(self.0, other.0) == 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for ManagedAvahiStringList {}
|
||||||
|
|
||||||
|
impl Default for ManagedAvahiStringList {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ManagedAvahiStringList {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { avahi_string_list_free(self.0) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(new)]
|
||||||
|
pub struct AvahiStringListNode(*mut AvahiStringList);
|
||||||
|
|
||||||
|
impl AvahiStringListNode {
|
||||||
|
pub fn get_pair(&mut self) -> AvahiPair {
|
||||||
|
let mut key: *mut c_char = ptr::null_mut();
|
||||||
|
let mut value: *mut c_char = ptr::null_mut();
|
||||||
|
let mut value_size: usize = 0;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
avahi_string_list_get_pair(self.0, &mut key, &mut value, &mut value_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
AvahiPair::new(key.into(), value.into(), value_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(new, Getters)]
|
||||||
|
pub struct AvahiPair {
|
||||||
|
key: AvahiString,
|
||||||
|
value: AvahiString,
|
||||||
|
value_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(new)]
|
||||||
|
pub struct AvahiString(*mut c_char);
|
||||||
|
|
||||||
|
impl AvahiString {
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
unsafe { c_str::raw_to_str(self.0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<*mut c_char> for AvahiString {
|
||||||
|
fn from(s: *mut c_char) -> Self {
|
||||||
|
Self::new(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for AvahiString {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
self.as_str().to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for AvahiString {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !self.0.is_null() {
|
||||||
|
unsafe { avahi_free(self.0 as *mut c_void) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_get_pair_success() {
|
||||||
|
crate::tests::setup();
|
||||||
|
|
||||||
|
let mut list = ManagedAvahiStringList::new();
|
||||||
|
let key1 = c_string!("foo");
|
||||||
|
let value1 = c_string!("bar");
|
||||||
|
|
||||||
|
list.add_pair(
|
||||||
|
key1.as_ptr() as *const c_char,
|
||||||
|
value1.as_ptr() as *const c_char,
|
||||||
|
);
|
||||||
|
|
||||||
|
let key2 = c_string!("hello");
|
||||||
|
let value2 = c_string!("world");
|
||||||
|
|
||||||
|
list.add_pair(
|
||||||
|
key2.as_ptr() as *const c_char,
|
||||||
|
value2.as_ptr() as *const c_char,
|
||||||
|
);
|
||||||
|
|
||||||
|
let pair1 = list
|
||||||
|
.find(key1.as_ptr() as *const c_char)
|
||||||
|
.unwrap()
|
||||||
|
.get_pair();
|
||||||
|
|
||||||
|
let pair2 = list
|
||||||
|
.find(key2.as_ptr() as *const c_char)
|
||||||
|
.unwrap()
|
||||||
|
.get_pair();
|
||||||
|
|
||||||
|
assert_eq!(pair1.key().as_str(), "foo");
|
||||||
|
assert_eq!(pair1.value().as_str(), "bar");
|
||||||
|
assert_eq!(pair2.key().as_str(), "hello");
|
||||||
|
assert_eq!(pair2.value().as_str(), "world");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_pair_replaces_success() {
|
||||||
|
crate::tests::setup();
|
||||||
|
|
||||||
|
let mut list = ManagedAvahiStringList::new();
|
||||||
|
let key = c_string!("foo");
|
||||||
|
let value = c_string!("bar");
|
||||||
|
|
||||||
|
list.add_pair(
|
||||||
|
key.as_ptr() as *const c_char,
|
||||||
|
value.as_ptr() as *const c_char,
|
||||||
|
);
|
||||||
|
|
||||||
|
let pair = list.find(key.as_ptr() as *const c_char).unwrap().get_pair();
|
||||||
|
|
||||||
|
assert_eq!(pair.value().as_str(), "bar");
|
||||||
|
|
||||||
|
let value = c_string!("baz");
|
||||||
|
|
||||||
|
list.add_pair(
|
||||||
|
key.as_ptr() as *const c_char,
|
||||||
|
value.as_ptr() as *const c_char,
|
||||||
|
);
|
||||||
|
|
||||||
|
let pair = list.find(key.as_ptr() as *const c_char).unwrap().get_pair();
|
||||||
|
|
||||||
|
assert_eq!(pair.value().as_str(), "baz");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn length_success() {
|
||||||
|
crate::tests::setup();
|
||||||
|
|
||||||
|
let mut list = ManagedAvahiStringList::new();
|
||||||
|
let key = c_string!("foo");
|
||||||
|
let value = c_string!("bar");
|
||||||
|
|
||||||
|
list.add_pair(
|
||||||
|
key.as_ptr() as *const c_char,
|
||||||
|
value.as_ptr() as *const c_char,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(list.length(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn to_string_success() {
|
||||||
|
crate::tests::setup();
|
||||||
|
|
||||||
|
let mut list = ManagedAvahiStringList::new();
|
||||||
|
let key = c_string!("foo");
|
||||||
|
let value = c_string!("bar");
|
||||||
|
|
||||||
|
list.add_pair(
|
||||||
|
key.as_ptr() as *const c_char,
|
||||||
|
value.as_ptr() as *const c_char,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(list.to_string().as_str(), "\"foo=bar\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn equals_success() {
|
||||||
|
crate::tests::setup();
|
||||||
|
|
||||||
|
let mut list = ManagedAvahiStringList::new();
|
||||||
|
let key = c_string!("foo");
|
||||||
|
let value = c_string!("bar");
|
||||||
|
|
||||||
|
list.add_pair(
|
||||||
|
key.as_ptr() as *const c_char,
|
||||||
|
value.as_ptr() as *const c_char,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(list.clone(), list);
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
use crate::Result;
|
use crate::Result;
|
||||||
use std::collections::{hash_map, HashMap};
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||||
pub struct AvahiTxtRecord(HashMap<String, String>);
|
pub struct AvahiTxtRecord(HashMap<String, String>);
|
||||||
|
|
||||||
impl AvahiTxtRecord {
|
impl AvahiTxtRecord {
|
||||||
@ -43,4 +43,30 @@ impl AvahiTxtRecord {
|
|||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.len() == 0
|
self.len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a new `txt_record::Iter` for iterating over the record as you would a `HashMap`.
|
||||||
|
pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (String, &'a str)> + 'a> {
|
||||||
|
Box::new(self.0.iter().map(|(k, v)| (k.to_string(), v.as_str())))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new `txt_record::Iter` over the records keys.
|
||||||
|
pub fn keys<'a>(&'a self) -> Box<dyn Iterator<Item = String> + 'a> {
|
||||||
|
Box::new(self.0.keys().map(|k| k.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new `txt_record::Iter` over the records values.
|
||||||
|
pub fn values<'a>(&'a self) -> Box<dyn Iterator<Item = &'a str> + 'a> {
|
||||||
|
Box::new(self.0.values().map(|v| v.as_str()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new `HashMap` with this record's keys and values.
|
||||||
|
pub fn to_map(&self) -> HashMap<String, String> {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<HashMap<String, String>> for AvahiTxtRecord {
|
||||||
|
fn from(map: HashMap<String, String>) -> AvahiTxtRecord {
|
||||||
|
Self(map)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,36 @@ impl BonjourTxtRecord {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<HashMap<String, String>> for BonjourTxtRecord {
|
||||||
|
fn from(map: HashMap<String, String>) -> BonjourTxtRecord {
|
||||||
|
let mut record = BonjourTxtRecord::new();
|
||||||
|
for (key, value) in map {
|
||||||
|
record.insert(&key, &value).unwrap();
|
||||||
|
}
|
||||||
|
record
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for BonjourTxtRecord {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
self.to_map().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for BonjourTxtRecord {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.to_map() == other.to_map()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for BonjourTxtRecord {}
|
||||||
|
|
||||||
|
impl Default for BonjourTxtRecord {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An `Iterator` that allows iteration over a [`BonjourTxtRecord`] similar to a `HashMap`.
|
/// An `Iterator` that allows iteration over a [`BonjourTxtRecord`] similar to a `HashMap`.
|
||||||
#[derive(new)]
|
#[derive(new)]
|
||||||
pub struct Iter<'a> {
|
pub struct Iter<'a> {
|
||||||
@ -169,48 +199,3 @@ impl<'a> Iterator for Values<'a> {
|
|||||||
self.0.next().map(|e| e.1)
|
self.0.next().map(|e| e.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<HashMap<String, String>> for BonjourTxtRecord {
|
|
||||||
fn from(map: HashMap<String, String>) -> BonjourTxtRecord {
|
|
||||||
let mut record = BonjourTxtRecord::new();
|
|
||||||
for (key, value) in map {
|
|
||||||
record.insert(&key, &value).unwrap();
|
|
||||||
}
|
|
||||||
record
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<HashMap<&str, &str>> for BonjourTxtRecord {
|
|
||||||
fn from(map: HashMap<&str, &str>) -> BonjourTxtRecord {
|
|
||||||
map.iter()
|
|
||||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
|
||||||
.collect::<HashMap<String, String>>()
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for BonjourTxtRecord {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
self.to_map().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for BonjourTxtRecord {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.to_map() == other.to_map()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for BonjourTxtRecord {}
|
|
||||||
|
|
||||||
impl Default for BonjourTxtRecord {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToString for BonjourTxtRecord {
|
|
||||||
fn to_string(&self) -> String {
|
|
||||||
unsafe { c_str::raw_to_str(self.0.get_bytes_ptr() as *const c_char).to_string() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -13,6 +13,7 @@ macro_rules! c_string {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
macro_rules! bonjour {
|
macro_rules! bonjour {
|
||||||
($call:expr, $msg:expr) => {{
|
($call:expr, $msg:expr) => {{
|
||||||
#[allow(unused_unsafe)]
|
#[allow(unused_unsafe)]
|
||||||
|
@ -2,7 +2,7 @@ use std::sync::Once;
|
|||||||
|
|
||||||
static INIT: Once = Once::new();
|
static INIT: Once = Once::new();
|
||||||
|
|
||||||
pub(self) fn setup() {
|
pub(crate) fn setup() {
|
||||||
INIT.call_once(|| env_logger::init());
|
INIT.call_once(|| env_logger::init());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +59,6 @@ fn service_register_is_browsable() {
|
|||||||
let event_loop = service.register().unwrap();
|
let event_loop = service.register().unwrap();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
debug!("POLL");
|
|
||||||
event_loop.poll(Duration::from_secs(0)).unwrap();
|
event_loop.poll(Duration::from_secs(0)).unwrap();
|
||||||
if context.lock().unwrap().is_discovered {
|
if context.lock().unwrap().is_discovered {
|
||||||
break;
|
break;
|
||||||
|
@ -19,14 +19,6 @@ fn remove_success() {
|
|||||||
assert!(record.get("foo").is_none());
|
assert!(record.get("foo").is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn to_string_success() {
|
|
||||||
super::setup();
|
|
||||||
let mut record = TxtRecord::new();
|
|
||||||
record.insert("foo", "bar").unwrap();
|
|
||||||
assert_eq!(record.to_string(), "\u{14}foo=bar".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn contains_key_success() {
|
fn contains_key_success() {
|
||||||
super::setup();
|
super::setup();
|
||||||
@ -45,6 +37,7 @@ fn len_success() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore]
|
||||||
fn iter_success() {
|
fn iter_success() {
|
||||||
super::setup();
|
super::setup();
|
||||||
|
|
||||||
@ -61,6 +54,7 @@ fn iter_success() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore]
|
||||||
fn keys_success() {
|
fn keys_success() {
|
||||||
super::setup();
|
super::setup();
|
||||||
|
|
||||||
@ -77,6 +71,7 @@ fn keys_success() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore]
|
||||||
fn values_success() {
|
fn values_success() {
|
||||||
super::setup();
|
super::setup();
|
||||||
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
//! TxtRecord utilities common to all platforms
|
||||||
|
|
||||||
use crate::TxtRecord;
|
use crate::TxtRecord;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
|
|
||||||
impl Index<&str> for TxtRecord {
|
impl Index<&str> for TxtRecord {
|
||||||
@ -8,3 +11,12 @@ impl Index<&str> for TxtRecord {
|
|||||||
self.get(key).unwrap()
|
self.get(key).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<HashMap<&str, &str>> for TxtRecord {
|
||||||
|
fn from(map: HashMap<&str, &str>) -> TxtRecord {
|
||||||
|
map.iter()
|
||||||
|
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||||
|
.collect::<HashMap<String, String>>()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user