Monorepo for Tangled
tangled.org
1use crate::{RepoIdResolver, Resolution};
2use bobbin_types::search::SearchableRecord;
3use bobbin_types::sh_tangled::repo::artifact::Artifact;
4use jacquard_common::DefaultStr;
5use jacquard_common::IntoStatic;
6use jacquard_common::types::did::Did;
7use jacquard_common::types::ident::AtIdentifier;
8use jacquard_common::types::recordkey::Rkey;
9use jacquard_common::types::string::AtUri;
10
11pub(crate) const REPO_COLLECTION: &str = "sh.tangled.repo";
12
13pub trait NormalizeRepoRefs: Sized {
14 fn normalize(
15 self,
16 resolver: &RepoIdResolver,
17 ) -> impl std::future::Future<Output = Option<Self>> + Send;
18}
19
20pub(crate) fn is_repo_at_uri(uri: &AtUri<DefaultStr>) -> bool {
21 uri.collection()
22 .map(|c| c.as_ref() == REPO_COLLECTION)
23 .unwrap_or(false)
24}
25
26pub(crate) async fn resolve_repo_uri(
27 resolver: &RepoIdResolver,
28 uri: &AtUri<DefaultStr>,
29) -> Option<Did<DefaultStr>> {
30 if !is_repo_at_uri(uri) {
31 return None;
32 }
33 let owner = match uri.authority() {
34 AtIdentifier::Did(d) => d.clone().into_static(),
35 AtIdentifier::Handle(_) => return None,
36 };
37 let rkey: Rkey<DefaultStr> = uri.rkey()?.clone().into_static();
38 match resolver.resolve(&owner, &rkey).await {
39 Resolution::Mapped(did) => Some(did),
40 Resolution::NoRepoDid | Resolution::Unresolvable => None,
41 }
42}
43
44async fn fill_repo_did(
45 resolver: &RepoIdResolver,
46 uri_field: &mut Option<AtUri<DefaultStr>>,
47 did_field: &mut Option<Did<DefaultStr>>,
48) -> bool {
49 if did_field.is_some() {
50 *uri_field = None;
51 return true;
52 }
53 let Some(uri) = uri_field.as_ref() else {
54 return false;
55 };
56 let Some(did) = resolve_repo_uri(resolver, uri).await else {
57 return false;
58 };
59 *did_field = Some(did);
60 *uri_field = None;
61 true
62}
63
64impl NormalizeRepoRefs for Artifact<DefaultStr> {
65 async fn normalize(mut self, resolver: &RepoIdResolver) -> Option<Self> {
66 if !fill_repo_did(resolver, &mut self.repo, &mut self.repo_did).await {
67 return None;
68 }
69 Some(self)
70 }
71}
72
73impl NormalizeRepoRefs for bobbin_types::sh_tangled::pipeline::Pipeline<DefaultStr> {
74 async fn normalize(mut self, resolver: &RepoIdResolver) -> Option<Self> {
75 let trig = &mut self.trigger_metadata.repo;
76 if trig.repo_did.is_some() {
77 trig.repo = None;
78 return Some(self);
79 }
80 let raw = trig.repo.as_deref()?;
81 let parsed = AtUri::<DefaultStr>::new_owned(raw).ok()?;
82 let did = resolve_repo_uri(resolver, &parsed).await?;
83 trig.repo_did = Some(did);
84 trig.repo = None;
85 Some(self)
86 }
87}
88
89impl NormalizeRepoRefs for SearchableRecord {
90 async fn normalize(self, _resolver: &RepoIdResolver) -> Option<Self> {
91 Some(self)
92 }
93}
94
95macro_rules! identity_normalize {
96 ($($t:ty),+ $(,)?) => {
97 $(
98 impl NormalizeRepoRefs for $t {
99 async fn normalize(self, _resolver: &RepoIdResolver) -> Option<Self> {
100 Some(self)
101 }
102 }
103 )+
104 };
105}
106
107use bobbin_types::sh_tangled::actor::profile::Profile;
108use bobbin_types::sh_tangled::feed::comment::Comment as FeedComment;
109use bobbin_types::sh_tangled::feed::reaction::Reaction;
110use bobbin_types::sh_tangled::feed::star::Star;
111use bobbin_types::sh_tangled::git::ref_update::RefUpdate;
112use bobbin_types::sh_tangled::graph::follow::Follow;
113use bobbin_types::sh_tangled::graph::vouch::Vouch;
114use bobbin_types::sh_tangled::knot::Knot;
115use bobbin_types::sh_tangled::knot::member::Member as KnotMember;
116use bobbin_types::sh_tangled::label::definition::Definition as LabelDefinition;
117use bobbin_types::sh_tangled::label::op::Op as LabelOp;
118use bobbin_types::sh_tangled::pipeline::status::Status as PipelineStatus;
119use bobbin_types::sh_tangled::public_key::PublicKey;
120use bobbin_types::sh_tangled::repo::Repo;
121use bobbin_types::sh_tangled::repo::collaborator::Collaborator;
122use bobbin_types::sh_tangled::repo::issue::Issue;
123use bobbin_types::sh_tangled::repo::issue::state::State as IssueState;
124use bobbin_types::sh_tangled::repo::pull::Pull;
125use bobbin_types::sh_tangled::repo::pull::status::Status as PullStatus;
126use bobbin_types::sh_tangled::spindle::Spindle;
127use bobbin_types::sh_tangled::spindle::member::Member as SpindleMember;
128use bobbin_types::sh_tangled::string::TangledString;
129
130identity_normalize!(
131 Profile<DefaultStr>,
132 FeedComment<DefaultStr>,
133 Reaction<DefaultStr>,
134 Star<DefaultStr>,
135 RefUpdate<DefaultStr>,
136 Follow<DefaultStr>,
137 Vouch<DefaultStr>,
138 Knot<DefaultStr>,
139 KnotMember<DefaultStr>,
140 LabelDefinition<DefaultStr>,
141 LabelOp<DefaultStr>,
142 PipelineStatus<DefaultStr>,
143 PublicKey<DefaultStr>,
144 Repo<DefaultStr>,
145 Collaborator<DefaultStr>,
146 Issue<DefaultStr>,
147 IssueState<DefaultStr>,
148 Pull<DefaultStr>,
149 PullStatus<DefaultStr>,
150 Spindle<DefaultStr>,
151 SpindleMember<DefaultStr>,
152 TangledString<DefaultStr>,
153);