mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-14 13:31:03 +00:00
[chore] add some more slice related utility functions + remove duplicated functions (#3149)
This commit is contained in:
parent
a237e2b295
commit
47c26818d6
7 changed files with 216 additions and 43 deletions
|
@ -162,7 +162,9 @@ func (f *Federator) PostInboxRequestBodyHook(ctx context.Context, r *http.Reques
|
||||||
|
|
||||||
// OtherIRIs will likely contain some
|
// OtherIRIs will likely contain some
|
||||||
// duplicate entries now, so remove them.
|
// 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
|
// Finished, set other IRIs on the context
|
||||||
// so they can be checked for blocks later.
|
// so they can be checked for blocks later.
|
||||||
|
|
|
@ -62,7 +62,7 @@ type Conversation struct {
|
||||||
|
|
||||||
// ConversationOtherAccountsKey creates an OtherAccountsKey from a list of OtherAccountIDs.
|
// ConversationOtherAccountsKey creates an OtherAccountsKey from a list of OtherAccountIDs.
|
||||||
func ConversationOtherAccountsKey(otherAccountIDs []string) string {
|
func ConversationOtherAccountsKey(otherAccountIDs []string) string {
|
||||||
otherAccountIDs = util.UniqueStrings(otherAccountIDs)
|
otherAccountIDs = util.Deduplicate(otherAccountIDs)
|
||||||
slices.Sort(otherAccountIDs)
|
slices.Sort(otherAccountIDs)
|
||||||
return strings.Join(otherAccountIDs, ",")
|
return strings.Join(otherAccountIDs, ",")
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,8 +218,13 @@ func (p *ProcessingMedia) store(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if width > 0 && height > 0 {
|
if width > 0 && height > 0 {
|
||||||
// Determine thumbnail dimensions to use.
|
// Determine thumbnail dimens to use.
|
||||||
thumbWidth, thumbHeight := thumbSize(width, height, aspect, result.rotation)
|
thumbWidth, thumbHeight := thumbSize(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
aspect,
|
||||||
|
result.rotation,
|
||||||
|
)
|
||||||
p.media.FileMeta.Small.Width = thumbWidth
|
p.media.FileMeta.Small.Width = thumbWidth
|
||||||
p.media.FileMeta.Small.Height = thumbHeight
|
p.media.FileMeta.Small.Height = thumbHeight
|
||||||
p.media.FileMeta.Small.Size = (thumbWidth * thumbHeight)
|
p.media.FileMeta.Small.Size = (thumbWidth * thumbHeight)
|
||||||
|
|
|
@ -856,6 +856,7 @@ func (c *Converter) statusToAPIFilterResults(
|
||||||
for _, mention := range s.Mentions {
|
for _, mention := range s.Mentions {
|
||||||
otherAccounts = append(otherAccounts, mention.TargetAccount)
|
otherAccounts = append(otherAccounts, mention.TargetAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are no other accounts, skip this check.
|
// If there are no other accounts, skip this check.
|
||||||
if len(otherAccounts) > 0 {
|
if len(otherAccounts) > 0 {
|
||||||
// Start by assuming that they're all invisible or muted.
|
// Start by assuming that they're all invisible or muted.
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
|
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import "slices"
|
import (
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
// Deduplicate deduplicates entries in the given slice.
|
// Deduplicate deduplicates entries in the given slice.
|
||||||
func Deduplicate[T comparable](in []T) []T {
|
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
|
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,
|
// Collate will collect the values of type K from input type []T,
|
||||||
// passing each item to 'get' and deduplicating the end result.
|
// 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 {
|
func Collate[T any, K comparable](in []T, get func(T) K) []K {
|
||||||
if get == nil {
|
if get == nil {
|
||||||
panic("nil func")
|
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
|
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
|
// ToSet creates a Set[T] from given values,
|
||||||
// slice of strings, without changing the order of the entries.
|
// noting that this does not maintain any order.
|
||||||
func UniqueStrings(strings []string) []string {
|
func ToSet[T comparable](in []T) Set[T] {
|
||||||
var (
|
set := make(Set[T], len(in))
|
||||||
l = len(strings)
|
for _, v := range in {
|
||||||
keys = make(map[string]any, l) // Use map to dedupe items.
|
set[v] = struct{}{}
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
return unique
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// UniqueURIs returns a deduplicated version of the given
|
// In returns input slice filtered to
|
||||||
// slice of URIs, without changing the order of the entries.
|
// only contain those in receiving set.
|
||||||
func UniqueURIs(uris []*url.URL) []*url.URL {
|
func (s Set[T]) In(vs []T) []T {
|
||||||
var (
|
out := make([]T, 0, len(vs))
|
||||||
l = len(uris)
|
for _, v := range vs {
|
||||||
keys = make(map[string]any, l) // Use map to dedupe items.
|
if _, ok := s[v]; ok {
|
||||||
unique = make([]*url.URL, 0, l) // Return slice.
|
out = append(out, v)
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return out
|
||||||
return unique
|
}
|
||||||
|
|
||||||
|
// 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 a new issue