mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-07 08:54:39 +01:00
[chore] add some more slice related utility functions + remove duplicated functions (#3149)
This commit is contained in:
parent
a237e2b295
commit
47c26818d6
@ -162,7 +162,9 @@ func (f *Federator) PostInboxRequestBodyHook(ctx context.Context, r *http.Reques
|
||||
|
||||
// OtherIRIs will likely contain some
|
||||
// duplicate entries now, so remove them.
|
||||
otherIRIs = util.UniqueURIs(otherIRIs)
|
||||
otherIRIs = util.DeduplicateFunc(otherIRIs,
|
||||
(*url.URL).String, // serialized URL is 'key()'
|
||||
)
|
||||
|
||||
// Finished, set other IRIs on the context
|
||||
// so they can be checked for blocks later.
|
||||
|
@ -62,7 +62,7 @@ type Conversation struct {
|
||||
|
||||
// ConversationOtherAccountsKey creates an OtherAccountsKey from a list of OtherAccountIDs.
|
||||
func ConversationOtherAccountsKey(otherAccountIDs []string) string {
|
||||
otherAccountIDs = util.UniqueStrings(otherAccountIDs)
|
||||
otherAccountIDs = util.Deduplicate(otherAccountIDs)
|
||||
slices.Sort(otherAccountIDs)
|
||||
return strings.Join(otherAccountIDs, ",")
|
||||
}
|
||||
|
@ -218,8 +218,13 @@ func (p *ProcessingMedia) store(ctx context.Context) error {
|
||||
}
|
||||
|
||||
if width > 0 && height > 0 {
|
||||
// Determine thumbnail dimensions to use.
|
||||
thumbWidth, thumbHeight := thumbSize(width, height, aspect, result.rotation)
|
||||
// Determine thumbnail dimens to use.
|
||||
thumbWidth, thumbHeight := thumbSize(
|
||||
width,
|
||||
height,
|
||||
aspect,
|
||||
result.rotation,
|
||||
)
|
||||
p.media.FileMeta.Small.Width = thumbWidth
|
||||
p.media.FileMeta.Small.Height = thumbHeight
|
||||
p.media.FileMeta.Small.Size = (thumbWidth * thumbHeight)
|
||||
|
@ -856,6 +856,7 @@ func (c *Converter) statusToAPIFilterResults(
|
||||
for _, mention := range s.Mentions {
|
||||
otherAccounts = append(otherAccounts, mention.TargetAccount)
|
||||
}
|
||||
|
||||
// If there are no other accounts, skip this check.
|
||||
if len(otherAccounts) > 0 {
|
||||
// Start by assuming that they're all invisible or muted.
|
||||
|
@ -17,7 +17,9 @@
|
||||
|
||||
package util
|
||||
|
||||
import "slices"
|
||||
import (
|
||||
"slices"
|
||||
)
|
||||
|
||||
// Deduplicate deduplicates entries in the given slice.
|
||||
func Deduplicate[T comparable](in []T) []T {
|
||||
@ -68,9 +70,69 @@ func DeduplicateFunc[T any, C comparable](in []T, key func(v T) C) []T {
|
||||
return deduped
|
||||
}
|
||||
|
||||
// Gather will collect the values of type V from input type []T,
|
||||
// passing each item to 'get' and appending V to the return slice.
|
||||
func Gather[T, V any](out []V, in []T, get func(T) V) []V {
|
||||
if get == nil {
|
||||
panic("nil func")
|
||||
}
|
||||
|
||||
// Starting write index
|
||||
// in the resliced / re
|
||||
// alloc'd output slice.
|
||||
start := len(out)
|
||||
|
||||
// Total required slice len.
|
||||
total := start + len(in)
|
||||
|
||||
if total > cap(out) {
|
||||
// Reallocate output with
|
||||
// capacity for total len.
|
||||
out2 := make([]V, len(out), total)
|
||||
copy(out2, out)
|
||||
out = out2
|
||||
}
|
||||
|
||||
// Reslice with capacity
|
||||
// up to total required.
|
||||
out = out[:total]
|
||||
|
||||
// Gather vs from 'in'.
|
||||
for i, v := range in {
|
||||
j := start + i
|
||||
out[j] = get(v)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// GatherIf is functionally similar to Gather(), but only when return bool is true.
|
||||
// If you don't need to check the boolean, Gather() will be very slightly faster.
|
||||
func GatherIf[T, V any](out []V, in []T, get func(T) (V, bool)) []V {
|
||||
if get == nil {
|
||||
panic("nil func")
|
||||
}
|
||||
|
||||
if cap(out)-len(out) < len(in) {
|
||||
// Reallocate output with capacity for 'in'.
|
||||
out2 := make([]V, len(out), cap(out)+len(in))
|
||||
copy(out2, out)
|
||||
out = out2
|
||||
}
|
||||
|
||||
// Gather vs from 'in'.
|
||||
for _, v := range in {
|
||||
if v, ok := get(v); ok {
|
||||
out = append(out, v)
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// Collate will collect the values of type K from input type []T,
|
||||
// passing each item to 'get' and deduplicating the end result.
|
||||
// Compared to Deduplicate() this returns []K, NOT input type []T.
|
||||
// This is equivalent to calling Gather() followed by Deduplicate().
|
||||
func Collate[T any, K comparable](in []T, get func(T) K) []K {
|
||||
if get == nil {
|
||||
panic("nil func")
|
||||
|
94
internal/util/slices_test.go
Normal file
94
internal/util/slices_test.go
Normal file
@ -0,0 +1,94 @@
|
||||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package util_test
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
)
|
||||
|
||||
var (
|
||||
testURLSlice = []*url.URL{}
|
||||
)
|
||||
|
||||
func TestGather(t *testing.T) {
|
||||
out := util.Gather(nil, []*url.URL{
|
||||
{Scheme: "https", Host: "google.com", Path: "/some-search"},
|
||||
{Scheme: "http", Host: "example.com", Path: "/robots.txt"},
|
||||
}, (*url.URL).String)
|
||||
if !slices.Equal(out, []string{
|
||||
"https://google.com/some-search",
|
||||
"http://example.com/robots.txt",
|
||||
}) {
|
||||
t.Fatal("unexpected gather output")
|
||||
}
|
||||
|
||||
out = util.Gather([]string{
|
||||
"starting input string",
|
||||
"another starting input",
|
||||
}, []*url.URL{
|
||||
{Scheme: "https", Host: "google.com", Path: "/some-search"},
|
||||
{Scheme: "http", Host: "example.com", Path: "/robots.txt"},
|
||||
}, (*url.URL).String)
|
||||
if !slices.Equal(out, []string{
|
||||
"starting input string",
|
||||
"another starting input",
|
||||
"https://google.com/some-search",
|
||||
"http://example.com/robots.txt",
|
||||
}) {
|
||||
t.Fatal("unexpected gather output")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGatherIf(t *testing.T) {
|
||||
out := util.GatherIf(nil, []string{
|
||||
"hello world",
|
||||
"not hello world",
|
||||
"hello world",
|
||||
}, func(s string) (string, bool) {
|
||||
return s, s == "hello world"
|
||||
})
|
||||
if !slices.Equal(out, []string{
|
||||
"hello world",
|
||||
"hello world",
|
||||
}) {
|
||||
t.Fatal("unexpected gatherif output")
|
||||
}
|
||||
|
||||
out = util.GatherIf([]string{
|
||||
"starting input string",
|
||||
"another starting input",
|
||||
}, []string{
|
||||
"hello world",
|
||||
"not hello world",
|
||||
"hello world",
|
||||
}, func(s string) (string, bool) {
|
||||
return s, s == "hello world"
|
||||
})
|
||||
if !slices.Equal(out, []string{
|
||||
"starting input string",
|
||||
"another starting input",
|
||||
"hello world",
|
||||
"hello world",
|
||||
}) {
|
||||
t.Fatal("unexpected gatherif output")
|
||||
}
|
||||
}
|
@ -17,48 +17,57 @@
|
||||
|
||||
package util
|
||||
|
||||
import "net/url"
|
||||
// Set represents a hashmap of only keys,
|
||||
// useful for deduplication / key checking.
|
||||
type Set[T comparable] map[T]struct{}
|
||||
|
||||
// UniqueStrings returns a deduplicated version of the given
|
||||
// slice of strings, without changing the order of the entries.
|
||||
func UniqueStrings(strings []string) []string {
|
||||
var (
|
||||
l = len(strings)
|
||||
keys = make(map[string]any, l) // Use map to dedupe items.
|
||||
unique = make([]string, 0, l) // Return slice.
|
||||
)
|
||||
|
||||
for _, str := range strings {
|
||||
// Check if already set as a key in the map;
|
||||
// if not, add to return slice + mark key as set.
|
||||
if _, set := keys[str]; !set {
|
||||
keys[str] = nil // Value doesn't matter.
|
||||
unique = append(unique, str)
|
||||
}
|
||||
// ToSet creates a Set[T] from given values,
|
||||
// noting that this does not maintain any order.
|
||||
func ToSet[T comparable](in []T) Set[T] {
|
||||
set := make(Set[T], len(in))
|
||||
for _, v := range in {
|
||||
set[v] = struct{}{}
|
||||
}
|
||||
|
||||
return unique
|
||||
return set
|
||||
}
|
||||
|
||||
// UniqueURIs returns a deduplicated version of the given
|
||||
// slice of URIs, without changing the order of the entries.
|
||||
func UniqueURIs(uris []*url.URL) []*url.URL {
|
||||
var (
|
||||
l = len(uris)
|
||||
keys = make(map[string]any, l) // Use map to dedupe items.
|
||||
unique = make([]*url.URL, 0, l) // Return slice.
|
||||
)
|
||||
// FromSet extracts the values from set to slice,
|
||||
// noting that this does not maintain any order.
|
||||
func FromSet[T comparable](in Set[T]) []T {
|
||||
out := make([]T, len(in))
|
||||
var i int
|
||||
for v := range in {
|
||||
out[i] = v
|
||||
i++
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
for _, uri := range uris {
|
||||
uriStr := uri.String()
|
||||
|
||||
// Check if already set as a key in the map;
|
||||
// if not, add to return slice + mark key as set.
|
||||
if _, set := keys[uriStr]; !set {
|
||||
keys[uriStr] = nil // Value doesn't matter.
|
||||
unique = append(unique, uri)
|
||||
// In returns input slice filtered to
|
||||
// only contain those in receiving set.
|
||||
func (s Set[T]) In(vs []T) []T {
|
||||
out := make([]T, 0, len(vs))
|
||||
for _, v := range vs {
|
||||
if _, ok := s[v]; ok {
|
||||
out = append(out, v)
|
||||
}
|
||||
}
|
||||
|
||||
return unique
|
||||
return out
|
||||
}
|
||||
|
||||
// NotIn is the functional inverse of In().
|
||||
func (s Set[T]) NotIn(vs []T) []T {
|
||||
out := make([]T, 0, len(vs))
|
||||
for _, v := range vs {
|
||||
if _, ok := s[v]; !ok {
|
||||
out = append(out, v)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Has returns if value is in Set.
|
||||
func (s Set[T]) Has(v T) bool {
|
||||
_, ok := s[v]
|
||||
return ok
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user