Skip to content

Commit

Permalink
nostr: some little changes for PR#777
Browse files Browse the repository at this point in the history
This must be squashed with other changes!

Signed-off-by: Yuki Kishimoto <yukikishimoto@protonmail.com>
  • Loading branch information
yukibtc committed Feb 20, 2025
1 parent 4ff6a0e commit c367ef1
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 110 deletions.
2 changes: 1 addition & 1 deletion crates/nostr-lmdb/src/store/ingester.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ impl Ingester {
}

// Handle deletion events
if event.kind == Kind::EventDeletion {
if let Kind::EventDeletion = event.kind {
let invalid: bool = self.handle_deletion_event(&mut txn, &event)?;

if invalid {
Expand Down
45 changes: 29 additions & 16 deletions crates/nostr/src/event/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ pub enum Error {
Event(super::Error),
/// NIP01 error
NIP01(nip01::Error),
/// Empty tags, while at least one tag is required
EmptyTags,
/// OpenTimestamps error
#[cfg(feature = "nip03")]
NIP03(String),
Expand All @@ -68,6 +66,8 @@ pub enum Error {
/// The expected kind (single or range)
expected: WrongKindError,
},
/// Empty tags, while at least one tag is required
EmptyTags,
}

#[cfg(feature = "std")]
Expand All @@ -78,7 +78,6 @@ impl fmt::Display for Error {
match self {
Self::Event(e) => write!(f, "{e}"),
Self::NIP01(e) => write!(f, "{e}"),
Self::EmptyTags => write!(f, "Empty tags, while at least one tag is required"),
#[cfg(feature = "nip03")]
Self::NIP03(e) => write!(f, "{e}"),
#[cfg(feature = "nip04")]
Expand All @@ -91,6 +90,7 @@ impl fmt::Display for Error {
Self::WrongKind { received, expected } => {
write!(f, "Wrong kind: received={received}, expected={expected}")
}
Self::EmptyTags => write!(f, "Empty tags, while at least one tag is required"),
}
}
}
Expand Down Expand Up @@ -728,26 +728,39 @@ impl EventBuilder {
Self::new(Kind::EventDeletion, reason.into()).tags(tags)
}

/// Request to vanish with reason
/// Request to vanish
///
/// Must include at least one relay.
/// <https://github.com/nostr-protocol/nips/blob/master/62.md>
#[inline]
pub fn request_vanish<I>(target: VanishTarget) -> Result<Self, Error> {
Self::request_vanish_with_reason(target, "")
}

/// Request to vanish with reason
///
/// <https://github.com/nostr-protocol/nips/blob/master/62.md>
pub fn request_vanish_with_reason<S>(vanish_target: VanishTarget, reason: S) -> Self
pub fn request_vanish_with_reason<S>(target: VanishTarget, reason: S) -> Result<Self, Error>
where
S: Into<String>,
{
Self::new(Kind::RequestToVanish, reason).tags(Tags::from(vanish_target))
}
let mut builder = Self::new(Kind::RequestToVanish, reason);

/// Request to vanish
///
/// Must include at least one relay.
///
/// <https://github.com/nostr-protocol/nips/blob/master/62.md>
#[inline]
pub fn request_vanish<I>(vanish_target: VanishTarget) -> Self {
Self::request_vanish_with_reason(vanish_target, "")
match target {
VanishTarget::AllRelays => {
builder = builder.tag(Tag::all_relays());
}
VanishTarget::Relays(list) => {
// Check if the list is empty
if list.is_empty() {
// Empty list, return error.
return Err(Error::EmptyTags);
}

builder = builder.tags(list.into_iter().map(Tag::relay));
}
}

Ok(builder)
}

/// Add reaction (like/upvote, dislike/downvote or emoji) to an event
Expand Down
18 changes: 18 additions & 0 deletions crates/nostr/src/event/tag/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,24 @@ impl Tag {
})
}

/// Relay url
///
/// JSON: `["relay", "<relay-url>"]`
#[inline]
pub fn relay(url: RelayUrl) -> Self {
Self::from_standardized_without_cell(TagStandard::Relay(url))
}

/// All relays
///
/// JSON: `["relay", "ALL_RELAYS"]`
///
/// <https://github.com/nostr-protocol/nips/blob/master/62.md>
#[inline]
pub fn all_relays() -> Self {
Self::from_standardized_without_cell(TagStandard::AllRelays)
}

/// Compose `["t", "<hashtag>"]` tag
#[inline]
pub fn hashtag<T>(hashtag: T) -> Self
Expand Down
30 changes: 27 additions & 3 deletions crates/nostr/src/event/tag/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

//! Standardized tags
use alloc::borrow::ToOwned;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::str::FromStr;
Expand Down Expand Up @@ -33,6 +32,8 @@ use crate::{
Alphabet, Event, ImageDimensions, JsonUtil, Kind, PublicKey, SingleLetterTag, Timestamp,
};

const ALL_RELAYS: &str = "ALL_RELAYS";

/// Standardized tag
#[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
Expand Down Expand Up @@ -379,7 +380,13 @@ impl TagStandard {
character: Alphabet::U,
uppercase: false,
}) => Ok(Self::AbsoluteURL(Url::parse(tag_1)?)),
TagKind::Relay => Ok(Self::Relay(RelayUrl::parse(tag_1)?)),
TagKind::Relay => {
if tag_1 == ALL_RELAYS {
Ok(Self::AllRelays)
} else {
Ok(Self::Relay(RelayUrl::parse(tag_1)?))
}
}
TagKind::Expiration => Ok(Self::Expiration(Timestamp::from_str(tag_1)?)),
TagKind::Subject => Ok(Self::Subject(tag_1.to_string())),
TagKind::Challenge => Ok(Self::Challenge(tag_1.to_string())),
Expand Down Expand Up @@ -795,7 +802,7 @@ impl From<TagStandard> for Vec<String> {
}
TagStandard::Kind { kind, .. } => vec![tag_kind, kind.to_string()],
TagStandard::Relay(url) => vec![tag_kind, url.to_string()],
TagStandard::AllRelays => vec![tag_kind, "ALL_RELAYS".to_owned()],
TagStandard::AllRelays => vec![tag_kind, ALL_RELAYS.to_string()],
TagStandard::POW { nonce, difficulty } => {
vec![tag_kind, nonce.to_string(), difficulty.to_string()]
}
Expand Down Expand Up @@ -1803,6 +1810,13 @@ mod tests {
.to_vec()
);

assert_eq!(
vec!["relay", "wss://relay.damus.io"],
TagStandard::Relay(RelayUrl::parse("wss://relay.damus.io").unwrap()).to_vec()
);

assert_eq!(vec!["relay", "ALL_RELAYS"], TagStandard::AllRelays.to_vec());

assert_eq!(
vec![
"delegation",
Expand Down Expand Up @@ -2401,6 +2415,16 @@ mod tests {
).unwrap(), conditions: Conditions::from_str("kind=1").unwrap(), sig: Signature::from_str("fd0954de564cae9923c2d8ee9ab2bf35bc19757f8e328a978958a2fcc950eaba0754148a203adec29b7b64080d0cf5a32bebedd768ea6eb421a6b751bb4584a8").unwrap() }
);

assert_eq!(
TagStandard::parse(&["relay", "wss://relay.damus.io"]).unwrap(),
TagStandard::Relay(RelayUrl::parse("wss://relay.damus.io").unwrap())
);

assert_eq!(
TagStandard::parse(&["relay", "ALL_RELAYS"]).unwrap(),
TagStandard::AllRelays
);

assert_eq!(
TagStandard::parse(&[
"relays",
Expand Down
101 changes: 11 additions & 90 deletions crates/nostr/src/nips/nip62.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,116 +6,37 @@
//!
//! https://github.com/nostr-protocol/nips/blob/master/62.md
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

use crate::{RelayUrl, Tag, TagStandard, Tags};
use crate::RelayUrl;

/// Request to Vanish target, which is multiple relays or all relays "ALL_RELAYS".
/// Request to Vanish target
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum VanishTarget {
/// All relays target. `["r", "ALL_RELAYS"]`
/// Request to vanish from all relays
AllRelays,
/// Multiple relay targets. `[["r", "RELAY"], ...]`
/// Request to vanish from a specific list of relays.
Relays(Vec<RelayUrl>),
}

impl VanishTarget {
/// A single relay target.
/// Vanish from a single relay
#[inline]
pub fn relay(relay: RelayUrl) -> Self {
Self::Relays(vec![relay])
}

/// Multiple relay targets. If the given iterator is empty, the target is all relays.
/// Vanish from multiple relays
#[inline]
pub fn relays<I>(relays: I) -> Self
where
I: IntoIterator<Item = RelayUrl>,
{
let relays: Vec<_> = relays.into_iter().collect();
if relays.is_empty() {
Self::AllRelays
} else {
Self::Relays(relays)
}
Self::Relays(relays.into_iter().collect())
}

/// All relays target. This is equivalent to `VanishTarget::relays(vec![])`.
/// Vanish from all relays
pub fn all_relays() -> Self {
Self::AllRelays
}
}

impl From<VanishTarget> for Vec<TagStandard> {
fn from(vanish_target: VanishTarget) -> Self {
match vanish_target {
VanishTarget::AllRelays => vec![TagStandard::AllRelays],
VanishTarget::Relays(relays) => relays.into_iter().map(TagStandard::Relay).collect(),
}
}
}

impl From<VanishTarget> for Tags {
fn from(vanish_target: VanishTarget) -> Self {
Self::from_list(
Vec::<TagStandard>::from(vanish_target)
.into_iter()
.map(Tag::from)
.collect(),
)
}
}

#[cfg(test)]
mod tests {
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

use super::VanishTarget;
use crate::Tags;

#[test]
fn test_single_relay() {
let expected_tag = ["relay", "wss://example.com"];
let tag = VanishTarget::relay("wss://example.com".parse().unwrap());
let tags = Tags::from(tag);
assert_eq!(tags.len(), 1);

assert_eq!(tags.first().unwrap().as_slice(), expected_tag)
}

#[test]
fn test_relays() {
let expected_tag = vec![
["relay", "wss://example.com"],
["relay", "wss://example1.com"],
];
let tag = VanishTarget::relays([
"wss://example.com".parse().unwrap(),
"wss://example1.com".parse().unwrap(),
]);
let tags = Tags::from(tag);
assert_eq!(tags.len(), 2);

assert_eq!(
tags.to_vec()
.into_iter()
.map(|t| t.as_slice().to_vec())
.collect::<Vec<_>>(),
expected_tag
)
}

#[test]
fn test_all_relays() {
let expected_tag = ["relay", "ALL_RELAYS"];
let tag = VanishTarget::all_relays();
let empty_relays = VanishTarget::relays(Vec::new());
let tags = Tags::from(tag);
assert_eq!(tags.len(), 1);

assert_eq!(tags.first().unwrap().as_slice(), expected_tag);
assert_eq!(
Tags::from(empty_relays).first().unwrap().as_slice(),
expected_tag
);
}
}

0 comments on commit c367ef1

Please sign in to comment.