upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/sub_commands/prs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2023-10-01 00:00:00 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2023-10-01 00:00:00 +0100
commit000901c0cbca8464b5a89bcc93c5474f6564bafd (patch)
tree0ae11836c173ec6246e8b1eab7dc1e265e125426 /src/sub_commands/prs
parentb9a88672b8734448615354e3f46748d2fdc2f647 (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.rs279
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 @@
1use std::time::Duration;
2
1use anyhow::{bail, Context, Result}; 3use anyhow::{bail, Context, Result};
4use console::Term;
5use futures::future::join_all;
6use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
2use nostr::{prelude::sha1::Hash as Sha1Hash, EventBuilder, Marker, Tag, TagKind}; 7use nostr::{prelude::sha1::Hash as Sha1Hash, EventBuilder, Marker, Tag, TagKind};
3 8
4use crate::{ 9use 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
126async 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)`
236fn 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>)
240where
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
267mod 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
314fn 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