Skip to content

Commit

Permalink
Merge "nostr: extend Tags functionality"
Browse files Browse the repository at this point in the history
- Add `Tag::len` method
- Add `push`, `pop`, `insert`, `remove`,  `extend` and `retain` methods to `Tags` struct
- Add `Tags::from_list`, `Tags::with_capacity` and `Tags::parse` constructors
- Add `Tags::dedup` method
- Drop `WeakTag` in favor of `Tags::dedup`

Pull-Request: #755

Signed-off-by: Yuki Kishimoto <yukikishimoto@protonmail.com>
  • Loading branch information
yukibtc committed Feb 13, 2025
2 parents 7b89609 + bb5dde4 commit 1c04b39
Show file tree
Hide file tree
Showing 14 changed files with 540 additions and 82 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
### Breaking changes

* nostr: update `Nip19Event` relays field type from `Vec<String>` to `Vec<RelayUrl>` ([Yuki Kishimoto])
* nostr: update `Tags::new` signature ([Yuki Kishimoto])
* nostr: remove `WeakTag` ([Yuki Kishimoto])

### Changed

Expand All @@ -53,6 +55,10 @@

* nostr: add `EventBuilder::allow_self_tagging` ([Yuki Kishimoto])
* nostr: add `Nip19Event::from_event` ([Yuki Kishimoto])
* nostr: add `Tag::len` method ([Yuki Kishimoto])
* nostr: add `push`, `pop`, `insert`, `remove`, `extend` and `retain` methods to `Tags` struct ([Yuki Kishimoto])
* nostr: add `Tags::from_list`, `Tags::with_capacity` and `Tags::parse` constructors ([Yuki Kishimoto])
* nostr: add `Tags::dedup` method ([Yuki Kishimoto])
* pool: event verification cache ([Yuki Kishimoto])
* ffi: add Mac Catalyst support in Swift package ([Yuki Kishimoto])

Expand Down
1 change: 1 addition & 0 deletions bindings/nostr-sdk-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Distributed under the MIT software license

#![allow(clippy::new_without_default)]
#![allow(clippy::len_without_is_empty)]

pub mod client;
pub mod connect;
Expand Down
12 changes: 10 additions & 2 deletions bindings/nostr-sdk-ffi/src/protocol/event/tag/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use nostr::event::tag::list;
use uniffi::Object;

use super::{Tag, TagKind, TagStandard};
use crate::error::Result;
use crate::protocol::event::{EventId, PublicKey, Timestamp};
use crate::protocol::nips::nip01::Coordinate;

Expand All @@ -26,16 +27,23 @@ impl From<list::Tags> for Tags {
#[uniffi::export]
impl Tags {
#[uniffi::constructor]
pub fn new(list: Vec<Arc<Tag>>) -> Self {
pub fn from_list(list: Vec<Arc<Tag>>) -> Self {
Self {
inner: list::Tags::new(
inner: list::Tags::from_list(
list.into_iter()
.map(|t| t.as_ref().deref().clone())
.collect(),
),
}
}

#[uniffi::constructor]
pub fn parse(tags: Vec<Vec<String>>) -> Result<Self> {
Ok(Self {
inner: list::Tags::parse(tags)?,
})
}

/// Get number of tags
pub fn len(&self) -> u64 {
self.inner.len() as u64
Expand Down
5 changes: 5 additions & 0 deletions bindings/nostr-sdk-ffi/src/protocol/event/tag/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ impl Tag {
self.inner.as_standardized().cloned().map(|t| t.into())
}

/// Get tag len
pub fn len(&self) -> u64 {
self.inner.len() as u64
}

/// Get array of strings
pub fn as_vec(&self) -> Vec<String> {
self.inner.as_slice().to_vec()
Expand Down
1 change: 1 addition & 0 deletions bindings/nostr-sdk-js/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#![allow(clippy::arc_with_non_send_sync)]
#![allow(clippy::should_implement_trait)]
#![allow(clippy::new_without_default)]
#![allow(clippy::len_without_is_empty)]
#![allow(clippy::drop_non_drop)]
#![allow(non_snake_case)]
// rust-analyzer not work well with multiple different targets in workspace
Expand Down
32 changes: 29 additions & 3 deletions bindings/nostr-sdk-js/src/protocol/event/tag/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
// Copyright (c) 2023-2025 Rust Nostr Developers
// Distributed under the MIT software license

use js_sys::Array;
use nostr_sdk::prelude::*;
use wasm_bindgen::prelude::*;

use super::JsTag;
use crate::error::{into_err, Result};
use crate::protocol::event::JsEventId;
use crate::protocol::key::JsPublicKey;
use crate::protocol::nips::nip01::JsCoordinate;
use crate::protocol::types::JsTimestamp;
use crate::JsStringArray;

#[wasm_bindgen(js_name = Tags)]
pub struct JsTags {
Expand All @@ -24,13 +27,36 @@ impl From<Tags> for JsTags {

#[wasm_bindgen(js_class = Tags)]
impl JsTags {
#[wasm_bindgen(constructor)]
pub fn new(list: Vec<JsTag>) -> Self {
#[wasm_bindgen(js_name = fromList)]
pub fn from_list(list: Vec<JsTag>) -> Self {
Self {
inner: Tags::new(list.into_iter().map(|t| t.inner).collect()),
inner: Tags::from_list(list.into_iter().map(|t| t.inner).collect()),
}
}

#[wasm_bindgen]
pub fn parse(tags: Vec<JsStringArray>) -> Result<JsTags> {
let mut new_tags: Vec<Vec<String>> = Vec::with_capacity(tags.len());

for tag in tags.into_iter() {
let array: Array = tag.dyn_into()?;
let mut tag: Vec<String> = Vec::with_capacity(array.length() as usize);

for val in array.into_iter() {
let val: String = val
.as_string()
.ok_or_else(|| JsValue::from_str("tag values must be strings"))?;
tag.push(val);
}

new_tags.push(tag);
}

Ok(Self {
inner: Tags::parse(new_tags).map_err(into_err)?,
})
}

/// Get number of tags
pub fn len(&self) -> u64 {
self.inner.len() as u64
Expand Down
5 changes: 5 additions & 0 deletions bindings/nostr-sdk-js/src/protocol/event/tag/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ impl JsTag {
self.inner.single_letter_tag().map(|s| s.into())
}

/// Get tag len
pub fn len(&self) -> u64 {
self.inner.len() as u64
}

/// Get array of strings
#[inline]
#[wasm_bindgen(js_name = asVec)]
Expand Down
2 changes: 1 addition & 1 deletion crates/nostr/src/event/borrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl EventBorrow<'_> {
PublicKey::from_byte_array(*self.pubkey),
self.created_at,
Kind::from_u16(self.kind),
Tags::new(self.tags.into_iter().map(|t| t.into_owned()).collect()),
Tags::from_list(self.tags.into_iter().map(|t| t.into_owned()).collect()),
self.content,
// SAFETY: signature panic only if it's not 64 byte long
Signature::from_slice(self.sig.as_slice()).expect("valid signature"),
Expand Down
24 changes: 9 additions & 15 deletions crates/nostr/src/event/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

//! Event builder
use alloc::collections::BTreeSet;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::fmt;
Expand All @@ -16,7 +15,6 @@ use secp256k1::rand::{CryptoRng, Rng};
use secp256k1::{Secp256k1, Signing, Verification};
use serde_json::{json, Value};

use super::tag::weak::WeakTag;
#[cfg(all(feature = "std", feature = "nip04", feature = "nip46"))]
use crate::nips::nip46::Message as NostrConnectMessage;
use crate::prelude::*;
Expand Down Expand Up @@ -267,7 +265,7 @@ impl EventBuilder {
pubkey: public_key,
created_at,
kind: self.kind,
tags: Tags::new(tags),
tags: Tags::from_list(tags),
content: self.content,
};
}
Expand All @@ -284,7 +282,7 @@ impl EventBuilder {
.custom_created_at
.unwrap_or_else(|| Timestamp::now_with_supplier(supplier)),
kind: self.kind,
tags: Tags::new(tags),
tags: Tags::from_list(tags),
content: self.content,
};
unsigned.ensure_id();
Expand Down Expand Up @@ -414,7 +412,7 @@ impl EventBuilder {
where
S: Into<String>,
{
let mut tags: Vec<Tag> = Vec::new();
let mut tags: Tags = Tags::new();

// Add `e` and `p` tag of **root** event
match root {
Expand Down Expand Up @@ -482,13 +480,11 @@ impl EventBuilder {
.cloned(),
);

// Dedup tags using `WeakTag`
// TODO: compose directly tags with `BTreeSet<WeakTag>` instead of iterate and collect?
#[allow(clippy::mutable_key_type)]
let tags: BTreeSet<WeakTag> = tags.into_iter().map(WeakTag::new).collect();
// Dedup tags
tags.dedup();

// Compose event
Self::new(Kind::TextNote, content).tags(tags.into_iter().map(|w| w.into_inner()))
Self::new(Kind::TextNote, content).tags(tags)
}

/// Comment
Expand Down Expand Up @@ -580,13 +576,11 @@ impl EventBuilder {
// Add others `p` tags of comment_to event
extend_nip22_p_tags(comment_to, &mut tags);

// Dedup tags using `WeakTag`
// TODO: compose directly tags with `BTreeSet<WeakTag>` instead of iterate and collect?
#[allow(clippy::mutable_key_type)]
let tags: BTreeSet<WeakTag> = tags.into_iter().map(WeakTag::new).collect();
// Dedup tags
tags.dedup();

// Compose event
Self::new(Kind::Comment, content).tags(tags.into_iter().map(|w| w.into_inner()))
Self::new(Kind::Comment, content).tags(tags)
}

/// Long-form text note (generally referred to as "articles" or "blog posts").
Expand Down
2 changes: 1 addition & 1 deletion crates/nostr/src/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ impl Event {
pubkey: public_key,
created_at,
kind,
tags: Tags::new(tags.into_iter().collect()),
tags: Tags::from_list(tags.into_iter().collect()),
content: content.into(),
sig,
}
Expand Down
Loading

0 comments on commit 1c04b39

Please sign in to comment.