diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2024-09-04 11:32:05 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2024-09-04 14:23:54 +0100 |
| commit | 771f944af447c202eba045936a36dee71ab797ac (patch) | |
| tree | e691de4ebc8dde7ac4855e139881ff923bc254ce /src/bin/ngit/sub_commands/list.rs | |
| parent | 949c6459aa7683453a7160423b689ceadb08954b (diff) | |
refactor: fix imports, etc based on restructure
move some functions out of ngit and into lib/mod
and lib/git_events
remove MockConnect from binaries so it is only used in the library.
this was done:
* mainly because automocks were not being imported from
lib into each binary
* but also because the these functions were being
tested with MockConnect
Diffstat (limited to 'src/bin/ngit/sub_commands/list.rs')
| -rw-r--r-- | src/bin/ngit/sub_commands/list.rs | 216 |
1 files changed, 16 insertions, 200 deletions
diff --git a/src/bin/ngit/sub_commands/list.rs b/src/bin/ngit/sub_commands/list.rs index ac1f4ab..0755e3b 100644 --- a/src/bin/ngit/sub_commands/list.rs +++ b/src/bin/ngit/sub_commands/list.rs | |||
| @@ -1,23 +1,25 @@ | |||
| 1 | use std::{collections::HashSet, io::Write, ops::Add, path::Path}; | 1 | use std::{io::Write, ops::Add}; |
| 2 | 2 | ||
| 3 | use anyhow::{bail, Context, Result}; | 3 | use anyhow::{bail, Context, Result}; |
| 4 | use nostr::nips::nip01::Coordinate; | 4 | use ngit::{ |
| 5 | use nostr_sdk::{Kind, PublicKey}; | 5 | client::{get_all_proposal_patch_events_from_cache, get_proposals_and_revisions_from_cache}, |
| 6 | 6 | git_events::{ | |
| 7 | use super::send::event_is_patch_set_root; | 7 | get_commit_id_from_patch, get_most_recent_patch_with_ancestors, status_kinds, tag_value, |
| 8 | #[cfg(test)] | 8 | }, |
| 9 | use crate::client::MockConnect; | 9 | }; |
| 10 | #[cfg(not(test))] | 10 | use nostr_sdk::Kind; |
| 11 | use crate::client::{Client, Connect}; | 11 | |
| 12 | use crate::{ | 12 | use crate::{ |
| 13 | cli_interactor::{Interactor, InteractorPrompt, PromptChoiceParms, PromptConfirmParms}, | 13 | cli_interactor::{Interactor, InteractorPrompt, PromptChoiceParms, PromptConfirmParms}, |
| 14 | client::{fetching_with_report, get_events_from_cache, get_repo_ref_from_cache}, | 14 | client::{ |
| 15 | fetching_with_report, get_events_from_cache, get_repo_ref_from_cache, Client, Connect, | ||
| 16 | }, | ||
| 15 | git::{str_to_sha1, Repo, RepoActions}, | 17 | git::{str_to_sha1, Repo, RepoActions}, |
| 16 | repo_ref::{get_repo_coordinates, RepoRef}, | 18 | git_events::{ |
| 17 | sub_commands::send::{ | 19 | commit_msg_from_patch_oneliner, event_is_revision_root, event_to_cover_letter, |
| 18 | commit_msg_from_patch_oneliner, event_is_cover_letter, event_is_revision_root, | 20 | patch_supports_commit_ids, |
| 19 | event_to_cover_letter, patch_supports_commit_ids, | ||
| 20 | }, | 21 | }, |
| 22 | repo_ref::get_repo_coordinates, | ||
| 21 | }; | 23 | }; |
| 22 | 24 | ||
| 23 | #[allow(clippy::too_many_lines)] | 25 | #[allow(clippy::too_many_lines)] |
| @@ -29,10 +31,7 @@ pub async fn launch() -> Result<()> { | |||
| 29 | // TODO: check for existing maintaiers file | 31 | // TODO: check for existing maintaiers file |
| 30 | // TODO: check for other claims | 32 | // TODO: check for other claims |
| 31 | 33 | ||
| 32 | #[cfg(not(test))] | ||
| 33 | let client = Client::default(); | 34 | let client = Client::default(); |
| 34 | #[cfg(test)] | ||
| 35 | let client = <MockConnect as std::default::Default>::default(); | ||
| 36 | 35 | ||
| 37 | let repo_coordinates = get_repo_coordinates(&git_repo, &client).await?; | 36 | let repo_coordinates = get_repo_coordinates(&git_repo, &client).await?; |
| 38 | 37 | ||
| @@ -721,186 +720,3 @@ fn check_clean(git_repo: &Repo) -> Result<()> { | |||
| 721 | } | 720 | } |
| 722 | Ok(()) | 721 | Ok(()) |
| 723 | } | 722 | } |
| 724 | |||
| 725 | pub fn tag_value(event: &nostr::Event, tag_name: &str) -> Result<String> { | ||
| 726 | Ok(event | ||
| 727 | .tags | ||
| 728 | .iter() | ||
| 729 | .find(|t| t.as_vec()[0].eq(tag_name)) | ||
| 730 | .context(format!("tag '{tag_name}'not present"))? | ||
| 731 | .as_vec()[1] | ||
| 732 | .clone()) | ||
| 733 | } | ||
| 734 | |||
| 735 | pub fn get_commit_id_from_patch(event: &nostr::Event) -> Result<String> { | ||
| 736 | let value = tag_value(event, "commit"); | ||
| 737 | |||
| 738 | if value.is_ok() { | ||
| 739 | value | ||
| 740 | } else if event.content.starts_with("From ") && event.content.len().gt(&45) { | ||
| 741 | Ok(event.content[5..45].to_string()) | ||
| 742 | } else { | ||
| 743 | bail!("event is not a patch") | ||
| 744 | } | ||
| 745 | } | ||
| 746 | |||
| 747 | fn get_event_parent_id(event: &nostr::Event) -> Result<String> { | ||
| 748 | Ok(if let Some(reply_tag) = event | ||
| 749 | .tags | ||
| 750 | .iter() | ||
| 751 | .find(|t| t.as_vec().len().gt(&3) && t.as_vec()[3].eq("reply")) | ||
| 752 | { | ||
| 753 | reply_tag | ||
| 754 | } else { | ||
| 755 | event | ||
| 756 | .tags | ||
| 757 | .iter() | ||
| 758 | .find(|t| t.as_vec().len().gt(&3) && t.as_vec()[3].eq("root")) | ||
| 759 | .context("no reply or root e tag present".to_string())? | ||
| 760 | } | ||
| 761 | .as_vec()[1] | ||
| 762 | .clone()) | ||
| 763 | } | ||
| 764 | |||
| 765 | pub fn get_most_recent_patch_with_ancestors( | ||
| 766 | mut patches: Vec<nostr::Event>, | ||
| 767 | ) -> Result<Vec<nostr::Event>> { | ||
| 768 | patches.sort_by_key(|e| e.created_at); | ||
| 769 | |||
| 770 | let youngest_patch = patches.last().context("no patches found")?; | ||
| 771 | |||
| 772 | let patches_with_youngest_created_at: Vec<&nostr::Event> = patches | ||
| 773 | .iter() | ||
| 774 | .filter(|p| p.created_at.eq(&youngest_patch.created_at)) | ||
| 775 | .collect(); | ||
| 776 | |||
| 777 | let mut res = vec![]; | ||
| 778 | |||
| 779 | let mut event_id_to_search = patches_with_youngest_created_at | ||
| 780 | .clone() | ||
| 781 | .iter() | ||
| 782 | .find(|p| { | ||
| 783 | !patches_with_youngest_created_at.iter().any(|p2| { | ||
| 784 | if let Ok(reply_to) = get_event_parent_id(p2) { | ||
| 785 | reply_to.eq(&p.id.to_string()) | ||
| 786 | } else { | ||
| 787 | false | ||
| 788 | } | ||
| 789 | }) | ||
| 790 | }) | ||
| 791 | .context("cannot find patches_with_youngest_created_at")? | ||
| 792 | .id | ||
| 793 | .to_string(); | ||
| 794 | |||
| 795 | while let Some(event) = patches | ||
| 796 | .iter() | ||
| 797 | .find(|e| e.id.to_string().eq(&event_id_to_search)) | ||
| 798 | { | ||
| 799 | res.push(event.clone()); | ||
| 800 | if event_is_patch_set_root(event) { | ||
| 801 | break; | ||
| 802 | } | ||
| 803 | event_id_to_search = get_event_parent_id(event).unwrap_or_default(); | ||
| 804 | } | ||
| 805 | Ok(res) | ||
| 806 | } | ||
| 807 | |||
| 808 | pub fn status_kinds() -> Vec<nostr::Kind> { | ||
| 809 | vec![ | ||
| 810 | nostr::Kind::GitStatusOpen, | ||
| 811 | nostr::Kind::GitStatusApplied, | ||
| 812 | nostr::Kind::GitStatusClosed, | ||
| 813 | nostr::Kind::GitStatusDraft, | ||
| 814 | ] | ||
| 815 | } | ||
| 816 | |||
| 817 | pub async fn get_proposals_and_revisions_from_cache( | ||
| 818 | git_repo_path: &Path, | ||
| 819 | repo_coordinates: HashSet<Coordinate>, | ||
| 820 | ) -> Result<Vec<nostr::Event>> { | ||
| 821 | let mut proposals = get_events_from_cache( | ||
| 822 | git_repo_path, | ||
| 823 | vec![ | ||
| 824 | nostr::Filter::default() | ||
| 825 | .kind(nostr::Kind::GitPatch) | ||
| 826 | .custom_tag( | ||
| 827 | nostr::SingleLetterTag::lowercase(nostr_sdk::Alphabet::A), | ||
| 828 | repo_coordinates | ||
| 829 | .iter() | ||
| 830 | .map(std::string::ToString::to_string) | ||
| 831 | .collect::<Vec<String>>(), | ||
| 832 | ), | ||
| 833 | ], | ||
| 834 | ) | ||
| 835 | .await? | ||
| 836 | .iter() | ||
| 837 | .filter(|e| event_is_patch_set_root(e)) | ||
| 838 | .cloned() | ||
| 839 | .collect::<Vec<nostr::Event>>(); | ||
| 840 | proposals.sort_by_key(|e| e.created_at); | ||
| 841 | proposals.reverse(); | ||
| 842 | Ok(proposals) | ||
| 843 | } | ||
| 844 | |||
| 845 | pub async fn get_all_proposal_patch_events_from_cache( | ||
| 846 | git_repo_path: &Path, | ||
| 847 | repo_ref: &RepoRef, | ||
| 848 | proposal_id: &nostr::EventId, | ||
| 849 | ) -> Result<Vec<nostr::Event>> { | ||
| 850 | let mut commit_events = get_events_from_cache( | ||
| 851 | git_repo_path, | ||
| 852 | vec![ | ||
| 853 | nostr::Filter::default() | ||
| 854 | .kind(nostr::Kind::GitPatch) | ||
| 855 | .event(*proposal_id), | ||
| 856 | nostr::Filter::default() | ||
| 857 | .kind(nostr::Kind::GitPatch) | ||
| 858 | .id(*proposal_id), | ||
| 859 | ], | ||
| 860 | ) | ||
| 861 | .await?; | ||
| 862 | |||
| 863 | let permissioned_users: HashSet<PublicKey> = [ | ||
| 864 | repo_ref.maintainers.clone(), | ||
| 865 | vec![ | ||
| 866 | commit_events | ||
| 867 | .iter() | ||
| 868 | .find(|e| e.id().eq(proposal_id)) | ||
| 869 | .context("proposal not in cache")? | ||
| 870 | .author(), | ||
| 871 | ], | ||
| 872 | ] | ||
| 873 | .concat() | ||
| 874 | .iter() | ||
| 875 | .copied() | ||
| 876 | .collect(); | ||
| 877 | commit_events.retain(|e| permissioned_users.contains(&e.author())); | ||
| 878 | |||
| 879 | let revision_roots: HashSet<nostr::EventId> = commit_events | ||
| 880 | .iter() | ||
| 881 | .filter(|e| event_is_revision_root(e)) | ||
| 882 | .map(nostr::Event::id) | ||
| 883 | .collect(); | ||
| 884 | |||
| 885 | if !revision_roots.is_empty() { | ||
| 886 | for event in get_events_from_cache( | ||
| 887 | git_repo_path, | ||
| 888 | vec![ | ||
| 889 | nostr::Filter::default() | ||
| 890 | .kind(nostr::Kind::GitPatch) | ||
| 891 | .events(revision_roots) | ||
| 892 | .authors(permissioned_users.clone()), | ||
| 893 | ], | ||
| 894 | ) | ||
| 895 | .await? | ||
| 896 | { | ||
| 897 | commit_events.push(event); | ||
| 898 | } | ||
| 899 | } | ||
| 900 | |||
| 901 | Ok(commit_events | ||
| 902 | .iter() | ||
| 903 | .filter(|e| !event_is_cover_letter(e) && permissioned_users.contains(&e.author())) | ||
| 904 | .cloned() | ||
| 905 | .collect()) | ||
| 906 | } | ||