actpub_nodeinfo/
discovery.rs1use serde::{Deserialize, Serialize};
4use url::Url;
5
6use crate::schema::Version;
7
8pub const SCHEMA_REL_PREFIX: &str = "http://nodeinfo.diaspora.software/ns/schema/";
13
14#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
20#[non_exhaustive]
21pub struct Discovery {
22 pub links: Vec<DiscoveryLink>,
24}
25
26impl Discovery {
27 #[must_use]
40 pub fn for_version(version: Version, href: Url) -> Self {
41 Self {
42 links: vec![DiscoveryLink::new(version, href)],
43 }
44 }
45
46 #[must_use]
48 pub fn with_version(mut self, version: Version, href: Url) -> Self {
49 self.links.push(DiscoveryLink::new(version, href));
50 self
51 }
52
53 #[must_use]
59 pub fn preferred_link(&self) -> Option<&DiscoveryLink> {
60 self.find_link(Version::V2_1)
61 .or_else(|| self.find_link(Version::V2_0))
62 }
63
64 #[must_use]
66 pub fn find_link(&self, version: Version) -> Option<&DiscoveryLink> {
67 self.links.iter().find(|l| l.rel == version.schema_uri())
68 }
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
73#[non_exhaustive]
74pub struct DiscoveryLink {
75 pub rel: String,
77
78 pub href: Url,
80}
81
82impl DiscoveryLink {
83 #[must_use]
85 pub fn new(version: Version, href: Url) -> Self {
86 Self {
87 rel: version.schema_uri().to_owned(),
88 href,
89 }
90 }
91
92 #[must_use]
94 pub fn version(&self) -> Option<Version> {
95 match self.rel.as_str() {
96 s if s == Version::V2_1.schema_uri() => Some(Version::V2_1),
97 s if s == Version::V2_0.schema_uri() => Some(Version::V2_0),
98 _ => None,
99 }
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use pretty_assertions::assert_eq;
106 use serde_json::json;
107
108 use super::*;
109
110 #[test]
111 fn discovery_roundtrips_mastodon_style() {
112 let raw = json!({
113 "links": [
114 {
115 "rel": "http://nodeinfo.diaspora.software/ns/schema/2.0",
116 "href": "https://mastodon.social/nodeinfo/2.0"
117 },
118 {
119 "rel": "http://nodeinfo.diaspora.software/ns/schema/2.1",
120 "href": "https://mastodon.social/nodeinfo/2.1"
121 }
122 ]
123 });
124
125 let d: Discovery = serde_json::from_value(raw.clone()).unwrap();
126 assert_eq!(d.links.len(), 2);
127
128 let preferred = d.preferred_link().unwrap();
129 assert_eq!(preferred.version(), Some(Version::V2_1));
130
131 let back = serde_json::to_value(&d).unwrap();
132 assert_eq!(back, raw);
133 }
134
135 #[test]
136 fn for_version_builds_single_link() {
137 let d = Discovery::for_version(
138 Version::V2_1,
139 "https://example.com/nodeinfo/2.1".parse().unwrap(),
140 );
141 assert_eq!(d.links.len(), 1);
142 assert_eq!(d.links[0].version(), Some(Version::V2_1));
143 }
144
145 #[test]
146 fn discovery_link_version_is_none_for_unknown() {
147 let link = DiscoveryLink::new(Version::V2_0, "https://example.com/ni/99".parse().unwrap());
148 let mut unknown = link;
150 unknown.rel = "http://example.com/schema/99".to_owned();
151 assert_eq!(unknown.version(), None);
152 }
153}