diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2023-10-01 00:00:00 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2023-10-01 00:00:00 +0100 |
| commit | 000901c0cbca8464b5a89bcc93c5474f6564bafd (patch) | |
| tree | 0ae11836c173ec6246e8b1eab7dc1e265e125426 /src/sub_commands/prs | |
| parent | b9a88672b8734448615354e3f46748d2fdc2f647 (diff) | |
feat(prs-create) send to multiple relays
add tests but these currently don't work when run together
Diffstat (limited to 'src/sub_commands/prs')
| -rw-r--r-- | src/sub_commands/prs/create.rs | 279 |
1 files changed, 248 insertions, 31 deletions
diff --git a/src/sub_commands/prs/create.rs b/src/sub_commands/prs/create.rs index 89ea652..3047e92 100644 --- a/src/sub_commands/prs/create.rs +++ b/src/sub_commands/prs/create.rs | |||
| @@ -1,4 +1,9 @@ | |||
| 1 | use std::time::Duration; | ||
| 2 | |||
| 1 | use anyhow::{bail, Context, Result}; | 3 | use anyhow::{bail, Context, Result}; |
| 4 | use console::Term; | ||
| 5 | use futures::future::join_all; | ||
| 6 | use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; | ||
| 2 | use nostr::{prelude::sha1::Hash as Sha1Hash, EventBuilder, Marker, Tag, TagKind}; | 7 | use nostr::{prelude::sha1::Hash as Sha1Hash, EventBuilder, Marker, Tag, TagKind}; |
| 3 | 8 | ||
| 4 | use crate::{ | 9 | use crate::{ |
| @@ -79,14 +84,245 @@ pub async fn launch( | |||
| 79 | .input(PromptInputParms::default().with_prompt("description (Optional)"))?, | 84 | .input(PromptInputParms::default().with_prompt("description (Optional)"))?, |
| 80 | }; | 85 | }; |
| 81 | 86 | ||
| 82 | let root_commit = git_repo | ||
| 83 | .get_root_commit(to_branch.as_str()) | ||
| 84 | .context("failed to get root commit of the repository")?; | ||
| 85 | |||
| 86 | // create PR event | 87 | // create PR event |
| 87 | 88 | ||
| 88 | let keys = login::launch(&cli_args.nsec, &cli_args.password)?; | 89 | let keys = login::launch(&cli_args.nsec, &cli_args.password)?; |
| 89 | 90 | ||
| 91 | let events = | ||
| 92 | generate_pr_and_patch_events(&title, &description, &to_branch, &git_repo, &ahead, keys)?; | ||
| 93 | |||
| 94 | let my_write_relays: Vec<String> = vec![ | ||
| 95 | "ws://localhost:8051".to_string(), | ||
| 96 | "ws://localhost:8052".to_string(), | ||
| 97 | ]; | ||
| 98 | |||
| 99 | let repo_read_relays: Vec<String> = vec![ | ||
| 100 | "ws://localhost:8051".to_string(), | ||
| 101 | "ws://localhost:8053".to_string(), | ||
| 102 | ]; | ||
| 103 | |||
| 104 | send_events( | ||
| 105 | events, | ||
| 106 | keys, | ||
| 107 | my_write_relays, | ||
| 108 | repo_read_relays, | ||
| 109 | !cli_args.disable_cli_spinners, | ||
| 110 | ) | ||
| 111 | .await?; | ||
| 112 | // TODO check if there is already a similarly named PR | ||
| 113 | |||
| 114 | // println!("failures:"); | ||
| 115 | // println!("ws://relay.anon.io Error: Payment Required"); | ||
| 116 | |||
| 117 | // should we have a relays in Repository event? | ||
| 118 | // yes | ||
| 119 | // | ||
| 120 | |||
| 121 | // TODO connect to relays and post | ||
| 122 | |||
| 123 | Ok(()) | ||
| 124 | } | ||
| 125 | |||
| 126 | async fn send_events( | ||
| 127 | events: Vec<nostr::Event>, | ||
| 128 | keys: nostr::Keys, | ||
| 129 | my_write_relays: Vec<String>, | ||
| 130 | repo_read_relays: Vec<String>, | ||
| 131 | animate: bool, | ||
| 132 | ) -> Result<()> { | ||
| 133 | let (_, _, _, all) = unique_and_duplicate_all(&my_write_relays, &repo_read_relays); | ||
| 134 | |||
| 135 | let client = Client::new( | ||
| 136 | ClientParams::default() | ||
| 137 | .with_keys(keys) | ||
| 138 | // .with_fallback_relays(vec!["ws://localhost:8080".to_string()]), | ||
| 139 | .with_fallback_relays(all.iter().map(std::string::ToString::to_string).collect()), | ||
| 140 | ); | ||
| 141 | |||
| 142 | let term = Term::stdout(); | ||
| 143 | term.write_line("connecting to relays...")?; | ||
| 144 | client.connect().await?; | ||
| 145 | term.clear_last_lines(1)?; | ||
| 146 | |||
| 147 | println!( | ||
| 148 | "posting 1 pull request with {} commits...", | ||
| 149 | events.len() - 1 | ||
| 150 | ); | ||
| 151 | |||
| 152 | let m = MultiProgress::new(); | ||
| 153 | let pb_style = ProgressStyle::with_template(if animate { | ||
| 154 | " {spinner} {prefix} {bar} {pos}/{len} {msg}" | ||
| 155 | } else { | ||
| 156 | " - {prefix} {bar} {pos}/{len} {msg}" | ||
| 157 | })? | ||
| 158 | .progress_chars("##-"); | ||
| 159 | |||
| 160 | let pb_after_style = | ||
| 161 | |symbol| ProgressStyle::with_template(format!(" {symbol} {}", "{prefix} {msg}",).as_str()); | ||
| 162 | let pb_after_style_succeeded = pb_after_style(if animate { | ||
| 163 | console::style("✔".to_string()) | ||
| 164 | .for_stderr() | ||
| 165 | .green() | ||
| 166 | .to_string() | ||
| 167 | } else { | ||
| 168 | "y".to_string() | ||
| 169 | })?; | ||
| 170 | |||
| 171 | let pb_after_style_failed = pb_after_style(if animate { | ||
| 172 | console::style("✘".to_string()) | ||
| 173 | .for_stderr() | ||
| 174 | .red() | ||
| 175 | .to_string() | ||
| 176 | } else { | ||
| 177 | "x".to_string() | ||
| 178 | })?; | ||
| 179 | |||
| 180 | join_all(all.iter().map(|&relay| async { | ||
| 181 | let details = format!( | ||
| 182 | "{}{} {}", | ||
| 183 | if my_write_relays.iter().any(|r| relay.eq(r)) { | ||
| 184 | " [my-relay]" | ||
| 185 | } else { | ||
| 186 | "" | ||
| 187 | }, | ||
| 188 | if repo_read_relays.iter().any(|r| relay.eq(r)) { | ||
| 189 | " [repo-relay]" | ||
| 190 | } else { | ||
| 191 | "" | ||
| 192 | }, | ||
| 193 | *relay, | ||
| 194 | ); | ||
| 195 | let pb = m.add( | ||
| 196 | ProgressBar::new(events.len() as u64) | ||
| 197 | .with_prefix(details.to_string()) | ||
| 198 | .with_style(pb_style.clone()), | ||
| 199 | ); | ||
| 200 | if animate { | ||
| 201 | pb.enable_steady_tick(Duration::from_millis(300)); | ||
| 202 | } | ||
| 203 | pb.inc(0); // need to make pb display intially | ||
| 204 | let mut failed = false; | ||
| 205 | for event in &events { | ||
| 206 | match client.send_event_to(relay.as_str(), event.clone()).await { | ||
| 207 | Ok(_) => pb.inc(1), | ||
| 208 | Err(e) => { | ||
| 209 | pb.set_style(pb_after_style_failed.clone()); | ||
| 210 | pb.finish_with_message( | ||
| 211 | console::style( | ||
| 212 | e.to_string() | ||
| 213 | .replace("relay pool error:", "error:") | ||
| 214 | .replace("event not published: ", ""), | ||
| 215 | ) | ||
| 216 | .for_stderr() | ||
| 217 | .red() | ||
| 218 | .to_string(), | ||
| 219 | ); | ||
| 220 | failed = true; | ||
| 221 | break; | ||
| 222 | } | ||
| 223 | }; | ||
| 224 | } | ||
| 225 | if !failed { | ||
| 226 | pb.set_style(pb_after_style_succeeded.clone()); | ||
| 227 | pb.finish_with_message(""); | ||
| 228 | } | ||
| 229 | })) | ||
| 230 | .await; | ||
| 231 | client.disconnect().await?; | ||
| 232 | Ok(()) | ||
| 233 | } | ||
| 234 | |||
| 235 | /// returns `(unique_vec1, unique_vec2, duplicates, all)` | ||
| 236 | fn unique_and_duplicate_all<'a, S>( | ||
| 237 | vec1: &'a Vec<S>, | ||
| 238 | vec2: &'a Vec<S>, | ||
| 239 | ) -> (Vec<&'a S>, Vec<&'a S>, Vec<&'a S>, Vec<&'a S>) | ||
| 240 | where | ||
| 241 | S: PartialEq, | ||
| 242 | { | ||
| 243 | let mut vec1_u = vec![]; | ||
| 244 | let mut vec2_u = vec![]; | ||
| 245 | let mut dup = vec![]; | ||
| 246 | let mut all = vec![]; | ||
| 247 | for s1 in vec1 { | ||
| 248 | if vec2.iter().any(|s2| s1.eq(s2)) { | ||
| 249 | dup.push(s1); | ||
| 250 | } else { | ||
| 251 | vec1_u.push(s1); | ||
| 252 | } | ||
| 253 | } | ||
| 254 | for s2 in vec2 { | ||
| 255 | if !vec1.iter().any(|s1| s2.eq(s1)) { | ||
| 256 | vec2_u.push(s2); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | for a in [&dup, &vec1_u, &vec2_u] { | ||
| 260 | for e in a { | ||
| 261 | all.push(&**e); | ||
| 262 | } | ||
| 263 | } | ||
| 264 | (vec1_u, vec2_u, dup, all) | ||
| 265 | } | ||
| 266 | |||
| 267 | mod tests_unique_and_duplicate { | ||
| 268 | |||
| 269 | #[test] | ||
| 270 | fn correct_number_of_unique_and_duplicate_items() { | ||
| 271 | let v1 = vec![ | ||
| 272 | "t1".to_string(), | ||
| 273 | "t2".to_string(), | ||
| 274 | "t3".to_string(), | ||
| 275 | "t4".to_string(), | ||
| 276 | "t5".to_string(), | ||
| 277 | ]; | ||
| 278 | let v2 = vec![ | ||
| 279 | "t3".to_string(), | ||
| 280 | "t4".to_string(), | ||
| 281 | "t5".to_string(), | ||
| 282 | "t6".to_string(), | ||
| 283 | ]; | ||
| 284 | |||
| 285 | let (v1_u, v2_u, d, a) = super::unique_and_duplicate_all(&v1, &v2); | ||
| 286 | |||
| 287 | assert_eq!(v1_u.len(), 2); | ||
| 288 | assert_eq!(v2_u.len(), 1); | ||
| 289 | assert_eq!(d.len(), 3); | ||
| 290 | assert_eq!(a.len(), 6); | ||
| 291 | } | ||
| 292 | #[test] | ||
| 293 | fn all_begins_with_duplicates() { | ||
| 294 | let v1 = vec![ | ||
| 295 | "t1".to_string(), | ||
| 296 | "t2".to_string(), | ||
| 297 | "t3".to_string(), | ||
| 298 | "t4".to_string(), | ||
| 299 | "t5".to_string(), | ||
| 300 | ]; | ||
| 301 | let v2 = vec![ | ||
| 302 | "t3".to_string(), | ||
| 303 | "t4".to_string(), | ||
| 304 | "t5".to_string(), | ||
| 305 | "t6".to_string(), | ||
| 306 | ]; | ||
| 307 | |||
| 308 | let (_, _, d, a) = super::unique_and_duplicate_all(&v1, &v2); | ||
| 309 | |||
| 310 | assert_eq!(a[0], d[0]); | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | fn generate_pr_and_patch_events( | ||
| 315 | title: &String, | ||
| 316 | description: &String, | ||
| 317 | to_branch: &str, | ||
| 318 | git_repo: &Repo, | ||
| 319 | commits: &Vec<Sha1Hash>, | ||
| 320 | keys: nostr::Keys, | ||
| 321 | ) -> Result<Vec<nostr::Event>> { | ||
| 322 | let root_commit = git_repo | ||
| 323 | .get_root_commit(to_branch) | ||
| 324 | .context("failed to get root commit of the repository")?; | ||
| 325 | |||
| 90 | let pr_event = EventBuilder::new( | 326 | let pr_event = EventBuilder::new( |
| 91 | nostr::event::Kind::Custom(318), | 327 | nostr::event::Kind::Custom(318), |
| 92 | format!("{title}\r\n\r\n{description}"), | 328 | format!("{title}\r\n\r\n{description}"), |
| @@ -103,12 +339,14 @@ pub async fn launch( | |||
| 103 | .to_event(&keys) | 339 | .to_event(&keys) |
| 104 | .context("failed to create pr event")?; | 340 | .context("failed to create pr event")?; |
| 105 | 341 | ||
| 106 | let mut patch_events = vec![]; | 342 | let pr_event_id = pr_event.id; |
| 107 | for commit in &ahead { | 343 | |
| 344 | let mut events = vec![pr_event]; | ||
| 345 | for commit in commits { | ||
| 108 | let commit_parent = git_repo | 346 | let commit_parent = git_repo |
| 109 | .get_commit_parent(commit) | 347 | .get_commit_parent(commit) |
| 110 | .context("failed to create patch event")?; | 348 | .context("failed to create patch event")?; |
| 111 | patch_events.push( | 349 | events.push( |
| 112 | EventBuilder::new( | 350 | EventBuilder::new( |
| 113 | nostr::event::Kind::Custom(317), | 351 | nostr::event::Kind::Custom(317), |
| 114 | git_repo | 352 | git_repo |
| @@ -119,7 +357,7 @@ pub async fn launch( | |||
| 119 | Tag::Hashtag(commit.to_string()), | 357 | Tag::Hashtag(commit.to_string()), |
| 120 | Tag::Hashtag(commit_parent.to_string()), | 358 | Tag::Hashtag(commit_parent.to_string()), |
| 121 | Tag::Event( | 359 | Tag::Event( |
| 122 | pr_event.id, | 360 | pr_event_id, |
| 123 | None, // TODO: add relay | 361 | None, // TODO: add relay |
| 124 | Some(Marker::Root), | 362 | Some(Marker::Root), |
| 125 | ), | 363 | ), |
| @@ -136,32 +374,11 @@ pub async fn launch( | |||
| 136 | // TODO: add relay tags | 374 | // TODO: add relay tags |
| 137 | ], | 375 | ], |
| 138 | ) | 376 | ) |
| 139 | .to_event(&keys), | 377 | .to_event(&keys)?, |
| 140 | ); | 378 | ); |
| 141 | } | 379 | } |
| 142 | 380 | Ok(events) | |
| 143 | let client = Client::new(ClientParams::default().with_keys(keys)); | ||
| 144 | |||
| 145 | println!("connecting..."); | ||
| 146 | client.connect().await?; | ||
| 147 | println!("connected..."); | ||
| 148 | |||
| 149 | // TODO check if there is already a similarly named PR | ||
| 150 | let _ = client | ||
| 151 | .send_event_to("ws://localhost:8080", pr_event) | ||
| 152 | .await?; | ||
| 153 | // TODO post each PR | ||
| 154 | // TODO report | ||
| 155 | println!("posted successfully to 4/5 of your relays and 0/4 of maintainers relays"); | ||
| 156 | // should we have a relays in Repository event? | ||
| 157 | // yes | ||
| 158 | // | ||
| 159 | |||
| 160 | // TODO connect to relays and post | ||
| 161 | |||
| 162 | Ok(()) | ||
| 163 | } | 381 | } |
| 164 | |||
| 165 | // TODO | 382 | // TODO |
| 166 | // - find profile | 383 | // - find profile |
| 167 | // - file relays | 384 | // - file relays |