Files
gf/container/gset/gset_str_set.go
hailaz ee24da4e72 refactor: interface{} to any and reflect.Ptr to reflect.Pointer (#4395)
This pull request standardizes the use of the Go 1.18+ `any` type alias
instead of `interface{}` throughout the codebase. The change improves
code readability and aligns with modern Go best practices. The update
touches many files, including core data structures, code generation
templates, logging utilities, and test data, ensuring consistency across
all usages.

**Type alias migration to `any`:**

* Replaced all instances of `interface{}` with `any` in core data
structures such as `garray` and in generated model structs (e.g.,
`TableUser`, `User1`, `User2`) to modernize type usage.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[3]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[4]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[5]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[6]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
* Updated function signatures, method parameters, and return types from
`interface{}` to `any` in various parts of the codebase, including code
generation, service logic, and logging utilities (e.g., `mlog`).
[[1]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[2]](diffhunk://#diff-2b1953fb78cf3593d8c2c7d911e95b65fd0b847c30ed0b4d167d16fe6d781235L54-R74)
[[3]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[4]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
[[5]](diffhunk://#diff-c5d51d56f487779a2b6207c7ad26c7a20bbadcc846ce094fe60ab4cabff58c51L107-R107)
[[6]](diffhunk://#diff-f96e6a9fdb416eb1804ceaba1fe0ac637bff22c43837f8bb849c2366ce72d4a1L116-R121)
[[7]](diffhunk://#diff-f94c83a1b08ae060d9346f4a6031fc4a7b9a0b894e02d9afaa09018b6598eac0L112-R112)
[[8]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L36-R36)
[[9]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L74-R74)
[[10]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L96-R96)

**Generated code and templates:**

* Adjusted generated files and code generation templates to output `any`
instead of `interface{}` for relevant struct fields and function
signatures, ensuring that new code generation aligns with the updated
convention.
[[1]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[2]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[3]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[4]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[5]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
[[6]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[7]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[8]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)

**Container and utility updates:**

* Refactored the `garray` container implementation and related
constructors/methods to use `[]any` instead of `[]interface{}`, along
with corresponding function signatures.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L52-R52)
[[3]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L62-R62)
[[4]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L73-R86)
[[5]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L96-R97)
[[6]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L107-R114)
[[7]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L124-R124)
[[8]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L135-R143)
[[9]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L167-R167)

These changes collectively modernize the codebase and prepare it for
future Go developments by using the idiomatic `any` type.
2025-08-28 16:53:19 +08:00

519 lines
11 KiB
Go

// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
//
package gset
import (
"bytes"
"strings"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/rwmutex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
)
// StrSet is consisted of string items.
type StrSet struct {
mu rwmutex.RWMutex
data map[string]struct{}
}
// NewStrSet create and returns a new set, which contains un-repeated items.
// The parameter `safe` is used to specify whether using set in concurrent-safety,
// which is false in default.
func NewStrSet(safe ...bool) *StrSet {
return &StrSet{
mu: rwmutex.Create(safe...),
data: make(map[string]struct{}),
}
}
// NewStrSetFrom returns a new set from `items`.
func NewStrSetFrom(items []string, safe ...bool) *StrSet {
m := make(map[string]struct{})
for _, v := range items {
m[v] = struct{}{}
}
return &StrSet{
mu: rwmutex.Create(safe...),
data: m,
}
}
// Iterator iterates the set readonly with given callback function `f`,
// if `f` returns true then continue iterating; or false to stop.
func (set *StrSet) Iterator(f func(v string) bool) {
for _, k := range set.Slice() {
if !f(k) {
break
}
}
}
// Add adds one or multiple items to the set.
func (set *StrSet) Add(item ...string) {
set.mu.Lock()
if set.data == nil {
set.data = make(map[string]struct{})
}
for _, v := range item {
set.data[v] = struct{}{}
}
set.mu.Unlock()
}
// AddIfNotExist checks whether item exists in the set,
// it adds the item to set and returns true if it does not exist in the set,
// or else it does nothing and returns false.
func (set *StrSet) AddIfNotExist(item string) bool {
if !set.Contains(item) {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[string]struct{})
}
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
return false
}
// AddIfNotExistFunc checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set and
// function `f` returns true, or else it does nothing and returns false.
//
// Note that, the function `f` is executed without writing lock.
func (set *StrSet) AddIfNotExistFunc(item string, f func() bool) bool {
if !set.Contains(item) {
if f() {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[string]struct{})
}
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
}
return false
}
// AddIfNotExistFuncLock checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set and
// function `f` returns true, or else it does nothing and returns false.
//
// Note that, the function `f` is executed without writing lock.
func (set *StrSet) AddIfNotExistFuncLock(item string, f func() bool) bool {
if !set.Contains(item) {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[string]struct{})
}
if f() {
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
}
return false
}
// Contains checks whether the set contains `item`.
func (set *StrSet) Contains(item string) bool {
var ok bool
set.mu.RLock()
if set.data != nil {
_, ok = set.data[item]
}
set.mu.RUnlock()
return ok
}
// ContainsI checks whether a value exists in the set with case-insensitively.
// Note that it internally iterates the whole set to do the comparison with case-insensitively.
func (set *StrSet) ContainsI(item string) bool {
set.mu.RLock()
defer set.mu.RUnlock()
for k := range set.data {
if strings.EqualFold(k, item) {
return true
}
}
return false
}
// Remove deletes `item` from set.
func (set *StrSet) Remove(item string) {
set.mu.Lock()
if set.data != nil {
delete(set.data, item)
}
set.mu.Unlock()
}
// Size returns the size of the set.
func (set *StrSet) Size() int {
set.mu.RLock()
l := len(set.data)
set.mu.RUnlock()
return l
}
// Clear deletes all items of the set.
func (set *StrSet) Clear() {
set.mu.Lock()
set.data = make(map[string]struct{})
set.mu.Unlock()
}
// Slice returns the an of items of the set as slice.
func (set *StrSet) Slice() []string {
set.mu.RLock()
var (
i = 0
ret = make([]string, len(set.data))
)
for item := range set.data {
ret[i] = item
i++
}
set.mu.RUnlock()
return ret
}
// Join joins items with a string `glue`.
func (set *StrSet) Join(glue string) string {
set.mu.RLock()
defer set.mu.RUnlock()
if len(set.data) == 0 {
return ""
}
var (
l = len(set.data)
i = 0
buffer = bytes.NewBuffer(nil)
)
for k := range set.data {
buffer.WriteString(k)
if i != l-1 {
buffer.WriteString(glue)
}
i++
}
return buffer.String()
}
// String returns items as a string, which implements like json.Marshal does.
func (set *StrSet) String() string {
if set == nil {
return ""
}
set.mu.RLock()
defer set.mu.RUnlock()
var (
l = len(set.data)
i = 0
buffer = bytes.NewBuffer(nil)
)
buffer.WriteByte('[')
for k := range set.data {
buffer.WriteString(`"` + gstr.QuoteMeta(k, `"\`) + `"`)
if i != l-1 {
buffer.WriteByte(',')
}
i++
}
buffer.WriteByte(']')
return buffer.String()
}
// LockFunc locks writing with callback function `f`.
func (set *StrSet) LockFunc(f func(m map[string]struct{})) {
set.mu.Lock()
defer set.mu.Unlock()
f(set.data)
}
// RLockFunc locks reading with callback function `f`.
func (set *StrSet) RLockFunc(f func(m map[string]struct{})) {
set.mu.RLock()
defer set.mu.RUnlock()
f(set.data)
}
// Equal checks whether the two sets equal.
func (set *StrSet) Equal(other *StrSet) bool {
if set == other {
return true
}
set.mu.RLock()
defer set.mu.RUnlock()
other.mu.RLock()
defer other.mu.RUnlock()
if len(set.data) != len(other.data) {
return false
}
for key := range set.data {
if _, ok := other.data[key]; !ok {
return false
}
}
return true
}
// IsSubsetOf checks whether the current set is a sub-set of `other`.
func (set *StrSet) IsSubsetOf(other *StrSet) bool {
if set == other {
return true
}
set.mu.RLock()
defer set.mu.RUnlock()
other.mu.RLock()
defer other.mu.RUnlock()
for key := range set.data {
if _, ok := other.data[key]; !ok {
return false
}
}
return true
}
// Union returns a new set which is the union of `set` and `other`.
// Which means, all the items in `newSet` are in `set` or in `other`.
func (set *StrSet) Union(others ...*StrSet) (newSet *StrSet) {
newSet = NewStrSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range set.data {
newSet.data[k] = v
}
if set != other {
for k, v := range other.data {
newSet.data[k] = v
}
}
if set != other {
other.mu.RUnlock()
}
}
return
}
// Diff returns a new set which is the difference set from `set` to `other`.
// Which means, all the items in `newSet` are in `set` but not in `other`.
func (set *StrSet) Diff(others ...*StrSet) (newSet *StrSet) {
newSet = NewStrSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set == other {
continue
}
other.mu.RLock()
for k, v := range set.data {
if _, ok := other.data[k]; !ok {
newSet.data[k] = v
}
}
other.mu.RUnlock()
}
return
}
// Intersect returns a new set which is the intersection from `set` to `other`.
// Which means, all the items in `newSet` are in `set` and also in `other`.
func (set *StrSet) Intersect(others ...*StrSet) (newSet *StrSet) {
newSet = NewStrSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range set.data {
if _, ok := other.data[k]; ok {
newSet.data[k] = v
}
}
if set != other {
other.mu.RUnlock()
}
}
return
}
// Complement returns a new set which is the complement from `set` to `full`.
// Which means, all the items in `newSet` are in `full` and not in `set`.
//
// It returns the difference between `full` and `set`
// if the given set `full` is not the full set of `set`.
func (set *StrSet) Complement(full *StrSet) (newSet *StrSet) {
newSet = NewStrSet()
set.mu.RLock()
defer set.mu.RUnlock()
if set != full {
full.mu.RLock()
defer full.mu.RUnlock()
}
for k, v := range full.data {
if _, ok := set.data[k]; !ok {
newSet.data[k] = v
}
}
return
}
// Merge adds items from `others` sets into `set`.
func (set *StrSet) Merge(others ...*StrSet) *StrSet {
set.mu.Lock()
defer set.mu.Unlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range other.data {
set.data[k] = v
}
if set != other {
other.mu.RUnlock()
}
}
return set
}
// Sum sums items.
// Note: The items should be converted to int type,
// or you'd get a result that you unexpected.
func (set *StrSet) Sum() (sum int) {
set.mu.RLock()
defer set.mu.RUnlock()
for k := range set.data {
sum += gconv.Int(k)
}
return
}
// Pop randomly pops an item from set.
func (set *StrSet) Pop() string {
set.mu.Lock()
defer set.mu.Unlock()
for k := range set.data {
delete(set.data, k)
return k
}
return ""
}
// Pops randomly pops `size` items from set.
// It returns all items if size == -1.
func (set *StrSet) Pops(size int) []string {
set.mu.Lock()
defer set.mu.Unlock()
if size > len(set.data) || size == -1 {
size = len(set.data)
}
if size <= 0 {
return nil
}
index := 0
array := make([]string, size)
for k := range set.data {
delete(set.data, k)
array[index] = k
index++
if index == size {
break
}
}
return array
}
// Walk applies a user supplied function `f` to every item of set.
func (set *StrSet) Walk(f func(item string) string) *StrSet {
set.mu.Lock()
defer set.mu.Unlock()
m := make(map[string]struct{}, len(set.data))
for k, v := range set.data {
m[f(k)] = v
}
set.data = m
return set
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (set StrSet) MarshalJSON() ([]byte, error) {
return json.Marshal(set.Slice())
}
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (set *StrSet) UnmarshalJSON(b []byte) error {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[string]struct{})
}
var array []string
if err := json.UnmarshalUseNumber(b, &array); err != nil {
return err
}
for _, v := range array {
set.data[v] = struct{}{}
}
return nil
}
// UnmarshalValue is an interface implement which sets any type of value for set.
func (set *StrSet) UnmarshalValue(value any) (err error) {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[string]struct{})
}
var array []string
switch value.(type) {
case string, []byte:
err = json.UnmarshalUseNumber(gconv.Bytes(value), &array)
default:
array = gconv.SliceStr(value)
}
for _, v := range array {
set.data[v] = struct{}{}
}
return
}
// DeepCopy implements interface for deep copy of current type.
func (set *StrSet) DeepCopy() any {
if set == nil {
return nil
}
set.mu.RLock()
defer set.mu.RUnlock()
var (
slice = make([]string, len(set.data))
index = 0
)
for k := range set.data {
slice[index] = k
index++
}
return NewStrSetFrom(slice, set.mu.IsSafe())
}