1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! TxtRecord utilities common to all platforms

use crate::{Result, TxtRecord};
use serde::de::{MapAccess, Visitor};
use serde::ser::SerializeMap;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::HashMap;
use std::fmt;
use std::marker::PhantomData;

/// Interface for interacting with underlying mDNS implementation TXT record capabilities
pub trait TTxtRecord: Clone + PartialEq + Eq {
    /// Constructs a new TXT record
    fn new() -> Self;

    /// Inserts the specified value at the specified key.
    fn insert(&mut self, key: &str, value: &str) -> Result<()>;

    /// Returns the value at the specified key or `None` if no such key exists.
    ///
    /// This function returns an owned `String` because there are no guarantees that the
    /// implementation provides access to the underlying value pointer.
    fn get(&self, key: &str) -> Option<String>;

    /// Removes the value at the specified key. Returns `Err` if no such key exists.
    fn remove(&mut self, key: &str) -> Result<()>;

    /// Returns true if the TXT record contains the specified key.
    fn contains_key(&self, key: &str) -> bool;

    /// Returns the amount of entries in the TXT record.
    fn len(&self) -> usize;

    /// Returns a new iterator for iterating over the record as you would a `HashMap`.
    fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (String, String)> + 'a>;

    /// Returns a new iterator over the records keys.
    fn keys<'a>(&'a self) -> Box<dyn Iterator<Item = String> + 'a>;

    /// Returns a new iterator over the records values.
    fn values<'a>(&'a self) -> Box<dyn Iterator<Item = String> + 'a>;

    /// Returns true if there are no entries in the record.
    fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns a new `HashMap` with this record's keys and values.
    fn to_map(&self) -> HashMap<String, String> {
        let mut m = HashMap::new();
        for (key, value) in self.iter() {
            m.insert(key, value.to_string());
        }
        m
    }
}

impl From<HashMap<String, String>> for TxtRecord {
    fn from(map: HashMap<String, String>) -> TxtRecord {
        let mut record = TxtRecord::new();
        for (key, value) in map {
            record.insert(&key, &value).unwrap();
        }
        record
    }
}

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()
    }
}

impl Eq for TxtRecord {}

impl Default for TxtRecord {
    fn default() -> Self {
        Self::new()
    }
}

impl Serialize for TxtRecord {
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut map = serializer.serialize_map(Some(self.len()))?;
        for (key, value) in self.iter() {
            map.serialize_entry(&key, &value)?;
        }
        map.end()
    }
}

#[derive(new)]
struct TxtRecordVisitor {
    marker: PhantomData<fn() -> TxtRecord>,
}

impl<'de> Visitor<'de> for TxtRecordVisitor {
    type Value = TxtRecord;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("map containing TXT record data")
    }

    fn visit_map<M>(self, mut access: M) -> std::result::Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut map = TxtRecord::new();

        while let Some((key, value)) = access.next_entry()? {
            map.insert(key, value).unwrap();
        }

        Ok(map)
    }
}

impl<'de> Deserialize<'de> for TxtRecord {
    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(TxtRecordVisitor::new())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn serialize_success() {
        crate::tests::setup();

        let mut txt = TxtRecord::new();
        txt.insert("foo", "bar").unwrap();

        let json = serde_json::to_string(&txt).unwrap();
        let txt_de: TxtRecord = serde_json::from_str(&json).unwrap();

        assert_eq!(txt, txt_de);
    }
}