mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-01 12:43:08 -04:00
* JMAP query limit of 0 is synonymous with "no limit", but we actually want to be able to perform queries without any results, for cases where we only want to count the total number of objects, and also because it makes more sense semantically * introduce query parameter validation checks, in order to only allow query parameters that are actually supported, which is going to be useful during development of clients
312 lines
7.5 KiB
Go
312 lines
7.5 KiB
Go
// Package structs provides some utility functions for dealing with structs.
|
|
package structs
|
|
|
|
import (
|
|
"iter"
|
|
"maps"
|
|
"slices"
|
|
|
|
orderedmap "github.com/wk8/go-ordered-map"
|
|
)
|
|
|
|
// CopyOrZeroValue returns a copy of s if s is not nil otherwise the zero value of T will be returned.
|
|
func CopyOrZeroValue[T any](s *T) *T {
|
|
cp := new(T)
|
|
if s != nil {
|
|
*cp = *s
|
|
}
|
|
return cp
|
|
}
|
|
|
|
// Create an iterator from a slice, iterating over every single element of the slice, in order.
|
|
func Seq[T any](s []T) iter.Seq[T] {
|
|
return func(yield func(T) bool) {
|
|
for _, elem := range s {
|
|
if !yield(elem) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create an iterator from a slice that yields the position and the value,
|
|
// iterating over every single element of the slice, in order.
|
|
func Seq2[T any](s []T) iter.Seq2[int, T] {
|
|
return func(yield func(int, T) bool) {
|
|
for i, elem := range s {
|
|
if !yield(i, elem) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns a copy of an array with a unique set of elements.
|
|
//
|
|
// Element order is retained.
|
|
func Uniq[T comparable](source []T) []T {
|
|
m := orderedmap.New()
|
|
for _, v := range source {
|
|
m.Set(v, true)
|
|
}
|
|
set := make([]T, m.Len())
|
|
i := 0
|
|
for pair := m.Oldest(); pair != nil; pair = pair.Next() {
|
|
set[i] = pair.Key.(T)
|
|
i++
|
|
}
|
|
return set
|
|
}
|
|
|
|
// Returns a slice containing the keys of the map.
|
|
func Keys[K comparable, V any](source map[K]V) []K {
|
|
if source == nil {
|
|
var zero []K
|
|
return zero
|
|
}
|
|
return slices.Collect(maps.Keys(source))
|
|
}
|
|
|
|
// Creates a map from a slice, using the indexer func to determine the key for each value,
|
|
// and the value being as-is.
|
|
func Index[K comparable, V any](source []V, indexer func(V) K) map[K]V {
|
|
if source == nil {
|
|
var zero map[K]V
|
|
return zero
|
|
}
|
|
result := map[K]V{}
|
|
for _, v := range source {
|
|
k := indexer(v)
|
|
result[k] = v
|
|
}
|
|
return result
|
|
}
|
|
|
|
func Set[V comparable](source []V) map[V]struct{} {
|
|
if source == nil {
|
|
var zero map[V]struct{}
|
|
return zero
|
|
}
|
|
result := map[V]struct{}{}
|
|
for _, v := range source {
|
|
result[v] = struct{}{}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Creates a slice from a slice, putting each value from the source slice through the
|
|
// mapper function to determine the value to store into the resulting slice.
|
|
func Map[E any, R any](source []E, mapper func(E) R) []R {
|
|
if source == nil {
|
|
var zero []R
|
|
return zero
|
|
}
|
|
result := make([]R, len(source))
|
|
for i, e := range source {
|
|
result[i] = mapper(e)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Wraps an iterator with a transformer function.
|
|
func MapSeq[A, B any](it iter.Seq[A], transformer func(A) B) iter.Seq[B] {
|
|
return func(yield func(b B) bool) {
|
|
for v := range it {
|
|
t := transformer(v)
|
|
if !yield(t) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Creates a slice from a slice, putting each value from the source slice through the
|
|
// mapper function to determine the value to store into the resulting slice, but skipping
|
|
// the result of the mapper function if it returns nil.
|
|
func MapN[E any, R any](source []E, indexer func(E) *R) []R {
|
|
if source == nil {
|
|
var zero []R
|
|
return zero
|
|
}
|
|
result := []R{}
|
|
for _, e := range source {
|
|
opt := indexer(e)
|
|
if opt != nil {
|
|
result = append(result, *opt)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Creates a slice from a slice, putting each value from the source slice through the
|
|
// mapper function to determine the value to store into the resulting slice, but skipping
|
|
// the result of the mapper function if it returns false as its second return value.
|
|
func MapO[E any, R any](source []E, indexer func(E) (R, bool)) []R {
|
|
if source == nil {
|
|
var zero []R
|
|
return zero
|
|
}
|
|
result := []R{}
|
|
for _, e := range source {
|
|
if value, keep := indexer(e); keep {
|
|
result = append(result, value)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Creates a map from a map, keeping each key as-is, and using the mapper
|
|
// function to determine the value to store into the resulting map.
|
|
func MapValues[K comparable, S any, T any](m map[K]S, mapper func(S) T) map[K]T {
|
|
r := make(map[K]T, len(m))
|
|
for k, s := range m {
|
|
r[k] = mapper(s)
|
|
}
|
|
return r
|
|
}
|
|
|
|
// Creates a map from a map, keeping each key as-is, and using the mapper function
|
|
// that takes both the key and the value to determine the value to store into the resulting map.
|
|
func MapValues2[K comparable, S any, T any](m map[K]S, mapper func(K, S) T) map[K]T {
|
|
r := make(map[K]T, len(m))
|
|
for k, s := range m {
|
|
r[k] = mapper(k, s)
|
|
}
|
|
return r
|
|
}
|
|
|
|
// Creates a map from a map, keeping each value as-is, and using the mapper
|
|
// function to determine the key to store into the resulting map.
|
|
func MapKeys[S comparable, T comparable, V any](m map[S]V, mapper func(S) T) map[T]V {
|
|
r := make(map[T]V, len(m))
|
|
for s, v := range m {
|
|
r[mapper(s)] = v
|
|
}
|
|
return r
|
|
}
|
|
|
|
// Creates a map from a map, keeping each value as-is, and using the mapper function
|
|
// that takes both the key and the value to determine the key to store into the resulting map.
|
|
func MapKeys2[S comparable, T comparable, V any](m map[S]V, mapper func(S, V) T) map[T]V {
|
|
r := make(map[T]V, len(m))
|
|
for s, v := range m {
|
|
r[mapper(s, v)] = v
|
|
}
|
|
return r
|
|
}
|
|
|
|
// Creates a map from a slice, using the mapper function to determine the key and value
|
|
// pair to use for each slice element in the resulting map.
|
|
func ToMap[E any, K comparable, V any](source []E, mapper func(E) (K, V)) map[K]V {
|
|
m := map[K]V{}
|
|
for _, e := range source {
|
|
k, v := mapper(e)
|
|
m[k] = v
|
|
}
|
|
return m
|
|
}
|
|
|
|
// Creates a map of booleans, using the values of the source slice as keys in the
|
|
// resulting map.
|
|
func ToBoolMap[E comparable](source []E) map[E]bool {
|
|
m := make(map[E]bool, len(source))
|
|
for _, v := range source {
|
|
m[v] = true
|
|
}
|
|
return m
|
|
}
|
|
|
|
// Creates a map of ints, using the values of the source slice as keys in the
|
|
// resulting map, and storing the number of occurences of every given value
|
|
// as the int value in the map.
|
|
func ToIntMap[E comparable](source []E) map[E]int {
|
|
m := make(map[E]int, len(source))
|
|
for _, v := range source {
|
|
if e, ok := m[v]; ok {
|
|
m[v] = e + 1
|
|
} else {
|
|
m[v] = 1
|
|
}
|
|
}
|
|
return m
|
|
}
|
|
|
|
// Check whether two slices contain the same elements, ignoring order.
|
|
func SameSlices[E comparable](x, y []E) bool {
|
|
// https://stackoverflow.com/a/36000696
|
|
if len(x) != len(y) {
|
|
return false
|
|
}
|
|
// create a map of string -> int
|
|
diff := make(map[E]int, len(x))
|
|
for _, _x := range x {
|
|
// 0 value for int is 0, so just increment a counter for the string
|
|
diff[_x]++
|
|
}
|
|
for _, _y := range y {
|
|
// If the string _y is not in diff bail out early
|
|
if _, ok := diff[_y]; !ok {
|
|
return false
|
|
}
|
|
diff[_y]--
|
|
if diff[_y] == 0 {
|
|
delete(diff, _y)
|
|
}
|
|
}
|
|
return len(diff) == 0
|
|
}
|
|
|
|
// Concatenate the elements of multiple slices into a single slice.
|
|
//
|
|
// Element order is preserved.
|
|
func Concat[E any](arys ...[]E) []E {
|
|
l := 0
|
|
for _, ary := range arys {
|
|
l += len(ary)
|
|
}
|
|
r := make([]E, l)
|
|
|
|
i := 0
|
|
for _, ary := range arys {
|
|
if ary != nil {
|
|
i += copy(r[i:], ary)
|
|
}
|
|
}
|
|
return r
|
|
}
|
|
|
|
// Create a new slice from a slice, determining whether each element should
|
|
// be added to the new slice by passing it to the predicate function.
|
|
//
|
|
// When the predicate function returns true, the element is stored in the
|
|
// new slice.
|
|
// When the predicate functoin returns false, the element is skipped and not
|
|
// stored in the new slice.
|
|
func Filter[E any](s []E, predicate func(E) bool) []E {
|
|
if s == nil {
|
|
var zero []E
|
|
return zero
|
|
}
|
|
r := []E{}
|
|
for _, e := range s {
|
|
if predicate(e) {
|
|
r = append(r, e)
|
|
}
|
|
}
|
|
return r
|
|
}
|
|
|
|
// Wrap an iterator with a conditional/filtering predicate function.
|
|
func FilterSeq[T any](it iter.Seq[T], predicate func(T) bool) iter.Seq[T] {
|
|
return func(yield func(s T) bool) {
|
|
for v := range it {
|
|
b := predicate(v)
|
|
if b {
|
|
if !yield(v) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|