file_metadata.rs (3556B)
1 #[cfg(not(feature = "std"))] 2 use alloc::{string::String, vec::Vec}; 3 4 use crate::social::{SocialMediaDimensions, SocialMediaThumbnail}; 5 6 #[cfg_attr( 7 any(feature = "serde", test), 8 derive(serde::Serialize, serde::Deserialize) 9 )] 10 #[derive(Clone, Debug)] 11 pub struct FileMetadata { 12 pub url: String, 13 pub mime_type: String, 14 pub sha256: String, 15 #[cfg_attr( 16 feature = "serde", 17 serde(default, skip_serializing_if = "Option::is_none") 18 )] 19 pub original_sha256: Option<String>, 20 #[cfg_attr( 21 feature = "serde", 22 serde(default, skip_serializing_if = "Option::is_none") 23 )] 24 pub size: Option<u64>, 25 #[cfg_attr( 26 feature = "serde", 27 serde(default, skip_serializing_if = "Option::is_none") 28 )] 29 pub dimensions: Option<SocialMediaDimensions>, 30 #[cfg_attr( 31 feature = "serde", 32 serde(default, skip_serializing_if = "Option::is_none") 33 )] 34 pub blurhash: Option<String>, 35 #[cfg_attr( 36 feature = "serde", 37 serde(default, skip_serializing_if = "Option::is_none") 38 )] 39 pub thumbnails: Option<Vec<SocialMediaThumbnail>>, 40 #[cfg_attr( 41 feature = "serde", 42 serde(default, skip_serializing_if = "Option::is_none") 43 )] 44 pub summary: Option<String>, 45 #[cfg_attr( 46 feature = "serde", 47 serde(default, skip_serializing_if = "Option::is_none") 48 )] 49 pub alt: Option<String>, 50 #[cfg_attr( 51 feature = "serde", 52 serde(default, skip_serializing_if = "Option::is_none") 53 )] 54 pub fallback: Option<String>, 55 #[cfg_attr( 56 feature = "serde", 57 serde(default, skip_serializing_if = "Option::is_none") 58 )] 59 pub magnet: Option<String>, 60 #[cfg_attr( 61 feature = "serde", 62 serde(default, skip_serializing_if = "Option::is_none") 63 )] 64 pub content_hashes: Option<Vec<String>>, 65 #[cfg_attr( 66 feature = "serde", 67 serde(default, skip_serializing_if = "Option::is_none") 68 )] 69 pub services: Option<Vec<String>>, 70 #[cfg_attr( 71 feature = "serde", 72 serde(default, skip_serializing_if = "Option::is_none") 73 )] 74 pub content: Option<String>, 75 } 76 77 #[cfg(all(test, feature = "std", feature = "serde"))] 78 #[cfg_attr(coverage_nightly, coverage(off))] 79 mod tests { 80 use super::*; 81 82 #[test] 83 fn file_metadata_represents_required_nip94_fields() { 84 let metadata = FileMetadata { 85 url: "https://example.test/file.jpg".to_string(), 86 mime_type: "image/jpeg".to_string(), 87 sha256: "a".repeat(64), 88 original_sha256: Some("b".repeat(64)), 89 size: Some(1024), 90 dimensions: Some(SocialMediaDimensions { 91 width: 640, 92 height: 480, 93 }), 94 blurhash: None, 95 thumbnails: None, 96 summary: Some("field image".to_string()), 97 alt: Some("rows of lettuce".to_string()), 98 fallback: None, 99 magnet: Some("magnet:?xt=urn:btih:abc".to_string()), 100 content_hashes: Some(vec!["sha256:a".to_string()]), 101 services: Some(vec!["https://media.example.test".to_string()]), 102 content: Some("caption".to_string()), 103 }; 104 105 assert_eq!(metadata.mime_type, "image/jpeg"); 106 assert_eq!(metadata.sha256.len(), 64); 107 assert_eq!(metadata.dimensions.expect("dimensions").width, 640); 108 assert!(metadata.magnet.expect("magnet").starts_with("magnet:")); 109 assert_eq!(metadata.services.expect("services").len(), 1); 110 } 111 }