upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock179
-rw-r--r--Cargo.toml11
-rw-r--r--src/bin/git_remote_nostr/fetch.rs2
-rw-r--r--src/bin/git_remote_nostr/main.rs4
-rw-r--r--src/bin/git_remote_nostr/push.rs17
-rw-r--r--src/bin/ngit/sub_commands/init.rs21
-rw-r--r--src/bin/ngit/sub_commands/send.rs8
-rw-r--r--src/lib/client.rs223
-rw-r--r--src/lib/git/mod.rs2
-rw-r--r--src/lib/git/nostr_url.rs150
-rw-r--r--src/lib/git_events.rs42
-rw-r--r--src/lib/login/existing.rs2
-rw-r--r--src/lib/login/fresh.rs2
-rw-r--r--src/lib/login/key_encryption.rs2
-rw-r--r--src/lib/login/mod.rs4
-rw-r--r--src/lib/repo_ref.rs103
-rw-r--r--test_utils/Cargo.toml8
-rw-r--r--test_utils/src/git.rs12
-rw-r--r--test_utils/src/lib.rs54
-rw-r--r--test_utils/src/relay.rs27
-rw-r--r--tests/git_remote_nostr/main.rs12
-rw-r--r--tests/ngit_init.rs12
-rw-r--r--tests/ngit_list.rs15
23 files changed, 474 insertions, 438 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 3155370..bcd1090 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -48,12 +48,6 @@ dependencies = [
48] 48]
49 49
50[[package]] 50[[package]]
51name = "allocator-api2"
52version = "0.2.21"
53source = "registry+https://github.com/rust-lang/crates.io-index"
54checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
55
56[[package]]
57name = "anstream" 51name = "anstream"
58version = "0.6.18" 52version = "0.6.18"
59source = "registry+https://github.com/rust-lang/crates.io-index" 53source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -302,9 +296,9 @@ dependencies = [
302 296
303[[package]] 297[[package]]
304name = "async-utility" 298name = "async-utility"
305version = "0.2.0" 299version = "0.3.1"
306source = "registry+https://github.com/rust-lang/crates.io-index" 300source = "registry+https://github.com/rust-lang/crates.io-index"
307checksum = "a349201d80b4aa18d17a34a182bdd7f8ddf845e9e57d2ea130a12e10ef1e3a47" 301checksum = "a34a3b57207a7a1007832416c3e4862378c8451b4e8e093e436f48c2d3d2c151"
308dependencies = [ 302dependencies = [
309 "futures-util", 303 "futures-util",
310 "gloo-timers", 304 "gloo-timers",
@@ -314,9 +308,9 @@ dependencies = [
314 308
315[[package]] 309[[package]]
316name = "async-wsocket" 310name = "async-wsocket"
317version = "0.10.1" 311version = "0.13.1"
318source = "registry+https://github.com/rust-lang/crates.io-index" 312source = "registry+https://github.com/rust-lang/crates.io-index"
319checksum = "8d50cb541e6d09e119e717c64c46ed33f49be7fa592fa805d56c11d6a7ff093c" 313checksum = "9a7d8c7d34a225ba919dd9ba44d4b9106d20142da545e086be8ae21d1897e043"
320dependencies = [ 314dependencies = [
321 "async-utility", 315 "async-utility",
322 "futures", 316 "futures",
@@ -325,7 +319,7 @@ dependencies = [
325 "tokio", 319 "tokio",
326 "tokio-rustls", 320 "tokio-rustls",
327 "tokio-socks", 321 "tokio-socks",
328 "tokio-tungstenite 0.24.0", 322 "tokio-tungstenite 0.26.2",
329 "url", 323 "url",
330 "wasm-bindgen", 324 "wasm-bindgen",
331 "web-sys", 325 "web-sys",
@@ -333,12 +327,9 @@ dependencies = [
333 327
334[[package]] 328[[package]]
335name = "atomic-destructor" 329name = "atomic-destructor"
336version = "0.2.0" 330version = "0.3.0"
337source = "registry+https://github.com/rust-lang/crates.io-index" 331source = "registry+https://github.com/rust-lang/crates.io-index"
338checksum = "7d919cb60ba95c87ba42777e9e246c4e8d658057299b437b7512531ce0a09a23" 332checksum = "ef49f5882e4b6afaac09ad239a4f8c70a24b8f2b0897edb1f706008efd109cf4"
339dependencies = [
340 "tracing",
341]
342 333
343[[package]] 334[[package]]
344name = "atomic-waker" 335name = "atomic-waker"
@@ -379,16 +370,6 @@ dependencies = [
379] 370]
380 371
381[[package]] 372[[package]]
382name = "base58ck"
383version = "0.1.0"
384source = "registry+https://github.com/rust-lang/crates.io-index"
385checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f"
386dependencies = [
387 "bitcoin-internals 0.3.0",
388 "bitcoin_hashes 0.14.0",
389]
390
391[[package]]
392name = "base64" 373name = "base64"
393version = "0.22.1" 374version = "0.22.1"
394source = "registry+https://github.com/rust-lang/crates.io-index" 375source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -418,61 +399,24 @@ dependencies = [
418] 399]
419 400
420[[package]] 401[[package]]
421name = "bitcoin"
422version = "0.32.5"
423source = "registry+https://github.com/rust-lang/crates.io-index"
424checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026"
425dependencies = [
426 "base58ck",
427 "bech32",
428 "bitcoin-internals 0.3.0",
429 "bitcoin-io",
430 "bitcoin-units",
431 "bitcoin_hashes 0.14.0",
432 "hex-conservative 0.2.1",
433 "hex_lit",
434 "secp256k1",
435 "serde",
436]
437
438[[package]]
439name = "bitcoin-internals" 402name = "bitcoin-internals"
440version = "0.2.0" 403version = "0.2.0"
441source = "registry+https://github.com/rust-lang/crates.io-index" 404source = "registry+https://github.com/rust-lang/crates.io-index"
442checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" 405checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb"
443 406
444[[package]] 407[[package]]
445name = "bitcoin-internals"
446version = "0.3.0"
447source = "registry+https://github.com/rust-lang/crates.io-index"
448checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2"
449dependencies = [
450 "serde",
451]
452
453[[package]]
454name = "bitcoin-io" 408name = "bitcoin-io"
455version = "0.1.3" 409version = "0.1.3"
456source = "registry+https://github.com/rust-lang/crates.io-index" 410source = "registry+https://github.com/rust-lang/crates.io-index"
457checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" 411checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf"
458 412
459[[package]] 413[[package]]
460name = "bitcoin-units"
461version = "0.1.2"
462source = "registry+https://github.com/rust-lang/crates.io-index"
463checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2"
464dependencies = [
465 "bitcoin-internals 0.3.0",
466 "serde",
467]
468
469[[package]]
470name = "bitcoin_hashes" 414name = "bitcoin_hashes"
471version = "0.13.0" 415version = "0.13.0"
472source = "registry+https://github.com/rust-lang/crates.io-index" 416source = "registry+https://github.com/rust-lang/crates.io-index"
473checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" 417checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b"
474dependencies = [ 418dependencies = [
475 "bitcoin-internals 0.2.0", 419 "bitcoin-internals",
476 "hex-conservative 0.1.2", 420 "hex-conservative 0.1.2",
477] 421]
478 422
@@ -1038,12 +982,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1038checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 982checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
1039 983
1040[[package]] 984[[package]]
1041name = "foldhash"
1042version = "0.1.5"
1043source = "registry+https://github.com/rust-lang/crates.io-index"
1044checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
1045
1046[[package]]
1047name = "form_urlencoded" 985name = "form_urlencoded"
1048version = "1.2.1" 986version = "1.2.1"
1049source = "registry+https://github.com/rust-lang/crates.io-index" 987source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1235,9 +1173,9 @@ dependencies = [
1235 1173
1236[[package]] 1174[[package]]
1237name = "gloo-timers" 1175name = "gloo-timers"
1238version = "0.2.6" 1176version = "0.3.0"
1239source = "registry+https://github.com/rust-lang/crates.io-index" 1177source = "registry+https://github.com/rust-lang/crates.io-index"
1240checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" 1178checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
1241dependencies = [ 1179dependencies = [
1242 "futures-channel", 1180 "futures-channel",
1243 "futures-core", 1181 "futures-core",
@@ -1256,11 +1194,6 @@ name = "hashbrown"
1256version = "0.15.2" 1194version = "0.15.2"
1257source = "registry+https://github.com/rust-lang/crates.io-index" 1195source = "registry+https://github.com/rust-lang/crates.io-index"
1258checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 1196checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
1259dependencies = [
1260 "allocator-api2",
1261 "equivalent",
1262 "foldhash",
1263]
1264 1197
1265[[package]] 1198[[package]]
1266name = "heck" 1199name = "heck"
@@ -1342,12 +1275,6 @@ dependencies = [
1342] 1275]
1343 1276
1344[[package]] 1277[[package]]
1345name = "hex_lit"
1346version = "0.1.1"
1347source = "registry+https://github.com/rust-lang/crates.io-index"
1348checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd"
1349
1350[[package]]
1351name = "hkdf" 1278name = "hkdf"
1352version = "0.12.4" 1279version = "0.12.4"
1353source = "registry+https://github.com/rust-lang/crates.io-index" 1280source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1852,12 +1779,9 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
1852 1779
1853[[package]] 1780[[package]]
1854name = "lru" 1781name = "lru"
1855version = "0.12.5" 1782version = "0.13.0"
1856source = "registry+https://github.com/rust-lang/crates.io-index" 1783source = "registry+https://github.com/rust-lang/crates.io-index"
1857checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" 1784checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465"
1858dependencies = [
1859 "hashbrown 0.15.2",
1860]
1861 1785
1862[[package]] 1786[[package]]
1863name = "memchr" 1787name = "memchr"
@@ -1953,9 +1877,9 @@ checksum = "e664971378a3987224f7a0e10059782035e89899ae403718ee07de85bec42afe"
1953 1877
1954[[package]] 1878[[package]]
1955name = "negentropy" 1879name = "negentropy"
1956version = "0.4.3" 1880version = "0.5.0"
1957source = "registry+https://github.com/rust-lang/crates.io-index" 1881source = "registry+https://github.com/rust-lang/crates.io-index"
1958checksum = "43a88da9dd148bbcdce323dd6ac47d369b4769d4a3b78c6c52389b9269f77932" 1882checksum = "f0efe882e02d206d8d279c20eb40e03baf7cb5136a1476dc084a324fbc3ec42d"
1959 1883
1960[[package]] 1884[[package]]
1961name = "ngit" 1885name = "ngit"
@@ -1980,6 +1904,7 @@ dependencies = [
1980 "nostr-connect", 1904 "nostr-connect",
1981 "nostr-database", 1905 "nostr-database",
1982 "nostr-lmdb", 1906 "nostr-lmdb",
1907 "nostr-relay-pool",
1983 "nostr-sdk", 1908 "nostr-sdk",
1984 "once_cell", 1909 "once_cell",
1985 "passwords", 1910 "passwords",
@@ -2026,26 +1951,24 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
2026 1951
2027[[package]] 1952[[package]]
2028name = "nostr" 1953name = "nostr"
2029version = "0.37.0" 1954version = "0.40.0"
2030source = "registry+https://github.com/rust-lang/crates.io-index" 1955source = "registry+https://github.com/rust-lang/crates.io-index"
2031checksum = "8aad4b767bbed24ac5eb4465bfb83bc1210522eb99d67cf4e547ec2ec7e47786" 1956checksum = "2f900ddcdc28395759fcd44b18a03255e7deee8858551bfe5d5d5a07311d82ea"
2032dependencies = [ 1957dependencies = [
2033 "aes", 1958 "aes",
2034 "async-trait",
2035 "base64", 1959 "base64",
2036 "bech32", 1960 "bech32",
2037 "bip39", 1961 "bip39",
2038 "bitcoin", 1962 "bitcoin_hashes 0.14.0",
2039 "cbc", 1963 "cbc",
2040 "chacha20", 1964 "chacha20",
2041 "chacha20poly1305", 1965 "chacha20poly1305",
2042 "getrandom 0.2.15", 1966 "getrandom 0.2.15",
2043 "instant", 1967 "instant",
2044 "negentropy 0.3.1", 1968 "regex",
2045 "negentropy 0.4.3",
2046 "once_cell",
2047 "reqwest", 1969 "reqwest",
2048 "scrypt", 1970 "scrypt",
1971 "secp256k1",
2049 "serde", 1972 "serde",
2050 "serde_json", 1973 "serde_json",
2051 "unicode-normalization", 1974 "unicode-normalization",
@@ -2054,79 +1977,71 @@ dependencies = [
2054 1977
2055[[package]] 1978[[package]]
2056name = "nostr-connect" 1979name = "nostr-connect"
2057version = "0.37.0" 1980version = "0.40.0"
2058source = "registry+https://github.com/rust-lang/crates.io-index" 1981source = "registry+https://github.com/rust-lang/crates.io-index"
2059checksum = "5b1bf2a6324d2c1739be462e83d27caa2c025fd7ca14b401b4bc4b7cac11d382" 1982checksum = "52a0479b5f2da48ddd36061281bc2b3738610a287ad485aae397c44c148b3c6d"
2060dependencies = [ 1983dependencies = [
2061 "async-trait",
2062 "async-utility", 1984 "async-utility",
2063 "nostr", 1985 "nostr",
2064 "nostr-relay-pool", 1986 "nostr-relay-pool",
2065 "thiserror 1.0.69",
2066 "tokio", 1987 "tokio",
2067 "tracing", 1988 "tracing",
2068] 1989]
2069 1990
2070[[package]] 1991[[package]]
2071name = "nostr-database" 1992name = "nostr-database"
2072version = "0.37.0" 1993version = "0.40.0"
2073source = "registry+https://github.com/rust-lang/crates.io-index" 1994source = "registry+https://github.com/rust-lang/crates.io-index"
2074checksum = "23696338d51e45cd44e061823847f4b0d1d362eca80d5033facf9c184149f72f" 1995checksum = "714512e4653f4e7c4f4abb50a0ac82257541b22087dee780b9e3d787276e88d4"
2075dependencies = [ 1996dependencies = [
2076 "async-trait",
2077 "flatbuffers", 1997 "flatbuffers",
2078 "lru", 1998 "lru",
2079 "nostr", 1999 "nostr",
2080 "thiserror 1.0.69",
2081 "tokio", 2000 "tokio",
2082 "tracing",
2083] 2001]
2084 2002
2085[[package]] 2003[[package]]
2086name = "nostr-lmdb" 2004name = "nostr-lmdb"
2087version = "0.37.0" 2005version = "0.40.0"
2088source = "registry+https://github.com/rust-lang/crates.io-index" 2006source = "registry+https://github.com/rust-lang/crates.io-index"
2089checksum = "70c97ff4e384735527af2dc74b6c8f0042f02daea476bad6c7bb56be32739250" 2007checksum = "0ca52683835f416e5d4699f19eb616a622de54b75adb954430b1190e94f4e5ca"
2090dependencies = [ 2008dependencies = [
2009 "async-utility",
2091 "heed", 2010 "heed",
2092 "nostr", 2011 "nostr",
2093 "nostr-database", 2012 "nostr-database",
2094 "thiserror 1.0.69",
2095 "tokio", 2013 "tokio",
2096 "tracing", 2014 "tracing",
2097] 2015]
2098 2016
2099[[package]] 2017[[package]]
2100name = "nostr-relay-pool" 2018name = "nostr-relay-pool"
2101version = "0.37.0" 2019version = "0.40.1"
2102source = "registry+https://github.com/rust-lang/crates.io-index" 2020source = "registry+https://github.com/rust-lang/crates.io-index"
2103checksum = "15fcc6e3f0ca54d0fc779009bc5f2684cea9147be3b6aa68a7d301ea590f95f5" 2021checksum = "5bde07a729e0a1b306c9a07da81a0d1d55d0487316017090906f3b6660741b8d"
2104dependencies = [ 2022dependencies = [
2105 "async-utility", 2023 "async-utility",
2106 "async-wsocket", 2024 "async-wsocket",
2107 "atomic-destructor", 2025 "atomic-destructor",
2026 "lru",
2108 "negentropy 0.3.1", 2027 "negentropy 0.3.1",
2109 "negentropy 0.4.3", 2028 "negentropy 0.5.0",
2110 "nostr", 2029 "nostr",
2111 "nostr-database", 2030 "nostr-database",
2112 "thiserror 1.0.69",
2113 "tokio", 2031 "tokio",
2114 "tokio-stream",
2115 "tracing", 2032 "tracing",
2116] 2033]
2117 2034
2118[[package]] 2035[[package]]
2119name = "nostr-sdk" 2036name = "nostr-sdk"
2120version = "0.37.0" 2037version = "0.40.0"
2121source = "registry+https://github.com/rust-lang/crates.io-index" 2038source = "registry+https://github.com/rust-lang/crates.io-index"
2122checksum = "491221fc89b1aa189a0de640127127d68b4e7c5c1d44371b04d9a6d10694b5af" 2039checksum = "26238eee805d7dc3abcc8d570068c81cb4285b08e9db4d7999e54e20748c472e"
2123dependencies = [ 2040dependencies = [
2124 "async-utility", 2041 "async-utility",
2125 "atomic-destructor",
2126 "nostr", 2042 "nostr",
2127 "nostr-database", 2043 "nostr-database",
2128 "nostr-relay-pool", 2044 "nostr-relay-pool",
2129 "thiserror 1.0.69",
2130 "tokio", 2045 "tokio",
2131 "tracing", 2046 "tracing",
2132] 2047]
@@ -3023,7 +2938,6 @@ version = "0.29.1"
3023source = "registry+https://github.com/rust-lang/crates.io-index" 2938source = "registry+https://github.com/rust-lang/crates.io-index"
3024checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" 2939checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113"
3025dependencies = [ 2940dependencies = [
3026 "bitcoin_hashes 0.14.0",
3027 "rand 0.8.5", 2941 "rand 0.8.5",
3028 "secp256k1-sys", 2942 "secp256k1-sys",
3029 "serde", 2943 "serde",
@@ -3112,7 +3026,6 @@ version = "1.0.140"
3112source = "registry+https://github.com/rust-lang/crates.io-index" 3026source = "registry+https://github.com/rust-lang/crates.io-index"
3113checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 3027checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
3114dependencies = [ 3028dependencies = [
3115 "indexmap",
3116 "itoa", 3029 "itoa",
3117 "memchr", 3030 "memchr",
3118 "ryu", 3031 "ryu",
@@ -3538,17 +3451,6 @@ dependencies = [
3538] 3451]
3539 3452
3540[[package]] 3453[[package]]
3541name = "tokio-stream"
3542version = "0.1.17"
3543source = "registry+https://github.com/rust-lang/crates.io-index"
3544checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
3545dependencies = [
3546 "futures-core",
3547 "pin-project-lite",
3548 "tokio",
3549]
3550
3551[[package]]
3552name = "tokio-tungstenite" 3454name = "tokio-tungstenite"
3553version = "0.20.1" 3455version = "0.20.1"
3554source = "registry+https://github.com/rust-lang/crates.io-index" 3456source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3562,9 +3464,9 @@ dependencies = [
3562 3464
3563[[package]] 3465[[package]]
3564name = "tokio-tungstenite" 3466name = "tokio-tungstenite"
3565version = "0.24.0" 3467version = "0.26.2"
3566source = "registry+https://github.com/rust-lang/crates.io-index" 3468source = "registry+https://github.com/rust-lang/crates.io-index"
3567checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" 3469checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
3568dependencies = [ 3470dependencies = [
3569 "futures-util", 3471 "futures-util",
3570 "log", 3472 "log",
@@ -3572,7 +3474,7 @@ dependencies = [
3572 "rustls-pki-types", 3474 "rustls-pki-types",
3573 "tokio", 3475 "tokio",
3574 "tokio-rustls", 3476 "tokio-rustls",
3575 "tungstenite 0.24.0", 3477 "tungstenite 0.26.2",
3576 "webpki-roots", 3478 "webpki-roots",
3577] 3479]
3578 3480
@@ -3678,21 +3580,20 @@ dependencies = [
3678 3580
3679[[package]] 3581[[package]]
3680name = "tungstenite" 3582name = "tungstenite"
3681version = "0.24.0" 3583version = "0.26.2"
3682source = "registry+https://github.com/rust-lang/crates.io-index" 3584source = "registry+https://github.com/rust-lang/crates.io-index"
3683checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" 3585checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13"
3684dependencies = [ 3586dependencies = [
3685 "byteorder",
3686 "bytes", 3587 "bytes",
3687 "data-encoding", 3588 "data-encoding",
3688 "http 1.3.1", 3589 "http 1.3.1",
3689 "httparse", 3590 "httparse",
3690 "log", 3591 "log",
3691 "rand 0.8.5", 3592 "rand 0.9.0",
3692 "rustls", 3593 "rustls",
3693 "rustls-pki-types", 3594 "rustls-pki-types",
3694 "sha1", 3595 "sha1",
3695 "thiserror 1.0.69", 3596 "thiserror 2.0.12",
3696 "utf-8", 3597 "utf-8",
3697] 3598]
3698 3599
diff --git a/Cargo.toml b/Cargo.toml
index 14ef062..57cfb88 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,11 +24,12 @@ futures = "0.3.28"
24git2 = "0.19.0" 24git2 = "0.19.0"
25indicatif = "0.17.7" 25indicatif = "0.17.7"
26keyring = "2.0.5" 26keyring = "2.0.5"
27nostr = { version = "0.37.0", features = ["nip05", "nip49"] } 27nostr = { version = "0.40.0", features = ["nip05", "nip49"] }
28nostr-connect = "0.37.0" 28nostr-connect = "0.40.0"
29nostr-database = "0.37.0" 29nostr-database = "0.40.0"
30nostr-lmdb = "0.37.0" 30nostr-lmdb = "0.40.0"
31nostr-sdk = "0.37.0" 31nostr-relay-pool = "0.40.1"
32nostr-sdk = "0.40.0"
32passwords = "3.1.13" 33passwords = "3.1.13"
33qrcode = { version = "0.14.1", default-features = false } 34qrcode = { version = "0.14.1", default-features = false }
34scrypt = "0.11.0" 35scrypt = "0.11.0"
diff --git a/src/bin/git_remote_nostr/fetch.rs b/src/bin/git_remote_nostr/fetch.rs
index 5ee4271..a7210d0 100644
--- a/src/bin/git_remote_nostr/fetch.rs
+++ b/src/bin/git_remote_nostr/fetch.rs
@@ -115,7 +115,7 @@ pub fn make_commits_for_proposal(
115 author: Some(patch.pubkey), 115 author: Some(patch.pubkey),
116 kind: Some(patch.kind), 116 kind: Some(patch.kind),
117 relays: if let Some(relay) = repo_ref.relays.first() { 117 relays: if let Some(relay) = repo_ref.relays.first() {
118 vec![relay.to_string()] 118 vec![relay.to_owned()]
119 } else { 119 } else {
120 vec![] 120 vec![]
121 }, 121 },
diff --git a/src/bin/git_remote_nostr/main.rs b/src/bin/git_remote_nostr/main.rs
index 44359dd..daa924f 100644
--- a/src/bin/git_remote_nostr/main.rs
+++ b/src/bin/git_remote_nostr/main.rs
@@ -15,7 +15,7 @@ use anyhow::{Context, Result, bail};
15use client::{Connect, consolidate_fetch_reports, get_repo_ref_from_cache}; 15use client::{Connect, consolidate_fetch_reports, get_repo_ref_from_cache};
16use git::{RepoActions, nostr_url::NostrUrlDecoded}; 16use git::{RepoActions, nostr_url::NostrUrlDecoded};
17use ngit::{client, git, login::existing::load_existing_login}; 17use ngit::{client, git, login::existing::load_existing_login};
18use nostr::nips::nip01::Coordinate; 18use nostr::nips::nip19::Nip19Coordinate;
19use utils::read_line; 19use utils::read_line;
20 20
21use crate::{client::Client, git::Repo}; 21use crate::{client::Client, git::Repo};
@@ -148,7 +148,7 @@ async fn process_args() -> Result<Option<(NostrUrlDecoded, Repo)>> {
148async fn fetching_with_report_for_helper( 148async fn fetching_with_report_for_helper(
149 git_repo_path: &Path, 149 git_repo_path: &Path,
150 client: &Client, 150 client: &Client,
151 trusted_maintainer_coordinate: &Coordinate, 151 trusted_maintainer_coordinate: &Nip19Coordinate,
152) -> Result<()> { 152) -> Result<()> {
153 let term = console::Term::stderr(); 153 let term = console::Term::stderr();
154 term.write_line("nostr: fetching...")?; 154 term.write_line("nostr: fetching...")?;
diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs
index c0085bd..15adc13 100644
--- a/src/bin/git_remote_nostr/push.rs
+++ b/src/bin/git_remote_nostr/push.rs
@@ -32,7 +32,7 @@ use ngit::{
32}; 32};
33use nostr::nips::nip10::Marker; 33use nostr::nips::nip10::Marker;
34use nostr_sdk::{ 34use nostr_sdk::{
35 Event, EventBuilder, EventId, Kind, NostrSigner, PublicKey, RelayUrl, Tag, 35 Event, EventBuilder, EventId, Kind, NostrSigner, PublicKey, RelayUrl, Tag, TagStandard,
36 hashes::sha1::Hash as Sha1Hash, 36 hashes::sha1::Hash as Sha1Hash,
37}; 37};
38use repo_ref::RepoRef; 38use repo_ref::RepoRef;
@@ -1312,7 +1312,13 @@ async fn create_merge_status(
1312 repo_ref 1312 repo_ref
1313 .coordinates() 1313 .coordinates()
1314 .iter() 1314 .iter()
1315 .map(|c| Tag::coordinate(c.clone())) 1315 .map(|c| {
1316 Tag::from_standardized(TagStandard::Coordinate {
1317 coordinate: c.coordinate.clone(),
1318 relay_url: c.relays.first().cloned(),
1319 uppercase: false,
1320 })
1321 })
1316 .collect::<Vec<Tag>>(), 1322 .collect::<Vec<Tag>>(),
1317 vec![ 1323 vec![
1318 Tag::from_standardized(nostr::TagStandard::Reference( 1324 Tag::from_standardized(nostr::TagStandard::Reference(
@@ -1358,7 +1364,7 @@ async fn get_proposal_and_revision_root_from_patch(
1358 patch.clone() 1364 patch.clone()
1359 } else { 1365 } else {
1360 let proposal_or_revision_id = EventId::parse( 1366 let proposal_or_revision_id = EventId::parse(
1361 if let Some(t) = patch.tags.iter().find(|t| t.is_root()) { 1367 &if let Some(t) = patch.tags.iter().find(|t| t.is_root()) {
1362 t.clone() 1368 t.clone()
1363 } else if let Some(t) = patch.tags.iter().find(|t| t.is_reply()) { 1369 } else if let Some(t) = patch.tags.iter().find(|t| t.is_reply()) {
1364 t.clone() 1370 t.clone()
@@ -1389,13 +1395,12 @@ async fn get_proposal_and_revision_root_from_patch(
1389 { 1395 {
1390 Ok(( 1396 Ok((
1391 EventId::parse( 1397 EventId::parse(
1392 proposal_or_revision 1398 &proposal_or_revision
1393 .tags 1399 .tags
1394 .iter() 1400 .iter()
1395 .find(|t| t.is_reply()) 1401 .find(|t| t.is_reply())
1396 .unwrap() 1402 .unwrap()
1397 .as_slice()[1] 1403 .as_slice()[1],
1398 .clone(),
1399 )?, 1404 )?,
1400 Some(proposal_or_revision.id), 1405 Some(proposal_or_revision.id),
1401 )) 1406 ))
diff --git a/src/bin/ngit/sub_commands/init.rs b/src/bin/ngit/sub_commands/init.rs
index 4aa7ced..3731e3a 100644
--- a/src/bin/ngit/sub_commands/init.rs
+++ b/src/bin/ngit/sub_commands/init.rs
@@ -11,6 +11,7 @@ use nostr::{
11 nips::{ 11 nips::{
12 nip01::Coordinate, 12 nip01::Coordinate,
13 nip05::{self}, 13 nip05::{self},
14 nip19::Nip19Coordinate,
14 }, 15 },
15}; 16};
16use nostr_sdk::{Kind, RelayUrl}; 17use nostr_sdk::{Kind, RelayUrl};
@@ -437,10 +438,12 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> {
437 // TODO - does this git config item do more harm than good? 438 // TODO - does this git config item do more harm than good?
438 git_repo.save_git_config_item( 439 git_repo.save_git_config_item(
439 "nostr.repo", 440 "nostr.repo",
440 &Coordinate { 441 &Nip19Coordinate {
441 kind: Kind::GitRepoAnnouncement, 442 coordinate: Coordinate {
442 public_key: user_ref.public_key, 443 kind: Kind::GitRepoAnnouncement,
443 identifier: identifier.clone(), 444 public_key: user_ref.public_key,
445 identifier: identifier.clone(),
446 },
444 relays: vec![], 447 relays: vec![],
445 } 448 }
446 .to_bech32()?, 449 .to_bech32()?,
@@ -471,10 +474,12 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> {
471 repo_ref.set_nostr_git_url(NostrUrlDecoded { 474 repo_ref.set_nostr_git_url(NostrUrlDecoded {
472 original_string: String::new(), 475 original_string: String::new(),
473 nip05: Some(nip05.clone()), 476 nip05: Some(nip05.clone()),
474 coordinate: Coordinate { 477 coordinate: Nip19Coordinate {
475 kind: Kind::GitRepoAnnouncement, 478 coordinate: Coordinate {
476 public_key: user_ref.public_key, 479 kind: Kind::GitRepoAnnouncement,
477 identifier: repo_ref.identifier.clone(), 480 public_key: user_ref.public_key,
481 identifier: repo_ref.identifier.clone(),
482 },
478 relays: if inter.next().is_some() || relays.is_empty() { 483 relays: if inter.next().is_some() || relays.is_empty() {
479 vec![] 484 vec![]
480 } else { 485 } else {
diff --git a/src/bin/ngit/sub_commands/send.rs b/src/bin/ngit/sub_commands/send.rs
index 7c898bc..9fc00d9 100644
--- a/src/bin/ngit/sub_commands/send.rs
+++ b/src/bin/ngit/sub_commands/send.rs
@@ -238,7 +238,13 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs, no_fetch: bool) -> Re
238 if root_proposal_id.is_none() { 238 if root_proposal_id.is_none() {
239 if let Some(event) = events.first() { 239 if let Some(event) = events.first() {
240 let event_bech32 = if let Some(relay) = repo_ref.relays.first() { 240 let event_bech32 = if let Some(relay) = repo_ref.relays.first() {
241 Nip19Event::new(event.id, vec![relay.to_string()]).to_bech32()? 241 Nip19Event {
242 event_id: event.id,
243 relays: vec![relay.clone()],
244 author: None,
245 kind: None,
246 }
247 .to_bech32()?
242 } else { 248 } else {
243 event.id.to_bech32()? 249 event.id.to_bech32()?
244 }; 250 };
diff --git a/src/lib/client.rs b/src/lib/client.rs
index 59ec583..142df64 100644
--- a/src/lib/client.rs
+++ b/src/lib/client.rs
@@ -29,9 +29,14 @@ use futures::{
29use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget, ProgressState, ProgressStyle}; 29use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget, ProgressState, ProgressStyle};
30#[cfg(test)] 30#[cfg(test)]
31use mockall::*; 31use mockall::*;
32use nostr::{Event, nips::nip01::Coordinate, signer::SignerBackend}; 32use nostr::{
33use nostr_database::NostrEventsDatabase; 33 Event,
34 nips::{nip01::Coordinate, nip19::Nip19Coordinate},
35 signer::SignerBackend,
36};
37use nostr_database::{NostrEventsDatabase, SaveEventStatus};
34use nostr_lmdb::NostrLMDB; 38use nostr_lmdb::NostrLMDB;
39use nostr_relay_pool::relay::ReqExitPolicy;
35use nostr_sdk::{ 40use nostr_sdk::{
36 EventBuilder, EventId, Kind, NostrSigner, Options, PublicKey, RelayUrl, SingleLetterTag, 41 EventBuilder, EventId, Kind, NostrSigner, Options, PublicKey, RelayUrl, SingleLetterTag,
37 Timestamp, prelude::RelayLimits, 42 Timestamp, prelude::RelayLimits,
@@ -89,7 +94,7 @@ pub trait Connect {
89 async fn fetch_all<'a>( 94 async fn fetch_all<'a>(
90 &self, 95 &self,
91 git_repo_path: Option<&'a Path>, 96 git_repo_path: Option<&'a Path>,
92 repo_coordinates: Option<&'a Coordinate>, 97 repo_coordinates: Option<&'a Nip19Coordinate>,
93 user_profiles: &HashSet<PublicKey>, 98 user_profiles: &HashSet<PublicKey>,
94 ) -> Result<(Vec<Result<FetchReport>>, MultiProgress)>; 99 ) -> Result<(Vec<Result<FetchReport>>, MultiProgress)>;
95 async fn fetch_all_from_relay<'a>( 100 async fn fetch_all_from_relay<'a>(
@@ -182,8 +187,8 @@ impl Connect for Client {
182 187
183 if !relay.is_connected() { 188 if !relay.is_connected() {
184 #[allow(clippy::large_futures)] 189 #[allow(clippy::large_futures)]
185 relay 190 let _ = relay
186 .connect(Some(std::time::Duration::from_secs(CONNECTION_TIMEOUT))) 191 .try_connect(std::time::Duration::from_secs(CONNECTION_TIMEOUT))
187 .await; 192 .await;
188 } 193 }
189 194
@@ -194,7 +199,7 @@ impl Connect for Client {
194 } 199 }
195 200
196 async fn disconnect(&self) -> Result<()> { 201 async fn disconnect(&self) -> Result<()> {
197 self.client.disconnect().await?; 202 self.client.disconnect().await;
198 Ok(()) 203 Ok(())
199 } 204 }
200 205
@@ -223,11 +228,7 @@ impl Connect for Client {
223 self.client.add_relay(url).await?; 228 self.client.add_relay(url).await?;
224 #[allow(clippy::large_futures)] 229 #[allow(clippy::large_futures)]
225 self.client.connect_relay(url).await?; 230 self.client.connect_relay(url).await?;
226 self.client 231 self.client.relay(url).await?.send_event(&event).await?;
227 .relay(url)
228 .await?
229 .send_event(event.clone())
230 .await?;
231 if let Some(git_repo_path) = git_repo_path { 232 if let Some(git_repo_path) = git_repo_path {
232 save_event_in_local_cache(git_repo_path, &event).await?; 233 save_event_in_local_cache(git_repo_path, &event).await?;
233 } 234 }
@@ -329,7 +330,7 @@ impl Connect for Client {
329 async fn fetch_all<'a>( 330 async fn fetch_all<'a>(
330 &self, 331 &self,
331 git_repo_path: Option<&'a Path>, 332 git_repo_path: Option<&'a Path>,
332 trusted_maintainer_coordinate: Option<&'a Coordinate>, 333 trusted_maintainer_coordinate: Option<&'a Nip19Coordinate>,
333 user_profiles: &HashSet<PublicKey>, 334 user_profiles: &HashSet<PublicKey>,
334 ) -> Result<(Vec<Result<FetchReport>>, MultiProgress)> { 335 ) -> Result<(Vec<Result<FetchReport>>, MultiProgress)> {
335 let fallback_relays = &self 336 let fallback_relays = &self
@@ -504,7 +505,7 @@ impl Connect for Client {
504 request: FetchRequest, 505 request: FetchRequest,
505 pb: &Option<ProgressBar>, 506 pb: &Option<ProgressBar>,
506 ) -> Result<FetchReport> { 507 ) -> Result<FetchReport> {
507 let mut fresh_coordinates: HashSet<Coordinate> = HashSet::new(); 508 let mut fresh_coordinates: HashSet<Nip19Coordinate> = HashSet::new();
508 for (c, _) in request.repo_coordinates_without_relays.clone() { 509 for (c, _) in request.repo_coordinates_without_relays.clone() {
509 fresh_coordinates.insert(c); 510 fresh_coordinates.insert(c);
510 } 511 }
@@ -617,8 +618,8 @@ async fn get_events_of(
617 618
618 if !relay.is_connected() { 619 if !relay.is_connected() {
619 #[allow(clippy::large_futures)] 620 #[allow(clippy::large_futures)]
620 relay 621 let _ = relay
621 .connect(Some(std::time::Duration::from_secs(CONNECTION_TIMEOUT))) 622 .try_connect(std::time::Duration::from_secs(CONNECTION_TIMEOUT))
622 .await; 623 .await;
623 } 624 }
624 625
@@ -627,16 +628,27 @@ async fn get_events_of(
627 } else if let Some(pb) = pb { 628 } else if let Some(pb) = pb {
628 pb.set_prefix(format!("connected {}", relay.url())); 629 pb.set_prefix(format!("connected {}", relay.url()));
629 } 630 }
630 let events = relay 631
631 .fetch_events( 632 let events_res = join_all(filters.into_iter().map(|filter| async {
632 filters, 633 relay
633 // 20 is nostr_sdk default 634 .fetch_events(
634 std::time::Duration::from_secs(GET_EVENTS_TIMEOUT), 635 filter,
635 nostr_sdk::FilterOptions::ExitOnEOSE, 636 // 20 is nostr_sdk default
636 ) 637 std::time::Duration::from_secs(GET_EVENTS_TIMEOUT),
637 .await? 638 ReqExitPolicy::ExitOnEOSE,
638 .to_vec(); 639 )
639 Ok(events) 640 .await
641 }))
642 .await;
643
644 // no Event is being mutated, just new items added to the set
645 #[allow(clippy::mutable_key_type)]
646 let mut events: HashSet<Event> = HashSet::new();
647
648 for res in events_res {
649 events.extend(res?);
650 }
651 Ok(events.into_iter().collect())
640} 652}
641 653
642#[derive(Default)] 654#[derive(Default)]
@@ -770,50 +782,81 @@ pub async fn get_events_from_local_cache(
770 git_repo_path: &Path, 782 git_repo_path: &Path,
771 filters: Vec<nostr::Filter>, 783 filters: Vec<nostr::Filter>,
772) -> Result<Vec<nostr::Event>> { 784) -> Result<Vec<nostr::Event>> {
773 Ok(get_local_cache_database(git_repo_path) 785 let db = get_local_cache_database(git_repo_path).await?;
774 .await? 786
775 .query(filters.clone()) 787 let query_results = join_all(filters.into_iter().map(|filter| async {
776 .await 788 db.query(filter)
777 .context( 789 .await
778 "failed to execute query on opened git repo nostr cache database .git/nostr-cache.lmdb", 790 .context("failed to execute query on opened ngit nostr cache database")
779 )? 791 }))
780 .to_vec()) 792 .await;
793
794 // no Event is being mutated, just new items added to the set
795 #[allow(clippy::mutable_key_type)]
796 let mut events: HashSet<Event> = HashSet::new();
797
798 for result in query_results {
799 events.extend(result?);
800 }
801
802 Ok(events.into_iter().collect())
781} 803}
782 804
783pub async fn get_event_from_global_cache( 805pub async fn get_event_from_global_cache(
784 git_repo_path: Option<&Path>, 806 git_repo_path: Option<&Path>,
785 filters: Vec<nostr::Filter>, 807 filters: Vec<nostr::Filter>,
786) -> Result<Vec<nostr::Event>> { 808) -> Result<Vec<nostr::Event>> {
787 Ok(get_global_cache_database(git_repo_path) 809 let db = get_global_cache_database(git_repo_path).await?;
788 .await? 810
789 .query(filters.clone()) 811 let query_results = join_all(filters.into_iter().map(|filter| async {
790 .await 812 db.query(filter)
791 .context("failed to execute query on opened ngit nostr cache database")? 813 .await
792 .to_vec()) 814 .context("failed to execute query on opened ngit nostr cache database")
815 }))
816 .await;
817
818 // no Event is being mutated, just new items added to the set
819 #[allow(clippy::mutable_key_type)]
820 let mut events: HashSet<Event> = HashSet::new();
821
822 for result in query_results {
823 events.extend(result?);
824 }
825
826 Ok(events.into_iter().collect())
793} 827}
794 828
795pub async fn save_event_in_local_cache(git_repo_path: &Path, event: &nostr::Event) -> Result<bool> { 829pub async fn save_event_in_local_cache(git_repo_path: &Path, event: &nostr::Event) -> Result<bool> {
796 get_local_cache_database(git_repo_path) 830 match get_local_cache_database(git_repo_path)
797 .await? 831 .await?
798 .save_event(event) 832 .save_event(event)
799 .await 833 .await
800 .context("failed to save event in local cache") 834 .context("failed to save event in local cache")?
835 {
836 SaveEventStatus::Success => Ok(true),
837 _ => Ok(false),
838 }
801} 839}
802 840
803pub async fn save_event_in_global_cache( 841pub async fn save_event_in_global_cache(
804 git_repo_path: Option<&Path>, 842 git_repo_path: Option<&Path>,
805 event: &nostr::Event, 843 event: &nostr::Event,
806) -> Result<bool> { 844) -> Result<bool> {
807 get_global_cache_database(git_repo_path) 845 match get_global_cache_database(git_repo_path)
808 .await? 846 .await?
809 .save_event(event) 847 .save_event(event)
810 .await 848 .await
811 .context("failed to save event in local cache") 849 .context("failed to save event in local cache")
850 {
851 Ok(SaveEventStatus::Success) => Ok(true),
852 Ok(_) => Ok(false),
853 Err(e) => Err(e).context("failed to save event in local cache"),
854 }
812} 855}
813 856
814pub async fn get_repo_ref_from_cache( 857pub async fn get_repo_ref_from_cache(
815 git_repo_path: Option<&Path>, 858 git_repo_path: Option<&Path>,
816 repo_coordinate: &Coordinate, 859 repo_coordinate: &Nip19Coordinate,
817) -> Result<RepoRef> { 860) -> Result<RepoRef> {
818 let mut maintainers = HashSet::new(); 861 let mut maintainers = HashSet::new();
819 let mut new_coordinate: bool; 862 let mut new_coordinate: bool;
@@ -824,10 +867,12 @@ pub async fn get_repo_ref_from_cache(
824 new_coordinate = false; 867 new_coordinate = false;
825 let repo_events_filter = 868 let repo_events_filter =
826 get_filter_repo_events(&HashSet::from_iter(maintainers.iter().map(|m| { 869 get_filter_repo_events(&HashSet::from_iter(maintainers.iter().map(|m| {
827 Coordinate { 870 Nip19Coordinate {
828 kind: Kind::GitRepoAnnouncement, 871 coordinate: Coordinate {
829 public_key: *m, 872 kind: Kind::GitRepoAnnouncement,
830 identifier: repo_coordinate.identifier.to_string(), 873 public_key: *m,
874 identifier: repo_coordinate.identifier.to_string(),
875 },
831 relays: vec![], 876 relays: vec![],
832 } 877 }
833 }))); 878 })));
@@ -859,19 +904,21 @@ pub async fn get_repo_ref_from_cache(
859 let repo_ref = RepoRef::try_from(( 904 let repo_ref = RepoRef::try_from((
860 repo_events 905 repo_events
861 .first() 906 .first()
862 .context("no repo announcement event found at specified coordinates. if you are the repository maintainer consider running `ngit init` to create one")? 907 .context("no repo announcement event found at specified Nip19Coordinates. if you are the repository maintainer consider running `ngit init` to create one")?
863 .clone(), 908 .clone(),
864 Some(repo_coordinate.public_key), 909 Some(repo_coordinate.public_key),
865 ))?; 910 ))?;
866 911
867 let mut events: HashMap<Coordinate, nostr::Event> = HashMap::new(); 912 let mut events: HashMap<Nip19Coordinate, nostr::Event> = HashMap::new();
868 for m in &maintainers { 913 for m in &maintainers {
869 if let Some(e) = repo_events.iter().find(|e| e.pubkey.eq(m)) { 914 if let Some(e) = repo_events.iter().find(|e| e.pubkey.eq(m)) {
870 events.insert( 915 events.insert(
871 Coordinate { 916 Nip19Coordinate {
872 kind: e.kind, 917 coordinate: Coordinate {
873 identifier: e.tags.identifier().unwrap().to_string(), 918 kind: e.kind,
874 public_key: e.pubkey, 919 identifier: e.tags.identifier().unwrap().to_string(),
920 public_key: e.pubkey,
921 },
875 relays: vec![], 922 relays: vec![],
876 }, 923 },
877 e.clone(), 924 e.clone(),
@@ -912,7 +959,7 @@ pub async fn get_state_from_cache(
912#[allow(clippy::too_many_lines)] 959#[allow(clippy::too_many_lines)]
913async fn create_relays_request( 960async fn create_relays_request(
914 git_repo_path: Option<&Path>, 961 git_repo_path: Option<&Path>,
915 trusted_maintainer_coordinate: Option<&Coordinate>, 962 trusted_maintainer_coordinate: Option<&Nip19Coordinate>,
916 user_profiles: &HashSet<PublicKey>, 963 user_profiles: &HashSet<PublicKey>,
917 fallback_relays: HashSet<RelayUrl>, 964 fallback_relays: HashSet<RelayUrl>,
918) -> Result<FetchRequest> { 965) -> Result<FetchRequest> {
@@ -929,9 +976,9 @@ async fn create_relays_request(
929 }; 976 };
930 977
931 let repo_coordinates = { 978 let repo_coordinates = {
932 // add coordinates of users listed in maintainers to explicitly specified 979 // add Nip19Coordinates of users listed in maintainers to explicitly
933 // coodinates 980 // specified coodinates
934 let mut set: HashSet<Coordinate> = HashSet::new(); 981 let mut set: HashSet<Nip19Coordinate> = HashSet::new();
935 if let Some(trusted_maintainer_coordinate) = trusted_maintainer_coordinate { 982 if let Some(trusted_maintainer_coordinate) = trusted_maintainer_coordinate {
936 set.insert(trusted_maintainer_coordinate.clone()); 983 set.insert(trusted_maintainer_coordinate.clone());
937 } 984 }
@@ -951,10 +998,12 @@ async fn create_relays_request(
951 let repo_coordinates_without_relays = { 998 let repo_coordinates_without_relays = {
952 let mut set = HashSet::new(); 999 let mut set = HashSet::new();
953 for c in &repo_coordinates { 1000 for c in &repo_coordinates {
954 set.insert(Coordinate { 1001 set.insert(Nip19Coordinate {
955 kind: c.kind, 1002 coordinate: Coordinate {
956 identifier: c.identifier.clone(), 1003 kind: c.kind,
957 public_key: c.public_key, 1004 identifier: c.identifier.clone(),
1005 public_key: c.public_key,
1006 },
958 relays: vec![], 1007 relays: vec![],
959 }); 1008 });
960 } 1009 }
@@ -976,11 +1025,11 @@ async fn create_relays_request(
976 for event in &get_events_from_local_cache(git_repo_path, vec![ 1025 for event in &get_events_from_local_cache(git_repo_path, vec![
977 nostr::Filter::default() 1026 nostr::Filter::default()
978 .kinds(vec![Kind::GitPatch]) 1027 .kinds(vec![Kind::GitPatch])
979 .custom_tag( 1028 .custom_tags(
980 SingleLetterTag::lowercase(nostr_sdk::Alphabet::A), 1029 SingleLetterTag::lowercase(nostr_sdk::Alphabet::A),
981 repo_coordinates_without_relays 1030 repo_coordinates_without_relays
982 .iter() 1031 .iter()
983 .map(std::string::ToString::to_string) 1032 .map(|c| c.coordinate.to_string())
984 .collect::<Vec<String>>(), 1033 .collect::<Vec<String>>(),
985 ), 1034 ),
986 ]) 1035 ])
@@ -1153,7 +1202,7 @@ async fn process_fetched_events(
1153 events: Vec<nostr::Event>, 1202 events: Vec<nostr::Event>,
1154 request: &FetchRequest, 1203 request: &FetchRequest,
1155 git_repo_path: Option<&Path>, 1204 git_repo_path: Option<&Path>,
1156 fresh_coordinates: &mut HashSet<Coordinate>, 1205 fresh_coordinates: &mut HashSet<Nip19Coordinate>,
1157 fresh_proposal_roots: &mut HashSet<EventId>, 1206 fresh_proposal_roots: &mut HashSet<EventId>,
1158 fresh_profiles: &mut HashSet<PublicKey>, 1207 fresh_profiles: &mut HashSet<PublicKey>,
1159 report: &mut FetchReport, 1208 report: &mut FetchReport,
@@ -1188,10 +1237,12 @@ async fn process_fetched_events(
1188 }); 1237 });
1189 if update_to_existing { 1238 if update_to_existing {
1190 report.updated_repo_announcements.push(( 1239 report.updated_repo_announcements.push((
1191 Coordinate { 1240 Nip19Coordinate {
1192 kind: event.kind, 1241 coordinate: Coordinate {
1193 public_key: event.pubkey, 1242 kind: event.kind,
1194 identifier: event.tags.identifier().unwrap().to_owned(), 1243 public_key: event.pubkey,
1244 identifier: event.tags.identifier().unwrap().to_owned(),
1245 },
1195 relays: vec![], 1246 relays: vec![],
1196 }, 1247 },
1197 event.created_at, 1248 event.created_at,
@@ -1204,14 +1255,16 @@ async fn process_fetched_events(
1204 .repo_coordinates_without_relays // prexisting maintainers 1255 .repo_coordinates_without_relays // prexisting maintainers
1205 .iter() 1256 .iter()
1206 .map(|(c, _)| c.clone()) 1257 .map(|(c, _)| c.clone())
1207 .collect::<HashSet<Coordinate>>() 1258 .collect::<HashSet<Nip19Coordinate>>()
1208 .union(&report.repo_coordinates_without_relays) // already added maintainers 1259 .union(&report.repo_coordinates_without_relays) // already added maintainers
1209 .any(|c| c.identifier.eq(&repo_ref.identifier) && m.eq(&c.public_key)) 1260 .any(|c| c.identifier.eq(&repo_ref.identifier) && m.eq(&c.public_key))
1210 { 1261 {
1211 let c = Coordinate { 1262 let c = Nip19Coordinate {
1212 kind: event.kind, 1263 coordinate: Coordinate {
1213 public_key: *m, 1264 kind: event.kind,
1214 identifier: repo_ref.identifier.clone(), 1265 public_key: *m,
1266 identifier: repo_ref.identifier.clone(),
1267 },
1215 relays: vec![], 1268 relays: vec![],
1216 }; 1269 };
1217 fresh_coordinates.insert(c.clone()); 1270 fresh_coordinates.insert(c.clone());
@@ -1343,7 +1396,7 @@ pub fn consolidate_fetch_reports(reports: Vec<Result<FetchReport>>) -> FetchRepo
1343 report 1396 report
1344} 1397}
1345pub fn get_fetch_filters( 1398pub fn get_fetch_filters(
1346 repo_coordinates: &HashSet<Coordinate>, 1399 repo_coordinates: &HashSet<Nip19Coordinate>,
1347 proposal_ids: &HashSet<EventId>, 1400 proposal_ids: &HashSet<EventId>,
1348 required_profiles: &HashSet<PublicKey>, 1401 required_profiles: &HashSet<PublicKey>,
1349) -> Vec<nostr::Filter> { 1402) -> Vec<nostr::Filter> {
@@ -1356,11 +1409,11 @@ pub fn get_fetch_filters(
1356 get_filter_repo_events(repo_coordinates), 1409 get_filter_repo_events(repo_coordinates),
1357 nostr::Filter::default() 1410 nostr::Filter::default()
1358 .kinds(vec![Kind::GitPatch, Kind::EventDeletion]) 1411 .kinds(vec![Kind::GitPatch, Kind::EventDeletion])
1359 .custom_tag( 1412 .custom_tags(
1360 SingleLetterTag::lowercase(nostr_sdk::Alphabet::A), 1413 SingleLetterTag::lowercase(nostr_sdk::Alphabet::A),
1361 repo_coordinates 1414 repo_coordinates
1362 .iter() 1415 .iter()
1363 .map(std::string::ToString::to_string) 1416 .map(|c| c.coordinate.to_string())
1364 .collect::<Vec<String>>(), 1417 .collect::<Vec<String>>(),
1365 ), 1418 ),
1366 ] 1419 ]
@@ -1383,7 +1436,7 @@ pub fn get_fetch_filters(
1383 .concat() 1436 .concat()
1384} 1437}
1385 1438
1386pub fn get_filter_repo_events(repo_coordinates: &HashSet<Coordinate>) -> nostr::Filter { 1439pub fn get_filter_repo_events(repo_coordinates: &HashSet<Nip19Coordinate>) -> nostr::Filter {
1387 nostr::Filter::default() 1440 nostr::Filter::default()
1388 .kind(Kind::GitRepoAnnouncement) 1441 .kind(Kind::GitRepoAnnouncement)
1389 .identifiers( 1442 .identifiers(
@@ -1401,7 +1454,7 @@ pub fn get_filter_repo_events(repo_coordinates: &HashSet<Coordinate>) -> nostr::
1401} 1454}
1402 1455
1403pub static STATE_KIND: nostr::Kind = Kind::Custom(30618); 1456pub static STATE_KIND: nostr::Kind = Kind::Custom(30618);
1404pub fn get_filter_state_events(repo_coordinates: &HashSet<Coordinate>) -> nostr::Filter { 1457pub fn get_filter_state_events(repo_coordinates: &HashSet<Nip19Coordinate>) -> nostr::Filter {
1405 nostr::Filter::default() 1458 nostr::Filter::default()
1406 .kind(STATE_KIND) 1459 .kind(STATE_KIND)
1407 .identifiers( 1460 .identifiers(
@@ -1426,8 +1479,8 @@ pub fn get_filter_contributor_profiles(contributors: HashSet<PublicKey>) -> nost
1426 1479
1427#[derive(Default)] 1480#[derive(Default)]
1428pub struct FetchReport { 1481pub struct FetchReport {
1429 repo_coordinates_without_relays: HashSet<Coordinate>, 1482 repo_coordinates_without_relays: HashSet<Nip19Coordinate>,
1430 updated_repo_announcements: Vec<(Coordinate, Timestamp)>, 1483 updated_repo_announcements: Vec<(Nip19Coordinate, Timestamp)>,
1431 updated_state: Option<(Timestamp, EventId)>, 1484 updated_state: Option<(Timestamp, EventId)>,
1432 proposals: HashSet<EventId>, 1485 proposals: HashSet<EventId>,
1433 /// commits against existing propoals 1486 /// commits against existing propoals
@@ -1518,7 +1571,7 @@ pub struct FetchRequest {
1518 repo_relays: HashSet<RelayUrl>, 1571 repo_relays: HashSet<RelayUrl>,
1519 selected_relay: Option<RelayUrl>, 1572 selected_relay: Option<RelayUrl>,
1520 relay_column_width: usize, 1573 relay_column_width: usize,
1521 repo_coordinates_without_relays: Vec<(Coordinate, Option<Timestamp>)>, 1574 repo_coordinates_without_relays: Vec<(Nip19Coordinate, Option<Timestamp>)>,
1522 state: Option<(Timestamp, EventId)>, 1575 state: Option<(Timestamp, EventId)>,
1523 proposals: HashSet<EventId>, 1576 proposals: HashSet<EventId>,
1524 contributors: HashSet<PublicKey>, 1577 contributors: HashSet<PublicKey>,
@@ -1532,7 +1585,7 @@ pub async fn fetching_with_report(
1532 git_repo_path: &Path, 1585 git_repo_path: &Path,
1533 #[cfg(test)] client: &crate::client::MockConnect, 1586 #[cfg(test)] client: &crate::client::MockConnect,
1534 #[cfg(not(test))] client: &Client, 1587 #[cfg(not(test))] client: &Client,
1535 trusted_maintainer_coordinate: &Coordinate, 1588 trusted_maintainer_coordinate: &Nip19Coordinate,
1536) -> Result<FetchReport> { 1589) -> Result<FetchReport> {
1537 let term = console::Term::stderr(); 1590 let term = console::Term::stderr();
1538 term.write_line("fetching updates...")?; 1591 term.write_line("fetching updates...")?;
@@ -1557,16 +1610,16 @@ pub async fn fetching_with_report(
1557 1610
1558pub async fn get_proposals_and_revisions_from_cache( 1611pub async fn get_proposals_and_revisions_from_cache(
1559 git_repo_path: &Path, 1612 git_repo_path: &Path,
1560 repo_coordinates: HashSet<Coordinate>, 1613 repo_coordinates: HashSet<Nip19Coordinate>,
1561) -> Result<Vec<nostr::Event>> { 1614) -> Result<Vec<nostr::Event>> {
1562 let mut proposals = get_events_from_local_cache(git_repo_path, vec![ 1615 let mut proposals = get_events_from_local_cache(git_repo_path, vec![
1563 nostr::Filter::default() 1616 nostr::Filter::default()
1564 .kind(nostr::Kind::GitPatch) 1617 .kind(nostr::Kind::GitPatch)
1565 .custom_tag( 1618 .custom_tags(
1566 nostr::SingleLetterTag::lowercase(nostr_sdk::Alphabet::A), 1619 nostr::SingleLetterTag::lowercase(nostr_sdk::Alphabet::A),
1567 repo_coordinates 1620 repo_coordinates
1568 .iter() 1621 .iter()
1569 .map(std::string::ToString::to_string) 1622 .map(|c| c.coordinate.to_string())
1570 .collect::<Vec<String>>(), 1623 .collect::<Vec<String>>(),
1571 ), 1624 ),
1572 ]) 1625 ])
diff --git a/src/lib/git/mod.rs b/src/lib/git/mod.rs
index 2b78f38..1329820 100644
--- a/src/lib/git/mod.rs
+++ b/src/lib/git/mod.rs
@@ -1141,7 +1141,7 @@ mod tests {
1141 fn test(time: git2::Time) -> Result<()> { 1141 fn test(time: git2::Time) -> Result<()> {
1142 assert_eq!( 1142 assert_eq!(
1143 extract_sig_from_patch_tags( 1143 extract_sig_from_patch_tags(
1144 &Tags::new(vec![nostr::Tag::custom( 1144 &Tags::from_list(vec![nostr::Tag::custom(
1145 nostr::TagKind::Custom("author".to_string().into()), 1145 nostr::TagKind::Custom("author".to_string().into()),
1146 prep(&time)?, 1146 prep(&time)?,
1147 )]), 1147 )]),
diff --git a/src/lib/git/nostr_url.rs b/src/lib/git/nostr_url.rs
index ae0ac5f..54be292 100644
--- a/src/lib/git/nostr_url.rs
+++ b/src/lib/git/nostr_url.rs
@@ -2,8 +2,8 @@ use core::fmt;
2use std::{collections::HashMap, str::FromStr}; 2use std::{collections::HashMap, str::FromStr};
3 3
4use anyhow::{Context, Error, Result, anyhow, bail}; 4use anyhow::{Context, Error, Result, anyhow, bail};
5use nostr::nips::{nip01::Coordinate, nip05}; 5use nostr::nips::{nip01::Coordinate, nip05, nip19::Nip19Coordinate};
6use nostr_sdk::{PublicKey, RelayUrl, ToBech32, Url}; 6use nostr_sdk::{FromBech32, PublicKey, RelayUrl, ToBech32, Url};
7 7
8use super::{Repo, get_git_config_item, save_git_config_item}; 8use super::{Repo, get_git_config_item, save_git_config_item};
9 9
@@ -58,7 +58,7 @@ impl FromStr for ServerProtocol {
58#[derive(Debug, PartialEq, Clone)] 58#[derive(Debug, PartialEq, Clone)]
59pub struct NostrUrlDecoded { 59pub struct NostrUrlDecoded {
60 pub original_string: String, 60 pub original_string: String,
61 pub coordinate: Coordinate, 61 pub coordinate: Nip19Coordinate,
62 pub protocol: Option<ServerProtocol>, 62 pub protocol: Option<ServerProtocol>,
63 pub user: Option<String>, 63 pub user: Option<String>,
64 pub nip05: Option<String>, 64 pub nip05: Option<String>,
@@ -166,7 +166,7 @@ impl NostrUrlDecoded {
166 // extract naddr npub/<optional-relays>/identifer 166 // extract naddr npub/<optional-relays>/identifer
167 let part = parts.first().context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?; 167 let part = parts.first().context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?;
168 // naddr used 168 // naddr used
169 let coordinate = if let Ok(coordinate) = Coordinate::parse(part) { 169 let coordinate = if let Ok(coordinate) = Nip19Coordinate::from_bech32(part) {
170 if coordinate.kind.eq(&nostr_sdk::Kind::GitRepoAnnouncement) { 170 if coordinate.kind.eq(&nostr_sdk::Kind::GitRepoAnnouncement) {
171 coordinate 171 coordinate
172 } else { 172 } else {
@@ -225,10 +225,12 @@ impl NostrUrlDecoded {
225 } 225 }
226 } 226 }
227 }; 227 };
228 Coordinate { 228 Nip19Coordinate {
229 identifier, 229 coordinate: Coordinate {
230 public_key, 230 identifier,
231 kind: nostr_sdk::Kind::GitRepoAnnouncement, 231 public_key,
232 kind: nostr_sdk::Kind::GitRepoAnnouncement,
233 },
232 relays, 234 relays,
233 } 235 }
234 }; 236 };
@@ -959,7 +961,7 @@ mod tests {
959 } 961 }
960 } 962 }
961 mod nostr_git_url_format { 963 mod nostr_git_url_format {
962 use nostr::nips::nip01::Coordinate; 964 use nostr::nips::nip19::Nip19Coordinate;
963 use nostr_sdk::PublicKey; 965 use nostr_sdk::PublicKey;
964 966
965 use super::*; 967 use super::*;
@@ -970,13 +972,15 @@ mod tests {
970 assert_eq!( 972 assert_eq!(
971 format!("{}", NostrUrlDecoded { 973 format!("{}", NostrUrlDecoded {
972 original_string: String::new(), 974 original_string: String::new(),
973 coordinate: Coordinate { 975 coordinate: Nip19Coordinate {
974 identifier: "ngit".to_string(), 976 coordinate: Coordinate {
975 public_key: PublicKey::parse( 977 identifier: "ngit".to_string(),
976 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr", 978 public_key: PublicKey::parse(
977 ) 979 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr",
978 .unwrap(), 980 )
979 kind: nostr_sdk::Kind::GitRepoAnnouncement, 981 .unwrap(),
982 kind: nostr_sdk::Kind::GitRepoAnnouncement,
983 },
980 relays: vec![RelayUrl::parse("wss://nos.lol").unwrap()], 984 relays: vec![RelayUrl::parse("wss://nos.lol").unwrap()],
981 }, 985 },
982 protocol: None, 986 protocol: None,
@@ -993,13 +997,15 @@ mod tests {
993 assert_eq!( 997 assert_eq!(
994 format!("{}", NostrUrlDecoded { 998 format!("{}", NostrUrlDecoded {
995 original_string: String::new(), 999 original_string: String::new(),
996 coordinate: Coordinate { 1000 coordinate: Nip19Coordinate {
997 identifier: "ngit".to_string(), 1001 coordinate: Coordinate {
998 public_key: PublicKey::parse( 1002 identifier: "ngit".to_string(),
999 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr", 1003 public_key: PublicKey::parse(
1000 ) 1004 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr",
1001 .unwrap(), 1005 )
1002 kind: nostr_sdk::Kind::GitRepoAnnouncement, 1006 .unwrap(),
1007 kind: nostr_sdk::Kind::GitRepoAnnouncement,
1008 },
1003 relays: vec![], 1009 relays: vec![],
1004 }, 1010 },
1005 protocol: None, 1011 protocol: None,
@@ -1016,13 +1022,15 @@ mod tests {
1016 assert_eq!( 1022 assert_eq!(
1017 format!("{}", NostrUrlDecoded { 1023 format!("{}", NostrUrlDecoded {
1018 original_string: String::new(), 1024 original_string: String::new(),
1019 coordinate: Coordinate { 1025 coordinate: Nip19Coordinate {
1020 identifier: "ngit".to_string(), 1026 coordinate: Coordinate {
1021 public_key: PublicKey::parse( 1027 identifier: "ngit".to_string(),
1022 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr", 1028 public_key: PublicKey::parse(
1023 ) 1029 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr",
1024 .unwrap(), 1030 )
1025 kind: nostr_sdk::Kind::GitRepoAnnouncement, 1031 .unwrap(),
1032 kind: nostr_sdk::Kind::GitRepoAnnouncement,
1033 },
1026 relays: vec![RelayUrl::parse("wss://nos.lol").unwrap()], 1034 relays: vec![RelayUrl::parse("wss://nos.lol").unwrap()],
1027 }, 1035 },
1028 protocol: Some(ServerProtocol::Ssh), 1036 protocol: Some(ServerProtocol::Ssh),
@@ -1039,13 +1047,15 @@ mod tests {
1039 assert_eq!( 1047 assert_eq!(
1040 format!("{}", NostrUrlDecoded { 1048 format!("{}", NostrUrlDecoded {
1041 original_string: String::new(), 1049 original_string: String::new(),
1042 coordinate: Coordinate { 1050 coordinate: Nip19Coordinate {
1043 identifier: "ngit".to_string(), 1051 coordinate: Coordinate {
1044 public_key: PublicKey::parse( 1052 identifier: "ngit".to_string(),
1045 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr", 1053 public_key: PublicKey::parse(
1046 ) 1054 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr",
1047 .unwrap(), 1055 )
1048 kind: nostr_sdk::Kind::GitRepoAnnouncement, 1056 .unwrap(),
1057 kind: nostr_sdk::Kind::GitRepoAnnouncement,
1058 },
1049 relays: vec![RelayUrl::parse("wss://nos.lol").unwrap()], 1059 relays: vec![RelayUrl::parse("wss://nos.lol").unwrap()],
1050 }, 1060 },
1051 protocol: Some(ServerProtocol::Ssh), 1061 protocol: Some(ServerProtocol::Ssh),
@@ -1061,14 +1071,16 @@ mod tests {
1061 mod nostr_url_decoded_paramemters_from_str { 1071 mod nostr_url_decoded_paramemters_from_str {
1062 use super::*; 1072 use super::*;
1063 1073
1064 fn get_model_coordinate(relays: bool) -> Coordinate { 1074 fn get_model_coordinate(relays: bool) -> Nip19Coordinate {
1065 Coordinate { 1075 Nip19Coordinate {
1066 identifier: "ngit".to_string(), 1076 coordinate: Coordinate {
1067 public_key: PublicKey::parse( 1077 identifier: "ngit".to_string(),
1068 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr", 1078 public_key: PublicKey::parse(
1069 ) 1079 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr",
1070 .unwrap(), 1080 )
1071 kind: nostr_sdk::Kind::GitRepoAnnouncement, 1081 .unwrap(),
1082 kind: nostr_sdk::Kind::GitRepoAnnouncement,
1083 },
1072 relays: if relays { 1084 relays: if relays {
1073 vec![RelayUrl::parse("wss://nos.lol").unwrap()] 1085 vec![RelayUrl::parse("wss://nos.lol").unwrap()]
1074 } else { 1086 } else {
@@ -1084,13 +1096,15 @@ mod tests {
1084 NostrUrlDecoded::parse_and_resolve(&url, &None).await?, 1096 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
1085 NostrUrlDecoded { 1097 NostrUrlDecoded {
1086 original_string: url.clone(), 1098 original_string: url.clone(),
1087 coordinate: Coordinate { 1099 coordinate: Nip19Coordinate {
1088 identifier: "ngit".to_string(), 1100 coordinate: Coordinate {
1089 public_key: PublicKey::parse( 1101 identifier: "ngit".to_string(),
1090 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr", 1102 public_key: PublicKey::parse(
1091 ) 1103 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr",
1092 .unwrap(), 1104 )
1093 kind: nostr_sdk::Kind::GitRepoAnnouncement, 1105 .unwrap(),
1106 kind: nostr_sdk::Kind::GitRepoAnnouncement,
1107 },
1094 relays: vec![RelayUrl::parse("wss://nos.lol").unwrap()], /* wont add the 1108 relays: vec![RelayUrl::parse("wss://nos.lol").unwrap()], /* wont add the
1095 * slash */ 1109 * slash */
1096 }, 1110 },
@@ -1172,13 +1186,15 @@ mod tests {
1172 NostrUrlDecoded::parse_and_resolve(&url, &None).await?, 1186 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
1173 NostrUrlDecoded { 1187 NostrUrlDecoded {
1174 original_string: url.clone(), 1188 original_string: url.clone(),
1175 coordinate: Coordinate { 1189 coordinate: Nip19Coordinate {
1176 identifier: "ngit".to_string(), 1190 coordinate: Coordinate {
1177 public_key: PublicKey::parse( 1191 identifier: "ngit".to_string(),
1178 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr", 1192 public_key: PublicKey::parse(
1179 ) 1193 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr",
1180 .unwrap(), 1194 )
1181 kind: nostr_sdk::Kind::GitRepoAnnouncement, 1195 .unwrap(),
1196 kind: nostr_sdk::Kind::GitRepoAnnouncement,
1197 },
1182 relays: vec![ 1198 relays: vec![
1183 RelayUrl::parse("wss://nos.lol/").unwrap(), 1199 RelayUrl::parse("wss://nos.lol/").unwrap(),
1184 RelayUrl::parse("wss://relay.damus.io/").unwrap(), 1200 RelayUrl::parse("wss://relay.damus.io/").unwrap(),
@@ -1274,13 +1290,15 @@ mod tests {
1274 NostrUrlDecoded::parse_and_resolve(&url, &None).await?, 1290 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
1275 NostrUrlDecoded { 1291 NostrUrlDecoded {
1276 original_string: url.clone(), 1292 original_string: url.clone(),
1277 coordinate: Coordinate { 1293 coordinate: Nip19Coordinate {
1278 identifier: "ngit".to_string(), 1294 coordinate: Coordinate {
1279 public_key: PublicKey::parse( 1295 identifier: "ngit".to_string(),
1280 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr", 1296 public_key: PublicKey::parse(
1281 ) 1297 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr",
1282 .unwrap(), 1298 )
1283 kind: nostr_sdk::Kind::GitRepoAnnouncement, 1299 .unwrap(),
1300 kind: nostr_sdk::Kind::GitRepoAnnouncement,
1301 },
1284 relays: vec![ 1302 relays: vec![
1285 RelayUrl::parse("wss://nos.lol/").unwrap(), 1303 RelayUrl::parse("wss://nos.lol/").unwrap(),
1286 RelayUrl::parse("wss://relay.damus.io/").unwrap(), 1304 RelayUrl::parse("wss://relay.damus.io/").unwrap(),
diff --git a/src/lib/git_events.rs b/src/lib/git_events.rs
index 559155a..2b3df42 100644
--- a/src/lib/git_events.rs
+++ b/src/lib/git_events.rs
@@ -3,7 +3,7 @@ use std::{str::FromStr, sync::Arc};
3use anyhow::{Context, Result, bail}; 3use anyhow::{Context, Result, bail};
4use nostr::nips::{nip01::Coordinate, nip10::Marker, nip19::Nip19}; 4use nostr::nips::{nip01::Coordinate, nip10::Marker, nip19::Nip19};
5use nostr_sdk::{ 5use nostr_sdk::{
6 Event, EventBuilder, EventId, FromBech32, Kind, NostrSigner, PublicKey, RelayUrl, Tag, TagKind, 6 Event, EventBuilder, EventId, FromBech32, Kind, NostrSigner, PublicKey, Tag, TagKind,
7 TagStandard, hashes::sha1::Hash as Sha1Hash, 7 TagStandard, hashes::sha1::Hash as Sha1Hash,
8}; 8};
9 9
@@ -115,11 +115,14 @@ pub async fn generate_patch_event(
115 .maintainers 115 .maintainers
116 .iter() 116 .iter()
117 .map(|m| { 117 .map(|m| {
118 Tag::coordinate(Coordinate { 118 Tag::from_standardized(TagStandard::Coordinate {
119 kind: nostr::Kind::GitRepoAnnouncement, 119 coordinate: Coordinate {
120 public_key: *m, 120 kind: nostr::Kind::GitRepoAnnouncement,
121 identifier: repo_ref.identifier.to_string(), 121 public_key: *m,
122 relays: repo_ref.relays.clone(), 122 identifier: repo_ref.identifier.to_string(),
123 },
124 relay_url: repo_ref.relays.first().cloned(),
125 uppercase: false,
123 }) 126 })
124 }) 127 })
125 .collect::<Vec<Tag>>(), 128 .collect::<Vec<Tag>>(),
@@ -257,12 +260,12 @@ pub fn event_tag_from_nip19_or_hex(
257 PromptInputParms::default().with_prompt(format!("{reference_name} reference")), 260 PromptInputParms::default().with_prompt(format!("{reference_name} reference")),
258 )?; 261 )?;
259 } 262 }
260 if let Ok(nip19) = Nip19::from_bech32(bech32.clone()) { 263 if let Ok(nip19) = Nip19::from_bech32(&bech32) {
261 match nip19 { 264 match nip19 {
262 Nip19::Event(n) => { 265 Nip19::Event(n) => {
263 break Ok(Tag::from_standardized(nostr_sdk::TagStandard::Event { 266 break Ok(Tag::from_standardized(nostr_sdk::TagStandard::Event {
264 event_id: n.event_id, 267 event_id: n.event_id,
265 relay_url: n.relays.first().and_then(|url| RelayUrl::parse(url).ok()), 268 relay_url: n.relays.first().cloned(),
266 marker: Some(marker), 269 marker: Some(marker),
267 public_key: None, 270 public_key: None,
268 uppercase: false, 271 uppercase: false,
@@ -278,7 +281,11 @@ pub fn event_tag_from_nip19_or_hex(
278 })); 281 }));
279 } 282 }
280 Nip19::Coordinate(coordinate) => { 283 Nip19::Coordinate(coordinate) => {
281 break Ok(Tag::coordinate(coordinate)); 284 break Ok(Tag::from_standardized(TagStandard::Coordinate {
285 coordinate: coordinate.coordinate,
286 relay_url: coordinate.relays.first().cloned(),
287 uppercase: false,
288 }));
282 } 289 }
283 Nip19::Profile(profile) => { 290 Nip19::Profile(profile) => {
284 if allow_npub_reference { 291 if allow_npub_reference {
@@ -338,12 +345,17 @@ pub async fn generate_cover_letter_and_patch_events(
338 )) 345 ))
339 .tags( 346 .tags(
340 [ 347 [
341 repo_ref.maintainers.iter().map(|m| Tag::coordinate(Coordinate { 348 repo_ref.maintainers.iter().map(|m|
342 kind: nostr::Kind::GitRepoAnnouncement, 349 Tag::from_standardized(TagStandard::Coordinate {
343 public_key: *m, 350 coordinate: Coordinate {
344 identifier: repo_ref.identifier.to_string(), 351 kind: nostr::Kind::GitRepoAnnouncement,
345 relays: repo_ref.relays.clone(), 352 public_key: *m,
346 })).collect::<Vec<Tag>>(), 353 identifier: repo_ref.identifier.to_string(),
354 },
355 relay_url: repo_ref.relays.first().cloned(),
356 uppercase: false,
357 })
358 ).collect::<Vec<Tag>>(),
347 vec![ 359 vec![
348 Tag::from_standardized(TagStandard::Reference(format!("{root_commit}"))), 360 Tag::from_standardized(TagStandard::Reference(format!("{root_commit}"))),
349 Tag::hashtag("cover-letter"), 361 Tag::hashtag("cover-letter"),
diff --git a/src/lib/login/existing.rs b/src/lib/login/existing.rs
index efe187e..e60621d 100644
--- a/src/lib/login/existing.rs
+++ b/src/lib/login/existing.rs
@@ -208,7 +208,7 @@ async fn get_signer(
208 Duration::from_secs(10 * 60), 208 Duration::from_secs(10 * 60),
209 None, 209 None,
210 )?; 210 )?;
211 if let Some(public_key) = npub.clone().and_then(|npub| PublicKey::parse(npub).ok()) { 211 if let Some(public_key) = npub.clone().and_then(|npub| PublicKey::parse(&npub).ok()) {
212 s.non_secure_set_user_public_key(public_key)?; 212 s.non_secure_set_user_public_key(public_key)?;
213 let signer: Arc<dyn NostrSigner> = Arc::new(s); 213 let signer: Arc<dyn NostrSigner> = Arc::new(s);
214 Ok((signer, public_key)) 214 Ok((signer, public_key))
diff --git a/src/lib/login/fresh.rs b/src/lib/login/fresh.rs
index 635c0b3..76998ff 100644
--- a/src/lib/login/fresh.rs
+++ b/src/lib/login/fresh.rs
@@ -372,7 +372,7 @@ pub fn generate_nostr_connect_app(
372 client 372 client
373 .get_fallback_signer_relays() 373 .get_fallback_signer_relays()
374 .iter() 374 .iter()
375 .flat_map(RelayUrl::parse) 375 .flat_map(|s| RelayUrl::parse(s))
376 .collect::<Vec<RelayUrl>>() 376 .collect::<Vec<RelayUrl>>()
377 } else { 377 } else {
378 vec![] 378 vec![]
diff --git a/src/lib/login/key_encryption.rs b/src/lib/login/key_encryption.rs
index efb38d1..d57b3b5 100644
--- a/src/lib/login/key_encryption.rs
+++ b/src/lib/login/key_encryption.rs
@@ -7,7 +7,7 @@ pub fn decrypt_key(encrypted_key: &str, password: &str) -> Result<nostr::Keys> {
7 if encrypted_key.log_n() > 14 { 7 if encrypted_key.log_n() > 14 {
8 println!("this may take a few seconds..."); 8 println!("this may take a few seconds...");
9 } 9 }
10 Ok(nostr::Keys::new(encrypted_key.to_secret_key(password)?)) 10 Ok(nostr::Keys::new(encrypted_key.decrypt(password)?))
11} 11}
12 12
13#[cfg(test)] 13#[cfg(test)]
diff --git a/src/lib/login/mod.rs b/src/lib/login/mod.rs
index a1c45d5..c37375f 100644
--- a/src/lib/login/mod.rs
+++ b/src/lib/login/mod.rs
@@ -93,7 +93,7 @@ pub async fn get_likely_logged_in_user(git_repo_path: &Path) -> Result<Option<Pu
93 let git_repo = Repo::from_path(&git_repo_path.to_path_buf())?; 93 let git_repo = Repo::from_path(&git_repo_path.to_path_buf())?;
94 Ok( 94 Ok(
95 if let Some(npub) = git_repo.get_git_config_item("nostr.npub", None)? { 95 if let Some(npub) = git_repo.get_git_config_item("nostr.npub", None)? {
96 if let Ok(pubic_key) = PublicKey::parse(npub) { 96 if let Ok(pubic_key) = PublicKey::parse(&npub) {
97 Some(pubic_key) 97 Some(pubic_key)
98 } else { 98 } else {
99 None 99 None
@@ -107,7 +107,7 @@ pub async fn get_likely_logged_in_user(git_repo_path: &Path) -> Result<Option<Pu
107pub fn get_curent_user(git_repo: &Repo) -> Result<Option<PublicKey>> { 107pub fn get_curent_user(git_repo: &Repo) -> Result<Option<PublicKey>> {
108 Ok( 108 Ok(
109 if let Some(npub) = git_repo.get_git_config_item("nostr.npub", None)? { 109 if let Some(npub) = git_repo.get_git_config_item("nostr.npub", None)? {
110 if let Ok(public_key) = PublicKey::parse(npub) { 110 if let Ok(public_key) = PublicKey::parse(&npub) {
111 Some(public_key) 111 Some(public_key)
112 } else { 112 } else {
113 None 113 None
diff --git a/src/lib/repo_ref.rs b/src/lib/repo_ref.rs
index 38f6774..b21a911 100644
--- a/src/lib/repo_ref.rs
+++ b/src/lib/repo_ref.rs
@@ -8,7 +8,10 @@ use std::{
8 8
9use anyhow::{Context, Result, bail}; 9use anyhow::{Context, Result, bail};
10use console::Style; 10use console::Style;
11use nostr::{FromBech32, PublicKey, Tag, TagStandard, ToBech32, nips::nip01::Coordinate}; 11use nostr::{
12 FromBech32, PublicKey, Tag, TagStandard, ToBech32,
13 nips::{nip01::Coordinate, nip19::Nip19Coordinate},
14};
12use nostr_sdk::{Kind, NostrSigner, RelayUrl, Timestamp}; 15use nostr_sdk::{Kind, NostrSigner, RelayUrl, Timestamp};
13use serde::{Deserialize, Serialize}; 16use serde::{Deserialize, Serialize};
14 17
@@ -37,7 +40,7 @@ pub struct RepoRef {
37 pub relays: Vec<RelayUrl>, 40 pub relays: Vec<RelayUrl>,
38 pub maintainers: Vec<PublicKey>, 41 pub maintainers: Vec<PublicKey>,
39 pub trusted_maintainer: PublicKey, 42 pub trusted_maintainer: PublicKey,
40 pub events: HashMap<Coordinate, nostr::Event>, 43 pub events: HashMap<Nip19Coordinate, nostr::Event>,
41 pub nostr_git_url: Option<NostrUrlDecoded>, 44 pub nostr_git_url: Option<NostrUrlDecoded>,
42} 45}
43 46
@@ -119,10 +122,12 @@ impl TryFrom<(nostr::Event, Option<PublicKey>)> for RepoRef {
119 } 122 }
120 r.events = HashMap::new(); 123 r.events = HashMap::new();
121 r.events.insert( 124 r.events.insert(
122 Coordinate { 125 Nip19Coordinate {
123 kind: event.kind, 126 coordinate: Coordinate {
124 identifier: event.tags.identifier().unwrap().to_string(), 127 kind: event.kind,
125 public_key: event.pubkey, 128 identifier: event.tags.identifier().unwrap().to_string(),
129 public_key: event.pubkey,
130 },
126 relays: vec![], 131 relays: vec![],
127 }, 132 },
128 event, 133 event,
@@ -195,20 +200,24 @@ impl RepoRef {
195 .context("failed to create repository reference event") 200 .context("failed to create repository reference event")
196 } 201 }
197 /// coordinates without relay hints 202 /// coordinates without relay hints
198 pub fn coordinates(&self) -> HashSet<Coordinate> { 203 pub fn coordinates(&self) -> HashSet<Nip19Coordinate> {
199 let mut res = HashSet::new(); 204 let mut res = HashSet::new();
200 res.insert(Coordinate { 205 res.insert(Nip19Coordinate {
201 kind: Kind::GitRepoAnnouncement, 206 coordinate: Coordinate {
202 public_key: self.trusted_maintainer, 207 kind: Kind::GitRepoAnnouncement,
203 identifier: self.identifier.clone(), 208 public_key: self.trusted_maintainer,
209 identifier: self.identifier.clone(),
210 },
204 relays: vec![], 211 relays: vec![],
205 }); 212 });
206 213
207 for m in &self.maintainers { 214 for m in &self.maintainers {
208 res.insert(Coordinate { 215 res.insert(Nip19Coordinate {
209 kind: Kind::GitRepoAnnouncement, 216 coordinate: Coordinate {
210 public_key: *m, 217 kind: Kind::GitRepoAnnouncement,
211 identifier: self.identifier.clone(), 218 public_key: *m,
219 identifier: self.identifier.clone(),
220 },
212 relays: vec![], 221 relays: vec![],
213 }); 222 });
214 } 223 }
@@ -216,11 +225,13 @@ impl RepoRef {
216 } 225 }
217 226
218 /// coordinates without relay hints 227 /// coordinates without relay hints
219 pub fn coordinate_with_hint(&self) -> Coordinate { 228 pub fn coordinate_with_hint(&self) -> Nip19Coordinate {
220 Coordinate { 229 Nip19Coordinate {
221 kind: Kind::GitRepoAnnouncement, 230 coordinate: Coordinate {
222 public_key: self.trusted_maintainer, 231 kind: Kind::GitRepoAnnouncement,
223 identifier: self.identifier.clone(), 232 public_key: self.trusted_maintainer,
233 identifier: self.identifier.clone(),
234 },
224 relays: if let Some(relay) = self.relays.first() { 235 relays: if let Some(relay) = self.relays.first() {
225 vec![relay.clone()] 236 vec![relay.clone()]
226 } else { 237 } else {
@@ -230,11 +241,11 @@ impl RepoRef {
230 } 241 }
231 242
232 /// coordinates without relay hints 243 /// coordinates without relay hints
233 pub fn coordinates_with_timestamps(&self) -> Vec<(Coordinate, Option<Timestamp>)> { 244 pub fn coordinates_with_timestamps(&self) -> Vec<(Nip19Coordinate, Option<Timestamp>)> {
234 self.coordinates() 245 self.coordinates()
235 .iter() 246 .iter()
236 .map(|c| (c.clone(), self.events.get(c).map(|e| e.created_at))) 247 .map(|c| (c.clone(), self.events.get(c).map(|e| e.created_at)))
237 .collect::<Vec<(Coordinate, Option<Timestamp>)>>() 248 .collect::<Vec<(Nip19Coordinate, Option<Timestamp>)>>()
238 } 249 }
239 250
240 pub fn set_nostr_git_url(&mut self, nostr_git_url: NostrUrlDecoded) { 251 pub fn set_nostr_git_url(&mut self, nostr_git_url: NostrUrlDecoded) {
@@ -264,7 +275,7 @@ pub async fn get_repo_coordinates_when_remote_unknown(
264 git_repo: &Repo, 275 git_repo: &Repo,
265 #[cfg(test)] client: &crate::client::MockConnect, 276 #[cfg(test)] client: &crate::client::MockConnect,
266 #[cfg(not(test))] client: &Client, 277 #[cfg(not(test))] client: &Client,
267) -> Result<Coordinate> { 278) -> Result<Nip19Coordinate> {
268 if let Ok(c) = try_and_get_repo_coordinates_when_remote_unknown(git_repo).await { 279 if let Ok(c) = try_and_get_repo_coordinates_when_remote_unknown(git_repo).await {
269 Ok(c) 280 Ok(c)
270 } else { 281 } else {
@@ -274,7 +285,7 @@ pub async fn get_repo_coordinates_when_remote_unknown(
274 285
275pub async fn try_and_get_repo_coordinates_when_remote_unknown( 286pub async fn try_and_get_repo_coordinates_when_remote_unknown(
276 git_repo: &Repo, 287 git_repo: &Repo,
277) -> Result<Coordinate> { 288) -> Result<Nip19Coordinate> {
278 let remote_coordinates = get_repo_coordinates_from_nostr_remotes(git_repo).await?; 289 let remote_coordinates = get_repo_coordinates_from_nostr_remotes(git_repo).await?;
279 if remote_coordinates.is_empty() { 290 if remote_coordinates.is_empty() {
280 if let Ok(c) = get_repo_coordinates_from_git_config(git_repo) { 291 if let Ok(c) = get_repo_coordinates_from_git_config(git_repo) {
@@ -318,7 +329,7 @@ pub async fn try_and_get_repo_coordinates_when_remote_unknown(
318 329
319async fn get_nostr_git_remote_selection_labels( 330async fn get_nostr_git_remote_selection_labels(
320 git_repo: &Repo, 331 git_repo: &Repo,
321 remote_coordinates: &HashMap<String, Coordinate>, 332 remote_coordinates: &HashMap<String, Nip19Coordinate>,
322) -> Result<Vec<String>> { 333) -> Result<Vec<String>> {
323 let mut res = vec![]; 334 let mut res = vec![];
324 for (remote, c) in remote_coordinates { 335 for (remote, c) in remote_coordinates {
@@ -334,9 +345,9 @@ async fn get_nostr_git_remote_selection_labels(
334 Ok(res) 345 Ok(res)
335} 346}
336 347
337fn get_repo_coordinates_from_git_config(git_repo: &Repo) -> Result<Coordinate> { 348fn get_repo_coordinates_from_git_config(git_repo: &Repo) -> Result<Nip19Coordinate> {
338 Coordinate::parse( 349 Nip19Coordinate::from_bech32(
339 git_repo 350 &git_repo
340 .get_git_config_item("nostr.repo", Some(false))? 351 .get_git_config_item("nostr.repo", Some(false))?
341 .context("git config item \"nostr.repo\" is not set in local repository")?, 352 .context("git config item \"nostr.repo\" is not set in local repository")?,
342 ) 353 )
@@ -345,7 +356,7 @@ fn get_repo_coordinates_from_git_config(git_repo: &Repo) -> Result<Coordinate> {
345 356
346async fn get_repo_coordinates_from_nostr_remotes( 357async fn get_repo_coordinates_from_nostr_remotes(
347 git_repo: &Repo, 358 git_repo: &Repo,
348) -> Result<HashMap<String, Coordinate>> { 359) -> Result<HashMap<String, Nip19Coordinate>> {
349 let mut repo_coordinates = HashMap::new(); 360 let mut repo_coordinates = HashMap::new();
350 for remote_name in git_repo.git_repo.remotes()?.iter().flatten() { 361 for remote_name in git_repo.git_repo.remotes()?.iter().flatten() {
351 if let Some(remote_url) = git_repo.git_repo.find_remote(remote_name)?.url() { 362 if let Some(remote_url) = git_repo.git_repo.find_remote(remote_name)?.url() {
@@ -359,21 +370,23 @@ async fn get_repo_coordinates_from_nostr_remotes(
359 Ok(repo_coordinates) 370 Ok(repo_coordinates)
360} 371}
361 372
362async fn get_repo_coordinates_from_maintainers_yaml(git_repo: &Repo) -> Result<Coordinate> { 373async fn get_repo_coordinates_from_maintainers_yaml(git_repo: &Repo) -> Result<Nip19Coordinate> {
363 let repo_config = get_repo_config_from_yaml(git_repo)?; 374 let repo_config = get_repo_config_from_yaml(git_repo)?;
364 375
365 Ok(Coordinate { 376 Ok(Nip19Coordinate {
366 identifier: repo_config 377 coordinate: Coordinate {
367 .identifier 378 identifier: repo_config
368 .context("maintainers.yaml doesnt list the identifier")?, 379 .identifier
369 kind: Kind::GitRepoAnnouncement, 380 .context("maintainers.yaml doesnt list the identifier")?,
370 public_key: PublicKey::from_bech32( 381 kind: Kind::GitRepoAnnouncement,
371 repo_config 382 public_key: PublicKey::from_bech32(
372 .maintainers 383 repo_config
373 .first() 384 .maintainers
374 .context("maintainers.yaml doesnt list any maintainers")?, 385 .first()
375 ) 386 .context("maintainers.yaml doesnt list any maintainers")?,
376 .context("maintainers.yaml doesn't list the first maintainer using a valid npub")?, 387 )
388 .context("maintainers.yaml doesn't list the first maintainer using a valid npub")?,
389 },
377 relays: repo_config 390 relays: repo_config
378 .relays 391 .relays
379 .iter() 392 .iter()
@@ -386,7 +399,7 @@ async fn get_repo_coordinate_from_user_prompt(
386 git_repo: &Repo, 399 git_repo: &Repo,
387 #[cfg(test)] client: &crate::client::MockConnect, 400 #[cfg(test)] client: &crate::client::MockConnect,
388 #[cfg(not(test))] client: &Client, 401 #[cfg(not(test))] client: &Client,
389) -> Result<Coordinate> { 402) -> Result<Nip19Coordinate> {
390 // TODO: present list of events filter by root_commit 403 // TODO: present list of events filter by root_commit
391 // TODO: fallback to search based on identifier 404 // TODO: fallback to search based on identifier
392 let dim = Style::new().color256(247); 405 let dim = Style::new().color256(247);
@@ -401,7 +414,7 @@ async fn get_repo_coordinate_from_user_prompt(
401 loop { 414 loop {
402 let input = Interactor::default() 415 let input = Interactor::default()
403 .input(PromptInputParms::default().with_prompt("nostr repository"))?; 416 .input(PromptInputParms::default().with_prompt("nostr repository"))?;
404 let coordinate = if let Ok(c) = Coordinate::parse(&input) { 417 let coordinate = if let Ok(c) = Nip19Coordinate::from_bech32(&input) {
405 c 418 c
406 } else if let Ok(nostr_url) = 419 } else if let Ok(nostr_url) =
407 NostrUrlDecoded::parse_and_resolve(&input, &Some(git_repo)).await 420 NostrUrlDecoded::parse_and_resolve(&input, &Some(git_repo)).await
@@ -491,7 +504,7 @@ pub fn extract_pks(pk_strings: Vec<String>) -> Result<Vec<PublicKey>> {
491 let mut pks: Vec<PublicKey> = vec![]; 504 let mut pks: Vec<PublicKey> = vec![];
492 for s in pk_strings { 505 for s in pk_strings {
493 pks.push( 506 pks.push(
494 nostr_sdk::prelude::PublicKey::from_bech32(s.clone()).context(format!( 507 nostr_sdk::prelude::PublicKey::from_bech32(&s).context(format!(
495 "failed to convert {s} into a valid nostr public key" 508 "failed to convert {s} into a valid nostr public key"
496 ))?, 509 ))?,
497 ); 510 );
diff --git a/test_utils/Cargo.toml b/test_utils/Cargo.toml
index b354ff4..951bd39 100644
--- a/test_utils/Cargo.toml
+++ b/test_utils/Cargo.toml
@@ -10,10 +10,10 @@ dialoguer = "0.10.4"
10directories = "5.0.1" 10directories = "5.0.1"
11futures = "0.3.28" 11futures = "0.3.28"
12git2 = "0.19.0" 12git2 = "0.19.0"
13nostr = "0.37.0" 13nostr = "0.40.0"
14nostr-database = "0.37.0" 14nostr-database = "0.40.0"
15nostr-lmdb = "0.37.0" 15nostr-lmdb = "0.40.0"
16nostr-sdk = "0.37.0" 16nostr-sdk = "0.40.0"
17once_cell = "1.18.0" 17once_cell = "1.18.0"
18rand = "0.8" 18rand = "0.8"
19rexpect = { git = "https://github.com/rust-cli/rexpect.git", rev = "9eb61dd" } 19rexpect = { git = "https://github.com/rust-cli/rexpect.git", rev = "9eb61dd" }
diff --git a/test_utils/src/git.rs b/test_utils/src/git.rs
index 474fc59..5942a54 100644
--- a/test_utils/src/git.rs
+++ b/test_utils/src/git.rs
@@ -9,7 +9,7 @@ use std::{
9 9
10use anyhow::{Context, Result}; 10use anyhow::{Context, Result};
11use git2::{Branch, Oid, RepositoryInitOptions, Signature, Time}; 11use git2::{Branch, Oid, RepositoryInitOptions, Signature, Time};
12use nostr::nips::nip01::Coordinate; 12use nostr::nips::{nip01::Coordinate, nip19::Nip19Coordinate};
13use nostr_sdk::{Kind, RelayUrl, ToBech32}; 13use nostr_sdk::{Kind, RelayUrl, ToBech32};
14 14
15use crate::generate_repo_ref_event; 15use crate::generate_repo_ref_event;
@@ -23,10 +23,12 @@ pub struct GitTestRepo {
23impl Default for GitTestRepo { 23impl Default for GitTestRepo {
24 fn default() -> Self { 24 fn default() -> Self {
25 let repo_event = generate_repo_ref_event(); 25 let repo_event = generate_repo_ref_event();
26 let coordinate = Coordinate { 26 let coordinate = Nip19Coordinate {
27 kind: Kind::GitRepoAnnouncement, 27 coordinate: Coordinate {
28 public_key: repo_event.pubkey, 28 kind: Kind::GitRepoAnnouncement,
29 identifier: repo_event.tags.identifier().unwrap().to_string(), 29 public_key: repo_event.pubkey,
30 identifier: repo_event.tags.identifier().unwrap().to_string(),
31 },
30 relays: vec![ 32 relays: vec![
31 RelayUrl::parse("ws://localhost:8055").unwrap(), 33 RelayUrl::parse("ws://localhost:8055").unwrap(),
32 RelayUrl::parse("ws://localhost:8056").unwrap(), 34 RelayUrl::parse("ws://localhost:8056").unwrap(),
diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs
index 5c8d7a5..66d0df5 100644
--- a/test_utils/src/lib.rs
+++ b/test_utils/src/lib.rs
@@ -1,4 +1,5 @@
1use std::{ 1use std::{
2 collections::HashSet,
2 ffi::OsStr, 3 ffi::OsStr,
3 path::{Path, PathBuf}, 4 path::{Path, PathBuf},
4 str::FromStr, 5 str::FromStr,
@@ -8,13 +9,13 @@ use std::{
8 9
9use anyhow::{Context, Result, bail, ensure}; 10use anyhow::{Context, Result, bail, ensure};
10use dialoguer::theme::{ColorfulTheme, Theme}; 11use dialoguer::theme::{ColorfulTheme, Theme};
11use futures::executor::block_on; 12use futures::{executor::block_on, future::join_all};
12use git::GitTestRepo; 13use git::GitTestRepo;
13use git2::{Signature, Time}; 14use git2::{Signature, Time};
14use nostr::{self, Kind, Tag, nips::nip65::RelayMetadata}; 15use nostr::{self, Kind, Tag, nips::nip65::RelayMetadata};
15use nostr_database::NostrEventsDatabase; 16use nostr_database::NostrEventsDatabase;
16use nostr_lmdb::NostrLMDB; 17use nostr_lmdb::NostrLMDB;
17use nostr_sdk::{Client, NostrSigner, TagStandard, serde_json}; 18use nostr_sdk::{Client, Event, NostrSigner, TagStandard, serde_json};
18use once_cell::sync::Lazy; 19use once_cell::sync::Lazy;
19use rexpect::session::{Options, PtySession}; 20use rexpect::session::{Options, PtySession};
20use strip_ansi_escapes::strip_str; 21use strip_ansi_escapes::strip_str;
@@ -139,7 +140,7 @@ pub fn make_event_old_or_change_user(
139 &keys.public_key(), 140 &keys.public_key(),
140 &unsigned.created_at, 141 &unsigned.created_at,
141 &unsigned.kind, 142 &unsigned.kind,
142 &unsigned.tags.clone().to_vec(), 143 &unsigned.tags.clone(),
143 &unsigned.content, 144 &unsigned.content,
144 )); 145 ));
145 146
@@ -1107,14 +1108,24 @@ pub async fn get_events_from_cache(
1107 git_repo_path: &Path, 1108 git_repo_path: &Path,
1108 filters: Vec<nostr::Filter>, 1109 filters: Vec<nostr::Filter>,
1109) -> Result<Vec<nostr::Event>> { 1110) -> Result<Vec<nostr::Event>> {
1110 Ok(get_local_cache_database(git_repo_path) 1111 let db = get_local_cache_database(git_repo_path).await?;
1111 .await? 1112
1112 .query(filters.clone()) 1113 let query_results = join_all(filters.into_iter().map(|filter| async {
1113 .await 1114 db.query(filter).await.context(
1114 .context(
1115 "failed to execute query on opened git repo nostr cache database .git/nostr-cache.lmdb", 1115 "failed to execute query on opened git repo nostr cache database .git/nostr-cache.lmdb",
1116 )? 1116 )
1117 .to_vec()) 1117 }))
1118 .await;
1119
1120 // no Event is being mutated, just new items added to the set
1121 #[allow(clippy::mutable_key_type)]
1122 let mut events: HashSet<Event> = HashSet::new();
1123
1124 for result in query_results {
1125 events.extend(result?);
1126 }
1127
1128 Ok(events.into_iter().collect())
1118} 1129}
1119 1130
1120pub fn get_proposal_branch_name( 1131pub fn get_proposal_branch_name(
@@ -1436,19 +1447,18 @@ fn get_first_proposal_event_id() -> Result<nostr::EventId> {
1436 Handle::current().block_on(client.add_relay("ws://localhost:8055"))?; 1447 Handle::current().block_on(client.add_relay("ws://localhost:8055"))?;
1437 Handle::current().block_on(client.connect_relay("ws://localhost:8055"))?; 1448 Handle::current().block_on(client.connect_relay("ws://localhost:8055"))?;
1438 let proposals = Handle::current() 1449 let proposals = Handle::current()
1439 .block_on(client.fetch_events( 1450 .block_on(
1440 vec![ 1451 client.fetch_events(
1441 nostr::Filter::default() 1452 nostr::Filter::default()
1442 .kind(nostr::Kind::GitPatch) 1453 .kind(nostr::Kind::GitPatch)
1443 .custom_tag( 1454 .custom_tags(nostr::SingleLetterTag::lowercase(nostr::Alphabet::T), vec![
1444 nostr::SingleLetterTag::lowercase(nostr::Alphabet::T), 1455 "root",
1445 vec!["root"], 1456 ]),
1446 ), 1457 Duration::from_millis(500),
1447 ], 1458 ),
1448 Some(Duration::from_millis(500)), 1459 )?
1449 ))?
1450 .to_vec(); 1460 .to_vec();
1451 Handle::current().block_on(client.disconnect())?; 1461 Handle::current().block_on(client.disconnect());
1452 1462
1453 let proposal_1_id = proposals 1463 let proposal_1_id = proposals
1454 .iter() 1464 .iter()
diff --git a/test_utils/src/relay.rs b/test_utils/src/relay.rs
index 634b2d6..e820651 100644
--- a/test_utils/src/relay.rs
+++ b/test_utils/src/relay.rs
@@ -48,7 +48,7 @@ impl<'a> Relay<'a> {
48 let ok_json = RelayMessage::Ok { 48 let ok_json = RelayMessage::Ok {
49 event_id: event.id, 49 event_id: event.id,
50 status: error.is_none(), 50 status: error.is_none(),
51 message: error.unwrap_or("").to_string(), 51 message: error.unwrap_or("").to_string().into(),
52 } 52 }
53 .as_json(); 53 .as_json();
54 // bail!(format!("{}", &ok_json)); 54 // bail!(format!("{}", &ok_json));
@@ -63,7 +63,7 @@ impl<'a> Relay<'a> {
63 let responder = self.clients.get(&client_id).unwrap(); 63 let responder = self.clients.get(&client_id).unwrap();
64 64
65 Ok(responder.send(simple_websockets::Message::Text( 65 Ok(responder.send(simple_websockets::Message::Text(
66 RelayMessage::EndOfStoredEvents(subscription_id).as_json(), 66 RelayMessage::EndOfStoredEvents(std::borrow::Cow::Borrowed(&subscription_id)).as_json(),
67 ))) 67 )))
68 } 68 }
69 69
@@ -79,8 +79,8 @@ impl<'a> Relay<'a> {
79 for event in events { 79 for event in events {
80 let res = responder.send(simple_websockets::Message::Text( 80 let res = responder.send(simple_websockets::Message::Text(
81 RelayMessage::Event { 81 RelayMessage::Event {
82 subscription_id: subscription_id.clone(), 82 subscription_id: std::borrow::Cow::Borrowed(subscription_id),
83 event: Box::new(event.clone()), 83 event: std::borrow::Cow::Borrowed(event),
84 } 84 }
85 .as_json(), 85 .as_json(),
86 )); 86 ));
@@ -156,12 +156,14 @@ impl<'a> Relay<'a> {
156 } 156 }
157 } 157 }
158 158
159 if let Ok((subscription_id, filters)) = get_nreq(&message) { 159 if let Ok((subscription_id, filter)) = get_nreq(&message) {
160 self.reqs.push(filters.clone()); 160 self.reqs.push(vec![filter.clone()]);
161 if let Some(listner) = self.req_listener { 161 if let Some(listner) = self.req_listener {
162 listner(self, client_id, subscription_id, filters)?; 162 listner(self, client_id, subscription_id, vec![filter.clone()])?;
163 } else { 163 } else {
164 self.respond_standard_req(client_id, &subscription_id, &filters)?; 164 self.respond_standard_req(client_id, &subscription_id, &[
165 filter.clone()
166 ])?;
165 // self.respond_eose(client_id, subscription_id)?; 167 // self.respond_eose(client_id, subscription_id)?;
166 } 168 }
167 // respond with events 169 // respond with events
@@ -198,8 +200,7 @@ fn get_nevent(message: &simple_websockets::Message) -> Result<nostr::Event> {
198 if let simple_websockets::Message::Text(s) = message.clone() { 200 if let simple_websockets::Message::Text(s) = message.clone() {
199 let cm_result = ClientMessage::from_json(s); 201 let cm_result = ClientMessage::from_json(s);
200 if let Ok(ClientMessage::Event(event)) = cm_result { 202 if let Ok(ClientMessage::Event(event)) = cm_result {
201 let e = *event; 203 return Ok(event.into_owned());
202 return Ok(e.clone());
203 } 204 }
204 } 205 }
205 bail!("not nostr event") 206 bail!("not nostr event")
@@ -207,15 +208,15 @@ fn get_nevent(message: &simple_websockets::Message) -> Result<nostr::Event> {
207 208
208fn get_nreq( 209fn get_nreq(
209 message: &simple_websockets::Message, 210 message: &simple_websockets::Message,
210) -> Result<(nostr::SubscriptionId, Vec<nostr::Filter>)> { 211) -> Result<(nostr::SubscriptionId, nostr::Filter)> {
211 if let simple_websockets::Message::Text(s) = message.clone() { 212 if let simple_websockets::Message::Text(s) = message.clone() {
212 let cm_result = ClientMessage::from_json(s); 213 let cm_result = ClientMessage::from_json(s);
213 if let Ok(ClientMessage::Req { 214 if let Ok(ClientMessage::Req {
214 subscription_id, 215 subscription_id,
215 filters, 216 filter,
216 }) = cm_result 217 }) = cm_result
217 { 218 {
218 return Ok((subscription_id, filters)); 219 return Ok((subscription_id.into_owned(), filter.into_owned()));
219 } 220 }
220 } 221 }
221 bail!("not nostr event") 222 bail!("not nostr event")
diff --git a/tests/git_remote_nostr/main.rs b/tests/git_remote_nostr/main.rs
index 686a5df..6b51825 100644
--- a/tests/git_remote_nostr/main.rs
+++ b/tests/git_remote_nostr/main.rs
@@ -3,7 +3,7 @@ use std::{collections::HashSet, env::current_dir};
3use anyhow::{Context, Result}; 3use anyhow::{Context, Result};
4use futures::join; 4use futures::join;
5use git2::Oid; 5use git2::Oid;
6use nostr::nips::nip01::Coordinate; 6use nostr::nips::{nip01::Coordinate, nip19::Nip19Coordinate};
7use nostr_sdk::{Event, JsonUtil, Kind, RelayUrl, ToBech32, secp256k1::rand}; 7use nostr_sdk::{Event, JsonUtil, Kind, RelayUrl, ToBech32, secp256k1::rand};
8use relay::Relay; 8use relay::Relay;
9use serial_test::serial; 9use serial_test::serial;
@@ -18,10 +18,12 @@ static STATE_KIND: nostr::Kind = Kind::Custom(30618);
18 18
19fn get_nostr_remote_url() -> Result<String> { 19fn get_nostr_remote_url() -> Result<String> {
20 let repo_event = generate_repo_ref_event(); 20 let repo_event = generate_repo_ref_event();
21 let naddr = Coordinate { 21 let naddr = Nip19Coordinate {
22 kind: Kind::GitRepoAnnouncement, 22 coordinate: Coordinate {
23 public_key: repo_event.pubkey, 23 kind: Kind::GitRepoAnnouncement,
24 identifier: repo_event.tags.identifier().unwrap().to_string(), 24 public_key: repo_event.pubkey,
25 identifier: repo_event.tags.identifier().unwrap().to_string(),
26 },
25 relays: vec![ 27 relays: vec![
26 RelayUrl::parse("ws://localhost:8055").unwrap(), 28 RelayUrl::parse("ws://localhost:8055").unwrap(),
27 RelayUrl::parse("ws://localhost:8056").unwrap(), 29 RelayUrl::parse("ws://localhost:8056").unwrap(),
diff --git a/tests/ngit_init.rs b/tests/ngit_init.rs
index 4b61559..409bd51 100644
--- a/tests/ngit_init.rs
+++ b/tests/ngit_init.rs
@@ -194,7 +194,7 @@ mod when_repo_not_previously_claimed {
194 194
195 mod git_config_updated { 195 mod git_config_updated {
196 196
197 use nostr::nips::nip01::Coordinate; 197 use nostr::nips::{nip01::Coordinate, nip19::Nip19Coordinate};
198 use nostr_sdk::ToBech32; 198 use nostr_sdk::ToBech32;
199 199
200 use super::*; 200 use super::*;
@@ -236,10 +236,12 @@ mod when_repo_not_previously_claimed {
236 .get_entry("nostr.repo")? 236 .get_entry("nostr.repo")?
237 .value() 237 .value()
238 .unwrap(), 238 .unwrap(),
239 Coordinate { 239 Nip19Coordinate {
240 kind: nostr_sdk::Kind::GitRepoAnnouncement, 240 coordinate: Coordinate {
241 identifier: "example-identifier".to_string(), 241 kind: nostr_sdk::Kind::GitRepoAnnouncement,
242 public_key: TEST_KEY_1_KEYS.public_key(), 242 identifier: "example-identifier".to_string(),
243 public_key: TEST_KEY_1_KEYS.public_key(),
244 },
243 relays: vec![], 245 relays: vec![],
244 } 246 }
245 .to_bech32()?, 247 .to_bech32()?,
diff --git a/tests/ngit_list.rs b/tests/ngit_list.rs
index 4a3aad5..bb742cf 100644
--- a/tests/ngit_list.rs
+++ b/tests/ngit_list.rs
@@ -49,7 +49,10 @@ async fn prep_proposals_repo_and_repo_with_proposal_pulled_and_checkedout(
49mod cannot_find_repo_event { 49mod cannot_find_repo_event {
50 use super::*; 50 use super::*;
51 mod cli_prompts { 51 mod cli_prompts {
52 use nostr::{ToBech32, nips::nip01::Coordinate}; 52 use nostr::{
53 ToBech32,
54 nips::{nip01::Coordinate, nip19::Nip19Coordinate},
55 };
53 use nostr_sdk::RelayUrl; 56 use nostr_sdk::RelayUrl;
54 57
55 use super::*; 58 use super::*;
@@ -87,10 +90,12 @@ mod cannot_find_repo_event {
87 } 90 }
88 if naddr { 91 if naddr {
89 let mut input = p.expect_input("nostr repository")?; 92 let mut input = p.expect_input("nostr repository")?;
90 let coordinate = Coordinate { 93 let coordinate = Nip19Coordinate {
91 kind: nostr::Kind::GitRepoAnnouncement, 94 coordinate: Coordinate {
92 public_key: TEST_KEY_1_KEYS.public_key(), 95 kind: nostr::Kind::GitRepoAnnouncement,
93 identifier: repo_event.tags.identifier().unwrap().to_string(), 96 public_key: TEST_KEY_1_KEYS.public_key(),
97 identifier: repo_event.tags.identifier().unwrap().to_string(),
98 },
94 relays: vec![RelayUrl::parse("ws://localhost:8056").unwrap()], 99 relays: vec![RelayUrl::parse("ws://localhost:8056").unwrap()],
95 }; 100 };
96 input.succeeds_with(&coordinate.to_bech32()?)?; 101 input.succeeds_with(&coordinate.to_bech32()?)?;