upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/repo_ref.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2024-02-01 09:59:09 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2024-02-02 02:30:01 +0000
commit1cacf1bce77b88febc0cf9e2f0f9f468d4b30555 (patch)
tree5c2c69336eddeb79082249a9167cc46e3c155f12 /src/repo_ref.rs
parent4b66c76a5a3ba878b4603d992353327807b8d8e6 (diff)
feat(repo_ref)!: use nip34 kind and tags
- change kind number - do not rely on d identifiers for unique commit id. set it as default to unique commit id shorthand. - remove "r-" prefix from unique commit id r tag and instead add checks for SHA1 validity - rename tag git_server to clone - add web tag - use single relays tag instead of multiple relay tags BREAKING CHANGE: change repo event kind and tags to reflect nip34 draft. events with the older kind will no longer be found and will not be in a valid format
Diffstat (limited to 'src/repo_ref.rs')
-rw-r--r--src/repo_ref.rs217
1 files changed, 165 insertions, 52 deletions
diff --git a/src/repo_ref.rs b/src/repo_ref.rs
index 22236f7..1ec0ece 100644
--- a/src/repo_ref.rs
+++ b/src/repo_ref.rs
@@ -17,8 +17,10 @@ use crate::{
17pub struct RepoRef { 17pub struct RepoRef {
18 pub name: String, 18 pub name: String,
19 pub description: String, 19 pub description: String,
20 pub identifier: String,
20 pub root_commit: String, 21 pub root_commit: String,
21 pub git_server: String, 22 pub git_server: String,
23 pub web: Vec<String>,
22 pub relays: Vec<String>, 24 pub relays: Vec<String>,
23 pub maintainers: Vec<XOnlyPublicKey>, 25 pub maintainers: Vec<XOnlyPublicKey>,
24 // code languages and hashtags 26 // code languages and hashtags
@@ -33,6 +35,10 @@ impl TryFrom<nostr::Event> for RepoRef {
33 } 35 }
34 let mut r = Self::default(); 36 let mut r = Self::default();
35 37
38 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("d")) {
39 r.identifier = t.as_vec()[1].clone();
40 }
41
36 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("name")) { 42 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("name")) {
37 r.name = t.as_vec()[1].clone(); 43 r.name = t.as_vec()[1].clone();
38 } 44 }
@@ -41,20 +47,27 @@ impl TryFrom<nostr::Event> for RepoRef {
41 r.description = t.as_vec()[1].clone(); 47 r.description = t.as_vec()[1].clone();
42 } 48 }
43 49
44 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("git-server")) { 50 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("clone")) {
45 r.git_server = t.as_vec()[1].clone(); 51 r.git_server = t.as_vec()[1].clone();
46 } 52 }
47 53
48 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("d")) { 54 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("web")) {
55 r.web = t.as_vec().clone();
56 r.web.remove(0);
57 }
58
59 if let Some(t) = event.tags.iter().find(|t| {
60 t.as_vec()[0].eq("r")
61 && t.as_vec()[1].len().eq(&40)
62 && git2::Oid::from_str(t.as_vec()[1].as_str()).is_ok()
63 }) {
49 r.root_commit = t.as_vec()[1].clone(); 64 r.root_commit = t.as_vec()[1].clone();
50 } 65 }
51 66
52 r.relays = event 67 if let Some(t) = event.tags.iter().find(|t| t.as_vec()[0].eq("relays")) {
53 .tags 68 r.relays = t.as_vec().clone();
54 .iter() 69 r.relays.remove(0);
55 .filter(|t| t.as_vec()[0].eq("relay")) 70 }
56 .map(|t| t.as_vec()[1].clone())
57 .collect();
58 71
59 for tag in event.tags.iter().filter(|t| t.as_vec()[0].eq("p")) { 72 for tag in event.tags.iter().filter(|t| t.as_vec()[0].eq("p")) {
60 let pk = tag.as_vec()[1].clone(); 73 let pk = tag.as_vec()[1].clone();
@@ -68,7 +81,8 @@ impl TryFrom<nostr::Event> for RepoRef {
68 Ok(r) 81 Ok(r)
69 } 82 }
70} 83}
71static REPO_REF_KIND: u64 = 30_317; 84
85pub static REPO_REF_KIND: u64 = 30_617;
72 86
73impl RepoRef { 87impl RepoRef {
74 pub fn to_event(&self, keys: &nostr::Keys) -> Result<nostr::Event> { 88 pub fn to_event(&self, keys: &nostr::Keys) -> Result<nostr::Event> {
@@ -77,17 +91,41 @@ impl RepoRef {
77 "", 91 "",
78 [ 92 [
79 vec![ 93 vec![
80 Tag::Identifier(self.root_commit.to_string()), 94 Tag::Identifier(if self.identifier.to_string().is_empty() {
81 Tag::Reference(format!("r-{}", self.root_commit)), 95 // fiatjaf thought a random string. its not in the draft nip.
96 // thread_rng()
97 // .sample_iter(&Alphanumeric)
98 // .take(15)
99 // .map(char::from)
100 // .collect()
101
102 // an identifier based on first commit is better so that users dont
103 // accidentally create two seperate identifiers for the same repo
104 // there is a hesitancy to use the commit id
105 // in another conversaion with fiatjaf he suggested the first 6 character of
106 // the commit id
107 // here we are using 7 which is the standard for shorthand commit id
108 self.root_commit.to_string()[..7].to_string()
109 } else {
110 self.identifier.to_string()
111 }),
112 Tag::Reference(self.root_commit.to_string()),
82 Tag::Name(self.name.clone()), 113 Tag::Name(self.name.clone()),
83 Tag::Description(self.description.clone()), 114 Tag::Description(self.description.clone()),
84 Tag::Generic( 115 Tag::Generic(
85 nostr::TagKind::Custom("git-server".to_string()), 116 nostr::TagKind::Custom("clone".to_string()),
86 vec![self.git_server.clone()], 117 vec![self.git_server.clone()],
87 ), 118 ),
88 Tag::Reference(self.git_server.clone()), 119 Tag::Generic(nostr::TagKind::Custom("web".to_string()), self.web.clone()),
120 Tag::Generic(
121 nostr::TagKind::Custom("relays".to_string()),
122 self.relays.clone(),
123 ),
89 ], 124 ],
90 self.relays.iter().map(|r| Tag::Relay(r.into())).collect(), 125 // this appears like a number of relay tags but test suggest is is actually
126 // what we want which is a relays tag with lots of values. no need for the
127 // change?
128 // self.relays.iter().map(|r| Tag::Relay(r.into())).collect(),
91 self.maintainers 129 self.maintainers
92 .iter() 130 .iter()
93 .map(|pk| Tag::public_key(*pk)) 131 .map(|pk| Tag::public_key(*pk))
@@ -116,7 +154,7 @@ pub async fn fetch(
116 154
117 let mut repo_event_filter = nostr::Filter::default() 155 let mut repo_event_filter = nostr::Filter::default()
118 .kind(nostr::Kind::Custom(REPO_REF_KIND)) 156 .kind(nostr::Kind::Custom(REPO_REF_KIND))
119 .identifier(root_commit); 157 .reference(root_commit);
120 158
121 let mut relays = fallback_relays; 159 let mut relays = fallback_relays;
122 if let Ok(repo_config) = repo_config { 160 if let Ok(repo_config) = repo_config {
@@ -127,6 +165,15 @@ pub async fn fetch(
127 165
128 let events: Vec<nostr::Event> = client.get_events(relays, vec![repo_event_filter]).await?; 166 let events: Vec<nostr::Event> = client.get_events(relays, vec![repo_event_filter]).await?;
129 167
168 // TODO: fallback to ask user for nevent - to capture
169
170 // TODO: if maintainers.yaml isn't present, as the user to select from the
171 // pubkeys they want to use. could use WoT as an indicator as well as the repo
172 // and user name.
173
174 // TODO: if maintainers.yaml isn't present, save the selected repo pubkey
175 // somewhere within .git folder for future use and seek to get that next time
176
130 RepoRef::try_from( 177 RepoRef::try_from(
131 events 178 events
132 .iter() 179 .iter()
@@ -207,10 +254,15 @@ mod tests {
207 254
208 fn create() -> nostr::Event { 255 fn create() -> nostr::Event {
209 RepoRef { 256 RepoRef {
257 identifier: "123412341".to_string(),
210 name: "test name".to_string(), 258 name: "test name".to_string(),
211 description: "test description".to_string(), 259 description: "test description".to_string(),
212 root_commit: "23471389461".to_string(), 260 root_commit: "5e664e5a7845cd1373c79f580ca4fe29ab5b34d2".to_string(),
213 git_server: "https://localhost:1000".to_string(), 261 git_server: "https://localhost:1000".to_string(),
262 web: vec![
263 "https://exampleproject.xyz".to_string(),
264 "https://gitworkshop.dev/123".to_string(),
265 ],
214 relays: vec!["ws://relay1.io".to_string(), "ws://relay2.io".to_string()], 266 relays: vec!["ws://relay1.io".to_string(), "ws://relay2.io".to_string()],
215 maintainers: vec![TEST_KEY_1_KEYS.public_key(), TEST_KEY_2_KEYS.public_key()], 267 maintainers: vec![TEST_KEY_1_KEYS.public_key(), TEST_KEY_2_KEYS.public_key()],
216 } 268 }
@@ -221,6 +273,11 @@ mod tests {
221 use super::*; 273 use super::*;
222 274
223 #[test] 275 #[test]
276 fn identifier() {
277 assert_eq!(RepoRef::try_from(create()).unwrap().identifier, "123412341",)
278 }
279
280 #[test]
224 fn name() { 281 fn name() {
225 assert_eq!(RepoRef::try_from(create()).unwrap().name, "test name",) 282 assert_eq!(RepoRef::try_from(create()).unwrap().name, "test name",)
226 } 283 }
@@ -234,13 +291,60 @@ mod tests {
234 } 291 }
235 292
236 #[test] 293 #[test]
237 fn root_commit() { 294 fn root_commit_is_r_tag() {
238 assert_eq!( 295 assert_eq!(
239 RepoRef::try_from(create()).unwrap().root_commit, 296 RepoRef::try_from(create()).unwrap().root_commit,
240 "23471389461", 297 "5e664e5a7845cd1373c79f580ca4fe29ab5b34d2",
241 ) 298 )
242 } 299 }
243 300
301 mod root_commit_is_empty_if_no_r_tag_which_is_sha1_format {
302 use nostr::JsonUtil;
303
304 use super::*;
305 fn create_with_incorrect_first_commit_ref(s: &str) -> nostr::Event {
306 nostr::Event::from_json(
307 create()
308 .as_json()
309 .replace("5e664e5a7845cd1373c79f580ca4fe29ab5b34d2", s),
310 )
311 .unwrap()
312 }
313
314 #[test]
315 fn less_than_40_characters() {
316 let s = "5e664e5a7845cd1373";
317 assert_eq!(
318 RepoRef::try_from(create_with_incorrect_first_commit_ref(s))
319 .unwrap()
320 .root_commit,
321 "",
322 )
323 }
324
325 #[test]
326 fn more_than_40_characters() {
327 let s = "5e664e5a7845cd1373c79f580ca4fe29ab5b34d2111111111";
328 assert_eq!(
329 RepoRef::try_from(create_with_incorrect_first_commit_ref(s))
330 .unwrap()
331 .root_commit,
332 "",
333 )
334 }
335
336 #[test]
337 fn not_hex_characters() {
338 let s = "xxx64e5a7845cd1373c79f580ca4fe29ab5b34d2";
339 assert_eq!(
340 RepoRef::try_from(create_with_incorrect_first_commit_ref(s))
341 .unwrap()
342 .root_commit,
343 "",
344 )
345 }
346 }
347
244 #[test] 348 #[test]
245 fn git_server() { 349 fn git_server() {
246 assert_eq!( 350 assert_eq!(
@@ -250,6 +354,17 @@ mod tests {
250 } 354 }
251 355
252 #[test] 356 #[test]
357 fn web() {
358 assert_eq!(
359 RepoRef::try_from(create()).unwrap().web,
360 vec![
361 "https://exampleproject.xyz".to_string(),
362 "https://gitworkshop.dev/123".to_string()
363 ],
364 )
365 }
366
367 #[test]
253 fn relays() { 368 fn relays() {
254 assert_eq!( 369 assert_eq!(
255 RepoRef::try_from(create()).unwrap().relays, 370 RepoRef::try_from(create()).unwrap().relays,
@@ -272,67 +387,65 @@ mod tests {
272 use super::*; 387 use super::*;
273 388
274 #[test] 389 #[test]
275 fn name() { 390 fn identifier() {
276 assert!( 391 assert!(
277 create() 392 create()
278 .tags 393 .tags
279 .iter() 394 .iter()
280 .any(|t| t.as_vec()[0].eq("name") && t.as_vec()[1].eq("test name")) 395 .any(|t| t.as_vec()[0].eq("d") && t.as_vec()[1].eq("123412341"))
281 ) 396 )
282 } 397 }
283 #[test]
284 fn description() {
285 assert!(create().tags.iter().any(
286 |t| t.as_vec()[0].eq("description") && t.as_vec()[1].eq("test description")
287 ))
288 }
289 398
290 #[test] 399 #[test]
291 fn root_commit_as_d_replaceable_event_identifier() { 400 fn name() {
292 assert!( 401 assert!(
293 create() 402 create()
294 .tags 403 .tags
295 .iter() 404 .iter()
296 .any(|t| t.as_vec()[0].eq("d") && t.as_vec()[1].eq("23471389461")) 405 .any(|t| t.as_vec()[0].eq("name") && t.as_vec()[1].eq("test name"))
297 ) 406 )
298 } 407 }
299
300 #[test] 408 #[test]
301 fn git_server() { 409 fn description() {
302 assert!(create().tags.iter().any(|t| t.as_vec()[0].eq("git-server") 410 assert!(create().tags.iter().any(
303 && t.as_vec()[1].eq("https://localhost:1000"))) 411 |t| t.as_vec()[0].eq("description") && t.as_vec()[1].eq("test description")
412 ))
304 } 413 }
305 414
306 #[test] 415 #[test]
307 fn git_server_as_reference() { 416 fn root_commit_as_reference() {
308 assert!( 417 assert!(create().tags.iter().any(|t| t.as_vec()[0].eq("r")
309 create().tags.iter().any( 418 && t.as_vec()[1].eq("5e664e5a7845cd1373c79f580ca4fe29ab5b34d2")))
310 |t| t.as_vec()[0].eq("r") && t.as_vec()[1].eq("https://localhost:1000")
311 )
312 )
313 } 419 }
314 420
315 #[test] 421 #[test]
316 fn root_commit_as_reference() { 422 fn git_server() {
317 assert!( 423 assert!(create().tags.iter().any(
318 create() 424 |t| t.as_vec()[0].eq("clone") && t.as_vec()[1].eq("https://localhost:1000")
319 .tags 425 ))
320 .iter()
321 .any(|t| t.as_vec()[0].eq("r") && t.as_vec()[1].eq("r-23471389461"))
322 )
323 } 426 }
324 427
325 #[test] 428 #[test]
326 fn relays() { 429 fn relays() {
327 let event = create(); 430 let event = create();
328 let relay_tags = event 431 let relays_tag: &nostr::Tag = event
329 .tags 432 .tags
330 .iter() 433 .iter()
331 .filter(|t| t.as_vec()[0].eq("relay")) 434 .find(|t| t.as_vec()[0].eq("relays"))
332 .collect::<Vec<&nostr::Tag>>(); 435 .unwrap();
333 assert_eq!(relay_tags[0].as_vec().len(), 2); 436 assert_eq!(relays_tag.as_vec().len(), 3);
334 assert_eq!(relay_tags[0].as_vec()[1], "ws://relay1.io"); 437 assert_eq!(relays_tag.as_vec()[1], "ws://relay1.io");
335 assert_eq!(relay_tags[1].as_vec()[1], "ws://relay2.io"); 438 assert_eq!(relays_tag.as_vec()[2], "ws://relay2.io");
439 }
440
441 #[test]
442 fn web() {
443 let event = create();
444 let web_tag: &nostr::Tag =
445 event.tags.iter().find(|t| t.as_vec()[0].eq("web")).unwrap();
446 assert_eq!(web_tag.as_vec().len(), 3);
447 assert_eq!(web_tag.as_vec()[1], "https://exampleproject.xyz");
448 assert_eq!(web_tag.as_vec()[2], "https://gitworkshop.dev/123");
336 } 449 }
337 450
338 #[test] 451 #[test]
@@ -356,7 +469,7 @@ mod tests {
356 469
357 #[test] 470 #[test]
358 fn no_other_tags() { 471 fn no_other_tags() {
359 assert_eq!(create().tags.len(), 10) 472 assert_eq!(create().tags.len(), 9)
360 } 473 }
361 } 474 }
362 } 475 }