upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2024-06-11 16:21:44 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2024-06-13 09:09:00 +0100
commitf1b3fcc40ab666d8def97096d1942c274da9b279 (patch)
tree05d6af0c5c1ad9baedb90ef652152fba6d46abf5 /src
parent7c6a5ab4c5e7a81c7442061029b9230748a6639d (diff)
chore: bump rust-nostr to v0.32.0
both nostr and nostr-sdk packages and also in test_utils fix the many breaking changes fix: ignore trailing slash when depuplicate relays for send events. this was picked up as TagStandard::RelayMetadata has started adding a traling slash. refactor cli output test function `expect_send_with_progress` so that relays can succeed / fail in a random order
Diffstat (limited to 'src')
-rw-r--r--src/git.rs4
-rw-r--r--src/key_handling/users.rs48
-rw-r--r--src/repo_ref.rs47
-rw-r--r--src/sub_commands/list.rs2
-rw-r--r--src/sub_commands/send.rs338
5 files changed, 203 insertions, 236 deletions
diff --git a/src/git.rs b/src/git.rs
index e797cb8..fb3b353 100644
--- a/src/git.rs
+++ b/src/git.rs
@@ -948,8 +948,8 @@ mod tests {
948 fn test(time: git2::Time) -> Result<()> { 948 fn test(time: git2::Time) -> Result<()> {
949 assert_eq!( 949 assert_eq!(
950 extract_sig_from_patch_tags( 950 extract_sig_from_patch_tags(
951 &[nostr::Tag::Generic( 951 &[nostr::Tag::custom(
952 nostr::TagKind::Custom("author".to_string()), 952 nostr::TagKind::Custom("author".to_string().into()),
953 prep(&time)?, 953 prep(&time)?,
954 )], 954 )],
955 "author", 955 "author",
diff --git a/src/key_handling/users.rs b/src/key_handling/users.rs
index 1dd75a8..a79a977 100644
--- a/src/key_handling/users.rs
+++ b/src/key_handling/users.rs
@@ -567,15 +567,18 @@ mod tests {
567 nostr::Kind::RelayList, 567 nostr::Kind::RelayList,
568 "", 568 "",
569 [ 569 [
570 nostr::Tag::RelayMetadata( 570 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
571 "wss://fredswrite1.relay".into(), 571 relay_url: nostr::Url::from_str("wss://fredswrite1.relay/").unwrap(),
572 Some(nostr::RelayMetadata::Write), 572 metadata: Some(RelayMetadata::Write),
573 ), 573 }),
574 nostr::Tag::RelayMetadata( 574 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
575 "wss://fredsread1.relay".into(), 575 relay_url: nostr::Url::from_str("wss://fredsread1.relay/").unwrap(),
576 Some(nostr::RelayMetadata::Read), 576 metadata: Some(RelayMetadata::Read),
577 ), 577 }),
578 nostr::Tag::RelayMetadata("wss://fredsreadwrite.relay".into(), None), 578 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
579 relay_url: nostr::Url::from_str("wss://fredsreadwrite.relay/").unwrap(),
580 metadata: None,
581 }),
579 ], 582 ],
580 ) 583 )
581 .to_event(&TEST_KEY_1_KEYS) 584 .to_event(&TEST_KEY_1_KEYS)
@@ -587,15 +590,18 @@ mod tests {
587 nostr::Kind::RelayList, 590 nostr::Kind::RelayList,
588 "", 591 "",
589 [ 592 [
590 nostr::Tag::RelayMetadata( 593 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
591 "wss://carolswrite1.relay".into(), 594 relay_url: nostr::Url::from_str("wss://carolswrite1.relay/").unwrap(),
592 Some(nostr::RelayMetadata::Write), 595 metadata: Some(RelayMetadata::Write),
593 ), 596 }),
594 nostr::Tag::RelayMetadata( 597 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
595 "wss://carolsread1.relay".into(), 598 relay_url: nostr::Url::from_str("wss://carolsread1.relay/").unwrap(),
596 Some(nostr::RelayMetadata::Read), 599 metadata: Some(RelayMetadata::Read),
597 ), 600 }),
598 nostr::Tag::RelayMetadata("wss://carolsreadwrite.relay".into(), None), 601 nostr::Tag::from_standardized(nostr::TagStandard::RelayMetadata {
602 relay_url: nostr::Url::from_str("wss://carolsreadwrite.relay/").unwrap(),
603 metadata: None,
604 }),
599 ], 605 ],
600 ) 606 )
601 .to_event(&TEST_KEY_2_KEYS) 607 .to_event(&TEST_KEY_2_KEYS)
@@ -652,7 +658,7 @@ mod tests {
652 658
653 fn expected_userrelayrefs_write1() -> UserRelayRef { 659 fn expected_userrelayrefs_write1() -> UserRelayRef {
654 UserRelayRef { 660 UserRelayRef {
655 url: "wss://fredswrite1.relay".into(), 661 url: "wss://fredswrite1.relay/".into(),
656 read: false, 662 read: false,
657 write: true, 663 write: true,
658 } 664 }
@@ -661,7 +667,7 @@ mod tests {
661 667
662 fn expected_userrelayrefs_read_write1() -> UserRelayRef { 668 fn expected_userrelayrefs_read_write1() -> UserRelayRef {
663 UserRelayRef { 669 UserRelayRef {
664 url: "wss://fredsreadwrite.relay".into(), 670 url: "wss://fredsreadwrite.relay/".into(),
665 read: true, 671 read: true,
666 write: true, 672 write: true,
667 } 673 }
@@ -672,7 +678,7 @@ mod tests {
672 vec![ 678 vec![
673 expected_userrelayrefs_write1(), 679 expected_userrelayrefs_write1(),
674 UserRelayRef { 680 UserRelayRef {
675 url: "wss://fredsread1.relay".into(), 681 url: "wss://fredsread1.relay/".into(),
676 read: true, 682 read: true,
677 write: false, 683 write: false,
678 }, 684 },
diff --git a/src/repo_ref.rs b/src/repo_ref.rs
index d314e6d..9fb1f0c 100644
--- a/src/repo_ref.rs
+++ b/src/repo_ref.rs
@@ -1,7 +1,7 @@
1use std::{fs::File, io::BufReader, str::FromStr}; 1use std::{fs::File, io::BufReader, str::FromStr};
2 2
3use anyhow::{bail, Context, Result}; 3use anyhow::{bail, Context, Result};
4use nostr::{nips::nip19::Nip19, FromBech32, PublicKey, Tag, ToBech32}; 4use nostr::{nips::nip19::Nip19, FromBech32, PublicKey, Tag, TagStandard, ToBech32};
5use serde::{Deserialize, Serialize}; 5use serde::{Deserialize, Serialize};
6 6
7#[cfg(not(test))] 7#[cfg(not(test))]
@@ -31,7 +31,7 @@ impl TryFrom<nostr::Event> for RepoRef {
31 type Error = anyhow::Error; 31 type Error = anyhow::Error;
32 32
33 fn try_from(event: nostr::Event) -> Result<Self> { 33 fn try_from(event: nostr::Event) -> Result<Self> {
34 if !event.kind.as_u64().eq(&REPO_REF_KIND) { 34 if !event.kind.as_u16().eq(&REPO_REF_KIND) {
35 bail!("incorrect kind"); 35 bail!("incorrect kind");
36 } 36 }
37 let mut r = Self::default(); 37 let mut r = Self::default();
@@ -49,12 +49,12 @@ impl TryFrom<nostr::Event> for RepoRef {
49 } 49 }
50 50
51 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("clone")) { 51 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("clone")) {
52 r.git_server = t.as_vec().clone(); 52 r.git_server = t.clone().to_vec();
53 r.git_server.remove(0); 53 r.git_server.remove(0);
54 } 54 }
55 55
56 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("web")) { 56 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("web")) {
57 r.web = t.as_vec().clone(); 57 r.web = t.clone().to_vec();
58 r.web.remove(0); 58 r.web.remove(0);
59 } 59 }
60 60
@@ -67,12 +67,12 @@ impl TryFrom<nostr::Event> for RepoRef {
67 } 67 }
68 68
69 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("relays")) { 69 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("relays")) {
70 r.relays = t.as_vec().clone(); 70 r.relays = t.clone().to_vec();
71 r.relays.remove(0); 71 r.relays.remove(0);
72 } 72 }
73 73
74 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("maintainers")) { 74 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("maintainers")) {
75 let mut maintainers = t.as_vec().clone(); 75 let mut maintainers = t.clone().to_vec();
76 maintainers.remove(0); 76 maintainers.remove(0);
77 if !maintainers.contains(&event.pubkey.to_string()) { 77 if !maintainers.contains(&event.pubkey.to_string()) {
78 r.maintainers.push(event.pubkey); 78 r.maintainers.push(event.pubkey);
@@ -90,7 +90,7 @@ impl TryFrom<nostr::Event> for RepoRef {
90 } 90 }
91} 91}
92 92
93pub static REPO_REF_KIND: u64 = 30_617; 93pub static REPO_REF_KIND: u16 = 30_617;
94 94
95impl RepoRef { 95impl RepoRef {
96 pub fn to_event(&self, keys: &nostr::Keys) -> Result<nostr::Event> { 96 pub fn to_event(&self, keys: &nostr::Keys) -> Result<nostr::Event> {
@@ -99,7 +99,7 @@ impl RepoRef {
99 "", 99 "",
100 [ 100 [
101 vec![ 101 vec![
102 Tag::Identifier(if self.identifier.to_string().is_empty() { 102 Tag::identifier(if self.identifier.to_string().is_empty() {
103 // fiatjaf thought a random string. its not in the draft nip. 103 // fiatjaf thought a random string. its not in the draft nip.
104 // thread_rng() 104 // thread_rng()
105 // .sample_iter(&Alphanumeric) 105 // .sample_iter(&Alphanumeric)
@@ -117,27 +117,30 @@ impl RepoRef {
117 } else { 117 } else {
118 self.identifier.to_string() 118 self.identifier.to_string()
119 }), 119 }),
120 Tag::Reference(self.root_commit.to_string()), 120 Tag::from_standardized(TagStandard::Reference(self.root_commit.to_string())),
121 Tag::Name(self.name.clone()), 121 Tag::from_standardized(TagStandard::Name(self.name.clone())),
122 Tag::Description(self.description.clone()), 122 Tag::from_standardized(TagStandard::Description(self.description.clone())),
123 Tag::Generic( 123 Tag::custom(
124 nostr::TagKind::Custom("clone".to_string()), 124 nostr::TagKind::Custom(std::borrow::Cow::Borrowed("clone")),
125 self.git_server.clone(), 125 self.git_server.clone(),
126 ), 126 ),
127 Tag::Generic(nostr::TagKind::Custom("web".to_string()), self.web.clone()), 127 Tag::custom(
128 Tag::Generic( 128 nostr::TagKind::Custom(std::borrow::Cow::Borrowed("web")),
129 nostr::TagKind::Custom("relays".to_string()), 129 self.web.clone(),
130 ),
131 Tag::custom(
132 nostr::TagKind::Custom(std::borrow::Cow::Borrowed("relays")),
130 self.relays.clone(), 133 self.relays.clone(),
131 ), 134 ),
132 Tag::Generic( 135 Tag::custom(
133 nostr::TagKind::Custom("maintainers".to_string()), 136 nostr::TagKind::Custom(std::borrow::Cow::Borrowed("maintainers")),
134 self.maintainers 137 self.maintainers
135 .iter() 138 .iter()
136 .map(std::string::ToString::to_string) 139 .map(std::string::ToString::to_string)
137 .collect(), 140 .collect::<Vec<String>>(),
138 ), 141 ),
139 Tag::Generic( 142 Tag::custom(
140 nostr::TagKind::Custom("alt".to_string()), 143 nostr::TagKind::Custom(std::borrow::Cow::Borrowed("alt")),
141 vec![format!("git repository: {}", self.name.clone())], 144 vec![format!("git repository: {}", self.name.clone())],
142 ), 145 ),
143 ], 146 ],
@@ -188,7 +191,7 @@ pub async fn fetch(
188 // somewhere within .git folder for future use and seek to get that next time 191 // somewhere within .git folder for future use and seek to get that next time
189 if let Some(event) = events 192 if let Some(event) = events
190 .iter() 193 .iter()
191 .filter(|e| e.kind.as_u64() == REPO_REF_KIND) 194 .filter(|e| e.kind.as_u16() == REPO_REF_KIND)
192 .max_by_key(|e| e.created_at) 195 .max_by_key(|e| e.created_at)
193 { 196 {
194 break event.clone(); 197 break event.clone();
diff --git a/src/sub_commands/list.rs b/src/sub_commands/list.rs
index 1a30b9b..24979fe 100644
--- a/src/sub_commands/list.rs
+++ b/src/sub_commands/list.rs
@@ -801,7 +801,7 @@ pub async fn find_commits_for_proposal_root_events(
801 .context("cannot fetch patch events")? 801 .context("cannot fetch patch events")?
802 .iter() 802 .iter()
803 .filter(|e| { 803 .filter(|e| {
804 e.kind.as_u64() == PATCH_KIND 804 e.kind.as_u16() == PATCH_KIND
805 && e.tags.iter().any(|t| { 805 && e.tags.iter().any(|t| {
806 t.as_vec().len() > 2 806 t.as_vec().len() > 2
807 && proposal_root_events 807 && proposal_root_events
diff --git a/src/sub_commands/send.rs b/src/sub_commands/send.rs
index 189dc53..c8d900c 100644
--- a/src/sub_commands/send.rs
+++ b/src/sub_commands/send.rs
@@ -5,10 +5,10 @@ use console::Style;
5use futures::future::join_all; 5use futures::future::join_all;
6use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; 6use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
7use nostr::{ 7use nostr::{
8 nips::{nip01::Coordinate, nip19::Nip19}, 8 nips::{nip01::Coordinate, nip10::Marker, nip19::Nip19},
9 EventBuilder, FromBech32, Marker, Tag, TagKind, ToBech32, UncheckedUrl, 9 EventBuilder, FromBech32, Tag, TagKind, ToBech32, UncheckedUrl,
10}; 10};
11use nostr_sdk::hashes::sha1::Hash as Sha1Hash; 11use nostr_sdk::{hashes::sha1::Hash as Sha1Hash, TagStandard};
12 12
13use super::list::tag_value; 13use super::list::tag_value;
14#[cfg(not(test))] 14#[cfg(not(test))]
@@ -265,6 +265,7 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> {
265} 265}
266 266
267#[allow(clippy::module_name_repetitions)] 267#[allow(clippy::module_name_repetitions)]
268#[allow(clippy::too_many_lines)]
268pub async fn send_events( 269pub async fn send_events(
269 #[cfg(test)] client: &crate::client::MockConnect, 270 #[cfg(test)] client: &crate::client::MockConnect,
270 #[cfg(not(test))] client: &Client, 271 #[cfg(not(test))] client: &Client,
@@ -273,21 +274,41 @@ pub async fn send_events(
273 repo_read_relays: Vec<String>, 274 repo_read_relays: Vec<String>,
274 animate: bool, 275 animate: bool,
275) -> Result<()> { 276) -> Result<()> {
276 let (_, _, _, mut all) = unique_and_duplicate_all(&my_write_relays, &repo_read_relays); 277 let fallback = [
277 278 client.get_fallback_relays().clone(),
278 let mut fallback = client.get_fallback_relays().clone(); 279 if events.iter().any(|e| e.kind().as_u16().eq(&REPO_REF_KIND)) {
279 280 client.get_blaster_relays().clone()
280 // blast repo events 281 } else {
281 if events.iter().any(|e| e.kind().as_u64().eq(&REPO_REF_KIND)) { 282 vec![]
282 for r in client.get_blaster_relays() { 283 },
283 fallback.push(r.to_string()); 284 ]
285 .concat();
286 let mut relays: Vec<&String> = vec![];
287
288 let all = &[
289 repo_read_relays.clone(),
290 my_write_relays.clone(),
291 fallback.clone(),
292 ]
293 .concat();
294 // add duplicates first
295 for r in &repo_read_relays {
296 let r_clean = remove_trailing_slash(r);
297 if !my_write_relays
298 .iter()
299 .filter(|x| r_clean.eq(&remove_trailing_slash(x)))
300 .count()
301 > 1
302 && !relays.iter().any(|x| r_clean.eq(&remove_trailing_slash(x)))
303 {
304 relays.push(r);
284 } 305 }
285 } 306 }
286 307
287 // then remaining fallback list 308 for r in all {
288 for r in &fallback { 309 let r_clean = remove_trailing_slash(r);
289 if !all.iter().any(|r2| r2.eq(&r)) { 310 if !relays.iter().any(|x| r_clean.eq(&remove_trailing_slash(x))) {
290 all.push(r); 311 relays.push(r);
291 } 312 }
292 } 313 }
293 314
@@ -319,25 +340,36 @@ pub async fn send_events(
319 "x".to_string() 340 "x".to_string()
320 })?; 341 })?;
321 342
322 join_all(all.iter().map(|&relay| async { 343 #[allow(clippy::borrow_deref_ref)]
344 join_all(relays.iter().map(|&relay| async {
345 let relay_clean = remove_trailing_slash(&*relay);
323 let details = format!( 346 let details = format!(
324 "{}{}{} {}", 347 "{}{}{} {}",
325 if my_write_relays.iter().any(|r| relay.eq(r)) { 348 if my_write_relays
349 .iter()
350 .any(|r| relay_clean.eq(&remove_trailing_slash(r)))
351 {
326 " [my-relay]" 352 " [my-relay]"
327 } else { 353 } else {
328 "" 354 ""
329 }, 355 },
330 if repo_read_relays.iter().any(|r| relay.eq(r)) { 356 if repo_read_relays
357 .iter()
358 .any(|r| relay_clean.eq(&remove_trailing_slash(r)))
359 {
331 " [repo-relay]" 360 " [repo-relay]"
332 } else { 361 } else {
333 "" 362 ""
334 }, 363 },
335 if fallback.iter().any(|r| relay.eq(r)) { 364 if fallback
365 .iter()
366 .any(|r| relay_clean.eq(&remove_trailing_slash(r)))
367 {
336 " [default]" 368 " [default]"
337 } else { 369 } else {
338 "" 370 ""
339 }, 371 },
340 *relay, 372 relay_clean,
341 ); 373 );
342 let pb = m.add( 374 let pb = m.add(
343 ProgressBar::new(events.len() as u64) 375 ProgressBar::new(events.len() as u64)
@@ -378,36 +410,12 @@ pub async fn send_events(
378 Ok(()) 410 Ok(())
379} 411}
380 412
381/// returns `(unique_vec1, unique_vec2, duplicates, all)` 413fn remove_trailing_slash(s: &String) -> String {
382fn unique_and_duplicate_all<'a, S>( 414 match s.as_str().strip_suffix('/') {
383 vec1: &'a Vec<S>, 415 Some(s) => s,
384 vec2: &'a Vec<S>, 416 None => s,
385) -> (Vec<&'a S>, Vec<&'a S>, Vec<&'a S>, Vec<&'a S>)
386where
387 S: PartialEq,
388{
389 let mut vec1_u = vec![];
390 let mut vec2_u = vec![];
391 let mut dup = vec![];
392 let mut all = vec![];
393 for s1 in vec1 {
394 if vec2.iter().any(|s2| s1.eq(s2)) {
395 dup.push(s1);
396 } else {
397 vec1_u.push(s1);
398 }
399 } 417 }
400 for s2 in vec2 { 418 .to_string()
401 if !vec1.iter().any(|s1| s2.eq(s1)) {
402 vec2_u.push(s2);
403 }
404 }
405 for a in [&dup, &vec1_u, &vec2_u] {
406 for e in a {
407 all.push(&**e);
408 }
409 }
410 (vec1_u, vec2_u, dup, all)
411} 419}
412 420
413fn choose_commits(git_repo: &Repo, proposed_commits: Vec<Sha1Hash>) -> Result<Vec<Sha1Hash>> { 421fn choose_commits(git_repo: &Repo, proposed_commits: Vec<Sha1Hash>) -> Result<Vec<Sha1Hash>> {
@@ -501,53 +509,6 @@ fn summarise_commit_for_selection(git_repo: &Repo, commit: &Sha1Hash) -> Result<
501 )) 509 ))
502} 510}
503 511
504mod tests_unique_and_duplicate {
505
506 #[test]
507 fn correct_number_of_unique_and_duplicate_items() {
508 let v1 = vec![
509 "t1".to_string(),
510 "t2".to_string(),
511 "t3".to_string(),
512 "t4".to_string(),
513 "t5".to_string(),
514 ];
515 let v2 = vec![
516 "t3".to_string(),
517 "t4".to_string(),
518 "t5".to_string(),
519 "t6".to_string(),
520 ];
521
522 let (v1_u, v2_u, d, a) = super::unique_and_duplicate_all(&v1, &v2);
523
524 assert_eq!(v1_u.len(), 2);
525 assert_eq!(v2_u.len(), 1);
526 assert_eq!(d.len(), 3);
527 assert_eq!(a.len(), 6);
528 }
529 #[test]
530 fn all_begins_with_duplicates() {
531 let v1 = vec![
532 "t1".to_string(),
533 "t2".to_string(),
534 "t3".to_string(),
535 "t4".to_string(),
536 "t5".to_string(),
537 ];
538 let v2 = vec![
539 "t3".to_string(),
540 "t4".to_string(),
541 "t5".to_string(),
542 "t6".to_string(),
543 ];
544
545 let (_, _, d, a) = super::unique_and_duplicate_all(&v1, &v2);
546
547 assert_eq!(a[0], d[0]);
548 }
549}
550
551async fn get_root_proposal_id_and_mentions_from_in_reply_to( 512async fn get_root_proposal_id_and_mentions_from_in_reply_to(
552 #[cfg(test)] client: &crate::client::MockConnect, 513 #[cfg(test)] client: &crate::client::MockConnect,
553 #[cfg(not(test))] client: &Client, 514 #[cfg(not(test))] client: &Client,
@@ -555,20 +516,23 @@ async fn get_root_proposal_id_and_mentions_from_in_reply_to(
555 in_reply_to: &[String], 516 in_reply_to: &[String],
556) -> Result<(Option<String>, Vec<nostr::Tag>)> { 517) -> Result<(Option<String>, Vec<nostr::Tag>)> {
557 let root_proposal_id = if let Some(first) = in_reply_to.first() { 518 let root_proposal_id = if let Some(first) = in_reply_to.first() {
558 match event_tag_from_nip19_or_hex(first, "in-reply-to", nostr::Marker::Root, true, false)? { 519 match event_tag_from_nip19_or_hex(first, "in-reply-to", Marker::Root, true, false)?
559 Tag::Event { 520 .as_standardized()
521 {
522 Some(nostr_sdk::TagStandard::Event {
560 event_id, 523 event_id,
561 relay_url: _, 524 relay_url: _,
562 marker: _, 525 marker: _,
563 } => { 526 public_key: _,
527 }) => {
564 let events = client 528 let events = client
565 .get_events( 529 .get_events(
566 repo_relays.to_vec(), 530 repo_relays.to_vec(),
567 vec![nostr::Filter::new().id(event_id)], 531 vec![nostr::Filter::new().id(*event_id)],
568 ) 532 )
569 .await 533 .await
570 .context("whilst getting events specified in --in-reply-to")?; 534 .context("whilst getting events specified in --in-reply-to")?;
571 if let Some(first) = events.iter().find(|e| e.id.eq(&event_id)) { 535 if let Some(first) = events.iter().find(|e| e.id.eq(event_id)) {
572 if event_is_patch_set_root(first) { 536 if event_is_patch_set_root(first) {
573 Some(event_id.to_string()) 537 Some(event_id.to_string())
574 } else { 538 } else {
@@ -591,16 +555,10 @@ async fn get_root_proposal_id_and_mentions_from_in_reply_to(
591 for (i, reply_to) in in_reply_to.iter().enumerate() { 555 for (i, reply_to) in in_reply_to.iter().enumerate() {
592 if i.ne(&0) || root_proposal_id.is_none() { 556 if i.ne(&0) || root_proposal_id.is_none() {
593 mention_tags.push( 557 mention_tags.push(
594 event_tag_from_nip19_or_hex( 558 event_tag_from_nip19_or_hex(reply_to, "in-reply-to", Marker::Mention, true, false)
595 reply_to, 559 .context(format!(
596 "in-reply-to", 560 "{reply_to} in 'in-reply-to' not a valid nostr reference"
597 nostr::Marker::Mention, 561 ))?,
598 true,
599 false,
600 )
601 .context(format!(
602 "{reply_to} in 'in-reply-to' not a valid nostr reference"
603 ))?,
604 ); 562 );
605 } 563 }
606 } 564 }
@@ -608,7 +566,7 @@ async fn get_root_proposal_id_and_mentions_from_in_reply_to(
608 Ok((root_proposal_id, mention_tags)) 566 Ok((root_proposal_id, mention_tags))
609} 567}
610 568
611pub static PATCH_KIND: u64 = 1617; 569pub static PATCH_KIND: u16 = 1617;
612 570
613#[allow(clippy::too_many_lines)] 571#[allow(clippy::too_many_lines)]
614pub fn generate_cover_letter_and_patch_events( 572pub fn generate_cover_letter_and_patch_events(
@@ -637,33 +595,30 @@ pub fn generate_cover_letter_and_patch_events(
637 [ 595 [
638 vec![ 596 vec![
639 // TODO: why not tag all maintainer identifiers? 597 // TODO: why not tag all maintainer identifiers?
640 Tag::A { 598 Tag::coordinate(Coordinate {
641 coordinate: Coordinate { 599 kind: nostr::Kind::Custom(REPO_REF_KIND),
642 kind: nostr::Kind::Custom(REPO_REF_KIND), 600 public_key: *repo_ref.maintainers.first()
643 public_key: *repo_ref.maintainers.first() 601 .context("repo reference should always have at least one maintainer")?,
644 .context("repo reference should always have at least one maintainer")?, 602 identifier: repo_ref.identifier.to_string(),
645 identifier: repo_ref.identifier.to_string(), 603 relays: repo_ref.relays.clone(),
646 relays: repo_ref.relays.clone(), 604 }),
647 }, 605 Tag::from_standardized(TagStandard::Reference(format!("{root_commit}"))),
648 relay_url: repo_ref.relays.first().map(nostr::UncheckedUrl::from).clone(), 606 Tag::hashtag("cover-letter"),
649 }, 607 Tag::custom(
650 Tag::Reference(format!("{root_commit}")), 608 nostr::TagKind::Custom(std::borrow::Cow::Borrowed("alt")),
651 Tag::Hashtag("cover-letter".to_string()),
652 Tag::Generic(
653 nostr::TagKind::Custom("alt".to_string()),
654 vec![format!("git patch cover letter: {}", title.clone())], 609 vec![format!("git patch cover letter: {}", title.clone())],
655 ), 610 ),
656 ], 611 ],
657 if let Some(event_ref) = root_proposal_id.clone() { 612 if let Some(event_ref) = root_proposal_id.clone() {
658 vec![ 613 vec![
659 Tag::Hashtag("root".to_string()), 614 Tag::hashtag("root"),
660 Tag::Hashtag("revision-root".to_string()), 615 Tag::hashtag("revision-root"),
661 // TODO check if id is for a root proposal (perhaps its for an issue?) 616 // TODO check if id is for a root proposal (perhaps its for an issue?)
662 event_tag_from_nip19_or_hex(&event_ref,"proposal",nostr::Marker::Reply, false, false)?, 617 event_tag_from_nip19_or_hex(&event_ref,"proposal",Marker::Reply, false, false)?,
663 ] 618 ]
664 } else { 619 } else {
665 vec![ 620 vec![
666 Tag::Hashtag("root".to_string()), 621 Tag::hashtag("root"),
667 ] 622 ]
668 }, 623 },
669 mentions.to_vec(), 624 mentions.to_vec(),
@@ -677,10 +632,12 @@ pub fn generate_cover_letter_and_patch_events(
677 && !branch_name.eq("origin/main") 632 && !branch_name.eq("origin/main")
678 && !branch_name.eq("origin/master") 633 && !branch_name.eq("origin/master")
679 { 634 {
680 vec![Tag::Generic( 635 vec![
681 TagKind::Custom("branch-name".to_string()), 636 Tag::custom(
682 vec![branch_name], 637 nostr::TagKind::Custom(std::borrow::Cow::Borrowed("branch-name")),
683 )] 638 vec![branch_name],
639 ),
640 ]
684 } 641 }
685 else { vec![] } 642 else { vec![] }
686 } else { 643 } else {
@@ -740,7 +697,7 @@ pub fn generate_cover_letter_and_patch_events(
740fn event_tag_from_nip19_or_hex( 697fn event_tag_from_nip19_or_hex(
741 reference: &str, 698 reference: &str,
742 reference_name: &str, 699 reference_name: &str,
743 marker: nostr::Marker, 700 marker: Marker,
744 allow_npub_reference: bool, 701 allow_npub_reference: bool,
745 prompt_for_correction: bool, 702 prompt_for_correction: bool,
746) -> Result<nostr::Tag> { 703) -> Result<nostr::Tag> {
@@ -754,44 +711,44 @@ fn event_tag_from_nip19_or_hex(
754 if let Ok(nip19) = Nip19::from_bech32(bech32.clone()) { 711 if let Ok(nip19) = Nip19::from_bech32(bech32.clone()) {
755 match nip19 { 712 match nip19 {
756 Nip19::Event(n) => { 713 Nip19::Event(n) => {
757 break Ok(nostr::Tag::Event { 714 break Ok(Tag::from_standardized(nostr_sdk::TagStandard::Event {
758 event_id: n.event_id, 715 event_id: n.event_id,
759 relay_url: n.relays.first().map(UncheckedUrl::new), 716 relay_url: n.relays.first().map(UncheckedUrl::new),
760 marker: Some(marker), 717 marker: Some(marker),
761 }); 718 public_key: None,
719 }));
762 } 720 }
763 Nip19::EventId(id) => { 721 Nip19::EventId(id) => {
764 break Ok(nostr::Tag::Event { 722 break Ok(Tag::from_standardized(nostr_sdk::TagStandard::Event {
765 event_id: id, 723 event_id: id,
766 relay_url: None, 724 relay_url: None,
767 marker: Some(marker), 725 marker: Some(marker),
768 }); 726 public_key: None,
727 }));
769 } 728 }
770 Nip19::Coordinate(coordinate) => { 729 Nip19::Coordinate(coordinate) => {
771 break Ok(nostr::Tag::A { 730 break Ok(Tag::coordinate(coordinate));
772 coordinate,
773 relay_url: None,
774 });
775 } 731 }
776 Nip19::Profile(profile) => { 732 Nip19::Profile(profile) => {
777 if allow_npub_reference { 733 if allow_npub_reference {
778 break Ok(nostr::Tag::public_key(profile.public_key)); 734 break Ok(Tag::public_key(profile.public_key));
779 } 735 }
780 } 736 }
781 Nip19::Pubkey(public_key) => { 737 Nip19::Pubkey(public_key) => {
782 if allow_npub_reference { 738 if allow_npub_reference {
783 break Ok(nostr::Tag::public_key(public_key)); 739 break Ok(Tag::public_key(public_key));
784 } 740 }
785 } 741 }
786 _ => {} 742 _ => {}
787 } 743 }
788 } 744 }
789 if let Ok(id) = nostr::EventId::from_str(&bech32) { 745 if let Ok(id) = nostr::EventId::from_str(&bech32) {
790 break Ok(nostr::Tag::Event { 746 break Ok(Tag::from_standardized(nostr_sdk::TagStandard::Event {
791 event_id: id, 747 event_id: id,
792 relay_url: None, 748 relay_url: None,
793 marker: Some(marker), 749 marker: Some(marker),
794 }); 750 public_key: None,
751 }));
795 } 752 }
796 if prompt_for_correction { 753 if prompt_for_correction {
797 println!("not a valid {reference_name} event reference"); 754 println!("not a valid {reference_name} event reference");
@@ -813,7 +770,7 @@ pub fn event_is_cover_letter(event: &nostr::Event) -> bool {
813 // TODO: look for Subject:[ PATCH 0/n ] but watch out for: 770 // TODO: look for Subject:[ PATCH 0/n ] but watch out for:
814 // [PATCH v1 0/n ] or 771 // [PATCH v1 0/n ] or
815 // [PATCH subsystem v2 0/n ] 772 // [PATCH subsystem v2 0/n ]
816 event.kind.as_u64().eq(&PATCH_KIND) 773 event.kind.as_u16().eq(&PATCH_KIND)
817 && event.iter_tags().any(|t| t.as_vec()[1].eq("root")) 774 && event.iter_tags().any(|t| t.as_vec()[1].eq("root"))
818 && event.iter_tags().any(|t| t.as_vec()[1].eq("cover-letter")) 775 && event.iter_tags().any(|t| t.as_vec()[1].eq("cover-letter"))
819} 776}
@@ -883,16 +840,16 @@ pub fn event_to_cover_letter(event: &nostr::Event) -> Result<CoverLetter> {
883} 840}
884 841
885pub fn event_is_patch_set_root(event: &nostr::Event) -> bool { 842pub fn event_is_patch_set_root(event: &nostr::Event) -> bool {
886 event.kind.as_u64().eq(&PATCH_KIND) && event.iter_tags().any(|t| t.as_vec()[1].eq("root")) 843 event.kind.as_u16().eq(&PATCH_KIND) && event.iter_tags().any(|t| t.as_vec()[1].eq("root"))
887} 844}
888 845
889pub fn event_is_revision_root(event: &nostr::Event) -> bool { 846pub fn event_is_revision_root(event: &nostr::Event) -> bool {
890 event.kind.as_u64().eq(&PATCH_KIND) 847 event.kind.as_u16().eq(&PATCH_KIND)
891 && event.iter_tags().any(|t| t.as_vec()[1].eq("revision-root")) 848 && event.iter_tags().any(|t| t.as_vec()[1].eq("revision-root"))
892} 849}
893 850
894pub fn patch_supports_commit_ids(event: &nostr::Event) -> bool { 851pub fn patch_supports_commit_ids(event: &nostr::Event) -> bool {
895 event.kind.as_u64().eq(&PATCH_KIND) 852 event.kind.as_u16().eq(&PATCH_KIND)
896 && event 853 && event
897 .iter_tags() 854 .iter_tags()
898 .any(|t| t.as_vec()[0].eq("commit-pgp-sig")) 855 .any(|t| t.as_vec()[0].eq("commit-pgp-sig"))
@@ -925,55 +882,54 @@ pub fn generate_patch_event(
925 .context(format!("cannot make patch for commit {commit}"))?, 882 .context(format!("cannot make patch for commit {commit}"))?,
926 [ 883 [
927 vec![ 884 vec![
928 Tag::A { 885 Tag::coordinate(Coordinate {
929 coordinate: Coordinate { 886 kind: nostr::Kind::Custom(REPO_REF_KIND),
930 kind: nostr::Kind::Custom(REPO_REF_KIND), 887 public_key: *repo_ref.maintainers.first()
931 public_key: *repo_ref.maintainers.first() 888 .context("repo reference should always have at least one maintainer - the issuer of the repo event")
932 .context("repo reference should always have at least one maintainer - the issuer of the repo event") 889 ?,
933 ?, 890 identifier: repo_ref.identifier.to_string(),
934 identifier: repo_ref.identifier.to_string(), 891 relays: repo_ref.relays.clone(),
935 relays: repo_ref.relays.clone(), 892 }),
936 }, 893 Tag::from_standardized(TagStandard::Reference(root_commit.to_string())),
937 relay_url: relay_hint.clone(),
938 },
939 Tag::Reference(format!("{root_commit}")),
940 // commit id reference is a trade-off. its now 894 // commit id reference is a trade-off. its now
941 // unclear which one is the root commit id but it 895 // unclear which one is the root commit id but it
942 // enables easier location of code comments againt 896 // enables easier location of code comments againt
943 // code that makes it into the main branch, assuming 897 // code that makes it into the main branch, assuming
944 // the commit id is correct 898 // the commit id is correct
945 Tag::Reference(commit.to_string()), 899 Tag::from_standardized(TagStandard::Reference(commit.to_string())),
946 Tag::Generic( 900 Tag::custom(
947 nostr::TagKind::Custom("alt".to_string()), 901 TagKind::Custom(std::borrow::Cow::Borrowed("alt")),
948 vec![format!("git patch: {}", git_repo.get_commit_message_summary(commit).unwrap_or_default())], 902 vec![format!("git patch: {}", git_repo.get_commit_message_summary(commit).unwrap_or_default())],
949 ), 903 ),
950 ], 904 ],
951 905
952 if let Some(thread_event_id) = thread_event_id { 906 if let Some(thread_event_id) = thread_event_id {
953 vec![Tag::Event { 907 vec![Tag::from_standardized(nostr_sdk::TagStandard::Event {
954 event_id: thread_event_id, 908 event_id: thread_event_id,
955 relay_url: relay_hint.clone(), 909 relay_url: relay_hint.clone(),
956 marker: Some(Marker::Root), 910 marker: Some(Marker::Root),
957 }] 911 public_key: None,
912 })]
958 } else if let Some(event_ref) = root_proposal_id.clone() { 913 } else if let Some(event_ref) = root_proposal_id.clone() {
959 vec![ 914 vec![
960 Tag::Hashtag("root".to_string()), 915 Tag::hashtag("root"),
961 Tag::Hashtag("revision-root".to_string()), 916 Tag::hashtag("revision-root"),
962 // TODO check if id is for a root proposal (perhaps its for an issue?) 917 // TODO check if id is for a root proposal (perhaps its for an issue?)
963 event_tag_from_nip19_or_hex(&event_ref,"proposal",nostr::Marker::Reply, false, false)?, 918 event_tag_from_nip19_or_hex(&event_ref,"proposal", Marker::Reply, false, false)?,
964 ] 919 ]
965 } else { 920 } else {
966 vec![ 921 vec![
967 Tag::Hashtag("root".to_string()), 922 Tag::hashtag("root"),
968 ] 923 ]
969 }, 924 },
970 mentions.to_vec(), 925 mentions.to_vec(),
971 if let Some(id) = parent_patch_event_id { 926 if let Some(id) = parent_patch_event_id {
972 vec![Tag::Event { 927 vec![Tag::from_standardized(nostr_sdk::TagStandard::Event {
973 event_id: id, 928 event_id: id,
974 relay_url: relay_hint.clone(), 929 relay_url: relay_hint.clone(),
975 marker: Some(Marker::Reply), 930 marker: Some(Marker::Reply),
976 }] 931 public_key: None,
932 })]
977 } else { 933 } else {
978 vec![] 934 vec![]
979 }, 935 },
@@ -981,8 +937,8 @@ pub fn generate_patch_event(
981 if let Some(branch_name) = branch_name { 937 if let Some(branch_name) = branch_name {
982 if thread_event_id.is_none() { 938 if thread_event_id.is_none() {
983 vec![ 939 vec![
984 Tag::Generic( 940 Tag::custom(
985 TagKind::Custom("branch-name".to_string()), 941 TagKind::Custom(std::borrow::Cow::Borrowed("branch-name")),
986 vec![branch_name.to_string()], 942 vec![branch_name.to_string()],
987 ) 943 )
988 ] 944 ]
@@ -1002,33 +958,35 @@ pub fn generate_patch_event(
1002 .collect(), 958 .collect(),
1003 vec![ 959 vec![
1004 // a fallback is now in place to extract this from the patch 960 // a fallback is now in place to extract this from the patch
1005 Tag::Generic( 961 Tag::custom(
1006 TagKind::Custom("commit".to_string()), 962 TagKind::Custom(std::borrow::Cow::Borrowed("commit")),
1007 vec![commit.to_string()], 963 vec![commit.to_string()],
1008 ), 964 ),
1009 // this is required as patches cannot be relied upon to include the 'base commit' 965 // this is required as patches cannot be relied upon to include the 'base commit'
1010 Tag::Generic( 966 Tag::custom(
1011 TagKind::Custom("parent-commit".to_string()), 967 TagKind::Custom(std::borrow::Cow::Borrowed("parent-commit")),
1012 vec![commit_parent.to_string()], 968 vec![commit_parent.to_string()],
1013 ), 969 ),
1014 // this is required to ensure the commit id matches 970 // this is required to ensure the commit id matches
1015 Tag::Generic( 971 Tag::custom(
1016 TagKind::Custom("commit-pgp-sig".to_string()), 972 TagKind::Custom(std::borrow::Cow::Borrowed("commit-pgp-sig")),
1017 vec![ 973 vec![
1018 git_repo 974 git_repo
1019 .extract_commit_pgp_signature(commit) 975 .extract_commit_pgp_signature(commit)
1020 .unwrap_or_default(), 976 .unwrap_or_default(),
1021 ], 977 ],
1022 ), 978 ),
1023 // removing description tag will not cause anything to break 979 // removing description tag will not cause anything to break
1024 Tag::Description(git_repo.get_commit_message(commit)?.to_string()), 980 Tag::from_standardized(nostr_sdk::TagStandard::Description(
1025 Tag::Generic( 981 git_repo.get_commit_message(commit)?.to_string()
1026 TagKind::Custom("author".to_string()), 982 )),
983 Tag::custom(
984 TagKind::Custom(std::borrow::Cow::Borrowed("author")),
1027 git_repo.get_commit_author(commit)?, 985 git_repo.get_commit_author(commit)?,
1028 ), 986 ),
1029 // this is required to ensure the commit id matches 987 // this is required to ensure the commit id matches
1030 Tag::Generic( 988 Tag::custom(
1031 TagKind::Custom("committer".to_string()), 989 TagKind::Custom(std::borrow::Cow::Borrowed("committer")),
1032 git_repo.get_commit_comitter(commit)?, 990 git_repo.get_commit_comitter(commit)?,
1033 ), 991 ),
1034 ], 992 ],
@@ -1246,8 +1204,8 @@ mod tests {
1246 nostr::event::Kind::Custom(PATCH_KIND), 1204 nostr::event::Kind::Custom(PATCH_KIND),
1247 format!("From ea897e987ea9a7a98e7a987e97987ea98e7a3334 Mon Sep 17 00:00:00 2001\nSubject: [PATCH 0/2] {title}\n\n{description}"), 1205 format!("From ea897e987ea9a7a98e7a987e97987ea98e7a3334 Mon Sep 17 00:00:00 2001\nSubject: [PATCH 0/2] {title}\n\n{description}"),
1248 [ 1206 [
1249 Tag::Hashtag("cover-letter".to_string()), 1207 Tag::hashtag("cover-letter"),
1250 Tag::Hashtag("root".to_string()), 1208 Tag::hashtag("root"),
1251 ], 1209 ],
1252 ) 1210 )
1253 .to_event(&nostr::Keys::generate())?) 1211 .to_event(&nostr::Keys::generate())?)