From efc7b83930c85f7ad35ee0e88873c13d236e9575 Mon Sep 17 00:00:00 2001 From: Karcsesz Date: Tue, 7 May 2024 18:04:43 +0200 Subject: [PATCH] Add `template` field to Link because ostatus subscribe schema needs it. Also fixes a bug where Resource::compress lost every link and adds a lot more tests around that functionality. --- src/schema/resource.rs | 144 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 136 insertions(+), 8 deletions(-) diff --git a/src/schema/resource.rs b/src/schema/resource.rs index 6e3510e..26b540c 100644 --- a/src/schema/resource.rs +++ b/src/schema/resource.rs @@ -114,6 +114,7 @@ impl Resource { .map(clone_hashmap_with_option_value_as_complete) .unwrap_or_default(), ), + template: Some(link.template.clone().unwrap_or_default()), } })) }) @@ -146,6 +147,36 @@ impl Resource { .links .filter(|links| !links.is_empty()) .map(|mut links| { + // Collapse default subvalues + for link in &mut links { + if link.media_type.as_ref().map_or(false, String::is_empty) { + link.media_type = None; + } + if link.href.as_ref().map_or(false, String::is_empty) { + link.href = None; + } + if let Some(titles) = &mut link.titles { + titles.retain(|key, value| !key.is_empty() || !value.is_empty()) + } + if link.titles.as_ref().map_or(false, HashMap::is_empty) { + link.titles = None; + } + if let Some(properties) = &mut link.properties { + for value in properties.values_mut() { + if value.as_ref().map_or(false, String::is_empty) { + *value = None; + } + } + properties.retain(|key, value| !key.is_empty() || !value.is_none()) + } + if link.properties.as_ref().map_or(false, HashMap::is_empty) { + link.properties = None; + } + if link.template.as_ref().map_or(false, String::is_empty) { + link.template = None; + } + } + // Delete completely default links links.retain(|link| { // Empty `rel` is invalid, but short-circuiting here would delete records // that are only partially edited. Better to store invalid data than to delete @@ -153,22 +184,19 @@ impl Resource { let mut is_default = link.rel.is_empty(); is_default &= link .media_type - .as_ref() - .filter(|media_type| !media_type.is_empty()) .is_none(); is_default &= link.href.as_ref().filter(|href| !href.is_empty()).is_none(); is_default &= link .titles - .as_ref() - .filter(|titles| !titles.is_empty()) .is_none(); is_default &= link .properties - .as_ref() - .filter(|titles| !titles.is_empty()) + .is_none(); + is_default &= link + .template .is_none(); - is_default + !is_default }); links @@ -189,12 +217,15 @@ pub struct Link { pub titles: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub properties: Option>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub template: Option, } #[cfg(test)] /// Functions to generate data for testing functions that manipulate `Resource` structs pub mod test_data { - use crate::schema::resource::Resource; + use std::collections::HashMap; + use crate::schema::resource::{Link, Resource}; /// A [`Resource`] with only the `subject` field set pub fn barebones_user() -> Resource { @@ -220,6 +251,100 @@ pub mod test_data { links: None, } } + + pub fn example_mastodon_user() -> Resource { + Resource { + subject: "acct:user@example.com".to_string(), + aliases: Some(vec![ + "https://example.com/@user".to_string(), + "https://example.com/users/user".to_string() + ]), + properties: None, + links: Some(vec![ + Link { + rel: "http://webfinger.net/rel/profile-page".to_string(), + media_type: Some("text/html".to_string()), + href: Some("https://example.com/@user".to_string()), + titles: None, + properties: None, + template: None, + }, + Link { + rel: "self".to_string(), + media_type: Some("application/activity+json".to_string()), + href: Some("https://example.com/users/user".to_string()), + titles: None, + properties: None, + template: None, + }, + Link { + rel: "http://ostatus.org/schema/1.0/subscribe".to_string(), + media_type: None, + href: None, + titles: None, + properties: None, + template: Some("https://example.com/authorize_interaction?uri={uri}".to_string()), + }, + Link { + rel: "http://webfinger.net/rel/avatar".to_string(), + media_type: Some("image/png".to_string()), + href: Some("https://example.com/system/accounts/avatars/321/423/112/234/123/527/original/1j2ioff88a9wa.png".to_string()), + titles: None, + properties: None, + template: None, + } + ]), + } + } + + pub fn example_firefish_user() -> Resource { + Resource { + subject: "acct:user@example.com".to_string(), + aliases: None, + properties: None, + links: Some(vec![ + Link { + rel: "self".to_string(), + media_type: Some("application/activity+json".to_string()), + href: Some("https://example.com/users/8fsua89lcieaj".to_string()), + titles: None, + properties: None, + template: None, + }, + Link { + rel: "http://webfinger.net/rel/profile-page".to_string(), + media_type: Some("text/html".to_string()), + href: Some("https://example.com/@user".to_string()), + titles: None, + properties: None, + template: None, + }, + Link { + rel: "http://ostatus.org/schema/1.0/subscribe".to_string(), + media_type: None, + href: None, + titles: None, + properties: None, + template: Some("https://example.com/authorize-follow?acct={uri}".to_string()), + } + ]) + } + } + pub fn full_resource() -> Resource { + Resource { + subject: "some_subject".to_string(), + aliases: Some(vec!["ThisIsAnAlias".to_string()]), + properties: Some(HashMap::from_iter(std::iter::once(("property_key".to_string(), Some("property_value".to_string()))))), + links: Some(vec![Link { + rel: "link_relation".to_string(), + media_type: Some("media/type".to_string()), + href: Some("https://example.com/link_href".to_string()), + titles: Some(HashMap::from_iter(std::iter::once(("title_key".to_string(), "title_value".to_string())))), + properties: Some(HashMap::from_iter(std::iter::once(("property_key".to_string(), Some("property_value".to_string()))))), + template: Some("template".to_string()), + }]), + } + } } #[cfg(test)] @@ -288,6 +413,9 @@ mod tests { test_data::barebones_user(), test_data::user_with_matching_subject_and_alias(), test_data::user_with_single_alias(), + test_data::example_firefish_user(), + test_data::example_mastodon_user(), + test_data::full_resource(), ] { assert_eq!(data, data.as_completely_serializable().compress()); } -- 2.39.2