use serde::{Deserialize, Serialize};
use url::Url;
use crate::schema::Version;
pub const SCHEMA_REL_PREFIX: &str = "http://nodeinfo.diaspora.software/ns/schema/";
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Discovery {
pub links: Vec<DiscoveryLink>,
}
impl Discovery {
#[must_use]
pub fn for_version(version: Version, href: Url) -> Self {
Self {
links: vec![DiscoveryLink::new(version, href)],
}
}
#[must_use]
pub fn with_version(mut self, version: Version, href: Url) -> Self {
self.links.push(DiscoveryLink::new(version, href));
self
}
#[must_use]
pub fn preferred_link(&self) -> Option<&DiscoveryLink> {
self.find_link(Version::V2_1)
.or_else(|| self.find_link(Version::V2_0))
}
#[must_use]
pub fn find_link(&self, version: Version) -> Option<&DiscoveryLink> {
self.links.iter().find(|l| l.rel == version.schema_uri())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct DiscoveryLink {
pub rel: String,
pub href: Url,
}
impl DiscoveryLink {
#[must_use]
pub fn new(version: Version, href: Url) -> Self {
Self {
rel: version.schema_uri().to_owned(),
href,
}
}
#[must_use]
pub fn version(&self) -> Option<Version> {
match self.rel.as_str() {
s if s == Version::V2_1.schema_uri() => Some(Version::V2_1),
s if s == Version::V2_0.schema_uri() => Some(Version::V2_0),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use serde_json::json;
use super::*;
#[test]
fn discovery_roundtrips_mastodon_style() {
let raw = json!({
"links": [
{
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.0",
"href": "https://mastodon.social/nodeinfo/2.0"
},
{
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.1",
"href": "https://mastodon.social/nodeinfo/2.1"
}
]
});
let d: Discovery = serde_json::from_value(raw.clone()).unwrap();
assert_eq!(d.links.len(), 2);
let preferred = d.preferred_link().unwrap();
assert_eq!(preferred.version(), Some(Version::V2_1));
let back = serde_json::to_value(&d).unwrap();
assert_eq!(back, raw);
}
#[test]
fn for_version_builds_single_link() {
let d = Discovery::for_version(
Version::V2_1,
"https://example.com/nodeinfo/2.1".parse().unwrap(),
);
assert_eq!(d.links.len(), 1);
assert_eq!(d.links[0].version(), Some(Version::V2_1));
}
#[test]
fn discovery_link_version_is_none_for_unknown() {
let link = DiscoveryLink::new(Version::V2_0, "https://example.com/ni/99".parse().unwrap());
let mut unknown = link;
unknown.rel = "http://example.com/schema/99".to_owned();
assert_eq!(unknown.version(), None);
}
}