1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31#[serde(rename_all = "camelCase")]
32pub struct ProfileSection {
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub avatar_url: Option<String>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub banner_url: Option<String>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub tagline: Option<String>,
44
45 #[serde(default, skip_serializing_if = "Vec::is_empty")]
47 pub categories: Vec<String>,
48
49 #[serde(default, skip_serializing_if = "Vec::is_empty")]
51 pub languages: Vec<String>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub timezone: Option<String>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub accent_color: Option<String>,
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn serde_round_trip() {
68 let profile = ProfileSection {
69 avatar_url: Some("https://example.com/avatar.png".to_string()),
70 banner_url: None,
71 tagline: Some("Test agent".to_string()),
72 categories: vec!["test".to_string()],
73 languages: vec!["en".to_string()],
74 timezone: Some("UTC".to_string()),
75 accent_color: None,
76 };
77 let json = serde_json::to_string(&profile).unwrap_or_default();
78 let parsed: ProfileSection =
79 serde_json::from_str(&json).unwrap_or_else(|_| profile.clone());
80 assert_eq!(profile, parsed);
81 }
82
83 #[test]
84 fn minimal_profile_deserializes() {
85 let json = r#"{"tagline":"Hello"}"#;
86 let profile: Result<ProfileSection, _> = serde_json::from_str(json);
87 assert!(profile.is_ok());
88 let profile = profile.unwrap_or_else(|_| ProfileSection {
89 avatar_url: None,
90 banner_url: None,
91 tagline: None,
92 categories: vec![],
93 languages: vec![],
94 timezone: None,
95 accent_color: None,
96 });
97 assert_eq!(profile.tagline.as_deref(), Some("Hello"));
98 assert!(profile.categories.is_empty());
99 }
100
101 #[test]
102 fn empty_vecs_omitted_in_serialization() {
103 let profile = ProfileSection {
104 avatar_url: None,
105 banner_url: None,
106 tagline: Some("Minimal".to_string()),
107 categories: vec![],
108 languages: vec![],
109 timezone: None,
110 accent_color: None,
111 };
112 let json = serde_json::to_string(&profile).unwrap_or_default();
113 assert!(!json.contains("categories"));
114 assert!(!json.contains("languages"));
115 }
116}