1use crate::event::{Event, EventBuilder, Tag, TagKind};
15
16pub const PROTECTED_TAG: &str = "-";
18
19#[must_use]
21pub fn is_protected(event: &Event) -> bool {
22 let kind = TagKind::from_wire(PROTECTED_TAG);
23 event.tags.find_first(&kind).is_some()
24}
25
26impl EventBuilder {
27 #[must_use]
33 pub fn protected(mut self) -> Self {
34 let kind = TagKind::from_wire(PROTECTED_TAG);
35 let tag = Tag::with(&kind, std::iter::empty::<String>());
38 self.tags_mut().push_unique_kind(tag);
39 self
40 }
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46 use crate::Keys;
47 use crate::types::Timestamp;
48
49 fn keys() -> Keys {
50 Keys::parse("0000000000000000000000000000000000000000000000000000000000000003").unwrap()
51 }
52
53 #[test]
54 fn unmarked_event_is_not_protected() {
55 let event = EventBuilder::text_note("public")
56 .created_at(Timestamp::from_secs(1))
57 .sign_with_keys(&keys())
58 .unwrap();
59 assert!(!is_protected(&event));
60 }
61
62 #[test]
63 fn protected_marker_round_trip() {
64 let event = EventBuilder::text_note("private")
65 .created_at(Timestamp::from_secs(2))
66 .protected()
67 .sign_with_keys(&keys())
68 .unwrap();
69 event.verify().unwrap();
70 assert!(is_protected(&event));
71 }
72
73 #[test]
74 fn protected_is_idempotent() {
75 let event = EventBuilder::text_note("private")
76 .created_at(Timestamp::from_secs(3))
77 .protected()
78 .protected()
79 .protected()
80 .sign_with_keys(&keys())
81 .unwrap();
82 let count = event
83 .tags
84 .iter()
85 .filter(|t| t.kind() == TagKind::from_wire(PROTECTED_TAG))
86 .count();
87 assert_eq!(count, 1);
88 }
89}