Skip to content

Commit

Permalink
nostr: fix EventBuilder::git_patch constructor
Browse files Browse the repository at this point in the history
- Remove git patch alt tags
- Change `GitPatch` struct fields
- Change `EventBuilder::git_patch` signature
- Add unit test

Fixes ed5737c

Signed-off-by: Yuki Kishimoto <yukikishimoto@protonmail.com>
  • Loading branch information
yukibtc committed Feb 18, 2025
1 parent 4d9a24a commit 7b161e1
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 63 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
* nostr: change `EventId::new` signature ([Yuki Kishimoto])
* nostr: change `EventBuilder::git_repository_announcement` constructor signature ([Yuki Kishimoto])
* nostr: change `EventBuilder::git_issue` constructor signature ([Yuki Kishimoto])
* nostr: change `EventBuilder::git_patch` constructor signature ([Yuki Kishimoto])

### Changed

Expand Down Expand Up @@ -79,6 +80,7 @@

* nostr: fix `EventBuilder::git_repository_announcement` constructor according to last NIP34 rev ([Yuki Kishimoto])
* nostr: fix `EventBuilder::git_issue` constructor according to last NIP34 rev ([Yuki Kishimoto])
* nostr: fix `EventBuilder::git_patch` constructor according to last NIP34 rev ([Yuki Kishimoto])

### Removed

Expand Down
2 changes: 1 addition & 1 deletion bindings/nostr-sdk-ffi/src/protocol/event/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -858,7 +858,7 @@ impl EventBuilder {
#[uniffi::constructor]
pub fn git_patch(patch: GitPatch) -> Result<Self> {
Ok(Self {
inner: nostr::EventBuilder::git_patch(patch.try_into()?),
inner: nostr::EventBuilder::git_patch(patch.try_into()?)?,
})
}
}
18 changes: 7 additions & 11 deletions bindings/nostr-sdk-ffi/src/protocol/nips/nip34.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use nostr::{RelayUrl, Url};
use uniffi::{Enum, Record};

use crate::error::NostrSdkError;
use crate::protocol::event::EventId;
use crate::protocol::key::PublicKey;
use crate::protocol::nips::nip01::Coordinate;
use crate::protocol::types::Timestamp;
Expand Down Expand Up @@ -186,28 +185,25 @@ impl TryFrom<GitPatchContent> for nip34::GitPatchContent {
/// Git Patch
#[derive(Record)]
pub struct GitPatch {
/// Repository ID
pub repo_id: String,
/// Repository
pub repository: Arc<Coordinate>,
/// Patch
pub content: GitPatchContent,
/// Maintainers
pub maintainers: Vec<Arc<PublicKey>>,
/// Earliest unique commit ID of repo
pub euc: String,
/// Root proposal ID
pub root_proposal_id: Option<Arc<EventId>>,
/// Labels
pub labels: Vec<String>,
}

impl TryFrom<GitPatch> for nip34::GitPatch {
type Error = NostrSdkError;

fn try_from(value: GitPatch) -> Result<Self, Self::Error> {
Ok(Self {
repo_id: value.repo_id,
repository: value.repository.as_ref().deref().clone(),
content: value.content.try_into()?,
maintainers: value.maintainers.into_iter().map(|p| **p).collect(),
euc: value.euc,
root_proposal_id: value.root_proposal_id.map(|e| **e),
euc: Sha1Hash::from_str(&value.euc)?,
labels: value.labels,
})
}
}
2 changes: 1 addition & 1 deletion crates/nostr/src/event/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1723,7 +1723,7 @@ impl EventBuilder {
///
/// <https://github.com/nostr-protocol/nips/blob/master/34.md>
#[inline]
pub fn git_patch(patch: GitPatch) -> Self {
pub fn git_patch(patch: GitPatch) -> Result<Self, Error> {
patch.to_event_builder()
}

Expand Down
148 changes: 98 additions & 50 deletions crates/nostr/src/nips/nip34.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,12 @@ use hashes::sha1::Hash as Sha1Hash;

use crate::event::builder::{Error, EventBuilder, WrongKindError};
use crate::nips::nip01::{self, Coordinate};
use crate::nips::nip10::Marker;
use crate::types::url::Url;
use crate::{EventId, Kind, PublicKey, RelayUrl, Tag, TagKind, TagStandard, Timestamp};
use crate::{Kind, PublicKey, RelayUrl, Tag, TagKind, TagStandard, Timestamp};

/// Earlier unique commit ID marker
pub const EUC: &str = "euc";

const GIT_PATCH_ALT: &str = "git patch";
const GIT_PATCH_COVER_LETTER_ALT: &str = "git patch cover letter";

/// Git Repository Announcement
///
/// Git repositories are hosted in Git-enabled servers, but their existence can be announced using Nostr events,
Expand Down Expand Up @@ -234,41 +230,49 @@ impl fmt::Display for GitPatchContent {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GitPatch {
/// Repository ID
pub repo_id: String,
pub repository: Coordinate,
/// Patch
pub content: GitPatchContent,
/// Maintainers
pub maintainers: Vec<PublicKey>,
/// Earliest unique commit ID of repo
pub euc: String,
/// Root proposal ID
pub root_proposal_id: Option<EventId>,
pub euc: Sha1Hash,
/// Labels
pub labels: Vec<String>,
}

impl GitPatch {
pub(crate) fn to_event_builder(self) -> EventBuilder {
let content: String = self.content.to_string();
pub(crate) fn to_event_builder(self) -> Result<EventBuilder, Error> {
// Check if repository address kind is wrong
if self.repository.kind != Kind::GitRepoAnnouncement {
return Err(Error::WrongKind {
received: self.repository.kind,
expected: WrongKindError::Single(Kind::GitRepoAnnouncement),
});
}

let mut tags: Vec<Tag> = Vec::with_capacity(2);
// Verify coordinate
self.repository.verify()?;

// Add coordinate
tags.reserve_exact(self.maintainers.len());
tags.extend(self.maintainers.iter().copied().map(|p| {
Tag::coordinate(
Coordinate::new(Kind::GitRepoAnnouncement, p).identifier(self.repo_id.clone()),
)
}));
let owner_public_key: PublicKey = self.repository.public_key;

let mut tags: Vec<Tag> = Vec::with_capacity(3);

// Push coordinate
tags.push(Tag::coordinate(self.repository));

// Tag repo owner
tags.push(Tag::public_key(owner_public_key));

// Add EUC (as reference, not with `euc` marker)
tags.push(Tag::reference(self.euc));
// Add EUC (without `euc` marker)
tags.push(Tag::reference(self.euc.to_string()));

// Serialize content to string (used later)
let content: String = self.content.to_string();

// Handle patch content
match self.content {
GitPatchContent::CoverLetter { title, .. } => {
// Add cover letter tags
tags.reserve_exact(2);
GitPatchContent::CoverLetter { .. } => {
// Add cover letter hashtag
tags.push(Tag::hashtag("cover-letter"));
tags.push(Tag::alt(format!("{GIT_PATCH_COVER_LETTER_ALT}: {title}")));
}
GitPatchContent::Patch {
commit,
Expand All @@ -277,7 +281,7 @@ impl GitPatch {
committer,
..
} => {
tags.reserve_exact(6);
tags.reserve_exact(5);
tags.push(Tag::reference(commit.to_string()));
tags.push(Tag::from_standardized_without_cell(TagStandard::GitCommit(
commit,
Expand All @@ -299,33 +303,14 @@ impl GitPatch {
committer.offset_minutes.to_string(),
],
));
tags.push(Tag::alt(GIT_PATCH_ALT));
}
}

// Handle root proposal ID
match self.root_proposal_id {
Some(root_proposal_id) => {
tags.reserve_exact(3);
tags.push(Tag::hashtag("root"));
tags.push(Tag::hashtag("revision-root"));
tags.push(Tag::from_standardized_without_cell(TagStandard::Event {
event_id: root_proposal_id,
relay_url: None,
marker: Some(Marker::Reply),
public_key: None,
uppercase: false,
}));
}
None => tags.push(Tag::hashtag("root")),
}

// Add public keys
tags.reserve_exact(self.maintainers.len());
tags.extend(self.maintainers.into_iter().map(Tag::public_key));
// Add labels
tags.extend(self.labels.into_iter().map(Tag::hashtag));

// Build
EventBuilder::new(Kind::GitPatch, content).tags(tags)
Ok(EventBuilder::new(Kind::GitPatch, content).tags(tags))
}
}

Expand Down Expand Up @@ -420,4 +405,67 @@ mod tests {
.unwrap();
assert_eq!(event.tags, tags);
}

#[test]
fn test_git_patch() {
let pk =
PublicKey::parse("npub1drvpzev3syqt0kjrls50050uzf25gehpz9vgdw08hvex7e0vgfeq0eseet")
.unwrap();
let repository = Coordinate::new(Kind::GitRepoAnnouncement, pk).identifier("rust-nostr");

let repo = GitPatch {
repository,
content: GitPatchContent::Patch {
content: String::from("<patch>"),
commit: Sha1Hash::from_str("b1fa697b5cd42fbb6ec9fef9009609200387e0b4").unwrap(),
parent_commit: Sha1Hash::from_str("c88d901b42ff8389330d6d5d4044cf1d196696f3")
.unwrap(),
committer: GitPatchCommitter {
name: Some(String::from("Yuki Kishimoto")),
email: Some(String::from("yukikishimoto@protonmail.com")),
timestamp: Timestamp::from_secs(1739794763),
offset_minutes: 0,
},
commit_pgp_sig: None,
},
euc: Sha1Hash::from_str("59429cfc6cb35b0a1ddace73b5a5c5ed57b8f5ca").unwrap(),
labels: vec![String::from("root")],
};

let keys = Keys::generate();
let event: Event = repo
.to_event_builder()
.unwrap()
.sign_with_keys(&keys)
.unwrap();

assert_eq!(event.kind, Kind::GitPatch);
assert_eq!(event.content, "<patch>");

let tags = Tags::parse([
vec![
"a",
"30617:68d81165918100b7da43fc28f7d1fc12554466e1115886b9e7bb326f65ec4272:rust-nostr",
],
vec![
"p",
"68d81165918100b7da43fc28f7d1fc12554466e1115886b9e7bb326f65ec4272",
],
vec!["r", "59429cfc6cb35b0a1ddace73b5a5c5ed57b8f5ca"],
vec!["r", "b1fa697b5cd42fbb6ec9fef9009609200387e0b4"],
vec!["commit", "b1fa697b5cd42fbb6ec9fef9009609200387e0b4"],
vec!["parent-commit", "c88d901b42ff8389330d6d5d4044cf1d196696f3"],
vec!["commit-pgp-sig", ""],
vec![
"committer",
"Yuki Kishimoto",
"yukikishimoto@protonmail.com",
"1739794763",
"0",
],
vec!["t", "root"],
])
.unwrap();
assert_eq!(event.tags, tags);
}
}

0 comments on commit 7b161e1

Please sign in to comment.