tag.rs (1815B)
1 //! Portable Nostr tag conversion and validation. 2 //! 3 //! Ordered protocol-neutral tag parts cross the upstream Nostr boundary only 4 //! through this module's validated conversion functions. 5 6 use alloc::{string::String, vec::Vec}; 7 8 use crate::Error; 9 10 /// Upstream Nostr tag used only at the explicit protocol boundary. 11 pub type Tag = nostr::Tag; 12 /// Upstream Nostr tag kind used only at the explicit protocol boundary. 13 pub type TagKind<'a> = nostr::TagKind<'a>; 14 /// Upstream standardized Nostr tag used only at the explicit protocol boundary. 15 pub type TagStandard = nostr::TagStandard; 16 17 #[cfg(feature = "events")] 18 pub use crate::error::ResolveError; 19 #[cfg(feature = "events")] 20 pub use crate::tags::tags_resolve as resolve; 21 pub use crate::tags::{ 22 tag_at_value as value_at, tag_first_value as first_value, tag_match_geohash as match_geohash, 23 tag_match_l as match_location_coordinate, tag_match_location as match_location, 24 tag_match_summary as match_summary, tag_match_title as match_title, 25 tag_relays_parse as relay_urls, tag_slice as values_from, tags_match as match_parts, 26 }; 27 28 /// Parses canonical tag parts into the explicit Nostr tag boundary value. 29 pub fn from_parts(parts: Vec<String>) -> Result<Tag, Error> { 30 Tag::parse(parts).map_err(|_| Error::TagConversion) 31 } 32 33 /// Copies a Nostr tag into its protocol-neutral ordered string parts. 34 #[must_use] 35 pub fn to_parts(tag: &Tag) -> Vec<String> { 36 tag.as_slice().to_vec() 37 } 38 39 #[cfg(test)] 40 mod tests { 41 use super::*; 42 43 #[test] 44 fn tag_parts_round_trip_without_upstream_error_leakage() { 45 let parts = vec!["t".to_owned(), "soil".to_owned()]; 46 let tag = from_parts(parts.clone()).expect("tag"); 47 48 assert_eq!(to_parts(&tag), parts); 49 assert!(matches!(from_parts(Vec::new()), Err(Error::TagConversion))); 50 } 51 }