preparing for v0.5

This commit is contained in:
default
2024-08-01 10:06:42 +08:00
parent 24778d1093
commit 93359110a2
115 changed files with 5153 additions and 4395 deletions

View File

@@ -0,0 +1,69 @@
package functional
import "sync"
func ForEachKey[K comparable, V interface{}](obj map[K]V, do func(K)) {
for k := range obj {
do(k)
}
}
func ForEachValue[K comparable, V interface{}](obj map[K]V, do func(V)) {
for _, v := range obj {
do(v)
}
}
func ForEachKV[K comparable, V interface{}](obj map[K]V, do func(K, V)) {
for k, v := range obj {
do(k, v)
}
}
func ParallelForEach[T interface{}](obj []T, do func(T)) {
var wg sync.WaitGroup
wg.Add(len(obj))
for _, v := range obj {
go func(v T) {
do(v)
wg.Done()
}(v)
}
wg.Wait()
}
func ParallelForEachKey[K comparable, V interface{}](obj map[K]V, do func(K)) {
var wg sync.WaitGroup
wg.Add(len(obj))
for k := range obj {
go func(k K) {
do(k)
wg.Done()
}(k)
}
wg.Wait()
}
func ParallelForEachValue[K comparable, V interface{}](obj map[K]V, do func(V)) {
var wg sync.WaitGroup
wg.Add(len(obj))
for _, v := range obj {
go func(v V) {
do(v)
wg.Done()
}(v)
}
wg.Wait()
}
func ParallelForEachKV[K comparable, V interface{}](obj map[K]V, do func(K, V)) {
var wg sync.WaitGroup
wg.Add(len(obj))
for k, v := range obj {
go func(k K, v V) {
do(k, v)
wg.Done()
}(k, v)
}
wg.Wait()
}

225
src/utils/functional/map.go Normal file
View File

@@ -0,0 +1,225 @@
package functional
import (
"context"
"sync"
"gopkg.in/yaml.v3"
E "github.com/yusing/go-proxy/error"
)
type Map[KT comparable, VT interface{}] struct {
m map[KT]VT
defVals map[KT]VT
sync.RWMutex
}
// NewMap creates a new Map with the given map as its initial values.
//
// Parameters:
// - dv: optional default values for the Map
//
// Return:
// - *Map[KT, VT]: a pointer to the newly created Map.
func NewMap[KT comparable, VT interface{}](dv ...map[KT]VT) *Map[KT, VT] {
return NewMapFrom(make(map[KT]VT), dv...)
}
// NewMapOf creates a new Map with the given map as its initial values.
//
// Type parameters:
// - M: type for the new map.
//
// Parameters:
// - dv: optional default values for the Map
//
// Return:
// - *Map[KT, VT]: a pointer to the newly created Map.
func NewMapOf[M Map[KT, VT], KT comparable, VT interface{}](dv ...map[KT]VT) *Map[KT, VT] {
return NewMapFrom(make(map[KT]VT), dv...)
}
// NewMapFrom creates a new Map with the given map as its initial values.
//
// Parameters:
// - from: a map of type KT to VT, which will be the initial values of the Map.
// - dv: optional default values for the Map
//
// Return:
// - *Map[KT, VT]: a pointer to the newly created Map.
func NewMapFrom[KT comparable, VT interface{}](from map[KT]VT, dv ...map[KT]VT) *Map[KT, VT] {
if len(dv) > 0 {
return &Map[KT, VT]{m: from, defVals: dv[0]}
}
return &Map[KT, VT]{m: from}
}
func (m *Map[KT, VT]) Set(key KT, value VT) {
m.Lock()
m.m[key] = value
m.Unlock()
}
func (m *Map[KT, VT]) Get(key KT) VT {
m.RLock()
defer m.RUnlock()
value, ok := m.m[key]
if !ok && m.defVals != nil {
return m.defVals[key]
}
return value
}
// Find searches for the first element in the map that satisfies the given criteria.
//
// Parameters:
// - criteria: a function that takes a value of type VT and returns a tuple of any type and a boolean.
//
// Return:
// - any: the first value that satisfies the criteria, or nil if no match is found.
func (m *Map[KT, VT]) Find(criteria func(VT) (any, bool)) any {
m.RLock()
defer m.RUnlock()
result := make(chan any)
wg := sync.WaitGroup{}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for _, v := range m.m {
wg.Add(1)
go func(val VT) {
defer wg.Done()
if value, ok := criteria(val); ok {
select {
case result <- value:
cancel() // Cancel other goroutines if a result is found
case <-ctx.Done(): // If already cancelled
return
}
}
}(v)
}
go func() {
wg.Wait()
close(result)
}()
// The first valid match, if any
select {
case res, ok := <-result:
if ok {
return res
}
case <-ctx.Done():
}
return nil // Return nil if no matches found
}
func (m *Map[KT, VT]) UnsafeGet(key KT) (VT, bool) {
value, ok := m.m[key]
return value, ok
}
func (m *Map[KT, VT]) UnsafeSet(key KT, value VT) {
m.m[key] = value
}
func (m *Map[KT, VT]) Delete(key KT) {
m.Lock()
delete(m.m, key)
m.Unlock()
}
// MergeWith merges the contents of another Map[KT, VT]
// into the current Map[KT, VT] and
// returns a map that were duplicated.
//
// Parameters:
// - other: a pointer to another Map[KT, VT] to be merged into the current Map[KT, VT].
//
// Return:
// - Map[KT, VT]: a map of key-value pairs that were duplicated during the merge.
func (m *Map[KT, VT]) MergeWith(other *Map[KT, VT]) Map[KT, VT] {
dups := make(map[KT]VT)
m.Lock()
for k, v := range other.m {
if _, isDup := m.m[k]; !isDup {
m.m[k] = v
} else {
dups[k] = v
}
}
m.Unlock()
return Map[KT, VT]{m: dups}
}
func (m *Map[KT, VT]) Clear() {
m.Lock()
m.m = make(map[KT]VT)
m.Unlock()
}
func (m *Map[KT, VT]) Size() int {
m.RLock()
defer m.RUnlock()
return len(m.m)
}
func (m *Map[KT, VT]) Contains(key KT) bool {
m.RLock()
_, ok := m.m[key]
m.RUnlock()
return ok
}
func (m *Map[KT, VT]) Clone() *Map[KT, VT] {
m.RLock()
defer m.RUnlock()
clone := make(map[KT]VT, len(m.m))
for k, v := range m.m {
clone[k] = v
}
return &Map[KT, VT]{m: clone, defVals: m.defVals}
}
func (m *Map[KT, VT]) EachKV(fn func(k KT, v VT)) {
m.Lock()
for k, v := range m.m {
fn(k, v)
}
m.Unlock()
}
func (m *Map[KT, VT]) Each(fn func(v VT)) {
m.Lock()
for _, v := range m.m {
fn(v)
}
m.Unlock()
}
func (m *Map[KT, VT]) EachParallel(fn func(v VT)) {
m.Lock()
ParallelForEachValue(m.m, fn)
m.Unlock()
}
func (m *Map[KT, VT]) EachKVParallel(fn func(k KT, v VT)) {
m.Lock()
ParallelForEachKV(m.m, fn)
m.Unlock()
}
func (m *Map[KT, VT]) UnmarshalFromYAML(data []byte) E.NestedError {
return E.From(yaml.Unmarshal(data, m.m))
}
func (m *Map[KT, VT]) Iterator() map[KT]VT {
return m.m
}

View File

@@ -0,0 +1,69 @@
package functional
type Slice[T any] struct {
s []T
}
func NewSlice[T any]() *Slice[T] {
return &Slice[T]{make([]T, 0)}
}
func NewSliceN[T any](n int) *Slice[T] {
return &Slice[T]{make([]T, n)}
}
func NewSliceFrom[T any](s []T) *Slice[T] {
return &Slice[T]{s}
}
func (s *Slice[T]) Size() int {
return len(s.s)
}
func (s *Slice[T]) Empty() bool {
return len(s.s) == 0
}
func (s *Slice[T]) NotEmpty() bool {
return len(s.s) > 0
}
func (s *Slice[T]) Iterator() []T {
return s.s
}
func (s *Slice[T]) Set(i int, v T) {
s.s[i] = v
}
func (s *Slice[T]) Add(e T) *Slice[T] {
return &Slice[T]{append(s.s, e)}
}
func (s *Slice[T]) AddRange(other *Slice[T]) *Slice[T] {
return &Slice[T]{append(s.s, other.s...)}
}
func (s *Slice[T]) ForEach(do func(T)) {
for _, v := range s.s {
do(v)
}
}
func (s *Slice[T]) Map(m func(T) T) *Slice[T] {
n := make([]T, len(s.s))
for i, v := range s.s {
n[i] = m(v)
}
return &Slice[T]{n}
}
func (s *Slice[T]) Filter(f func(T) bool) *Slice[T] {
n := make([]T, 0)
for _, v := range s.s {
if f(v) {
n = append(n, v)
}
}
return &Slice[T]{n}
}

View File

@@ -0,0 +1,68 @@
package functional
import (
"fmt"
"strconv"
"strings"
)
type Stringable struct{ string }
func NewStringable(v any) Stringable {
switch vv := v.(type) {
case string:
return Stringable{vv}
case fmt.Stringer:
return Stringable{vv.String()}
default:
return Stringable{fmt.Sprint(vv)}
}
}
func (s Stringable) String() string {
return s.string
}
func (s Stringable) Len() int {
return len(s.string)
}
func (s Stringable) MarshalText() (text []byte, err error) {
return []byte(s.string), nil
}
func (s Stringable) SubStr(start int, end int) Stringable {
return Stringable{s.string[start:end]}
}
func (s Stringable) HasPrefix(p Stringable) bool {
return len(s.string) >= len(p.string) && s.string[0:len(p.string)] == p.string
}
func (s Stringable) HasSuffix(p Stringable) bool {
return len(s.string) >= len(p.string) && s.string[len(s.string)-len(p.string):] == p.string
}
func (s Stringable) IsEmpty() bool {
return len(s.string) == 0
}
func (s Stringable) IndexRune(r rune) int {
return strings.IndexRune(s.string, r)
}
func (s Stringable) ToInt() (int, error) {
return strconv.Atoi(s.string)
}
func (s Stringable) Split(sep string) []Stringable {
return Stringables(strings.Split(s.string, sep))
}
func Stringables(ss []string) []Stringable {
ret := make([]Stringable, len(ss))
for i, s := range ss {
ret[i] = Stringable{s}
}
return ret
}