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::{self, Debug};
use std::marker::PhantomData;
pub trait TTxtRecord: Clone + PartialEq + Eq + Debug {
fn new() -> Self;
fn insert(&mut self, key: &str, value: &str) -> Result<()>;
fn get(&self, key: &str) -> Option<String>;
fn remove(&mut self, key: &str) -> Result<()>;
fn contains_key(&self, key: &str) -> bool;
fn len(&self) -> usize;
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (String, String)> + 'a>;
fn keys<'a>(&'a self) -> Box<dyn Iterator<Item = String> + 'a>;
fn values<'a>(&'a self) -> Box<dyn Iterator<Item = String> + 'a>;
fn is_empty(&self) -> bool {
self.len() == 0
}
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())
}
}
impl Debug for TxtRecord {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TxtRecord")
.field("data", &self.to_map())
.finish()
}
}
#[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);
}
}