diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2024-02-01 09:59:09 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2024-02-02 02:30:01 +0000 |
| commit | 1cacf1bce77b88febc0cf9e2f0f9f468d4b30555 (patch) | |
| tree | 5c2c69336eddeb79082249a9167cc46e3c155f12 /src/repo_ref.rs | |
| parent | 4b66c76a5a3ba878b4603d992353327807b8d8e6 (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.rs | 217 |
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::{ | |||
| 17 | pub struct RepoRef { | 17 | pub 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 | } |
| 71 | static REPO_REF_KIND: u64 = 30_317; | 84 | |
| 85 | pub static REPO_REF_KIND: u64 = 30_617; | ||
| 72 | 86 | ||
| 73 | impl RepoRef { | 87 | impl 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 | } |