use alloc::string::String;
use alloc::vec::Vec;
use crate::{JsonUtil, Timestamp};
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct RelayInformationDocument {
pub name: Option<String>,
pub description: Option<String>,
pub pubkey: Option<String>,
pub contact: Option<String>,
pub supported_nips: Option<Vec<u16>>,
pub software: Option<String>,
pub version: Option<String>,
pub limitation: Option<Limitation>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub retention: Vec<Retention>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub relay_countries: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub language_tags: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub tags: Vec<String>,
pub posting_policy: Option<String>,
pub payments_url: Option<String>,
pub fees: Option<FeeSchedules>,
pub icon: Option<String>,
}
impl RelayInformationDocument {
#[inline]
pub fn new() -> Self {
Self::default()
}
}
impl JsonUtil for RelayInformationDocument {
type Err = serde_json::Error;
}
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Limitation {
pub max_message_length: Option<i32>,
pub max_subscriptions: Option<i32>,
pub max_filters: Option<i32>,
pub max_limit: Option<i32>,
pub max_subid_length: Option<i32>,
pub max_event_tags: Option<i32>,
pub max_content_length: Option<i32>,
pub min_pow_difficulty: Option<i32>,
pub auth_required: Option<bool>,
pub payment_required: Option<bool>,
pub created_at_lower_limit: Option<Timestamp>,
pub created_at_upper_limit: Option<Timestamp>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Retention {
pub kinds: Option<Vec<RetentionKind>>,
pub time: Option<u64>,
pub count: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RetentionKind {
Single(u64),
Range(u64, u64),
}
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct FeeSchedules {
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub admission: Vec<FeeSchedule>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub subscription: Vec<FeeSchedule>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub publication: Vec<FeeSchedule>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct FeeSchedule {
pub amount: i32,
pub unit: String,
pub period: Option<i32>,
pub kinds: Option<Vec<String>>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn correctly_serializes_retention_kind() {
let kinds = vec![
RetentionKind::Single(0),
RetentionKind::Single(1),
RetentionKind::Range(5, 7),
RetentionKind::Range(40, 49),
];
let got = serde_json::to_string(&kinds).unwrap();
let expected = "[0,1,[5,7],[40,49]]";
assert_eq!(got, expected, "got: {}, expected: {}", got, expected);
}
#[test]
fn correctly_deserializes_retention_kind() {
let kinds = "[0, 1, [5, 7], [40, 49]]";
let got = serde_json::from_str::<Vec<RetentionKind>>(kinds).unwrap();
let expected = vec![
RetentionKind::Single(0),
RetentionKind::Single(1),
RetentionKind::Range(5, 7),
RetentionKind::Range(40, 49),
];
assert_eq!(got, expected, "got: {:?}, expected: {:?}", got, expected);
}
#[test]
fn correctly_parses_relay_information_document() {
let json = r#"{
"name": "Test Relay",
"description": "A test relay for unit testing",
"pubkey": "bf2bee5281149c7c350f5d12ae32f514c7864ff10805182f4178538c2c421007",
"contact": "test@example.com",
"supported_nips": [1, 9, 11],
"software": "https://github.com/example/relay",
"version": "1.0.0",
"limitation": {
"max_message_length": 16384,
"max_subscriptions": 300,
"auth_required": false,
"payment_required": true
}
}"#;
let doc = RelayInformationDocument::from_json(json).unwrap();
assert_eq!(doc.name, Some(String::from("Test Relay")));
assert_eq!(
doc.description,
Some(String::from("A test relay for unit testing"))
);
}
#[test]
fn serialization_round_trip() {
let mut doc = RelayInformationDocument::new();
doc.name = Some(String::from("Round Trip Test"));
doc.supported_nips = Some(vec![1, 9, 11]);
let json = doc.as_json();
let parsed_doc = RelayInformationDocument::from_json(&json).unwrap();
assert_eq!(doc, parsed_doc);
}
#[test]
fn handles_invalid_json() {
let invalid_json = r#"{"name": "Invalid", "supported_nips": [1, 2, "invalid"]}"#;
let result = RelayInformationDocument::from_json(invalid_json);
assert!(result.is_err());
}
}