Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Code

392 commit(s) on main

Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys keenan · 1h ago 4b8c9e1d
history blame raw 2.3 KiB
1package metadata
2
3import (
4 "sort"
5 "strings"
6)
7
8// NormalizeLabels accepts what people type: a comma-separated list or the option
9// repeated. Labels are sorted so the written file does not depend on argument
10// order.
11func NormalizeLabels(in []string) []string {
12 var out []string
13 for _, v := range in {
14 for _, part := range strings.Split(v, ",") {
15 if l := strings.TrimSpace(part); l != "" {
16 out = append(out, l)
17 }
18 }
19 }
20 sort.Strings(out)
21 return out
22}
23
24// NormalizeAssignees accepts an optional leading @, a comma-separated list, or
25// the option repeated. Names are returned sorted and unique.
26func NormalizeAssignees(in []string) []string {
27 seen := map[string]bool{}
28 for _, v := range in {
29 for _, part := range strings.Split(v, ",") {
30 n := strings.ToLower(strings.TrimPrefix(strings.TrimSpace(part), "@"))
31 if n != "" {
32 seen[n] = true
33 }
34 }
35 }
36 return SortedNames(seen)
37}
38
39// SortedNames renders a set of names in the order the writer stores them.
40func SortedNames(set map[string]bool) []string {
41 if len(set) == 0 {
42 return nil
43 }
44 out := make([]string, 0, len(set))
45 for n := range set {
46 out = append(out, n)
47 }
48 sort.Strings(out)
49 return out
50}
51
52// SameNames reports whether have is already exactly set, so a command that
53// changes nothing commits nothing.
54func SameNames(have []string, set map[string]bool) bool {
55 if len(have) != len(set) {
56 return false
57 }
58 for _, n := range have {
59 if !set[n] {
60 return false
61 }
62 }
63 return true
64}
65
66// IncludesAll reports whether every wanted value is present in have.
67func IncludesAll(have, want []string) bool {
68 if len(want) == 0 {
69 return true
70 }
71 present := map[string]bool{}
72 for _, v := range have {
73 present[v] = true
74 }
75 for _, v := range want {
76 if !present[v] {
77 return false
78 }
79 }
80 return true
81}
82
83// Same reports whether two issues would render to the same file. Slices are
84// compared position by position because the writer sorts them: a different order
85// is not a different file.
86func Same(a, b Issue) bool {
87 if a.Title != b.Title || a.State != b.State || a.Description != b.Description {
88 return false
89 }
90 return sameList(a.Labels, b.Labels) && sameList(a.Assignees, b.Assignees)
91}
92
93func sameList(a, b []string) bool {
94 if len(a) != len(b) {
95 return false
96 }
97 for i := range a {
98 if a[i] != b[i] {
99 return false
100 }
101 }
102 return true
103}
104