steid

@jamesgill /

steid/src/domain/repository/label_repo.rs
Rust5.0 KBCode·Blame·History·Raw
1use super::RepositoryResult;
2use crate::domain::{IssueId, Label, LabelId, PullRequestId, RepoId};
3
4/// Persistence for [`Label`]s and for which issues and pull requests carry them.
5///
6/// The assignment table lives behind the same port rather than one of its own. A
7/// `(issue, label)` pair is not an aggregate: nothing addresses one except through one
8/// of its two ends, it has no fields of its own, and splitting it would mean every use
9/// case that touches labels holding two ports that can only be used together.
10///
11/// **Issues and pull requests are two sets of methods, not one polymorphic set**, for
12/// the reason `pull_request_labels` gives: the ids are different types precisely so one
13/// cannot stand in for the other, and the store's cascades depend on knowing which is
14/// which.
15pub trait LabelRepository: Send + Sync {
16 /// Every label on a repository, ordered by name.
17 ///
18 /// By name rather than by creation: the management page and the checkbox list are
19 /// both things you scan for a word you already have in mind, and creation order puts
20 /// it in a different place every time you look.
21 fn list_by_repo(
22 &self,
23 repo_id: &RepoId,
24 ) -> impl Future<Output = RepositoryResult<Vec<Label>>> + Send;
25
26 /// One label by id, or `None`.
27 ///
28 /// The repository is not part of the lookup, so **the caller must check that the
29 /// label it gets back belongs to the repository in the path** — an id from another
30 /// repository is otherwise a way to edit somebody else's label.
31 fn find_by_id(
32 &self,
33 id: &LabelId,
34 ) -> impl Future<Output = RepositoryResult<Option<Label>>> + Send;
35
36 /// The label with this exact name on this repository, or `None`.
37 ///
38 /// What `?label=bug` resolves through, and what the uniqueness check reads.
39 fn find_by_name(
40 &self,
41 repo_id: &RepoId,
42 name: &str,
43 ) -> impl Future<Output = RepositoryResult<Option<Label>>> + Send;
44
45 /// Inserts or replaces a label.
46 ///
47 /// Uniqueness of `(repo_id, name)` is enforced by the store; a use case checks it
48 /// first so the refusal is a message rather than a storage failure.
49 fn save(&self, label: &Label) -> impl Future<Output = RepositoryResult<()>> + Send;
50
51 /// Removes a label, and with it every assignment of it.
52 ///
53 /// Deleting a label is not deleting the issues that wore it — they simply stop
54 /// wearing it, which is what `on delete cascade` on the join table says.
55 fn delete(&self, id: &LabelId) -> impl Future<Output = RepositoryResult<()>> + Send;
56
57 /// The labels on one issue, ordered by name like every other listing here.
58 fn labels_for_issue(
59 &self,
60 issue_id: &IssueId,
61 ) -> impl Future<Output = RepositoryResult<Vec<Label>>> + Send;
62
63 /// Every `(issue, label)` pair on a repository, so a list page can render pills on
64 /// every row for one query rather than one per row.
65 fn labels_for_repo(
66 &self,
67 repo_id: &RepoId,
68 ) -> impl Future<Output = RepositoryResult<Vec<(IssueId, Label)>>> + Send;
69
70 /// How many issues carry each label on a repository.
71 ///
72 /// Every label gets an entry, including the unused ones: the management page says
73 /// "no issues" rather than leaving a row blank.
74 fn issue_counts(
75 &self,
76 repo_id: &RepoId,
77 ) -> impl Future<Output = RepositoryResult<Vec<(LabelId, u64)>>> + Send;
78
79 /// Replaces an issue's labels with exactly this set.
80 ///
81 /// The whole set rather than an add and a remove, because the control that submits it
82 /// is a list of checkboxes: a form that posts what is ticked can only be applied as a
83 /// replacement, and doing it this way makes a double submission idempotent.
84 fn set_issue_labels(
85 &self,
86 issue_id: &IssueId,
87 labels: &[LabelId],
88 ) -> impl Future<Output = RepositoryResult<()>> + Send;
89
90 /// The labels on one pull request, ordered by name.
91 fn labels_for_pull(
92 &self,
93 pull_id: &PullRequestId,
94 ) -> impl Future<Output = RepositoryResult<Vec<Label>>> + Send;
95
96 /// Every `(pull request, label)` pair on a repository, so the pull request list can
97 /// render pills on every row for one query rather than one per row.
98 fn labels_for_repo_pulls(
99 &self,
100 repo_id: &RepoId,
101 ) -> impl Future<Output = RepositoryResult<Vec<(PullRequestId, Label)>>> + Send;
102
103 /// How many pull requests carry each label on a repository.
104 ///
105 /// Beside [`issue_counts`](Self::issue_counts) rather than folded into it: the
106 /// labels page says what a label is on, and one number covering both would make
107 /// "delete this — it comes off 3 issues" a sentence that is sometimes false.
108 fn pull_counts(
109 &self,
110 repo_id: &RepoId,
111 ) -> impl Future<Output = RepositoryResult<Vec<(LabelId, u64)>>> + Send;
112
113 /// Replaces a pull request's labels with exactly this set.
114 fn set_pull_labels(
115 &self,
116 pull_id: &PullRequestId,
117 labels: &[LabelId],
118 ) -> impl Future<Output = RepositoryResult<()>> + Send;
119}