use super::PublicKeyHex;
use serde::de::{Deserialize, Deserializer, MapAccess, Visitor};
use serde::ser::{Serialize, SerializeMap, Serializer};
use serde_json::{json, Map, Value};
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RelayInformationDocument {
pub name: Option<String>,
pub description: Option<String>,
pub pubkey: Option<PublicKeyHex>,
pub contact: Option<String>,
pub supported_nips: Vec<u32>,
pub software: Option<String>,
pub version: Option<String>,
pub other: Map<String, Value>,
}
impl Default for RelayInformationDocument {
fn default() -> RelayInformationDocument {
RelayInformationDocument {
name: None,
description: None,
pubkey: None,
contact: None,
supported_nips: vec![],
software: None,
version: None,
other: Map::new(),
}
}
}
impl RelayInformationDocument {
pub fn supports_nip(&self, nip: u32) -> bool {
self.supported_nips.contains(&nip)
}
#[allow(dead_code)]
pub(crate) fn mock() -> RelayInformationDocument {
let mut m = Map::new();
let _ = m.insert(
"early_nips".to_string(),
Value::Array(vec![
Value::Number(5.into()),
Value::Number(6.into()),
Value::Number(7.into()),
]),
);
RelayInformationDocument {
name: Some("Crazy Horse".to_string()),
description: Some("A really wild horse".to_string()),
pubkey: Some(PublicKeyHex::mock()),
contact: None,
supported_nips: vec![11, 12, 13, 14],
software: None,
version: None,
other: m,
}
}
}
impl fmt::Display for RelayInformationDocument {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Relay Information:")?;
if let Some(name) = &self.name {
write!(f, " Name=\"{}\"", name)?;
}
if let Some(desc) = &self.description {
write!(f, " Description=\"{}\"", desc)?;
}
if let Some(pubkey) = &self.pubkey {
write!(f, " Pubkey=\"{}\"", pubkey)?;
}
if let Some(contact) = &self.contact {
write!(f, " Contact=\"{}\"", contact)?;
}
write!(f, " NIPS={:?}", self.supported_nips)?;
if let Some(software) = &self.software {
write!(f, " Software=\"{}\"", software)?;
}
if let Some(version) = &self.version {
write!(f, " Version=\"{}\"", version)?;
}
for (k, v) in self.other.iter() {
write!(f, " {}=\"{}\"", k, v)?;
}
Ok(())
}
}
impl Serialize for RelayInformationDocument {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(7 + self.other.len()))?;
map.serialize_entry("name", &json!(&self.name))?;
map.serialize_entry("description", &json!(&self.description))?;
map.serialize_entry("pubkey", &json!(&self.pubkey))?;
map.serialize_entry("contact", &json!(&self.contact))?;
map.serialize_entry("supported_nips", &json!(&self.supported_nips))?;
map.serialize_entry("software", &json!(&self.software))?;
map.serialize_entry("version", &json!(&self.version))?;
for (k, v) in &self.other {
map.serialize_entry(&k, &v)?;
}
map.end()
}
}
impl<'de> Deserialize<'de> for RelayInformationDocument {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(RidVisitor)
}
}
struct RidVisitor;
impl<'de> Visitor<'de> for RidVisitor {
type Value = RelayInformationDocument;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "A JSON object")
}
fn visit_map<M>(self, mut access: M) -> Result<RelayInformationDocument, M::Error>
where
M: MapAccess<'de>,
{
let mut map: Map<String, Value> = Map::new();
while let Some((key, value)) = access.next_entry::<String, Value>()? {
let _ = map.insert(key, value);
}
let mut rid: RelayInformationDocument = Default::default();
if let Some(Value::String(s)) = map.remove("name") {
rid.name = Some(s);
}
if let Some(Value::String(s)) = map.remove("description") {
rid.description = Some(s);
}
if let Some(Value::String(s)) = map.remove("pubkey") {
rid.pubkey = Some(PublicKeyHex(s));
}
if let Some(Value::String(s)) = map.remove("contact") {
rid.contact = Some(s);
}
if let Some(Value::Array(vec)) = map.remove("supported_nips") {
for elem in vec.iter() {
if let Value::Number(num) = elem {
if let Some(u) = num.as_u64() {
rid.supported_nips.push(u as u32);
}
}
}
}
if let Some(Value::String(s)) = map.remove("software") {
rid.software = Some(s);
}
if let Some(Value::String(s)) = map.remove("version") {
rid.version = Some(s);
}
rid.other = map;
Ok(rid)
}
}
#[cfg(test)]
mod test {
use super::*;
test_serde! {RelayInformationDocument, test_relay_information_document_serde}
#[test]
fn test_relay_information_document_json() {
let json = r##"{ "name": "A Relay", "description": null, "myfield": [1,2], "supported_nips": [11,12] }"##;
let rid: RelayInformationDocument = serde_json::from_str(json).unwrap();
let json2 = serde_json::to_string(&rid).unwrap();
let expected_json2 = r##"{"name":"A Relay","description":null,"pubkey":null,"contact":null,"supported_nips":[11,12],"software":null,"version":null,"myfield":[1,2]}"##;
assert_eq!(json2, expected_json2);
}
}