diff --git a/configuration/README.md b/configuration/README.md index fec6fff..33e053d 100644 --- a/configuration/README.md +++ b/configuration/README.md @@ -1,19 +1,20 @@ # Configuration -configuration 基于配置导表功能实现的配置加载及刷新功能 - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/configuration) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +configuration 基于配置导表功能实现的配置加载及刷新功能 + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[Init](#Init)|配置初始化 |[Load](#Load)|加载配置 @@ -24,54 +25,66 @@ configuration 基于配置导表功能实现的配置加载及刷新功能 |[OnConfigRefreshEvent](#OnConfigRefreshEvent)|暂无描述... -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[RefreshEventHandle](#refresheventhandle)|配置刷新事件处理函数 -|[Loader](#loader)|配置加载器 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[RefreshEventHandle](#refresheventhandle)|配置刷新事件处理函数 +|`INTERFACE`|[Loader](#loader)|配置加载器
+*** +## 详情信息 #### func Init(loader ...Loader) > 配置初始化 > - 在初始化后会立即加载配置 + *** #### func Load() > 加载配置 > - 加载后并不会刷新线上配置,需要执行 Refresh 函数对线上配置进行刷新 + *** #### func Refresh() > 刷新配置 + *** #### func WithTickerLoad(ticker *timer.Ticker, interval time.Duration) > 通过定时器加载配置 > - 通过定时器加载配置后,会自动刷新线上配置 > - 调用该函数后不会立即刷新,而是在 interval 后加载并刷新一次配置,之后每隔 interval 加载并刷新一次配置 + *** #### func StopTickerLoad() > 停止通过定时器加载配置 + *** #### func RegConfigRefreshEvent(handle RefreshEventHandle) > 当配置刷新时将立即执行被注册的事件处理函数 + *** #### func OnConfigRefreshEvent() + *** -### RefreshEventHandle +### RefreshEventHandle `STRUCT` 配置刷新事件处理函数 ```go -type RefreshEventHandle struct{} +type RefreshEventHandle func() ``` -### Loader +### Loader `INTERFACE` 配置加载器 ```go -type Loader struct{} +type Loader interface { + Load() + Refresh() +} ``` diff --git a/examples/internal/deadlock-detect-server/README.md b/examples/internal/deadlock-detect-server/README.md index 9b11297..ffcf1c6 100644 --- a/examples/internal/deadlock-detect-server/README.md +++ b/examples/internal/deadlock-detect-server/README.md @@ -1,27 +1,22 @@ # Main - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/main) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 diff --git a/examples/internal/room-shunt-server/README.md b/examples/internal/room-shunt-server/README.md index 9b11297..ffcf1c6 100644 --- a/examples/internal/room-shunt-server/README.md +++ b/examples/internal/room-shunt-server/README.md @@ -1,27 +1,22 @@ # Main - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/main) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 diff --git a/examples/internal/simple-echo-websocket-server/README.md b/examples/internal/simple-echo-websocket-server/README.md index 9b11297..ffcf1c6 100644 --- a/examples/internal/simple-echo-websocket-server/README.md +++ b/examples/internal/simple-echo-websocket-server/README.md @@ -1,27 +1,22 @@ # Main - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/main) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 diff --git a/examples/internal/ticker-server/README.md b/examples/internal/ticker-server/README.md index 9b11297..ffcf1c6 100644 --- a/examples/internal/ticker-server/README.md +++ b/examples/internal/ticker-server/README.md @@ -1,27 +1,22 @@ # Main - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/main) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 diff --git a/examples/internal/ticker/README.md b/examples/internal/ticker/README.md index 9b11297..ffcf1c6 100644 --- a/examples/internal/ticker/README.md +++ b/examples/internal/ticker/README.md @@ -1,27 +1,22 @@ # Main - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/main) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 diff --git a/game/README.md b/game/README.md index 21a18af..0a38652 100644 --- a/game/README.md +++ b/game/README.md @@ -1,27 +1,10 @@ # Game -game 目录下包含了各类通用的游戏玩法性内容,其中该目录主要为基础性内容,具体目录将对应不同的游戏功能性内容。 - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/game) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ -
-展开 / 折叠目录 包级函数定义 - -|函数|描述 -|:--|:-- - - -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
+game 目录下包含了各类通用的游戏玩法性内容,其中该目录主要为基础性内容,具体目录将对应不同的游戏功能性内容。 +*** +## 详情信息 diff --git a/game/activity/README.md b/game/activity/README.md index cc1ecd3..ba61965 100644 --- a/game/activity/README.md +++ b/game/activity/README.md @@ -1,19 +1,20 @@ # Activity -activity 活动状态管理 - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/activity) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +activity 活动状态管理 + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[SetTicker](#SetTicker)|设置自定义定时器,该方法必须在使用活动系统前调用,且只能调用一次 |[LoadGlobalData](#LoadGlobalData)|加载所有活动全局数据 @@ -38,111 +39,134 @@ activity 活动状态管理 |[NewOptions](#NewOptions)|创建活动选项 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Activity](#activity)|活动描述 -|[Controller](#controller)|活动控制器 -|[BasicActivityController](#basicactivitycontroller)|暂无描述... -|[NoneDataActivityController](#nonedataactivitycontroller)|无数据活动控制器 -|[GlobalDataActivityController](#globaldataactivitycontroller)|全局数据活动控制器 -|[EntityDataActivityController](#entitydataactivitycontroller)|实体数据活动控制器 -|[GlobalAndEntityDataActivityController](#globalandentitydataactivitycontroller)|全局数据和实体数据活动控制器 -|[DataMeta](#datameta)|全局活动数据 -|[EntityDataMeta](#entitydatameta)|活动实体数据 -|[UpcomingEventHandler](#upcomingeventhandler)|暂无描述... -|[Options](#options)|活动选项 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Activity](#activity)|活动描述 +|`STRUCT`|[Controller](#controller)|活动控制器 +|`INTERFACE`|[BasicActivityController](#basicactivitycontroller)|暂无描述... +|`INTERFACE`|[NoneDataActivityController](#nonedataactivitycontroller)|无数据活动控制器 +|`INTERFACE`|[GlobalDataActivityController](#globaldataactivitycontroller)|全局数据活动控制器 +|`INTERFACE`|[EntityDataActivityController](#entitydataactivitycontroller)|实体数据活动控制器 +|`INTERFACE`|[GlobalAndEntityDataActivityController](#globalandentitydataactivitycontroller)|全局数据和实体数据活动控制器 +|`STRUCT`|[DataMeta](#datameta)|全局活动数据 +|`STRUCT`|[EntityDataMeta](#entitydatameta)|活动实体数据 +|`STRUCT`|[UpcomingEventHandler](#upcomingeventhandler)|暂无描述... +|`STRUCT`|[Options](#options)|活动选项
+*** +## 详情信息 #### func SetTicker(size int, options ...timer.Option) > 设置自定义定时器,该方法必须在使用活动系统前调用,且只能调用一次 + *** #### func LoadGlobalData(handler func (activityType any)) > 加载所有活动全局数据 + *** #### func LoadEntityData(handler func (activityType any)) > 加载所有活动实体数据 + *** #### func LoadOrRefreshActivity(activityType Type, activityId ID, options ...*Options) error > 加载或刷新活动 > - 通常在活动配置刷新时候将活动通过该方法注册或刷新 + *** #### func DefineNoneDataActivity(activityType Type) NoneDataActivityController[Type, ID, *none, none, *none] > 声明无数据的活动类型 + *** #### func DefineGlobalDataActivity(activityType Type) GlobalDataActivityController[Type, ID, Data, none, *none] > 声明拥有全局数据的活动类型 + *** #### func DefineEntityDataActivity(activityType Type) EntityDataActivityController[Type, ID, *none, EntityID, EntityData] > 声明拥有实体数据的活动类型 + *** #### func DefineGlobalAndEntityDataActivity(activityType Type) GlobalAndEntityDataActivityController[Type, ID, Data, EntityID, EntityData] > 声明拥有全局数据和实体数据的活动类型 + *** #### func RegUpcomingEvent(activityType Type, handler UpcomingEventHandler[ID], priority ...int) > 注册即将开始的活动事件处理器 + *** #### func OnUpcomingEvent(activity *Activity[Type, ID]) > 即将开始的活动事件 + *** #### func RegStartedEvent(activityType Type, handler StartedEventHandler[ID], priority ...int) > 注册活动开始事件处理器 + *** #### func OnStartedEvent(activity *Activity[Type, ID]) > 活动开始事件 + *** #### func RegEndedEvent(activityType Type, handler EndedEventHandler[ID], priority ...int) > 注册活动结束事件处理器 + *** #### func OnEndedEvent(activity *Activity[Type, ID]) > 活动结束事件 + *** #### func RegExtendedShowStartedEvent(activityType Type, handler ExtendedShowStartedEventHandler[ID], priority ...int) > 注册活动结束后延长展示开始事件处理器 + *** #### func OnExtendedShowStartedEvent(activity *Activity[Type, ID]) > 活动结束后延长展示开始事件 + *** #### func RegExtendedShowEndedEvent(activityType Type, handler ExtendedShowEndedEventHandler[ID], priority ...int) > 注册活动结束后延长展示结束事件处理器 + *** #### func OnExtendedShowEndedEvent(activity *Activity[Type, ID]) > 活动结束后延长展示结束事件 + *** #### func RegNewDayEvent(activityType Type, handler NewDayEventHandler[ID], priority ...int) > 注册新的一天事件处理器 + *** #### func OnNewDayEvent(activity *Activity[Type, ID]) > 新的一天事件 + *** #### func NewOptions() *Options > 创建活动选项 + *** -### Activity +### Activity `STRUCT` 活动描述 ```go type Activity[Type generic.Basic, ID generic.Basic] struct { @@ -161,7 +185,7 @@ type Activity[Type generic.Basic, ID generic.Basic] struct { initializeData func() } ``` -### Controller +### Controller `STRUCT` 活动控制器 ```go type Controller[Type generic.Basic, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any] struct { @@ -175,32 +199,53 @@ type Controller[Type generic.Basic, ID generic.Basic, Data any, EntityID generic mutex sync.RWMutex } ``` -### BasicActivityController +### BasicActivityController `INTERFACE` ```go -type BasicActivityController[Type generic.Basic, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any] struct{} +type BasicActivityController[Type generic.Basic, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any] interface { + IsOpen(activityId ID) bool + IsShow(activityId ID) bool + IsOpenOrShow(activityId ID) bool + Refresh(activityId ID) +} ``` -### NoneDataActivityController +### NoneDataActivityController `INTERFACE` 无数据活动控制器 ```go -type NoneDataActivityController[Type generic.Basic, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any] struct{} +type NoneDataActivityController[Type generic.Basic, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any] interface { + BasicActivityController[Type, ID, Data, EntityID, EntityData] + InitializeNoneData(handler func(activityId ID, data *DataMeta[Data])) NoneDataActivityController[Type, ID, Data, EntityID, EntityData] +} ``` -### GlobalDataActivityController +### GlobalDataActivityController `INTERFACE` 全局数据活动控制器 ```go -type GlobalDataActivityController[Type generic.Basic, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any] struct{} +type GlobalDataActivityController[Type generic.Basic, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any] interface { + BasicActivityController[Type, ID, Data, EntityID, EntityData] + GetGlobalData(activityId ID) Data + InitializeGlobalData(handler func(activityId ID, data *DataMeta[Data])) GlobalDataActivityController[Type, ID, Data, EntityID, EntityData] +} ``` -### EntityDataActivityController +### EntityDataActivityController `INTERFACE` 实体数据活动控制器 ```go -type EntityDataActivityController[Type generic.Basic, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any] struct{} +type EntityDataActivityController[Type generic.Basic, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any] interface { + BasicActivityController[Type, ID, Data, EntityID, EntityData] + GetEntityData(activityId ID, entityId EntityID) EntityData + InitializeEntityData(handler func(activityId ID, entityId EntityID, data *EntityDataMeta[EntityData])) EntityDataActivityController[Type, ID, Data, EntityID, EntityData] +} ``` -### GlobalAndEntityDataActivityController +### GlobalAndEntityDataActivityController `INTERFACE` 全局数据和实体数据活动控制器 ```go -type GlobalAndEntityDataActivityController[Type generic.Basic, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any] struct{} +type GlobalAndEntityDataActivityController[Type generic.Basic, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any] interface { + BasicActivityController[Type, ID, Data, EntityID, EntityData] + GetGlobalData(activityId ID) Data + GetEntityData(activityId ID, entityId EntityID) EntityData + InitializeGlobalAndEntityData(handler func(activityId ID, data *DataMeta[Data]), entityHandler func(activityId ID, entityId EntityID, data *EntityDataMeta[EntityData])) GlobalAndEntityDataActivityController[Type, ID, Data, EntityID, EntityData] +} ``` -### DataMeta +### DataMeta `STRUCT` 全局活动数据 ```go type DataMeta[Data any] struct { @@ -209,7 +254,7 @@ type DataMeta[Data any] struct { LastNewDay time.Time } ``` -### EntityDataMeta +### EntityDataMeta `STRUCT` 活动实体数据 ```go type EntityDataMeta[Data any] struct { @@ -218,12 +263,12 @@ type EntityDataMeta[Data any] struct { LastNewDay time.Time } ``` -### UpcomingEventHandler +### UpcomingEventHandler `STRUCT` ```go -type UpcomingEventHandler[ID generic.Basic] struct{} +type UpcomingEventHandler[ID generic.Basic] func(activityId ID) ``` -### Options +### Options `STRUCT` 活动选项 ```go type Options struct { diff --git a/game/activity/internal/example/README.md b/game/activity/internal/example/README.md index 9b11297..ffcf1c6 100644 --- a/game/activity/internal/example/README.md +++ b/game/activity/internal/example/README.md @@ -1,27 +1,22 @@ # Main - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/main) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 diff --git a/game/activity/internal/example/activities/README.md b/game/activity/internal/example/activities/README.md index c54229d..aa77080 100644 --- a/game/activity/internal/example/activities/README.md +++ b/game/activity/internal/example/activities/README.md @@ -1,27 +1,22 @@ # Activities - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/activities) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 diff --git a/game/activity/internal/example/activities/demoactivity/README.md b/game/activity/internal/example/activities/demoactivity/README.md index 6c7406a..0fcb6dc 100644 --- a/game/activity/internal/example/activities/demoactivity/README.md +++ b/game/activity/internal/example/activities/demoactivity/README.md @@ -1,27 +1,22 @@ # Demoactivity - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/demoactivity) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 diff --git a/game/activity/internal/example/types/README.md b/game/activity/internal/example/types/README.md index fe640b0..2f2c1a0 100644 --- a/game/activity/internal/example/types/README.md +++ b/game/activity/internal/example/types/README.md @@ -1,32 +1,29 @@ # Types - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/types) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 -> 包级函数定义 +> 类型定义 -|函数|描述 -|:--|:-- - - -> 结构体定义 - -|结构体|描述 -|:--|:-- -|[DemoActivityData](#demoactivitydata)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[DemoActivityData](#demoactivitydata)|暂无描述...
-### DemoActivityData +*** +## 详情信息 +### DemoActivityData `STRUCT` ```go type DemoActivityData struct { diff --git a/game/fight/README.md b/game/fight/README.md index 7fb67f9..a82ccf6 100644 --- a/game/fight/README.md +++ b/game/fight/README.md @@ -1,42 +1,46 @@ # Fight - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/fight) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewTurnBased](#NewTurnBased)|创建一个新的回合制 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[TurnBased](#turnbased)|回合制 -|[TurnBasedControllerInfo](#turnbasedcontrollerinfo)|暂无描述... -|[TurnBasedControllerAction](#turnbasedcontrolleraction)|暂无描述... -|[TurnBasedController](#turnbasedcontroller)|回合制控制器 -|[TurnBasedEntitySwitchEventHandler](#turnbasedentityswitcheventhandler)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[TurnBased](#turnbased)|回合制 +|`INTERFACE`|[TurnBasedControllerInfo](#turnbasedcontrollerinfo)|暂无描述... +|`INTERFACE`|[TurnBasedControllerAction](#turnbasedcontrolleraction)|暂无描述... +|`STRUCT`|[TurnBasedController](#turnbasedcontroller)|回合制控制器 +|`STRUCT`|[TurnBasedEntitySwitchEventHandler](#turnbasedentityswitcheventhandler)|暂无描述...
+*** +## 详情信息 #### func NewTurnBased(calcNextTurnDuration func ( Camp, Entity) time.Duration) *TurnBased[CampID, EntityID, Camp, Entity] > 创建一个新的回合制 > - calcNextTurnDuration 将返回下一次行动时间间隔,适用于按照速度计算下一次行动时间间隔的情况。当返回 0 时,将使用默认的行动超时时间 + *** -### TurnBased +### TurnBased `STRUCT` 回合制 ```go type TurnBased[CampID comparable, EntityID comparable, Camp generic.IdR[CampID], Entity generic.IdR[EntityID]] struct { @@ -60,25 +64,37 @@ type TurnBased[CampID comparable, EntityID comparable, Camp generic.IdR[CampID], closed bool } ``` -### TurnBasedControllerInfo +### TurnBasedControllerInfo `INTERFACE` ```go -type TurnBasedControllerInfo[CampID comparable, EntityID comparable, Camp generic.IdR[CampID], Entity generic.IdR[EntityID]] struct{} +type TurnBasedControllerInfo[CampID comparable, EntityID comparable, Camp generic.IdR[CampID], Entity generic.IdR[EntityID]] interface { + GetRound() int + GetCamp() Camp + GetEntity() Entity + GetActionTimeoutDuration() time.Duration + GetActionStartTime() time.Time + GetActionEndTime() time.Time + Stop() +} ``` -### TurnBasedControllerAction +### TurnBasedControllerAction `INTERFACE` ```go -type TurnBasedControllerAction[CampID comparable, EntityID comparable, Camp generic.IdR[CampID], Entity generic.IdR[EntityID]] struct{} +type TurnBasedControllerAction[CampID comparable, EntityID comparable, Camp generic.IdR[CampID], Entity generic.IdR[EntityID]] interface { + TurnBasedControllerInfo[CampID, EntityID, Camp, Entity] + Finish() + Refresh(duration time.Duration) time.Time +} ``` -### TurnBasedController +### TurnBasedController `STRUCT` 回合制控制器 ```go type TurnBasedController[CampID comparable, EntityID comparable, Camp generic.IdR[CampID], Entity generic.IdR[EntityID]] struct { tb *TurnBased[CampID, EntityID, Camp, Entity] } ``` -### TurnBasedEntitySwitchEventHandler +### TurnBasedEntitySwitchEventHandler `STRUCT` ```go -type TurnBasedEntitySwitchEventHandler[CampID comparable, EntityID comparable, Camp generic.IdR[CampID], Entity generic.IdR[EntityID]] struct{} +type TurnBasedEntitySwitchEventHandler[CampID comparable, EntityID comparable, Camp generic.IdR[CampID], Entity generic.IdR[EntityID]] func(controller TurnBasedControllerAction[CampID, EntityID, Camp, Entity]) ``` diff --git a/game/space/README.md b/game/space/README.md index 24cc824..b9c3455 100644 --- a/game/space/README.md +++ b/game/space/README.md @@ -1,45 +1,60 @@ # Space -space 游戏中常见的空间设计,例如房间、地图等 - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/space) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +space 游戏中常见的空间设计,例如房间、地图等 + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewRoomManager](#NewRoomManager)|创建房间管理器 RoomManager 的实例 |[NewRoomControllerOptions](#NewRoomControllerOptions)|创建房间控制器选项 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[RoomController](#roomcontroller)|对房间进行操作的控制器,由 RoomManager 接管后返回 -|[RoomManager](#roommanager)|房间管理器是用于对房间进行管理的基本单元,通过该实例可以对房间进行增删改查等操作 -|[RoomAssumeControlEventHandle](#roomassumecontroleventhandle)|暂无描述... -|[RoomControllerOptions](#roomcontrolleroptions)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[RoomController](#roomcontroller)|对房间进行操作的控制器,由 RoomManager 接管后返回 +|`STRUCT`|[RoomManager](#roommanager)|房间管理器是用于对房间进行管理的基本单元,通过该实例可以对房间进行增删改查等操作 +|`STRUCT`|[RoomAssumeControlEventHandle](#roomassumecontroleventhandle)|暂无描述... +|`STRUCT`|[RoomControllerOptions](#roomcontrolleroptions)|暂无描述...
+*** +## 详情信息 #### func NewRoomManager() *RoomManager[EntityID, RoomID, Entity, Room] > 创建房间管理器 RoomManager 的实例 + +示例代码: +```go + +func ExampleNewRoomManager() { + var rm = space.NewRoomManager[string, int64, *Player, *Room]() + fmt.Println(rm == nil) +} + +``` + *** #### func NewRoomControllerOptions() *RoomControllerOptions[EntityID, RoomID, Entity, Room] > 创建房间控制器选项 + *** -### RoomController +### RoomController `STRUCT` 对房间进行操作的控制器,由 RoomManager 接管后返回 ```go type RoomController[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]] struct { @@ -53,7 +68,7 @@ type RoomController[EntityID comparable, RoomID comparable, Entity generic.IdR[E owner *EntityID } ``` -### RoomManager +### RoomManager `STRUCT` 房间管理器是用于对房间进行管理的基本单元,通过该实例可以对房间进行增删改查等操作 - 该实例是线程安全的 ```go @@ -63,12 +78,12 @@ type RoomManager[EntityID comparable, RoomID comparable, Entity generic.IdR[Enti rooms map[RoomID]*RoomController[EntityID, RoomID, Entity, Room] } ``` -### RoomAssumeControlEventHandle +### RoomAssumeControlEventHandle `STRUCT` ```go -type RoomAssumeControlEventHandle[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]] struct{} +type RoomAssumeControlEventHandle[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]] func(controller *RoomController[EntityID, RoomID, Entity, Room]) ``` -### RoomControllerOptions +### RoomControllerOptions `STRUCT` ```go type RoomControllerOptions[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]] struct { diff --git a/game/task/README.md b/game/task/README.md index 621a2b7..55425db 100644 --- a/game/task/README.md +++ b/game/task/README.md @@ -1,19 +1,20 @@ # Task - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/task) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[Cond](#Cond)|创建任务条件 |[RegisterRefreshTaskCounterEvent](#RegisterRefreshTaskCounterEvent)|注册特定任务类型的刷新任务计数器事件处理函数 @@ -29,48 +30,95 @@ |[NewTask](#NewTask)|生成任务 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Condition](#condition)|任务条件 -|[RefreshTaskCounterEventHandler](#refreshtaskcountereventhandler)|暂无描述... -|[Option](#option)|任务选项 -|[Status](#status)|暂无描述... -|[Task](#task)|是对任务信息进行描述和处理的结构体 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Condition](#condition)|任务条件 +|`STRUCT`|[RefreshTaskCounterEventHandler](#refreshtaskcountereventhandler)|暂无描述... +|`STRUCT`|[Option](#option)|任务选项 +|`STRUCT`|[Status](#status)|暂无描述... +|`STRUCT`|[Task](#task)|是对任务信息进行描述和处理的结构体
+*** +## 详情信息 #### func Cond(k any, v any) Condition > 创建任务条件 + +
+查看 / 收起单元测试 + + +```go + +func TestCond(t *testing.T) { + task := NewTask(WithType("T"), WithCounter(5), WithCondition(Cond("N", 5).Cond("M", 10))) + task.AssignConditionValueAndRefresh("N", 5) + task.AssignConditionValueAndRefresh("M", 10) + RegisterRefreshTaskCounterEvent[*Player](task.Type, func(taskType string, trigger *Player, count int64) { + fmt.Println("Player", count) + for _, t := range trigger.tasks[taskType] { + fmt.Println(t.CurrCount, t.IncrementCounter(count).Status) + } + }) + RegisterRefreshTaskConditionEvent[*Player](task.Type, func(taskType string, trigger *Player, condition Condition) { + fmt.Println("Player", condition) + for _, t := range trigger.tasks[taskType] { + fmt.Println(t.CurrCount, t.AssignConditionValueAndRefresh("N", 5).Status) + } + }) + RegisterRefreshTaskCounterEvent[*Monster](task.Type, func(taskType string, trigger *Monster, count int64) { + fmt.Println("Monster", count) + }) + player := &Player{tasks: map[string][]*Task{task.Type: {task}}} + OnRefreshTaskCounterEvent(task.Type, player, 1) + OnRefreshTaskCounterEvent(task.Type, player, 2) + OnRefreshTaskCounterEvent(task.Type, player, 3) + OnRefreshTaskCounterEvent(task.Type, new(Monster), 3) +} + +``` + + +
+ + *** #### func RegisterRefreshTaskCounterEvent(taskType string, handler RefreshTaskCounterEventHandler[Trigger]) > 注册特定任务类型的刷新任务计数器事件处理函数 + *** #### func OnRefreshTaskCounterEvent(taskType string, trigger any, count int64) > 触发特定任务类型的刷新任务计数器事件 + *** #### func RegisterRefreshTaskConditionEvent(taskType string, handler RefreshTaskConditionEventHandler[Trigger]) > 注册特定任务类型的刷新任务条件事件处理函数 + *** #### func OnRefreshTaskConditionEvent(taskType string, trigger any, condition Condition) > 触发特定任务类型的刷新任务条件事件 + *** #### func WithType(taskType string) Option > 设置任务类型 + *** #### func WithCondition(condition Condition) Option > 设置任务完成条件,当满足条件时,任务状态为完成 > - 任务条件值需要变更时可通过 Task.AssignConditionValueAndRefresh 方法变更 > - 当多次设置该选项时,后面的设置会覆盖之前的设置 + *** #### func WithCounter(counter int64, initCount ...int64) Option @@ -78,29 +126,34 @@ > - 一些场景下,任务计数器可能会溢出,此时可通过 WithOverflowCounter 设置可溢出的任务计数器 > - 当多次设置该选项时,后面的设置会覆盖之前的设置 > - 如果需要初始化计数器的值,可通过 initCount 参数设置 + *** #### func WithOverflowCounter(counter int64, initCount ...int64) Option > 设置可溢出的任务计数器,当计数器达到要求时,任务状态为完成 > - 当多次设置该选项时,后面的设置会覆盖之前的设置 > - 如果需要初始化计数器的值,可通过 initCount 参数设置 + *** #### func WithDeadline(deadline time.Time) Option > 设置任务截止时间,超过截至时间并且任务未完成时,任务状态为失败 + *** #### func WithLimitedDuration(start time.Time, duration time.Duration) Option > 设置任务限时,超过限时时间并且任务未完成时,任务状态为失败 + *** #### func NewTask(options ...Option) *Task > 生成任务 + *** -### Condition +### Condition `STRUCT` 任务条件 ```go -type Condition struct{} +type Condition map[any]any ``` #### func (Condition) Cond(k any, v any) Condition > 创建任务条件 @@ -168,24 +221,24 @@ type Condition struct{} #### func (Condition) GetAny(key any) any > 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值 *** -### RefreshTaskCounterEventHandler +### RefreshTaskCounterEventHandler `STRUCT` ```go -type RefreshTaskCounterEventHandler[Trigger any] struct{} +type RefreshTaskCounterEventHandler[Trigger any] func(taskType string, trigger Trigger, count int64) ``` -### Option +### Option `STRUCT` 任务选项 ```go -type Option struct{} +type Option func(task *Task) ``` -### Status +### Status `STRUCT` ```go -type Status struct{} +type Status byte ``` #### func (Status) String() string *** -### Task +### Task `STRUCT` 是对任务信息进行描述和处理的结构体 ```go type Task struct { diff --git a/notify/README.md b/notify/README.md index 8def00b..ae24472 100644 --- a/notify/README.md +++ b/notify/README.md @@ -1,39 +1,43 @@ # Notify -notify 包含了对外部第三方通知的实现,如机器人消息等 - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/notify) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +notify 包含了对外部第三方通知的实现,如机器人消息等 + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewManager](#NewManager)|通过指定的 Sender 创建一个通知管理器, senders 包中提供了一些内置的 Sender -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Manager](#manager)|通知管理器,可用于将通知同时发送至多个渠道 -|[Notify](#notify)|通用通知接口定义 -|[Sender](#sender)|通知发送器接口声明 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Manager](#manager)|通知管理器,可用于将通知同时发送至多个渠道 +|`INTERFACE`|[Notify](#notify)|通用通知接口定义 +|`INTERFACE`|[Sender](#sender)|通知发送器接口声明
+*** +## 详情信息 #### func NewManager(senders ...Sender) *Manager > 通过指定的 Sender 创建一个通知管理器, senders 包中提供了一些内置的 Sender + *** -### Manager +### Manager `STRUCT` 通知管理器,可用于将通知同时发送至多个渠道 ```go type Manager struct { @@ -48,13 +52,17 @@ type Manager struct { #### func (*Manager) Release() > 释放通知管理器 *** -### Notify +### Notify `INTERFACE` 通用通知接口定义 ```go -type Notify struct{} +type Notify interface { + Format() (string, error) +} ``` -### Sender +### Sender `INTERFACE` 通知发送器接口声明 ```go -type Sender struct{} +type Sender interface { + Push(notify Notify) error +} ``` diff --git a/notify/notifies/README.md b/notify/notifies/README.md index d599df0..91b7bc3 100644 --- a/notify/notifies/README.md +++ b/notify/notifies/README.md @@ -1,19 +1,20 @@ # Notifies -notifies 包含了内置通知内容的实现 - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/notifies) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +notifies 包含了内置通知内容的实现 + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewFeiShu](#NewFeiShu)|创建飞书通知消息 |[FeiShuMessageWithText](#FeiShuMessageWithText)|飞书文本消息 @@ -30,21 +31,24 @@ notifies 包含了内置通知内容的实现 |[NewFeiShuRichText](#NewFeiShuRichText)|创建一个飞书富文本 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[FeiShu](#feishu)|飞书通知消息 -|[FeiShuMessage](#feishumessage)|暂无描述... -|[FeiShuRichText](#feishurichtext)|飞书富文本结构 -|[FeiShuRichTextContent](#feishurichtextcontent)|飞书富文本内容体 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[FeiShu](#feishu)|飞书通知消息 +|`STRUCT`|[FeiShuMessage](#feishumessage)|暂无描述... +|`STRUCT`|[FeiShuRichText](#feishurichtext)|飞书富文本结构 +|`STRUCT`|[FeiShuRichTextContent](#feishurichtextcontent)|飞书富文本内容体
+*** +## 详情信息 #### func NewFeiShu(message FeiShuMessage) *FeiShu > 创建飞书通知消息 + *** #### func FeiShuMessageWithText(text string) FeiShuMessage @@ -62,20 +66,24 @@ notifies 包含了内置通知内容的实现 > 超链接使用说明 > - 超链接的使用格式为[文本](链接), 如[Feishu Open Platform](https://open.feishu.cn) 。 > - 请确保链接是合法的,否则会以原始内容发送消息。 + *** #### func FeiShuMessageWithRichText(richText *FeiShuRichText) FeiShuMessage > 飞书富文本消息 + *** #### func FeiShuMessageWithImage(imageKey string) FeiShuMessage > 飞书图片消息 > - imageKey 可通过上传图片接口获取 + *** #### func FeiShuMessageWithInteractive(json string) FeiShuMessage > 飞书卡片消息 > - json 表示卡片的 json 数据或者消息模板的 json 数据 + *** #### func FeiShuMessageWithShareChat(chatId string) FeiShuMessage @@ -83,6 +91,7 @@ notifies 包含了内置通知内容的实现 > - chatId 群ID获取方式请参见群ID说明 > > 群ID说明:https://open.feishu.cn/document/server-docs/group/chat/chat-id-description + *** #### func FeiShuMessageWithShareUser(userId string) FeiShuMessage @@ -90,6 +99,7 @@ notifies 包含了内置通知内容的实现 > - userId 表示用户的 OpenID 获取方式请参见了解更多:如何获取 Open ID > > 如何获取 Open ID:https://open.feishu.cn/document/faq/trouble-shooting/how-to-obtain-openid + *** #### func FeiShuMessageWithAudio(fileKey string) FeiShuMessage @@ -97,6 +107,7 @@ notifies 包含了内置通知内容的实现 > - fileKey 语音文件Key,可通过上传文件接口获取 > > 上传文件:https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/file/create + *** #### func FeiShuMessageWithMedia(fileKey string) FeiShuMessage @@ -104,6 +115,7 @@ notifies 包含了内置通知内容的实现 > - fileKey 视频文件Key,可通过上传文件接口获取 > > 上传文件:https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/file/create + *** #### func FeiShuMessageWithMediaAndCover(fileKey string, imageKey string) FeiShuMessage @@ -112,6 +124,7 @@ notifies 包含了内置通知内容的实现 > - imageKey 图片文件Key,可通过上传文件接口获取 > > 上传文件:https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/file/create + *** #### func FeiShuMessageWithFile(fileKey string) FeiShuMessage @@ -119,6 +132,7 @@ notifies 包含了内置通知内容的实现 > - fileKey 文件Key,可通过上传文件接口获取 > > 上传文件:https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/file/create + *** #### func FeiShuMessageWithSticker(fileKey string) FeiShuMessage @@ -126,12 +140,14 @@ notifies 包含了内置通知内容的实现 > - fileKey 表情包文件Key,目前仅支持发送机器人收到的表情包,可通过接收消息事件的推送获取表情包 file_key。 > > 接收消息事件:https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/events/receive + *** #### func NewFeiShuRichText() *FeiShuRichText > 创建一个飞书富文本 + *** -### FeiShu +### FeiShu `STRUCT` 飞书通知消息 ```go type FeiShu struct { @@ -142,12 +158,12 @@ type FeiShu struct { #### func (*FeiShu) Format() string, error > 格式化通知内容 *** -### FeiShuMessage +### FeiShuMessage `STRUCT` ```go -type FeiShuMessage struct{} +type FeiShuMessage func(feishu *FeiShu) ``` -### FeiShuRichText +### FeiShuRichText `STRUCT` 飞书富文本结构 ```go type FeiShuRichText struct { @@ -157,7 +173,7 @@ type FeiShuRichText struct { #### func (*FeiShuRichText) Create(lang string, title string) *FeiShuRichTextContent > 创建一个特定语言和标题的富文本内容 *** -### FeiShuRichTextContent +### FeiShuRichTextContent `STRUCT` 飞书富文本内容体 ```go type FeiShuRichTextContent struct { diff --git a/notify/senders/README.md b/notify/senders/README.md index f19bcef..a662c83 100644 --- a/notify/senders/README.md +++ b/notify/senders/README.md @@ -1,37 +1,41 @@ # Senders -senders Package 包含了内置通知发送器的实现 - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/senders) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +senders Package 包含了内置通知发送器的实现 + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewFeiShu](#NewFeiShu)|根据特定的 webhook 地址创建飞书发送器 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[FeiShu](#feishu)|飞书发送器 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[FeiShu](#feishu)|飞书发送器
+*** +## 详情信息 #### func NewFeiShu(webhook string) *FeiShu > 根据特定的 webhook 地址创建飞书发送器 + *** -### FeiShu +### FeiShu `STRUCT` 飞书发送器 ```go type FeiShu struct { @@ -41,4 +45,24 @@ type FeiShu struct { ``` #### func (*FeiShu) Push(notify notify.Notify) error > 推送通知 +
+查看 / 收起单元测试 + + +```go + +func TestFeiShu_Push(t *testing.T) { + fs := NewFeiShu("https://open.feishu.cn/open-apis/bot/v2/hook/d886f30f-814c-47b1-aeb0-b508da0f7f22") + rt := notifies.NewFeiShu(notifies.FeiShuMessageWithRichText(notifies.NewFeiShuRichText().Create("zh_cn", "标题咯").AddText("哈哈哈").Ok())) + if err := fs.Push(rt); err != nil { + panic(err) + } +} + +``` + + +
+ + *** diff --git a/planner/README.md b/planner/README.md index c2a0708..926856a 100644 --- a/planner/README.md +++ b/planner/README.md @@ -1,27 +1,10 @@ # Planner -planner 包含了策划工具相关的内容 - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/planner) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ -
-展开 / 折叠目录 包级函数定义 - -|函数|描述 -|:--|:-- - - -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
+planner 包含了策划工具相关的内容 +*** +## 详情信息 diff --git a/planner/pce/README.md b/planner/pce/README.md index 1ea14bf..5ba8b74 100644 --- a/planner/pce/README.md +++ b/planner/pce/README.md @@ -1,19 +1,20 @@ # Pce - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/pce) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewExporter](#NewExporter)|创建导出器 |[GetFieldGolangType](#GetFieldGolangType)|获取字段的 Golang 类型 @@ -21,81 +22,112 @@ |[NewLoader](#NewLoader)|创建加载器 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Config](#config)|配置解析接口 -|[DataTmpl](#datatmpl)|数据导出模板 -|[Exporter](#exporter)|导出器 -|[Field](#field)|基本字段类型接口 -|[Int](#int)|暂无描述... -|[Int8](#int8)|暂无描述... -|[Int16](#int16)|暂无描述... -|[Int32](#int32)|暂无描述... -|[Int64](#int64)|暂无描述... -|[Uint](#uint)|暂无描述... -|[Uint8](#uint8)|暂无描述... -|[Uint16](#uint16)|暂无描述... -|[Uint32](#uint32)|暂无描述... -|[Uint64](#uint64)|暂无描述... -|[Float32](#float32)|暂无描述... -|[Float64](#float64)|暂无描述... -|[String](#string)|暂无描述... -|[Bool](#bool)|暂无描述... -|[Byte](#byte)|暂无描述... -|[Rune](#rune)|暂无描述... -|[Complex64](#complex64)|暂无描述... -|[Complex128](#complex128)|暂无描述... -|[Uintptr](#uintptr)|暂无描述... -|[Double](#double)|暂无描述... -|[Float](#float)|暂无描述... -|[Long](#long)|暂无描述... -|[Short](#short)|暂无描述... -|[Char](#char)|暂无描述... -|[Number](#number)|暂无描述... -|[Integer](#integer)|暂无描述... -|[Boolean](#boolean)|暂无描述... -|[Loader](#loader)|配置加载器 -|[DataInfo](#datainfo)|配置数据 -|[DataField](#datafield)|配置数据字段 -|[Tmpl](#tmpl)|配置结构模板接口 -|[TmplField](#tmplfield)|模板字段 -|[TmplStruct](#tmplstruct)|模板结构 +|类型|名称|描述 +|:--|:--|:-- +|`INTERFACE`|[Config](#config)|配置解析接口 +|`INTERFACE`|[DataTmpl](#datatmpl)|数据导出模板 +|`STRUCT`|[Exporter](#exporter)|导出器 +|`INTERFACE`|[Field](#field)|基本字段类型接口 +|`STRUCT`|[Int](#int)|暂无描述... +|`STRUCT`|[Int8](#int8)|暂无描述... +|`STRUCT`|[Int16](#int16)|暂无描述... +|`STRUCT`|[Int32](#int32)|暂无描述... +|`STRUCT`|[Int64](#int64)|暂无描述... +|`STRUCT`|[Uint](#uint)|暂无描述... +|`STRUCT`|[Uint8](#uint8)|暂无描述... +|`STRUCT`|[Uint16](#uint16)|暂无描述... +|`STRUCT`|[Uint32](#uint32)|暂无描述... +|`STRUCT`|[Uint64](#uint64)|暂无描述... +|`STRUCT`|[Float32](#float32)|暂无描述... +|`STRUCT`|[Float64](#float64)|暂无描述... +|`STRUCT`|[String](#string)|暂无描述... +|`STRUCT`|[Bool](#bool)|暂无描述... +|`STRUCT`|[Byte](#byte)|暂无描述... +|`STRUCT`|[Rune](#rune)|暂无描述... +|`STRUCT`|[Complex64](#complex64)|暂无描述... +|`STRUCT`|[Complex128](#complex128)|暂无描述... +|`STRUCT`|[Uintptr](#uintptr)|暂无描述... +|`STRUCT`|[Double](#double)|暂无描述... +|`STRUCT`|[Float](#float)|暂无描述... +|`STRUCT`|[Long](#long)|暂无描述... +|`STRUCT`|[Short](#short)|暂无描述... +|`STRUCT`|[Char](#char)|暂无描述... +|`STRUCT`|[Number](#number)|暂无描述... +|`STRUCT`|[Integer](#integer)|暂无描述... +|`STRUCT`|[Boolean](#boolean)|暂无描述... +|`STRUCT`|[Loader](#loader)|配置加载器 +|`STRUCT`|[DataInfo](#datainfo)|配置数据 +|`STRUCT`|[DataField](#datafield)|配置数据字段 +|`INTERFACE`|[Tmpl](#tmpl)|配置结构模板接口 +|`STRUCT`|[TmplField](#tmplfield)|模板字段 +|`STRUCT`|[TmplStruct](#tmplstruct)|模板结构
+*** +## 详情信息 #### func NewExporter() *Exporter > 创建导出器 + *** #### func GetFieldGolangType(field Field) string > 获取字段的 Golang 类型 + +
+查看 / 收起单元测试 + + +```go + +func TestGetFieldGolangType(t *testing.T) { + fmt.Println(pce.GetFieldGolangType(new(pce.String))) +} + +``` + + +
+ + *** #### func GetFields() []Field > 获取所有内置支持的字段 + *** #### func NewLoader(fields []Field) *Loader > 创建加载器 > - 加载器被用于加载配置表的数据和结构信息 + *** -### Config +### Config `INTERFACE` 配置解析接口 - 用于将配置文件解析为可供分析的数据结构 - 可以在 cs 包中找到内置提供的实现及其模板,例如 cs.XlsxIndexConfig ```go -type Config struct{} +type Config interface { + GetConfigName() string + GetDisplayName() string + GetDescription() string + GetIndexCount() int + GetFields() []DataField + GetData() [][]DataInfo +} ``` -### DataTmpl +### DataTmpl `INTERFACE` 数据导出模板 ```go -type DataTmpl struct{} +type DataTmpl interface { + Render(data map[any]any) (string, error) +} ``` -### Exporter +### Exporter `STRUCT` 导出器 ```go type Exporter struct{} @@ -106,15 +138,19 @@ type Exporter struct{} #### func (*Exporter) ExportData(tmpl DataTmpl, data map[any]any) []byte, error > 导出数据 *** -### Field +### Field `INTERFACE` 基本字段类型接口 ```go -type Field struct{} +type Field interface { + TypeName() string + Zero() any + Parse(value string) any +} ``` -### Int +### Int `STRUCT` ```go -type Int struct{} +type Int int ``` #### func (Int) TypeName() string *** @@ -122,10 +158,10 @@ type Int struct{} *** #### func (Int) Parse(value string) any *** -### Int8 +### Int8 `STRUCT` ```go -type Int8 struct{} +type Int8 int8 ``` #### func (Int8) TypeName() string *** @@ -133,10 +169,10 @@ type Int8 struct{} *** #### func (Int8) Parse(value string) any *** -### Int16 +### Int16 `STRUCT` ```go -type Int16 struct{} +type Int16 int16 ``` #### func (Int16) TypeName() string *** @@ -144,10 +180,10 @@ type Int16 struct{} *** #### func (Int16) Parse(value string) any *** -### Int32 +### Int32 `STRUCT` ```go -type Int32 struct{} +type Int32 int32 ``` #### func (Int32) TypeName() string *** @@ -155,10 +191,10 @@ type Int32 struct{} *** #### func (Int32) Parse(value string) any *** -### Int64 +### Int64 `STRUCT` ```go -type Int64 struct{} +type Int64 int64 ``` #### func (Int64) TypeName() string *** @@ -166,10 +202,10 @@ type Int64 struct{} *** #### func (Int64) Parse(value string) any *** -### Uint +### Uint `STRUCT` ```go -type Uint struct{} +type Uint uint ``` #### func (Uint) TypeName() string *** @@ -177,10 +213,10 @@ type Uint struct{} *** #### func (Uint) Parse(value string) any *** -### Uint8 +### Uint8 `STRUCT` ```go -type Uint8 struct{} +type Uint8 uint8 ``` #### func (Uint8) TypeName() string *** @@ -188,10 +224,10 @@ type Uint8 struct{} *** #### func (Uint8) Parse(value string) any *** -### Uint16 +### Uint16 `STRUCT` ```go -type Uint16 struct{} +type Uint16 uint16 ``` #### func (Uint16) TypeName() string *** @@ -199,10 +235,10 @@ type Uint16 struct{} *** #### func (Uint16) Parse(value string) any *** -### Uint32 +### Uint32 `STRUCT` ```go -type Uint32 struct{} +type Uint32 uint32 ``` #### func (Uint32) TypeName() string *** @@ -210,10 +246,10 @@ type Uint32 struct{} *** #### func (Uint32) Parse(value string) any *** -### Uint64 +### Uint64 `STRUCT` ```go -type Uint64 struct{} +type Uint64 uint64 ``` #### func (Uint64) TypeName() string *** @@ -221,10 +257,10 @@ type Uint64 struct{} *** #### func (Uint64) Parse(value string) any *** -### Float32 +### Float32 `STRUCT` ```go -type Float32 struct{} +type Float32 float32 ``` #### func (Float32) TypeName() string *** @@ -232,10 +268,10 @@ type Float32 struct{} *** #### func (Float32) Parse(value string) any *** -### Float64 +### Float64 `STRUCT` ```go -type Float64 struct{} +type Float64 float64 ``` #### func (Float64) TypeName() string *** @@ -243,10 +279,10 @@ type Float64 struct{} *** #### func (Float64) Parse(value string) any *** -### String +### String `STRUCT` ```go -type String struct{} +type String string ``` #### func (String) TypeName() string *** @@ -254,10 +290,10 @@ type String struct{} *** #### func (String) Parse(value string) any *** -### Bool +### Bool `STRUCT` ```go -type Bool struct{} +type Bool bool ``` #### func (Bool) TypeName() string *** @@ -265,10 +301,10 @@ type Bool struct{} *** #### func (Bool) Parse(value string) any *** -### Byte +### Byte `STRUCT` ```go -type Byte struct{} +type Byte byte ``` #### func (Byte) TypeName() string *** @@ -276,10 +312,10 @@ type Byte struct{} *** #### func (Byte) Parse(value string) any *** -### Rune +### Rune `STRUCT` ```go -type Rune struct{} +type Rune rune ``` #### func (Rune) TypeName() string *** @@ -287,10 +323,10 @@ type Rune struct{} *** #### func (Rune) Parse(value string) any *** -### Complex64 +### Complex64 `STRUCT` ```go -type Complex64 struct{} +type Complex64 complex64 ``` #### func (Complex64) TypeName() string *** @@ -298,10 +334,10 @@ type Complex64 struct{} *** #### func (Complex64) Parse(value string) any *** -### Complex128 +### Complex128 `STRUCT` ```go -type Complex128 struct{} +type Complex128 complex128 ``` #### func (Complex128) TypeName() string *** @@ -309,10 +345,10 @@ type Complex128 struct{} *** #### func (Complex128) Parse(value string) any *** -### Uintptr +### Uintptr `STRUCT` ```go -type Uintptr struct{} +type Uintptr uintptr ``` #### func (Uintptr) TypeName() string *** @@ -320,10 +356,10 @@ type Uintptr struct{} *** #### func (Uintptr) Parse(value string) any *** -### Double +### Double `STRUCT` ```go -type Double struct{} +type Double float64 ``` #### func (Double) TypeName() string *** @@ -331,10 +367,10 @@ type Double struct{} *** #### func (Double) Parse(value string) any *** -### Float +### Float `STRUCT` ```go -type Float struct{} +type Float float32 ``` #### func (Float) TypeName() string *** @@ -342,10 +378,10 @@ type Float struct{} *** #### func (Float) Parse(value string) any *** -### Long +### Long `STRUCT` ```go -type Long struct{} +type Long int64 ``` #### func (Long) TypeName() string *** @@ -353,10 +389,10 @@ type Long struct{} *** #### func (Long) Parse(value string) any *** -### Short +### Short `STRUCT` ```go -type Short struct{} +type Short int16 ``` #### func (Short) TypeName() string *** @@ -364,10 +400,10 @@ type Short struct{} *** #### func (Short) Parse(value string) any *** -### Char +### Char `STRUCT` ```go -type Char struct{} +type Char int8 ``` #### func (Char) TypeName() string *** @@ -375,10 +411,10 @@ type Char struct{} *** #### func (Char) Parse(value string) any *** -### Number +### Number `STRUCT` ```go -type Number struct{} +type Number float64 ``` #### func (Number) TypeName() string *** @@ -386,10 +422,10 @@ type Number struct{} *** #### func (Number) Parse(value string) any *** -### Integer +### Integer `STRUCT` ```go -type Integer struct{} +type Integer int64 ``` #### func (Integer) TypeName() string *** @@ -397,10 +433,10 @@ type Integer struct{} *** #### func (Integer) Parse(value string) any *** -### Boolean +### Boolean `STRUCT` ```go -type Boolean struct{} +type Boolean bool ``` #### func (Boolean) TypeName() string *** @@ -408,7 +444,7 @@ type Boolean struct{} *** #### func (Boolean) Parse(value string) any *** -### Loader +### Loader `STRUCT` 配置加载器 ```go type Loader struct { @@ -421,7 +457,7 @@ type Loader struct { #### func (*Loader) LoadData(config Config) map[any]any > 加载配置并得到配置数据 *** -### DataInfo +### DataInfo `STRUCT` 配置数据 ```go type DataInfo struct { @@ -429,7 +465,7 @@ type DataInfo struct { Value string } ``` -### DataField +### DataField `STRUCT` 配置数据字段 ```go type DataField struct { @@ -440,12 +476,14 @@ type DataField struct { ExportType string } ``` -### Tmpl +### Tmpl `INTERFACE` 配置结构模板接口 ```go -type Tmpl struct{} +type Tmpl interface { + Render(templates ...*TmplStruct) (string, error) +} ``` -### TmplField +### TmplField `STRUCT` 模板字段 ```go type TmplField struct { @@ -467,7 +505,7 @@ type TmplField struct { #### func (*TmplField) IsSlice() bool > 是否是切片类型 *** -### TmplStruct +### TmplStruct `STRUCT` 模板结构 ```go type TmplStruct struct { diff --git a/planner/pce/cs/README.md b/planner/pce/cs/README.md index f86ec3d..c59acbf 100644 --- a/planner/pce/cs/README.md +++ b/planner/pce/cs/README.md @@ -1,42 +1,46 @@ # Cs - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/cs) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewXlsx](#NewXlsx)|暂无描述... -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[XlsxExportType](#xlsxexporttype)|暂无描述... -|[Xlsx](#xlsx)|内置的 Xlsx 配置 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[XlsxExportType](#xlsxexporttype)|暂无描述... +|`STRUCT`|[Xlsx](#xlsx)|内置的 Xlsx 配置
+*** +## 详情信息 #### func NewXlsx(sheet *xlsx.Sheet, exportType XlsxExportType) *Xlsx + *** -### XlsxExportType +### XlsxExportType `STRUCT` ```go -type XlsxExportType struct{} +type XlsxExportType int ``` -### Xlsx +### Xlsx `STRUCT` 内置的 Xlsx 配置 ```go type Xlsx struct { diff --git a/planner/pce/exporter/README.md b/planner/pce/exporter/README.md index 9b11297..ffcf1c6 100644 --- a/planner/pce/exporter/README.md +++ b/planner/pce/exporter/README.md @@ -1,27 +1,22 @@ # Main - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/main) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 diff --git a/planner/pce/exporter/cmd/README.md b/planner/pce/exporter/cmd/README.md index fd9f5fd..56aceb4 100644 --- a/planner/pce/exporter/cmd/README.md +++ b/planner/pce/exporter/cmd/README.md @@ -1,32 +1,113 @@ # Cmd - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/cmd) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[Execute](#Execute)|将所有子命令添加到根命令并适当设置标志。这是由 main.main() 调用的。 rootCmd 只需要发生一次 -> 结构体定义 +*** +## 详情信息 +#### func Execute() + +> 将所有子命令添加到根命令并适当设置标志。这是由 main.main() 调用的。 rootCmd 只需要发生一次 + +
+查看 / 收起单元测试 + + +```go + +func TestExecute(t *testing.T) { + var filePath, outPath, exclude, exportType, prefix string + exportType = "s" + filePath = `.\游戏配置.xlsx` + filePath = `../xlsx_template.xlsx` + outPath = `.` + isDir, err := file.IsDir(outPath) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + isDir = filepath.Ext(outPath) == "" + } else { + panic(err) + } + } + if !isDir { + panic(errors.New("output must be a directory path")) + } + _ = os.MkdirAll(outPath, os.ModePerm) + fpd, err := file.IsDir(filePath) + if err != nil { + panic(err) + } + var xlsxFiles []string + if fpd { + files, err := os.ReadDir(filePath) + if err != nil { + panic(err) + } + for _, f := range files { + if f.IsDir() || !strings.HasSuffix(f.Name(), ".xlsx") || strings.HasPrefix(f.Name(), "~") { + continue + } + xlsxFiles = append(xlsxFiles, filepath.Join(filePath, f.Name())) + } + } else { + xlsxFiles = append(xlsxFiles, filePath) + } + var exporter = pce.NewExporter() + loader := pce.NewLoader(pce.GetFields()) + excludes := collection.ConvertSliceToBoolMap(str.SplitTrimSpace(exclude, ",")) + for _, xlsxFile := range xlsxFiles { + xf, err := xlsx.OpenFile(xlsxFile) + if err != nil { + panic(err) + } + for _, sheet := range xf.Sheets { + var cx *cs.Xlsx + switch strings.TrimSpace(strings.ToLower(exportType)) { + case "c": + cx = cs.NewXlsx(sheet, cs.XlsxExportTypeClient) + case "s": + cx = cs.NewXlsx(sheet, cs.XlsxExportTypeServer) + } + if strings.HasPrefix(cx.GetDisplayName(), "#") || strings.HasPrefix(cx.GetConfigName(), "#") || excludes[cx.GetConfigName()] || excludes[cx.GetDisplayName()] { + continue + } + if raw, err := exporter.ExportData(tmpls.NewJSON(), loader.LoadData(cx)); err != nil { + panic(err) + } else { + var jsonPath string + if len(prefix) == 0 { + jsonPath = filepath.Join(outPath, fmt.Sprintf("%s.json", cx.GetConfigName())) + } else { + jsonPath = filepath.Join(outPath, fmt.Sprintf("%s.%s.json", prefix, cx.GetConfigName())) + } + if err := file.WriterFile(jsonPath, raw); err != nil { + panic(err) + } + } + } + } +} + +``` -|结构体|描述 -|:--|:--
-#### func Execute() - -> 将所有子命令添加到根命令并适当设置标志。这是由 main.main() 调用的。 rootCmd 只需要发生一次 *** diff --git a/planner/pce/tmpls/README.md b/planner/pce/tmpls/README.md index 032b674..7d3b033 100644 --- a/planner/pce/tmpls/README.md +++ b/planner/pce/tmpls/README.md @@ -1,42 +1,47 @@ # Tmpls - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/tmpls) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewGolang](#NewGolang)|创建一个 Golang 配置导出模板 |[NewJSON](#NewJSON)|暂无描述... -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Golang](#golang)|配置导出模板 -|[JSON](#json)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Golang](#golang)|配置导出模板 +|`STRUCT`|[JSON](#json)|暂无描述...
+*** +## 详情信息 #### func NewGolang(packageName string) *Golang > 创建一个 Golang 配置导出模板 + *** #### func NewJSON() *JSON + *** -### Golang +### Golang `STRUCT` 配置导出模板 ```go type Golang struct { @@ -52,7 +57,7 @@ type Golang struct { *** #### func (*Golang) HasIndex(config *pce.TmplStruct) bool *** -### JSON +### JSON `STRUCT` ```go type JSON struct { diff --git a/server/README.md b/server/README.md index 59ad65c..49de6f1 100644 --- a/server/README.md +++ b/server/README.md @@ -1,19 +1,20 @@ # Server -server 提供了包含多种网络类型的服务器实现 - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/server) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +server 提供了包含多种网络类型的服务器实现 + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewBot](#NewBot)|创建一个机器人,目前仅支持 Socket 服务器 |[WithBotNetworkDelay](#WithBotNetworkDelay)|设置机器人网络延迟及波动范围 @@ -49,157 +50,221 @@ server 提供了包含多种网络类型的服务器实现 |[BindService](#BindService)|绑定服务到特定 Server,被绑定的服务将会在 Server 初始化时执行 Service.OnInit 方法 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Bot](#bot)|暂无描述... -|[BotOption](#botoption)|暂无描述... -|[Conn](#conn)|服务器连接单次消息的包装 -|[ConsoleParams](#consoleparams)|控制台参数 -|[MessageReadyEventHandler](#messagereadyeventhandler)|暂无描述... -|[Http](#http)|基于 gin.Engine 包装的 http 服务器 -|[HttpContext](#httpcontext)|基于 gin.Context 的 http 请求上下文 -|[HandlerFunc](#handlerfunc)|暂无描述... -|[ContextPacker](#contextpacker)|暂无描述... -|[HttpRouter](#httprouter)|暂无描述... -|[HttpWrapperHandleFunc](#httpwrapperhandlefunc)|暂无描述... -|[HttpWrapper](#httpwrapper)|http 包装器 -|[HttpWrapperGroup](#httpwrappergroup)|http 包装器 -|[MessageType](#messagetype)|暂无描述... -|[Message](#message)|服务器消息 -|[MultipleServer](#multipleserver)|暂无描述... -|[Network](#network)|暂无描述... -|[Option](#option)|暂无描述... -|[Server](#server)|网络服务器 -|[Service](#service)|兼容传统 service 设计模式的接口 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Bot](#bot)|暂无描述... +|`STRUCT`|[BotOption](#botoption)|暂无描述... +|`STRUCT`|[Conn](#conn)|服务器连接单次消息的包装 +|`STRUCT`|[ConsoleParams](#consoleparams)|控制台参数 +|`STRUCT`|[MessageReadyEventHandler](#messagereadyeventhandler)|暂无描述... +|`STRUCT`|[Http](#http)|基于 gin.Engine 包装的 http 服务器 +|`STRUCT`|[HttpContext](#httpcontext)|基于 gin.Context 的 http 请求上下文 +|`STRUCT`|[HandlerFunc](#handlerfunc)|暂无描述... +|`STRUCT`|[ContextPacker](#contextpacker)|暂无描述... +|`STRUCT`|[HttpRouter](#httprouter)|暂无描述... +|`STRUCT`|[HttpWrapperHandleFunc](#httpwrapperhandlefunc)|暂无描述... +|`STRUCT`|[HttpWrapper](#httpwrapper)|http 包装器 +|`STRUCT`|[HttpWrapperGroup](#httpwrappergroup)|http 包装器 +|`STRUCT`|[MessageType](#messagetype)|暂无描述... +|`STRUCT`|[Message](#message)|服务器消息 +|`STRUCT`|[MultipleServer](#multipleserver)|暂无描述... +|`STRUCT`|[Network](#network)|暂无描述... +|`STRUCT`|[Option](#option)|暂无描述... +|`STRUCT`|[Server](#server)|网络服务器 +|`INTERFACE`|[Service](#service)|兼容传统 service 设计模式的接口
+*** +## 详情信息 #### func NewBot(srv *Server, options ...BotOption) *Bot > 创建一个机器人,目前仅支持 Socket 服务器 + +
+查看 / 收起单元测试 + + +```go + +func TestNewBot(t *testing.T) { + srv := server.New(server.NetworkWebsocket) + srv.RegConnectionOpenedEvent(func(srv *server.Server, conn *server.Conn) { + t.Logf("connection opened: %s", conn.GetID()) + conn.Close() + conn.Write([]byte("hello")) + }) + srv.RegConnectionClosedEvent(func(srv *server.Server, conn *server.Conn, err any) { + t.Logf("connection closed: %s", conn.GetID()) + }) + srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet []byte) { + t.Logf("connection %s receive packet: %s", conn.GetID(), string(packet)) + conn.Write([]byte("world")) + }) + srv.RegStartFinishEvent(func(srv *server.Server) { + bot := server.NewBot(srv, server.WithBotNetworkDelay(100, 20), server.WithBotWriter(func(bot *server.Bot) io.Writer { + return &Writer{t: t, bot: bot} + })) + bot.JoinServer() + time.Sleep(time.Second) + bot.SendPacket([]byte("hello")) + }) + _ = srv.Run(":9600") +} + +``` + + +
+ + *** #### func WithBotNetworkDelay(delay time.Duration, fluctuation time.Duration) BotOption > 设置机器人网络延迟及波动范围 > - delay 延迟 > - fluctuation 波动范围 + *** #### func WithBotWriter(construction func (bot *Bot) io.Writer) BotOption > 设置机器人写入器,默认为 os.Stdout + *** #### func DefaultWebsocketUpgrader() *websocket.Upgrader + *** #### func NewHttpHandleWrapper(srv *Server, packer ContextPacker[Context]) *Http[Context] > 创建一个新的 http 处理程序包装器 > - 默认使用 server.HttpContext 作为上下文,如果需要依赖其作为新的上下文,可以通过 NewHttpContext 创建 + *** #### func NewHttpContext(ctx *gin.Context) *HttpContext > 基于 gin.Context 创建一个新的 HttpContext + *** #### func NewGinWrapper(server *gin.Engine, pack func (ctx *gin.Context) CTX) *HttpWrapper[CTX] > 创建 gin 包装器,用于对 NewHttpWrapper 函数的替代 + *** #### func HasMessageType(mt MessageType) bool > 检查是否存在指定的消息类型 + *** -#### func NewMultipleServer(serverHandle ...func () (addr string, srv *Server)) *MultipleServer +#### func NewMultipleServer(serverHandle ...func () ((addr string, srv *Server))) *MultipleServer + *** #### func GetNetworks() []Network > 获取所有支持的网络模式 + *** #### func WithLowMessageDuration(duration time.Duration) Option > 通过指定慢消息时长的方式创建服务器,当消息处理时间超过指定时长时,将会输出 WARN 类型的日志 > - 默认值为 DefaultLowMessageDuration > - 当 duration <= 0 时,表示关闭慢消息检测 + *** #### func WithAsyncLowMessageDuration(duration time.Duration) Option > 通过指定异步消息的慢消息时长的方式创建服务器,当消息处理时间超过指定时长时,将会输出 WARN 类型的日志 > - 默认值为 DefaultAsyncLowMessageDuration > - 当 duration <= 0 时,表示关闭慢消息检测 + *** #### func WithWebsocketConnInitializer(initializer func (writer http.ResponseWriter, request *http.Request, conn *websocket.Conn) error) Option > 通过 websocket 连接初始化的方式创建服务器,当 initializer 返回错误时,服务器将不会处理该连接的后续逻辑 > - 该选项仅在创建 NetworkWebsocket 服务器时有效 + *** #### func WithWebsocketUpgrade(upgrader *websocket.Upgrader) Option > 通过指定 websocket.Upgrader 的方式创建服务器 > - 默认值为 DefaultWebsocketUpgrader > - 该选项仅在创建 NetworkWebsocket 服务器时有效 + *** #### func WithConnWriteBufferSize(size int) Option > 通过连接写入缓冲区大小的方式创建服务器 > - 默认值为 DefaultConnWriteBufferSize > - 设置合适的缓冲区大小可以提高服务器性能,但是会占用更多的内存 + *** #### func WithDispatcherBufferSize(size int) Option > 通过消息分发器缓冲区大小的方式创建服务器 > - 默认值为 DefaultDispatcherBufferSize > - 设置合适的缓冲区大小可以提高服务器性能,但是会占用更多的内存 + *** #### func WithMessageStatistics(duration time.Duration, limit int) Option > 通过消息统计的方式创建服务器 > - 默认不开启,当 duration 和 limit 均大于 0 的时候,服务器将记录每 duration 期间的消息数量,并保留最多 limit 条 + *** #### func WithPacketWarnSize(size int) Option > 通过数据包大小警告的方式创建服务器,当数据包大小超过指定大小时,将会输出 WARN 类型的日志 > - 默认值为 DefaultPacketWarnSize > - 当 size <= 0 时,表示不设置警告 + *** #### func WithLimitLife(t time.Duration) Option > 通过限制最大生命周期的方式创建服务器 > - 通常用于测试服务器,服务器将在到达最大生命周期时自动关闭 + *** #### func WithWebsocketWriteCompression() Option > 通过数据写入压缩的方式创建Websocket服务器 > - 默认不开启数据压缩 + *** #### func WithWebsocketCompression(level int) Option > 通过数据压缩的方式创建Websocket服务器 > - 默认不开启数据压缩 + *** #### func WithDeadlockDetect(t time.Duration) Option > 通过死锁、死循环、永久阻塞检测的方式创建服务器 > - 当检测到死锁、死循环、永久阻塞时,服务器将会生成 WARN 类型的日志,关键字为 "SuspectedDeadlock" > - 默认不开启死锁检测 + *** #### func WithDisableAsyncMessage() Option > 通过禁用异步消息的方式创建服务器 + *** #### func WithAsyncPoolSize(size int) Option > 通过指定异步消息池大小的方式创建服务器 > - 当通过 WithDisableAsyncMessage 禁用异步消息时,此选项无效 > - 默认值为 DefaultAsyncPoolSize + *** #### func WithWebsocketReadDeadline(t time.Duration) Option > 设置 Websocket 读取超时时间 > - 默认: DefaultWebsocketReadDeadline > - 当 t <= 0 时,表示不设置超时时间 + *** #### func WithTicker(poolSize int, size int, connSize int, autonomy bool) Option @@ -208,33 +273,119 @@ server 提供了包含多种网络类型的服务器实现 > - size:服务器定时器时间轮大小 > - connSize:服务器连接定时器时间轮大小,当该值小于等于 0 的时候,在新连接建立时将不再为其创建定时器 > - autonomy:定时器是否独立运行(独立运行的情况下不会作为服务器消息运行,会导致并发问题) + *** #### func WithTLS(certFile string, keyFile string) Option > 通过安全传输层协议TLS创建服务器 > - 支持:Http、Websocket + *** #### func WithGRPCServerOptions(options ...grpc.ServerOption) Option > 通过GRPC的可选项创建GRPC服务器 + *** #### func WithWebsocketMessageType(messageTypes ...int) Option > 设置仅支持特定类型的Websocket消息 + *** #### func WithPProf(pattern ...string) Option > 通过性能分析工具PProf创建服务器 + *** #### func New(network Network, options ...Option) *Server > 根据特定网络类型创建一个服务器 + +示例代码: +```go + +func ExampleNew() { + srv := server.New(server.NetworkWebsocket, server.WithLimitLife(time.Millisecond)) + srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet []byte) { + conn.Write(packet) + }) + if err := srv.Run(":9999"); err != nil { + panic(err) + } +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestNew(t *testing.T) { + srv := server.New(server.NetworkWebsocket, server.WithPProf()) + srv.RegStartBeforeEvent(func(srv *server.Server) { + fmt.Println("启动前") + }) + srv.RegStartFinishEvent(func(srv *server.Server) { + fmt.Println("启动完成") + }) + srv.RegConnectionClosedEvent(func(srv *server.Server, conn *server.Conn, err any) { + fmt.Println("关闭", conn.GetID(), err, "IncrCount", srv.GetOnlineCount()) + }) + srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet []byte) { + conn.Write(packet) + }) + if err := srv.Run(":9999"); err != nil { + panic(err) + } +} + +``` + + +
+ + *** #### func BindService(srv *Server, services ...Service) > 绑定服务到特定 Server,被绑定的服务将会在 Server 初始化时执行 Service.OnInit 方法 + +示例代码: +```go + +func ExampleBindService() { + srv := server.New(server.NetworkNone, server.WithLimitLife(time.Second)) + server.BindService(srv, new(TestService)) + if err := srv.RunNone(); err != nil { + panic(err) + } +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestBindService(t *testing.T) { + srv := server.New(server.NetworkNone, server.WithLimitLife(time.Second)) + server.BindService(srv, new(TestService)) + if err := srv.RunNone(); err != nil { + t.Fatal(err) + } +} + +``` + + +
+ + *** -### Bot +### Bot `STRUCT` ```go type Bot struct { @@ -262,12 +413,12 @@ type Bot struct { #### func (*Bot) SendWSPacket(wst int, packet []byte) > 发送 WebSocket 数据包到服务器 *** -### BotOption +### BotOption `STRUCT` ```go -type BotOption struct{} +type BotOption func(bot *Bot) ``` -### Conn +### Conn `STRUCT` 服务器连接单次消息的包装 ```go type Conn struct { @@ -350,10 +501,10 @@ type Conn struct { #### func (*Conn) Close(err ...error) > 关闭连接 *** -### ConsoleParams +### ConsoleParams `STRUCT` 控制台参数 ```go -type ConsoleParams struct{} +type ConsoleParams map[string][]string ``` #### func (ConsoleParams) Get(key string) string > 获取参数值 @@ -376,12 +527,12 @@ type ConsoleParams struct{} #### func (ConsoleParams) Clear() > 清空参数 *** -### MessageReadyEventHandler +### MessageReadyEventHandler `STRUCT` ```go -type MessageReadyEventHandler struct{} +type MessageReadyEventHandler func(srv *Server) ``` -### Http +### Http `STRUCT` 基于 gin.Engine 包装的 http 服务器 ```go type Http[Context any] struct { @@ -392,7 +543,7 @@ type Http[Context any] struct { ``` #### func (*Http) Gin() *gin.Engine *** -### HttpContext +### HttpContext `STRUCT` 基于 gin.Context 的 http 请求上下文 ```go type HttpContext struct { @@ -405,17 +556,17 @@ type HttpContext struct { #### func (*HttpContext) ReadTo(dest any) error > 读取请求数据到指定结构体,如果失败则返回错误 *** -### HandlerFunc +### HandlerFunc `STRUCT` ```go -type HandlerFunc[Context any] struct{} +type HandlerFunc[Context any] func(ctx Context) ``` -### ContextPacker +### ContextPacker `STRUCT` ```go -type ContextPacker[Context any] struct{} +type ContextPacker[Context any] func(ctx *gin.Context) Context ``` -### HttpRouter +### HttpRouter `STRUCT` ```go type HttpRouter[Context any] struct { @@ -488,12 +639,12 @@ type HttpRouter[Context any] struct { #### func (*HttpRouter) Use(middleware ...HandlerFunc[Context]) *HttpRouter[Context] > 将中间件附加到路由组。 *** -### HttpWrapperHandleFunc +### HttpWrapperHandleFunc `STRUCT` ```go -type HttpWrapperHandleFunc[CTX any] struct{} +type HttpWrapperHandleFunc[CTX any] func(ctx CTX) ``` -### HttpWrapper +### HttpWrapper `STRUCT` http 包装器 ```go type HttpWrapper[CTX any] struct { @@ -552,7 +703,7 @@ type HttpWrapper[CTX any] struct { #### func (*HttpWrapper) Group(relativePath string, handlers ...HttpWrapperHandleFunc[CTX]) *HttpWrapperGroup[CTX] > 创建一个新的路由组。您应该添加所有具有共同中间件的路由。 *** -### HttpWrapperGroup +### HttpWrapperGroup `STRUCT` http 包装器 ```go type HttpWrapperGroup[CTX any] struct { @@ -587,15 +738,15 @@ type HttpWrapperGroup[CTX any] struct { #### func (*HttpWrapperGroup) Group(relativePath string, handlers ...HttpWrapperHandleFunc[CTX]) *HttpWrapperGroup[CTX] > 创建分组 *** -### MessageType +### MessageType `STRUCT` ```go -type MessageType struct{} +type MessageType byte ``` #### func (MessageType) String() string > 返回消息类型的字符串表示 *** -### Message +### Message `STRUCT` 服务器消息 ```go type Message struct { @@ -620,7 +771,7 @@ type Message struct { #### func (*Message) String() string > 返回消息的字符串表示 *** -### MultipleServer +### MultipleServer `STRUCT` ```go type MultipleServer struct { @@ -636,20 +787,20 @@ type MultipleServer struct { *** #### func (*MultipleServer) OnExitEvent() *** -### Network +### Network `STRUCT` ```go -type Network struct{} +type Network string ``` #### func (Network) IsSocket() bool > 返回当前服务器的网络模式是否为 Socket 模式 *** -### Option +### Option `STRUCT` ```go -type Option struct{} +type Option func(srv *Server) ``` -### Server +### Server `STRUCT` 网络服务器 ```go type Server struct { @@ -690,6 +841,21 @@ type Server struct { > - server.NetworkWebsocket (addr:":8888/ws") > - server.NetworkKcp (addr:":8888") > - server.NetworkNone (addr:"") +示例代码: +```go + +func ExampleServer_Run() { + srv := server.New(server.NetworkWebsocket, server.WithLimitLife(time.Millisecond)) + srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet []byte) { + conn.Write(packet) + }) + if err := srv.Run(":9999"); err != nil { + panic(err) + } +} + +``` + *** #### func (*Server) IsSocket() bool > 是否是 Socket 模式 @@ -800,8 +966,10 @@ type Server struct { #### func (*Server) HasMessageStatistics() bool > 是否了开启消息统计 *** -### Service +### Service `INTERFACE` 兼容传统 service 设计模式的接口 ```go -type Service struct{} +type Service interface { + OnInit(srv *Server) +} ``` diff --git a/server/client/README.md b/server/client/README.md index 7c0fb8d..18e594e 100644 --- a/server/client/README.md +++ b/server/client/README.md @@ -1,19 +1,20 @@ # Client - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/client) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewClient](#NewClient)|创建客户端 |[CloneClient](#CloneClient)|克隆客户端 @@ -22,40 +23,47 @@ |[NewWebsocket](#NewWebsocket)|创建 websocket 客户端 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Client](#client)|客户端 -|[Core](#core)|暂无描述... -|[ConnectionClosedEventHandle](#connectionclosedeventhandle)|暂无描述... -|[Packet](#packet)|暂无描述... -|[TCP](#tcp)|暂无描述... -|[UnixDomainSocket](#unixdomainsocket)|暂无描述... -|[Websocket](#websocket)|websocket 客户端 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Client](#client)|客户端 +|`INTERFACE`|[Core](#core)|暂无描述... +|`STRUCT`|[ConnectionClosedEventHandle](#connectionclosedeventhandle)|暂无描述... +|`STRUCT`|[Packet](#packet)|暂无描述... +|`STRUCT`|[TCP](#tcp)|暂无描述... +|`STRUCT`|[UnixDomainSocket](#unixdomainsocket)|暂无描述... +|`STRUCT`|[Websocket](#websocket)|websocket 客户端
+*** +## 详情信息 #### func NewClient(core Core) *Client > 创建客户端 + *** #### func CloneClient(client *Client) *Client > 克隆客户端 + *** #### func NewTCP(addr string) *Client + *** #### func NewUnixDomainSocket(addr string) *Client + *** #### func NewWebsocket(addr string) *Client > 创建 websocket 客户端 + *** -### Client +### Client `STRUCT` 客户端 ```go type Client struct { @@ -86,6 +94,43 @@ type Client struct { #### func (*Client) WriteWS(wst int, packet []byte, callback ...func (err error)) > 向连接中写入指定 websocket 数据类型 > - wst: websocket模式中指定消息类型 +
+查看 / 收起单元测试 + + +```go + +func TestClient_WriteWS(t *testing.T) { + var wait sync.WaitGroup + wait.Add(1) + srv := server.New(server.NetworkWebsocket) + srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet []byte) { + srv.Shutdown() + }) + srv.RegStopEvent(func(srv *server.Server) { + wait.Done() + }) + srv.RegMessageReadyEvent(func(srv *server.Server) { + cli := client.NewWebsocket("ws://127.0.0.1:9999") + cli.RegConnectionOpenedEvent(func(conn *client.Client) { + conn.WriteWS(2, []byte("Hello")) + }) + if err := cli.Run(); err != nil { + panic(err) + } + }) + if err := srv.Run(":9999"); err != nil { + panic(err) + } + wait.Wait() +} + +``` + + +
+ + *** #### func (*Client) Write(packet []byte, callback ...func (err error)) > 向连接中写入数据 @@ -93,17 +138,23 @@ type Client struct { #### func (*Client) GetServerAddr() string > 获取服务器地址 *** -### Core +### Core `INTERFACE` ```go -type Core struct{} +type Core interface { + Run(runState chan error, receive func(wst int, packet []byte)) + Write(packet *Packet) error + Close() + GetServerAddr() string + Clone() Core +} ``` -### ConnectionClosedEventHandle +### ConnectionClosedEventHandle `STRUCT` ```go -type ConnectionClosedEventHandle struct{} +type ConnectionClosedEventHandle func(conn *Client, err any) ``` -### Packet +### Packet `STRUCT` ```go type Packet struct { @@ -112,7 +163,7 @@ type Packet struct { callback func(err error) } ``` -### TCP +### TCP `STRUCT` ```go type TCP struct { @@ -131,7 +182,7 @@ type TCP struct { *** #### func (*TCP) Clone() Core *** -### UnixDomainSocket +### UnixDomainSocket `STRUCT` ```go type UnixDomainSocket struct { @@ -143,6 +194,48 @@ type UnixDomainSocket struct { #### func (*UnixDomainSocket) Run(runState chan error, receive func (wst int, packet []byte)) *** #### func (*UnixDomainSocket) Write(packet *Packet) error +
+查看 / 收起单元测试 + + +```go + +func TestUnixDomainSocket_Write(t *testing.T) { + var closed = make(chan struct{}) + srv := server.New(server.NetworkUnix) + srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet []byte) { + t.Log(string(packet)) + conn.Write(packet) + }) + srv.RegStartFinishEvent(func(srv *server.Server) { + time.Sleep(time.Second) + cli := client.NewUnixDomainSocket("./test.sock") + cli.RegConnectionOpenedEvent(func(conn *client.Client) { + conn.Write([]byte("Hello~")) + }) + cli.RegConnectionReceivePacketEvent(func(conn *client.Client, wst int, packet []byte) { + t.Log(packet) + closed <- struct{}{} + }) + if err := cli.Run(); err != nil { + panic(err) + } + }) + go func() { + if err := srv.Run("./test.sock"); err != nil { + panic(err) + } + }() + <-closed + srv.Shutdown() +} + +``` + + +
+ + *** #### func (*UnixDomainSocket) Close() *** @@ -150,7 +243,7 @@ type UnixDomainSocket struct { *** #### func (*UnixDomainSocket) Clone() Core *** -### Websocket +### Websocket `STRUCT` websocket 客户端 ```go type Websocket struct { diff --git a/server/gateway/README.md b/server/gateway/README.md index 8abacad..478e6a8 100644 --- a/server/gateway/README.md +++ b/server/gateway/README.md @@ -1,19 +1,20 @@ # Gateway -gateway 是用于处理服务器消息的网关模块,适用于对客户端消息进行处理、转发的情况。 - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/gateway) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +gateway 是用于处理服务器消息的网关模块,适用于对客户端消息进行处理、转发的情况。 + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewEndpoint](#NewEndpoint)|创建网关端点 |[WithEndpointStateEvaluator](#WithEndpointStateEvaluator)|设置端点健康值评估函数 @@ -27,28 +28,32 @@ gateway 是用于处理服务器消息的网关模块,适用于对客户端消 |[UnmarshalGatewayInPacket](#UnmarshalGatewayInPacket)|将网关入网数据包转换为数据包 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Endpoint](#endpoint)|网关端点 -|[EndpointOption](#endpointoption)|网关端点选项 -|[ConnectionOpenedEventHandle](#connectionopenedeventhandle)|暂无描述... -|[EndpointSelector](#endpointselector)|暂无描述... -|[Gateway](#gateway)|基于 server.Server 实现的网关服务器 -|[Option](#option)|网关选项 -|[Scanner](#scanner)|端点扫描器 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Endpoint](#endpoint)|网关端点 +|`STRUCT`|[EndpointOption](#endpointoption)|网关端点选项 +|`STRUCT`|[ConnectionOpenedEventHandle](#connectionopenedeventhandle)|暂无描述... +|`STRUCT`|[EndpointSelector](#endpointselector)|暂无描述... +|`STRUCT`|[Gateway](#gateway)|基于 server.Server 实现的网关服务器 +|`STRUCT`|[Option](#option)|网关选项 +|`INTERFACE`|[Scanner](#scanner)|端点扫描器
+*** +## 详情信息 #### func NewEndpoint(name string, cli *client.Client, options ...EndpointOption) *Endpoint > 创建网关端点 + *** #### func WithEndpointStateEvaluator(evaluator func (costUnixNano float64) float64) EndpointOption > 设置端点健康值评估函数 + *** #### func WithEndpointConnectionPoolSize(size int) EndpointOption @@ -56,43 +61,51 @@ gateway 是用于处理服务器消息的网关模块,适用于对客户端消 > - 默认为 DefaultEndpointConnectionPoolSize > - 端点连接池大小决定了网关服务器与端点服务器建立的连接数,如果 <= 0 则会使用默认值 > - 在网关服务器中,多个客户端在发送消息到端点服务器时,会共用一个连接,适当的增大连接池大小可以提高网关服务器的承载能力 + *** #### func WithEndpointReconnectInterval(interval time.Duration) EndpointOption > 设置端点重连间隔 > - 默认为 DefaultEndpointReconnectInterval > - 端点在连接失败后会在该间隔后重连,如果 <= 0 则不会重连 + *** #### func NewGateway(srv *server.Server, scanner Scanner, options ...Option) *Gateway > 基于 server.Server 创建 Gateway 网关服务器 + *** #### func WithEndpointSelector(selector EndpointSelector) Option > 设置端点选择器 > - 默认情况下,网关会随机选择一个端点作为目标,如果需要自定义端点选择器,可以通过该选项设置 + *** #### func MarshalGatewayOutPacket(addr string, packet []byte) []byte, error > 将数据包转换为网关出网数据包 > - | identifier(4) | ipv4(4) | port(2) | packet | + *** #### func UnmarshalGatewayOutPacket(data []byte) (addr string, packet []byte, err error) > 将网关出网数据包转换为数据包 > - | identifier(4) | ipv4(4) | port(2) | packet | + *** #### func MarshalGatewayInPacket(addr string, currentTime int64, packet []byte) []byte, error > 将数据包转换为网关入网数据包 > - | ipv4(4) | port(2) | cost(4) | packet | + *** #### func UnmarshalGatewayInPacket(data []byte) (addr string, sendTime int64, packet []byte, err error) > 将网关入网数据包转换为数据包 > - | ipv4(4) | port(2) | cost(4) | packet | + *** -### Endpoint +### Endpoint `STRUCT` 网关端点 - 每一个端点均表示了一个目标服务,网关会将数据包转发到该端点,由该端点负责将数据包转发到目标服务。 - 每个端点会建立一个连接池,默认大小为 DefaultEndpointConnectionPoolSize,可通过 WithEndpointConnectionPoolSize 进行设置。 @@ -128,22 +141,22 @@ type Endpoint struct { > 转发数据包到该端点 > - 端点在处理数据包时,应区分数据包为普通直连数据包还是网关数据包。可通过 UnmarshalGatewayOutPacket 进行数据包解析,当解析失败且无其他数据包协议时,可认为该数据包为普通直连数据包。 *** -### EndpointOption +### EndpointOption `STRUCT` 网关端点选项 ```go -type EndpointOption struct{} +type EndpointOption func(endpoint *Endpoint) ``` -### ConnectionOpenedEventHandle +### ConnectionOpenedEventHandle `STRUCT` ```go -type ConnectionOpenedEventHandle struct{} +type ConnectionOpenedEventHandle func(gateway *Gateway, conn *server.Conn) ``` -### EndpointSelector +### EndpointSelector `STRUCT` ```go -type EndpointSelector struct{} +type EndpointSelector func(endpoints []*Endpoint) *Endpoint ``` -### Gateway +### Gateway `STRUCT` 基于 server.Server 实现的网关服务器 - 网关服务器是一个特殊的服务器,它会通过扫描器扫描端点列表,然后连接到端点列表中的所有端点,当端点连接成功后,网关服务器会将客户端的连接数据转发到端点服务器 - 由于该网关为多个客户端共享一个端点的连接,所以不会受限于单机 65535 个端口的限制 @@ -170,6 +183,34 @@ type Gateway struct { ``` #### func (*Gateway) Run(addr string) error > 运行网关 +
+查看 / 收起单元测试 + + +```go + +func TestGateway_Run(t *testing.T) { + gw := gateway.NewGateway(server.New(server.NetworkWebsocket, server.WithDeadlockDetect(time.Second*3)), new(Scanner)) + gw.RegConnectionReceivePacketEventHandle(func(gateway *gateway.Gateway, conn *server.Conn, packet []byte) { + endpoint, err := gateway.GetConnEndpoint("test", conn) + if err == nil { + endpoint.Forward(conn, packet) + } + }) + gw.RegEndpointConnectReceivePacketEventHandle(func(gateway *gateway.Gateway, endpoint *gateway.Endpoint, conn *server.Conn, packet []byte) { + conn.Write(packet) + }) + if err := gw.Run(":8888"); err != nil { + panic(err) + } +} + +``` + + +
+ + *** #### func (*Gateway) Shutdown() > 关闭网关 @@ -189,15 +230,18 @@ type Gateway struct { #### func (*Gateway) SwitchEndpoint(source *Endpoint, dest *Endpoint) > 将端点端点的所有连接切换到另一个端点 *** -### Option +### Option `STRUCT` 网关选项 ```go -type Option struct{} +type Option func(gateway *Gateway) ``` -### Scanner +### Scanner `INTERFACE` 端点扫描器 ```go -type Scanner struct{} +type Scanner interface { + GetEndpoints() ([]*Endpoint, error) + GetInterval() time.Duration +} ``` #### func (*Scanner) GetEndpoints() []*gateway.Endpoint, error *** diff --git a/server/internal/dispatcher/README.md b/server/internal/dispatcher/README.md index 8815880..d6a9cbb 100644 --- a/server/internal/dispatcher/README.md +++ b/server/internal/dispatcher/README.md @@ -1,47 +1,171 @@ # Dispatcher - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/dispatcher) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewDispatcher](#NewDispatcher)|创建一个新的消息分发器 Dispatcher 实例 |[NewManager](#NewManager)|生成消息分发器管理器 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Action](#action)|消息分发器操作器,用于暴露外部可操作的消息分发器函数 -|[Handler](#handler)|消息处理器 -|[Dispatcher](#dispatcher)|用于服务器消息处理的消息分发器 -|[Manager](#manager)|消息分发器管理器 -|[Message](#message)|暂无描述... -|[Producer](#producer)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Action](#action)|消息分发器操作器,用于暴露外部可操作的消息分发器函数 +|`STRUCT`|[Handler](#handler)|消息处理器 +|`STRUCT`|[Dispatcher](#dispatcher)|用于服务器消息处理的消息分发器 +|`STRUCT`|[Manager](#manager)|消息分发器管理器 +|`INTERFACE`|[Message](#message)|暂无描述... +|`INTERFACE`|[Producer](#producer)|暂无描述...
+*** +## 详情信息 #### func NewDispatcher(bufferSize int, name string, handler Handler[P, M]) *Dispatcher[P, M] > 创建一个新的消息分发器 Dispatcher 实例 + +示例代码: +```go + +func ExampleNewDispatcher() { + m := new(atomic.Int64) + fm := new(atomic.Int64) + w := new(sync.WaitGroup) + w.Add(1) + d := dispatcher.NewDispatcher(1024, "example-dispatcher", func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) { + m.Add(1) + }) + d.SetClosedHandler(func(dispatcher *dispatcher.Action[string, *TestMessage]) { + w.Done() + }) + var producers = []string{"producer1", "producer2", "producer3"} + for i := 0; i < len(producers); i++ { + p := producers[i] + for i := 0; i < 10; i++ { + d.Put(&TestMessage{producer: p}) + } + d.SetProducerDoneHandler(p, func(p string, dispatcher *dispatcher.Action[string, *TestMessage]) { + fm.Add(1) + }) + } + d.Start() + d.Expel() + w.Wait() + fmt.Println(fmt.Sprintf("producer num: %d, producer done: %d, finished: %d", len(producers), fm.Load(), m.Load())) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestNewDispatcher(t *testing.T) { + var cases = []struct { + name string + bufferSize int + handler dispatcher.Handler[string, *TestMessage] + shouldPanic bool + }{{name: "TestNewDispatcher_BufferSize0AndHandlerNil", bufferSize: 0, handler: nil, shouldPanic: true}, {name: "TestNewDispatcher_BufferSize0AndHandlerNotNil", bufferSize: 0, handler: func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) { + }, shouldPanic: true}, {name: "TestNewDispatcher_BufferSize1AndHandlerNil", bufferSize: 1, handler: nil, shouldPanic: true}, {name: "TestNewDispatcher_BufferSize1AndHandlerNotNil", bufferSize: 1, handler: func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) { + }, shouldPanic: false}} + for _, c := range cases { + c := c + t.Run(c.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil && !c.shouldPanic { + t.Errorf("NewDispatcher() should not panic, but panic: %v", r) + } + }() + dispatcher.NewDispatcher(c.bufferSize, c.name, c.handler) + }) + } +} + +``` + + +
+ + *** #### func NewManager(bufferSize int, handler Handler[P, M]) *Manager[P, M] > 生成消息分发器管理器 + +示例代码: +```go + +func ExampleNewManager() { + mgr := dispatcher.NewManager[string, *TestMessage](10124*16, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) { + }) + mgr.BindProducer("player_001", "shunt-001") + mgr.BindProducer("player_002", "shunt-002") + mgr.BindProducer("player_003", "shunt-sys") + mgr.BindProducer("player_004", "shunt-sys") + mgr.UnBindProducer("player_001") + mgr.UnBindProducer("player_002") + mgr.UnBindProducer("player_003") + mgr.UnBindProducer("player_004") + mgr.Wait() + fmt.Println("done") +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestNewManager(t *testing.T) { + var cases = []struct { + name string + bufferSize int + handler dispatcher.Handler[string, *TestMessage] + shouldPanic bool + }{{name: "TestNewManager_BufferSize0AndHandlerNil", bufferSize: 0, handler: nil, shouldPanic: true}, {name: "TestNewManager_BufferSize0AndHandlerNotNil", bufferSize: 0, handler: func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) { + }, shouldPanic: true}, {name: "TestNewManager_BufferSize1AndHandlerNil", bufferSize: 1, handler: nil, shouldPanic: true}, {name: "TestNewManager_BufferSize1AndHandlerNotNil", bufferSize: 1, handler: func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) { + }, shouldPanic: false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil && !c.shouldPanic { + t.Errorf("NewManager() should not panic, but panic: %v", r) + } + }() + dispatcher.NewManager[string, *TestMessage](c.bufferSize, c.handler) + }) + } +} + +``` + + +
+ + *** -### Action +### Action `STRUCT` 消息分发器操作器,用于暴露外部可操作的消息分发器函数 ```go type Action[P Producer, M Message[P]] struct { @@ -49,12 +173,12 @@ type Action[P Producer, M Message[P]] struct { d *Dispatcher[P, M] } ``` -### Handler +### Handler `STRUCT` 消息处理器 ```go -type Handler[P Producer, M Message[P]] struct{} +type Handler[P Producer, M Message[P]] func(dispatcher *Dispatcher[P, M], message M) ``` -### Dispatcher +### Dispatcher `STRUCT` 用于服务器消息处理的消息分发器 这个消息分发器为并发安全的生产者和消费者模型,生产者可以是任意类型,消费者必须是 Message 接口的实现。 @@ -83,7 +207,7 @@ type Dispatcher[P Producer, M Message[P]] struct { abort chan struct{} } ``` -### Manager +### Manager `STRUCT` 消息分发器管理器 ```go type Manager[P Producer, M Message[P]] struct { @@ -99,13 +223,17 @@ type Manager[P Producer, M Message[P]] struct { createdHandler func(name string) } ``` -### Message +### Message `INTERFACE` ```go -type Message[P comparable] struct{} +type Message[P comparable] interface { + GetProducer() P +} ``` -### Producer +### Producer `INTERFACE` ```go -type Producer struct{} +type Producer interface { + comparable +} ``` diff --git a/server/internal/logger/README.md b/server/internal/logger/README.md index 83d11ee..3acdf3b 100644 --- a/server/internal/logger/README.md +++ b/server/internal/logger/README.md @@ -1,40 +1,37 @@ # Logger - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/logger) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 -> 包级函数定义 +> 类型定义 -|函数|描述 -|:--|:-- - - -> 结构体定义 - -|结构体|描述 -|:--|:-- -|[Ants](#ants)|暂无描述... -|[GNet](#gnet)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Ants](#ants)|暂无描述... +|`STRUCT`|[GNet](#gnet)|暂无描述...
-### Ants +*** +## 详情信息 +### Ants `STRUCT` ```go type Ants struct{} ``` #### func (*Ants) Printf(format string, args ...interface {}) *** -### GNet +### GNet `STRUCT` ```go type GNet struct{} diff --git a/server/lockstep/README.md b/server/lockstep/README.md index f041bf1..64b5a4e 100644 --- a/server/lockstep/README.md +++ b/server/lockstep/README.md @@ -1,19 +1,20 @@ # Lockstep - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/lockstep) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewLockstep](#NewLockstep)|创建一个锁步(帧)同步默认实现的组件(Lockstep)进行返回 |[WithFrameLimit](#WithFrameLimit)|通过特定逻辑帧上限创建锁步(帧)同步组件 @@ -22,31 +23,72 @@ |[WithInitFrame](#WithInitFrame)|通过特定的初始帧创建锁步(帧)同步组件 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Client](#client)|帧同步客户端接口定义 -|[StoppedEventHandle](#stoppedeventhandle)|暂无描述... -|[Lockstep](#lockstep)|锁步(帧)同步默认实现 -|[Option](#option)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`INTERFACE`|[Client](#client)|帧同步客户端接口定义 +|`STRUCT`|[StoppedEventHandle](#stoppedeventhandle)|暂无描述... +|`STRUCT`|[Lockstep](#lockstep)|锁步(帧)同步默认实现 +|`STRUCT`|[Option](#option)|暂无描述...
+*** +## 详情信息 #### func NewLockstep(options ...Option[ClientID, Command]) *Lockstep[ClientID, Command] > 创建一个锁步(帧)同步默认实现的组件(Lockstep)进行返回 + +
+查看 / 收起单元测试 + + +```go + +func TestNewLockstep(t *testing.T) { + ls := lockstep.NewLockstep[string, int](lockstep.WithInitFrame[string, int](1)) + ls.JoinClient(&Cli{id: "player_1"}) + ls.JoinClient(&Cli{id: "player_2"}) + count := 0 + ls.StartBroadcast() + endChan := make(chan bool) + go func() { + for { + ls.AddCommand(random.Int(1, 9999)) + count++ + if count >= 10 { + break + } + time.Sleep(time.Millisecond * time.Duration(random.Int(10, 200))) + } + ls.StopBroadcast() + endChan <- true + }() + <-endChan + time.Sleep(time.Second) + fmt.Println("end") +} + +``` + + +
+ + *** #### func WithFrameLimit(frameLimit int64) Option[ClientID, Command] > 通过特定逻辑帧上限创建锁步(帧)同步组件 > - 当达到上限时将停止广播 + *** #### func WithFrameRate(frameRate int64) Option[ClientID, Command] > 通过特定逻辑帧率创建锁步(帧)同步组件 > - 默认情况下为 15/s + *** #### func WithSerialization(handle func (frame int64, commands []Command) []byte) Option[ClientID, Command] @@ -58,24 +100,29 @@ > Frame int `json:"frame"` > Commands []Command `json:"commands"` > } + *** #### func WithInitFrame(initFrame int64) Option[ClientID, Command] > 通过特定的初始帧创建锁步(帧)同步组件 > - 默认情况下为 0,即第一帧索引为 0 + *** -### Client +### Client `INTERFACE` 帧同步客户端接口定义 - 客户端应该具备ID及写入数据包的实现 ```go -type Client[ID comparable] struct{} +type Client[ID comparable] interface { + GetID() ID + Write(packet []byte, callback ...func(err error)) +} ``` -### StoppedEventHandle +### StoppedEventHandle `STRUCT` ```go -type StoppedEventHandle[ClientID comparable, Command any] struct{} +type StoppedEventHandle[ClientID comparable, Command any] func(lockstep *Lockstep[ClientID, Command]) ``` -### Lockstep +### Lockstep `STRUCT` 锁步(帧)同步默认实现 - 支持最大帧上限 WithFrameLimit - 自定逻辑帧频率,默认为每秒15帧(帧/66ms) WithFrameRate @@ -102,8 +149,8 @@ type Lockstep[ClientID comparable, Command any] struct { lockstepStoppedEventHandles []StoppedEventHandle[ClientID, Command] } ``` -### Option +### Option `STRUCT` ```go -type Option[ClientID comparable, Command any] struct{} +type Option[ClientID comparable, Command any] func(lockstep *Lockstep[ClientID, Command]) ``` diff --git a/server/router/README.md b/server/router/README.md index eb2445d..71158d3 100644 --- a/server/router/README.md +++ b/server/router/README.md @@ -1,53 +1,67 @@ # Router - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/router) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewMultistage](#NewMultistage)|创建一个支持多级分类的路由器 |[WithRouteTrim](#WithRouteTrim)|路由修剪选项 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[MultistageBind](#multistagebind)|多级分类路由绑定函数 -|[Multistage](#multistage)|支持多级分类的路由器 -|[MultistageOption](#multistageoption)|路由器选项 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[MultistageBind](#multistagebind)|多级分类路由绑定函数 +|`STRUCT`|[Multistage](#multistage)|支持多级分类的路由器 +|`STRUCT`|[MultistageOption](#multistageoption)|路由器选项
+*** +## 详情信息 #### func NewMultistage(options ...MultistageOption[HandleFunc]) *Multistage[HandleFunc] > 创建一个支持多级分类的路由器 + +示例代码: +```go + +func ExampleNewMultistage() { + router.NewMultistage[func()]() +} + +``` + *** #### func WithRouteTrim(handle func (route any) any) MultistageOption[HandleFunc] > 路由修剪选项 > - 将在路由注册前对路由进行对应处理 + *** -### MultistageBind +### MultistageBind `STRUCT` 多级分类路由绑定函数 ```go -type MultistageBind[HandleFunc any] struct{} +type MultistageBind[HandleFunc any] func(HandleFunc) ``` #### func (MultistageBind) Bind(handleFunc HandleFunc) > 将处理函数绑定到预设的路由中 *** -### Multistage +### Multistage `STRUCT` 支持多级分类的路由器 ```go type Multistage[HandleFunc any] struct { @@ -60,19 +74,98 @@ type Multistage[HandleFunc any] struct { #### func (*Multistage) Register(routes ...any) MultistageBind[HandleFunc] > 注册路由是结合 Sub 和 Route 的快捷方式,用于一次性注册多级路由 > - 该函数将返回一个注册函数,可通过调用其将路由绑定到特定处理函数,例如:router.Register("a", "b").Bind(onExec()) +示例代码: +```go + +func ExampleMultistage_Register() { + r := router.NewMultistage[func()]() + r.Register("System", "Network", "Ping")(func() { + }) +} + +``` + *** #### func (*Multistage) Route(route any, handleFunc HandleFunc) > 为特定路由绑定处理函数,被绑定的处理函数将可以通过 Match 函数进行匹配 +示例代码: +```go + +func ExampleMultistage_Route() { + r := router.NewMultistage[func()]() + r.Route("ServerTime", func() { + }) +} + +``` + *** #### func (*Multistage) Match(routes ...any) HandleFunc > 匹配已绑定处理函数的路由,返回处理函数 > - 如果未找到将会返回空指针 +示例代码: +```go + +func ExampleMultistage_Match() { + r := router.NewMultistage[func()]() + r.Route("ServerTime", func() { + }) + r.Register("System", "Network", "Ping").Bind(func() { + }) + r.Match("ServerTime")() + r.Match("System", "Network", "Ping")() +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestMultistage_Match(t *testing.T) { + r := router.NewMultistage[func()]() + r.Sub("System").Route("Heartbeat", func() { + fmt.Println("Heartbeat") + }) + r.Route("ServerTime", func() { + fmt.Println("ServerTime") + }) + r.Register("System", "Network", "Ping")(func() { + fmt.Println("Ping") + }) + r.Register("System", "Network", "Echo").Bind(onEcho) + r.Match("System", "Heartbeat")() + r.Match("ServerTime")() + r.Match("System", "Network", "Ping")() + r.Match("System", "Network", "Echo")() + fmt.Println(r.Match("None") == nil) +} + +``` + + +
+ + *** #### func (*Multistage) Sub(route any) *Multistage[HandleFunc] > 获取子路由器 +示例代码: +```go + +func ExampleMultistage_Sub() { + r := router.NewMultistage[func()]() + r.Sub("System").Route("Heartbeat", func() { + }) +} + +``` + *** -### MultistageOption +### MultistageOption `STRUCT` 路由器选项 ```go -type MultistageOption[HandleFunc any] struct{} +type MultistageOption[HandleFunc any] func(multistage *Multistage[HandleFunc]) ``` diff --git a/server/writeloop/README.md b/server/writeloop/README.md index b2ad46e..acf1716 100644 --- a/server/writeloop/README.md +++ b/server/writeloop/README.md @@ -1,35 +1,38 @@ # Writeloop - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/writeloop) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewChannel](#NewChannel)|创建基于 Channel 的写循环 |[NewUnbounded](#NewUnbounded)|创建写循环 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Channel](#channel)|基于 chan 的写循环,与 Unbounded 相同,但是使用 Channel 实现 -|[Unbounded](#unbounded)|写循环 -|[WriteLoop](#writeloop)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Channel](#channel)|基于 chan 的写循环,与 Unbounded 相同,但是使用 Channel 实现 +|`STRUCT`|[Unbounded](#unbounded)|写循环 +|`INTERFACE`|[WriteLoop](#writeloop)|暂无描述...
+*** +## 详情信息 #### func NewChannel(pool *hub.ObjectPool[Message], channelSize int, writeHandler func (message Message) error, errorHandler func (err any)) *Channel[Message] > 创建基于 Channel 的写循环 @@ -39,6 +42,7 @@ > - errorHandler 错误处理函数 > > 传入 writeHandler 的消息对象是从 Channel 中获取的,因此 writeHandler 不应该持有消息对象的引用,同时也不应该主动释放消息对象 + *** #### func NewUnbounded(pool *hub.ObjectPool[Message], writeHandler func (message Message) error, errorHandler func (err any)) *Unbounded[Message] @@ -48,8 +52,61 @@ > - errorHandler 错误处理函数 > > 传入 writeHandler 的消息对象是从 pool 中获取的,并且在 writeHandler 执行完成后会被放回 pool 中,因此 writeHandler 不应该持有消息对象的引用,同时也不应该主动释放消息对象 + +示例代码: +```go + +func ExampleNewUnbounded() { + pool := hub.NewObjectPool[Message](func() *Message { + return &Message{} + }, func(data *Message) { + data.ID = 0 + }) + var wait sync.WaitGroup + wait.Add(10) + wl := writeloop.NewUnbounded(pool, func(message *Message) error { + fmt.Println(message.ID) + wait.Done() + return nil + }, func(err any) { + fmt.Println(err) + }) + for i := 0; i < 10; i++ { + m := pool.Get() + m.ID = i + wl.Put(m) + } + wait.Wait() + wl.Close() +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestNewUnbounded(t *testing.T) { + wl := writeloop.NewUnbounded(wp, func(message *Message) error { + t.Log(message.ID) + return nil + }, func(err any) { + t.Log(err) + }) + assert.NotNil(t, wl) + wl.Close() +} + +``` + + +
+ + *** -### Channel +### Channel `STRUCT` 基于 chan 的写循环,与 Unbounded 相同,但是使用 Channel 实现 ```go type Channel[T any] struct { @@ -62,7 +119,7 @@ type Channel[T any] struct { #### func (*Channel) Close() > 关闭写循环 *** -### Unbounded +### Unbounded `STRUCT` 写循环 - 用于将数据并发安全的写入到底层连接 ```go @@ -72,12 +129,99 @@ type Unbounded[Message any] struct { ``` #### func (*Unbounded) Put(message Message) > 将数据放入写循环,message 应该来源于 hub.ObjectPool +
+查看 / 收起单元测试 + + +```go + +func TestUnbounded_Put(t *testing.T) { + wl := writeloop.NewUnbounded(wp, func(message *Message) error { + t.Log(message.ID) + return nil + }, func(err any) { + t.Log(err) + }) + assert.NotNil(t, wl) + for i := 0; i < 100; i++ { + m := wp.Get() + m.ID = i + wl.Put(m) + } + wl.Close() +} + +``` + + +
+ + +
+查看 / 收起基准测试 + + +```go + +func BenchmarkUnbounded_Put(b *testing.B) { + wl := writeloop.NewUnbounded(wp, func(message *Message) error { + return nil + }, nil) + defer func() { + wl.Close() + }() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + wl.Put(wp.Get()) + } + }) + b.StopTimer() +} + +``` + + +
+ + *** #### func (*Unbounded) Close() > 关闭写循环 -*** -### WriteLoop +
+查看 / 收起单元测试 + ```go -type WriteLoop[Message any] struct{} + +func TestUnbounded_Close(t *testing.T) { + wl := writeloop.NewUnbounded(wp, func(message *Message) error { + t.Log(message.ID) + return nil + }, func(err any) { + t.Log(err) + }) + assert.NotNil(t, wl) + for i := 0; i < 100; i++ { + m := wp.Get() + m.ID = i + wl.Put(m) + } + wl.Close() +} + +``` + + +
+ + +*** +### WriteLoop `INTERFACE` + +```go +type WriteLoop[Message any] interface { + Put(message Message) + Close() +} ``` diff --git a/utils/README.md b/utils/README.md index 5e2bbb1..ecbc402 100644 --- a/utils/README.md +++ b/utils/README.md @@ -1,5 +1,8 @@ # Utils +[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/utils) +![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) + utils 旨在提供一组用于处理通用功能的函数和数据结构。该包旨在简化通用功能的实现,并提供一致的接口和易于使用的功能。 主要特性: - 通用功能:utils 包支持处理各种通用功能,如字符串处理、日期时间操作和文件操作等。您可以使用这些功能来解决各种通用问题,并提高代码的复用性和可维护性。 @@ -7,26 +10,6 @@ utils 旨在提供一组用于处理通用功能的函数和数据结构。该 - 算法实现:utils 包还提供了一些常用的算法实现,如排序算法、搜索算法和图算法等。这些算法可以用于解决各种问题,并提供高效的计算和处理能力。 - 工具函数:该包还提供了一些通用的工具函数,如字符串处理、日期时间操作和文件操作等。这些工具函数可以帮助您简化代码编写,处理文本数据,操作日期时间,读写文件等。 -[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/utils) -![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) - -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ -
-展开 / 折叠目录 包级函数定义 - -|函数|描述 -|:--|:-- - - -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- +*** +## 详情信息 diff --git a/utils/aoi/README.md b/utils/aoi/README.md index a7481d6..be002aa 100644 --- a/utils/aoi/README.md +++ b/utils/aoi/README.md @@ -1,5 +1,8 @@ # Aoi +[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/aoi) +![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) + aoi 提供了一种有效的方法来处理 AOI(Area of Interest)问题。 AOI 问题是在大规模多人在线游戏中常见的问题,它涉及到确定哪些对象对玩家来说是“感兴趣的”, @@ -7,37 +10,62 @@ AOI 问题是在大规模多人在线游戏中常见的问题,它涉及到确 这个包提供了一种数据结构和一些方法来有效地解决这个问题。 -[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/aoi) -![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewTwoDimensional](#NewTwoDimensional)|暂无描述... -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[TwoDimensional](#twodimensional)|暂无描述... -|[TwoDimensionalEntity](#twodimensionalentity)|基于2D定义的AOI对象功能接口 -|[EntityJoinVisionEventHandle](#entityjoinvisioneventhandle)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[TwoDimensional](#twodimensional)|暂无描述... +|`INTERFACE`|[TwoDimensionalEntity](#twodimensionalentity)|基于2D定义的AOI对象功能接口 +|`STRUCT`|[EntityJoinVisionEventHandle](#entityjoinvisioneventhandle)|暂无描述...
+*** +## 详情信息 #### func NewTwoDimensional(width int, height int, areaWidth int, areaHeight int) *TwoDimensional[EID, PosType, E] + +
+查看 / 收起单元测试 + + +```go + +func TestNewTwoDimensional(t *testing.T) { + aoiTW := aoi.NewTwoDimensional[int64, float64, *Ent](10000, 10000, 100, 100) + start := time.Now() + for i := 0; i < 50000; i++ { + aoiTW.AddEntity(&Ent{guid: int64(i), pos: geometry.NewPoint[float64](float64(random.Int64(0, 10000)), float64(random.Int64(0, 10000))), vision: 200}) + } + fmt.Println("添加耗时:", time.Since(start)) + start = time.Now() + aoiTW.SetSize(10100, 10100) + fmt.Println("重设大小耗时:", time.Since(start)) +} + +``` + + +
+ + *** -### TwoDimensional +### TwoDimensional `STRUCT` ```go type TwoDimensional[EID generic.Basic, PosType generic.SignedNumber, E TwoDimensionalEntity[EID, PosType]] struct { @@ -54,14 +82,18 @@ type TwoDimensional[EID generic.Basic, PosType generic.SignedNumber, E TwoDimens repartitionQueue []func() } ``` -### TwoDimensionalEntity +### TwoDimensionalEntity `INTERFACE` 基于2D定义的AOI对象功能接口 - AOI 对象提供了 AOI 系统中常用的属性,诸如位置坐标和视野范围等 ```go -type TwoDimensionalEntity[EID generic.Basic, PosType generic.SignedNumber] struct{} +type TwoDimensionalEntity[EID generic.Basic, PosType generic.SignedNumber] interface { + GetTwoDimensionalEntityID() EID + GetVision() float64 + GetPosition() geometry.Point[PosType] +} ``` -### EntityJoinVisionEventHandle +### EntityJoinVisionEventHandle `STRUCT` ```go -type EntityJoinVisionEventHandle[EID generic.Basic, PosType generic.SignedNumber, E TwoDimensionalEntity[EID, PosType]] struct{} +type EntityJoinVisionEventHandle[EID generic.Basic, PosType generic.SignedNumber, E TwoDimensionalEntity[EID, PosType]] func(entity E) ``` diff --git a/utils/arrangement/README.md b/utils/arrangement/README.md index 4b5582e..93aa589 100644 --- a/utils/arrangement/README.md +++ b/utils/arrangement/README.md @@ -1,21 +1,22 @@ # Arrangement +[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/arrangement) +![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) + arrangement 包提供了一些有用的函数来处理数组的排列。 更多的详细信息和使用示例,可以参考每个函数的文档。 -[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/arrangement) -![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[WithAreaConstraint](#WithAreaConstraint)|设置编排区域的约束条件 |[WithAreaConflict](#WithAreaConflict)|设置编排区域的冲突条件,冲突处理函数需要返回造成冲突的成员列表 @@ -29,62 +30,72 @@ arrangement 包提供了一些有用的函数来处理数组的排列。 |[WithConflictHandle](#WithConflictHandle)|设置编排时触发冲突时的处理函数 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Area](#area)|编排区域 -|[AreaOption](#areaoption)|编排区域选项 -|[AreaConstraintHandle](#areaconstrainthandle)|暂无描述... -|[Arrangement](#arrangement)|用于针对多条数据进行合理编排的数据结构 -|[Editor](#editor)|提供了大量辅助函数的编辑器 -|[Item](#item)|编排成员 -|[ItemOption](#itemoption)|编排成员选项 -|[ItemFixedAreaHandle](#itemfixedareahandle)|暂无描述... -|[Option](#option)|编排选项 -|[ConstraintHandle](#constrainthandle)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Area](#area)|编排区域 +|`STRUCT`|[AreaOption](#areaoption)|编排区域选项 +|`STRUCT`|[AreaConstraintHandle](#areaconstrainthandle)|暂无描述... +|`STRUCT`|[Arrangement](#arrangement)|用于针对多条数据进行合理编排的数据结构 +|`STRUCT`|[Editor](#editor)|提供了大量辅助函数的编辑器 +|`INTERFACE`|[Item](#item)|编排成员 +|`STRUCT`|[ItemOption](#itemoption)|编排成员选项 +|`STRUCT`|[ItemFixedAreaHandle](#itemfixedareahandle)|暂无描述... +|`STRUCT`|[Option](#option)|编排选项 +|`STRUCT`|[ConstraintHandle](#constrainthandle)|暂无描述...
+*** +## 详情信息 #### func WithAreaConstraint(constraint AreaConstraintHandle[ID, AreaInfo]) AreaOption[ID, AreaInfo] > 设置编排区域的约束条件 > - 该约束用于判断一个成员是否可以被添加到该编排区域中 > - 与 WithAreaConflict 不同的是,约束通常用于非成员关系导致的硬性约束,例如:成员的等级过滤、成员的性别等 + *** #### func WithAreaConflict(conflict AreaConflictHandle[ID, AreaInfo]) AreaOption[ID, AreaInfo] > 设置编排区域的冲突条件,冲突处理函数需要返回造成冲突的成员列表 > - 该冲突用于判断一个成员是否可以被添加到该编排区域中 > - 与 WithAreaConstraint 不同的是,冲突通常用于成员关系导致的软性约束,例如:成员的职业唯一性、成员的种族唯一性等 + *** #### func WithAreaEvaluate(evaluate AreaEvaluateHandle[ID, AreaInfo]) AreaOption[ID, AreaInfo] > 设置编排区域的评估函数 > - 该评估函数将影响成员被编入区域的优先级 + *** #### func NewArrangement(options ...Option[ID, AreaInfo]) *Arrangement[ID, AreaInfo] > 创建一个新的编排 + *** #### func WithItemFixed(matcher ItemFixedAreaHandle[AreaInfo]) ItemOption[ID, AreaInfo] > 设置成员的固定编排区域 + *** #### func WithItemPriority(priority ItemPriorityHandle[ID, AreaInfo]) ItemOption[ID, AreaInfo] > 设置成员的优先级 + *** #### func WithItemNotAllow(verify ItemNotAllowVerifyHandle[ID, AreaInfo]) ItemOption[ID, AreaInfo] > 设置成员不允许的编排区域 + *** #### func WithRetryThreshold(threshold int) Option[ID, AreaInfo] > 设置编排时的重试阈值 > - 当每一轮编排结束任有成员未被编排时,将会进行下一轮编排,直到编排次数达到该阈值 > - 默认的阈值为 10 次 + *** #### func WithConstraintHandle(handle ConstraintHandle[ID, AreaInfo]) Option[ID, AreaInfo] @@ -94,6 +105,7 @@ arrangement 包提供了一些有用的函数来处理数组的排列。 > - 当所有的约束处理函数都无法处理约束时,将会进入下一个编排区域的尝试,如果均无法完成,将会将该成员加入到编排队列的末端,等待下一次编排 > > 有意思的是,硬性约束应该永远是无解的,而当需要进行一些打破规则的操作时,则可以透过该函数传入的 editor 进行操作 + *** #### func WithConflictHandle(handle ConflictHandle[ID, AreaInfo]) Option[ID, AreaInfo] @@ -101,8 +113,9 @@ arrangement 包提供了一些有用的函数来处理数组的排列。 > - 当冲突条件触发时,将会调用该函数。如果无法在该函数中处理冲突,应该继续返回这一批成员,尝试进行下一层的冲突处理 > - 当该函数的返回值长度为 0 时,表示冲突已经被处理,将会命中当前的编排区域 > - 当所有的冲突处理函数都无法处理冲突时,将会进入下一个编排区域的尝试,如果均无法完成,将会将该成员加入到编排队列的末端,等待下一次编排 + *** -### Area +### Area `STRUCT` 编排区域 ```go type Area[ID comparable, AreaInfo any] struct { @@ -113,17 +126,17 @@ type Area[ID comparable, AreaInfo any] struct { evaluate AreaEvaluateHandle[ID, AreaInfo] } ``` -### AreaOption +### AreaOption `STRUCT` 编排区域选项 ```go -type AreaOption[ID comparable, AreaInfo any] struct{} +type AreaOption[ID comparable, AreaInfo any] func(area *Area[ID, AreaInfo]) ``` -### AreaConstraintHandle +### AreaConstraintHandle `STRUCT` ```go -type AreaConstraintHandle[ID comparable, AreaInfo any] struct{} +type AreaConstraintHandle[ID comparable, AreaInfo any] func(area *Area[ID, AreaInfo], item Item[ID]) error ``` -### Arrangement +### Arrangement `STRUCT` 用于针对多条数据进行合理编排的数据结构 - 我不知道这个数据结构的具体用途,但是我觉得这个数据结构应该是有用的 - 目前我能想到的用途只有我的过往经历:排课 @@ -140,7 +153,7 @@ type Arrangement[ID comparable, AreaInfo any] struct { conflictHandles []ConflictHandle[ID, AreaInfo] } ``` -### Editor +### Editor `STRUCT` 提供了大量辅助函数的编辑器 ```go type Editor[ID comparable, AreaInfo any] struct { @@ -151,28 +164,31 @@ type Editor[ID comparable, AreaInfo any] struct { retryCount int } ``` -### Item +### Item `INTERFACE` 编排成员 ```go -type Item[ID comparable] struct{} +type Item[ID comparable] interface { + GetID() ID + Equal(item Item[ID]) bool +} ``` -### ItemOption +### ItemOption `STRUCT` 编排成员选项 ```go -type ItemOption[ID comparable, AreaInfo any] struct{} +type ItemOption[ID comparable, AreaInfo any] func(arrangement *Arrangement[ID, AreaInfo], item Item[ID]) ``` -### ItemFixedAreaHandle +### ItemFixedAreaHandle `STRUCT` ```go -type ItemFixedAreaHandle[AreaInfo any] struct{} +type ItemFixedAreaHandle[AreaInfo any] func(areaInfo AreaInfo) bool ``` -### Option +### Option `STRUCT` 编排选项 ```go -type Option[ID comparable, AreaInfo any] struct{} +type Option[ID comparable, AreaInfo any] func(arrangement *Arrangement[ID, AreaInfo]) ``` -### ConstraintHandle +### ConstraintHandle `STRUCT` ```go -type ConstraintHandle[ID comparable, AreaInfo any] struct{} +type ConstraintHandle[ID comparable, AreaInfo any] func(editor *Editor[ID, AreaInfo], area *Area[ID, AreaInfo], item Item[ID], err error) error ``` diff --git a/utils/buffer/README.md b/utils/buffer/README.md index 8a9abfd..c968296 100644 --- a/utils/buffer/README.md +++ b/utils/buffer/README.md @@ -1,5 +1,8 @@ # Buffer +[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/buffer) +![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) + buffer 提供了缓冲区相关的实用程序。 包括创建、读取和写入缓冲区的函数。 @@ -8,45 +11,67 @@ buffer 提供了缓冲区相关的实用程序。 无界缓冲区的所有方法都是线程安全的,除了用于同步的互斥锁外,不会阻塞任何东西。 -[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/buffer) -![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewRing](#NewRing)|创建一个并发不安全的环形缓冲区 |[NewRingUnbounded](#NewRingUnbounded)|创建一个并发安全的基于环形缓冲区实现的无界缓冲区 |[NewUnbounded](#NewUnbounded)|创建一个无界缓冲区 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Ring](#ring)|环形缓冲区 -|[RingUnbounded](#ringunbounded)|基于环形缓冲区实现的无界缓冲区 -|[Unbounded](#unbounded)|是无界缓冲区的实现 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Ring](#ring)|环形缓冲区 +|`STRUCT`|[RingUnbounded](#ringunbounded)|基于环形缓冲区实现的无界缓冲区 +|`STRUCT`|[Unbounded](#unbounded)|是无界缓冲区的实现
+*** +## 详情信息 #### func NewRing(initSize ...int) *Ring[T] > 创建一个并发不安全的环形缓冲区 > - initSize: 初始容量 > > 当初始容量小于 2 或未设置时,将会使用默认容量 2 + +
+查看 / 收起单元测试 + + +```go + +func TestNewRing(t *testing.T) { + ring := buffer.NewRing[int]() + for i := 0; i < 100; i++ { + ring.Write(i) + t.Log(ring.Read()) + } +} + +``` + + +
+ + *** #### func NewRingUnbounded(bufferSize int) *RingUnbounded[T] > 创建一个并发安全的基于环形缓冲区实现的无界缓冲区 + *** #### func NewUnbounded() *Unbounded[V] @@ -55,8 +80,9 @@ buffer 提供了缓冲区相关的实用程序。 > > 该缓冲区来源于 gRPC 的实现,用于在不使用额外 goroutine 的情况下实现无界缓冲区 > - 该缓冲区的所有方法都是线程安全的,除了用于同步的互斥锁外,不会阻塞任何东西 + *** -### Ring +### Ring `STRUCT` 环形缓冲区 ```go type Ring[T any] struct { @@ -69,6 +95,29 @@ type Ring[T any] struct { ``` #### func (*Ring) Read() T, error > 读取数据 +
+查看 / 收起基准测试 + + +```go + +func BenchmarkRing_Read(b *testing.B) { + ring := buffer.NewRing[int](1024) + for i := 0; i < b.N; i++ { + ring.Write(i) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = ring.Read() + } +} + +``` + + +
+ + *** #### func (*Ring) ReadAll() []T > 读取所有数据 @@ -78,6 +127,26 @@ type Ring[T any] struct { *** #### func (*Ring) Write(v T) > 写入数据 +
+查看 / 收起基准测试 + + +```go + +func BenchmarkRing_Write(b *testing.B) { + ring := buffer.NewRing[int](1024) + b.ResetTimer() + for i := 0; i < b.N; i++ { + ring.Write(i) + } +} + +``` + + +
+ + *** #### func (*Ring) IsEmpty() bool > 是否为空 @@ -91,7 +160,7 @@ type Ring[T any] struct { #### func (*Ring) Reset() > 重置缓冲区 *** -### RingUnbounded +### RingUnbounded `STRUCT` 基于环形缓冲区实现的无界缓冲区 ```go type RingUnbounded[T any] struct { @@ -106,17 +175,87 @@ type RingUnbounded[T any] struct { ``` #### func (*RingUnbounded) Write(v T) > 写入数据 +
+查看 / 收起基准测试 + + +```go + +func BenchmarkRingUnbounded_Write(b *testing.B) { + ring := buffer.NewRingUnbounded[int](1024 * 16) + b.ResetTimer() + for i := 0; i < b.N; i++ { + ring.Write(i) + } +} + +``` + + +
+ + *** #### func (*RingUnbounded) Read() chan T > 读取数据 +
+查看 / 收起基准测试 + + +```go + +func BenchmarkRingUnbounded_Read(b *testing.B) { + ring := buffer.NewRingUnbounded[int](1024 * 16) + for i := 0; i < b.N; i++ { + ring.Write(i) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + <-ring.Read() + } +} + +``` + + +
+ + *** #### func (*RingUnbounded) Closed() bool > 判断缓冲区是否已关闭 *** #### func (*RingUnbounded) Close() chan struct {} > 关闭缓冲区,关闭后将不再接收新数据,但是已有数据仍然可以读取 +
+查看 / 收起单元测试 + + +```go + +func TestRingUnbounded_Close(t *testing.T) { + ring := buffer.NewRingUnbounded[int](1024 * 16) + for i := 0; i < 100; i++ { + ring.Write(i) + } + t.Log("write done") + ring.Close() + t.Log("close done") + for v := range ring.Read() { + ring.Write(v) + t.Log(v) + } + t.Log("read done") +} + +``` + + +
+ + *** -### Unbounded +### Unbounded `STRUCT` 是无界缓冲区的实现 ```go type Unbounded[V any] struct { @@ -135,6 +274,27 @@ type Unbounded[V any] struct { *** #### func (*Unbounded) Get() chan V > 获取读取通道 +
+查看 / 收起单元测试 + + +```go + +func TestUnbounded_Get(t *testing.T) { + ub := buffer.NewUnbounded[int]() + for i := 0; i < 100; i++ { + ub.Put(i + 1) + fmt.Println(<-ub.Get()) + ub.Load() + } +} + +``` + + +
+ + *** #### func (*Unbounded) Close() > 关闭 diff --git a/utils/buffer/ring_benchmark_test.go b/utils/buffer/ring_benchmark_test.go index 4d9d11c..ee1f6af 100644 --- a/utils/buffer/ring_benchmark_test.go +++ b/utils/buffer/ring_benchmark_test.go @@ -5,7 +5,7 @@ import ( "testing" ) -func BenchmarkRingWrite(b *testing.B) { +func BenchmarkRing_Write(b *testing.B) { ring := buffer.NewRing[int](1024) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -13,7 +13,7 @@ func BenchmarkRingWrite(b *testing.B) { } } -func BenchmarkRingRead(b *testing.B) { +func BenchmarkRing_Read(b *testing.B) { ring := buffer.NewRing[int](1024) for i := 0; i < b.N; i++ { ring.Write(i) diff --git a/utils/collection/README.md b/utils/collection/README.md index a8f29a9..4aa6e7d 100644 --- a/utils/collection/README.md +++ b/utils/collection/README.md @@ -1,19 +1,20 @@ # Collection -collection 用于对 input 和 map 操作的工具函数 - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/collection) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +collection 用于对 input 和 map 操作的工具函数 + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[CloneSlice](#CloneSlice)|克隆切片,该函数是 slices.Clone 的快捷方式 |[CloneMap](#CloneMap)|克隆 map @@ -128,501 +129,5295 @@ collection 用于对 input 和 map 操作的工具函数 |[ShuffleByClone](#ShuffleByClone)|对切片进行随机排序,返回排序后的切片 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[ComparisonHandler](#comparisonhandler)|暂无描述... -|[OrderedValueGetter](#orderedvaluegetter)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[ComparisonHandler](#comparisonhandler)|暂无描述... +|`STRUCT`|[OrderedValueGetter](#orderedvaluegetter)|暂无描述...
+*** +## 详情信息 #### func CloneSlice(slice S) S > 克隆切片,该函数是 slices.Clone 的快捷方式 + +示例代码: +```go + +func ExampleCloneSlice() { + var slice = []int{1, 2, 3} + var result = collection.CloneSlice(slice) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestCloneSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{{"TestCloneSlice_NonEmptySlice", []int{1, 2, 3}, []int{1, 2, 3}}, {"TestCloneSlice_EmptySlice", []int{}, []int{}}, {"TestCloneSlice_NilSlice", nil, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.CloneSlice(c.input) + if len(actual) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal") + } + for i := 0; i < len(actual); i++ { + if actual[i] != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the inputV of input is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func CloneMap(m M) M > 克隆 map + +示例代码: +```go + +func ExampleCloneMap() { + var m = map[int]int{1: 1, 2: 2, 3: 3} + var result = collection.CloneMap(m) + fmt.Println(len(result)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestCloneMap(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]int + }{{"TestCloneMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]int{1: 1, 2: 2, 3: 3}}, {"TestCloneMap_EmptyMap", map[int]int{}, map[int]int{}}, {"TestCloneMap_NilMap", nil, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.CloneMap(c.input) + if len(actual) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of map is not equal") + } + for k, v := range actual { + if v != c.expected[k] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the inputV of map is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func CloneSliceN(slice S, n int) []S > 克隆 slice 为 n 个切片进行返回 + +示例代码: +```go + +func ExampleCloneSliceN() { + var slice = []int{1, 2, 3} + var result = collection.CloneSliceN(slice, 2) + fmt.Println(len(result)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestCloneSliceN(t *testing.T) { + var cases = []struct { + name string + input []int + inputN int + expected [][]int + }{{"TestCloneSliceN_NonEmptySlice", []int{1, 2, 3}, 2, [][]int{{1, 2, 3}, {1, 2, 3}}}, {"TestCloneSliceN_EmptySlice", []int{}, 2, [][]int{{}, {}}}, {"TestCloneSliceN_NilSlice", nil, 2, nil}, {"TestCloneSliceN_NonEmptySlice_ZeroN", []int{1, 2, 3}, 0, [][]int{}}, {"TestCloneSliceN_EmptySlice_ZeroN", []int{}, 0, [][]int{}}, {"TestCloneSliceN_NilSlice_ZeroN", nil, 0, nil}, {"TestCloneSliceN_NonEmptySlice_NegativeN", []int{1, 2, 3}, -1, [][]int{}}, {"TestCloneSliceN_EmptySlice_NegativeN", []int{}, -1, [][]int{}}, {"TestCloneSliceN_NilSlice_NegativeN", nil, -1, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.CloneSliceN(c.input, c.inputN) + if actual == nil { + if c.expected != nil { + t.Fatalf("%s failed, expected: %v, actual: %v, inputN: %d, error: %s", c.name, c.expected, c.inputN, actual, "after clone, the expected is nil") + } + return + } + for a, i := range actual { + for b, v := range i { + if v != c.expected[a][b] { + t.Fatalf("%s failed, expected: %v, actual: %v, inputN: %d, error: %s", c.name, c.expected, c.inputN, actual, "after clone, the inputV of input is not equal") + } + } + } + }) + } +} + +``` + + +
+ + *** #### func CloneMapN(m M, n int) []M > 克隆 map 为 n 个 map 进行返回 + +示例代码: +```go + +func ExampleCloneMapN() { + var m = map[int]int{1: 1, 2: 2, 3: 3} + var result = collection.CloneMapN(m, 2) + fmt.Println(len(result)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestCloneMapN(t *testing.T) { + var cases = []struct { + name string + input map[int]int + inputN int + expected []map[int]int + }{{"TestCloneMapN_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, 2, []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 3: 3}}}, {"TestCloneMapN_EmptyMap", map[int]int{}, 2, []map[int]int{{}, {}}}, {"TestCloneMapN_NilMap", nil, 2, nil}, {"TestCloneMapN_NonEmptyMap_ZeroN", map[int]int{1: 1, 2: 2, 3: 3}, 0, []map[int]int{}}, {"TestCloneMapN_EmptyMap_ZeroN", map[int]int{}, 0, []map[int]int{}}, {"TestCloneMapN_NilMap_ZeroN", nil, 0, nil}, {"TestCloneMapN_NonEmptyMap_NegativeN", map[int]int{1: 1, 2: 2, 3: 3}, -1, []map[int]int{}}, {"TestCloneMapN_EmptyMap_NegativeN", map[int]int{}, -1, []map[int]int{}}, {"TestCloneMapN_NilMap_NegativeN", nil, -1, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.CloneMapN(c.input, c.inputN) + if actual == nil { + if c.expected != nil { + t.Fatalf("%s failed, expected: %v, actual: %v, inputN: %d, error: %s", c.name, c.expected, actual, c.inputN, "after clone, the expected is nil") + } + return + } + for a, i := range actual { + for b, v := range i { + if v != c.expected[a][b] { + t.Fatalf("%s failed, expected: %v, actual: %v, inputN: %d, error: %s", c.name, c.expected, actual, c.inputN, "after clone, the inputV of map is not equal") + } + } + } + }) + } +} + +``` + + +
+ + *** #### func CloneSlices(slices ...S) []S > 克隆多个切片 + +示例代码: +```go + +func ExampleCloneSlices() { + var slice1 = []int{1, 2, 3} + var slice2 = []int{1, 2, 3} + var result = collection.CloneSlices(slice1, slice2) + fmt.Println(len(result)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestCloneSlices(t *testing.T) { + var cases = []struct { + name string + input [][]int + expected [][]int + }{{"TestCloneSlices_NonEmptySlices", [][]int{{1, 2, 3}, {1, 2, 3}}, [][]int{{1, 2, 3}, {1, 2, 3}}}, {"TestCloneSlices_EmptySlices", [][]int{{}, {}}, [][]int{{}, {}}}, {"TestCloneSlices_NilSlices", nil, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.CloneSlices(c.input...) + if len(actual) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal") + } + for a, i := range actual { + for b, v := range i { + if v != c.expected[a][b] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the inputV of input is not equal") + } + } + } + }) + } +} + +``` + + +
+ + *** #### func CloneMaps(maps ...M) []M > 克隆多个 map + +示例代码: +```go + +func ExampleCloneMaps() { + var m1 = map[int]int{1: 1, 2: 2, 3: 3} + var m2 = map[int]int{1: 1, 2: 2, 3: 3} + var result = collection.CloneMaps(m1, m2) + fmt.Println(len(result)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestCloneMaps(t *testing.T) { + var cases = []struct { + name string + input []map[int]int + expected []map[int]int + }{{"TestCloneMaps_NonEmptyMaps", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 3: 3}}, []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 3: 3}}}, {"TestCloneMaps_EmptyMaps", []map[int]int{{}, {}}, []map[int]int{{}, {}}}, {"TestCloneMaps_NilMaps", nil, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.CloneMaps(c.input...) + if len(actual) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of maps is not equal") + } + for a, i := range actual { + for b, v := range i { + if v != c.expected[a][b] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the inputV of maps is not equal") + } + } + } + }) + } +} + +``` + + +
+ + *** #### func InSlice(slice S, v V, handler ComparisonHandler[V]) bool > 检查 v 是否被包含在 slice 中,当 handler 返回 true 时,表示 v 和 slice 中的某个元素相匹配 + +示例代码: +```go + +func ExampleInSlice() { + result := collection.InSlice([]int{1, 2, 3}, 2, func(source, target int) bool { + return source == target + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestInSlice(t *testing.T) { + var cases = []struct { + name string + input []int + inputV int + expected bool + }{{"TestInSlice_NonEmptySliceIn", []int{1, 2, 3}, 1, true}, {"TestInSlice_NonEmptySliceNotIn", []int{1, 2, 3}, 4, false}, {"TestInSlice_EmptySlice", []int{}, 1, false}, {"TestInSlice_NilSlice", nil, 1, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.InSlice(c.input, c.inputV, func(source, target int) bool { + return source == target + }) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func InComparableSlice(slice S, v V) bool > 检查 v 是否被包含在 slice 中 + +示例代码: +```go + +func ExampleInComparableSlice() { + result := collection.InComparableSlice([]int{1, 2, 3}, 2) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestInComparableSlice(t *testing.T) { + var cases = []struct { + name string + input []int + inputV int + expected bool + }{{"TestInComparableSlice_NonEmptySliceIn", []int{1, 2, 3}, 1, true}, {"TestInComparableSlice_NonEmptySliceNotIn", []int{1, 2, 3}, 4, false}, {"TestInComparableSlice_EmptySlice", []int{}, 1, false}, {"TestInComparableSlice_NilSlice", nil, 1, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.InComparableSlice(c.input, c.inputV) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func AllInSlice(slice S, values []V, handler ComparisonHandler[V]) bool > 检查 values 中的所有元素是否均被包含在 slice 中,当 handler 返回 true 时,表示 values 中的某个元素和 slice 中的某个元素相匹配 > - 在所有 values 中的元素都被包含在 slice 中时,返回 true > - 当 values 长度为 0 或为 nil 时,将返回 true + +示例代码: +```go + +func ExampleAllInSlice() { + result := collection.AllInSlice([]int{1, 2, 3}, []int{1, 2}, func(source, target int) bool { + return source == target + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAllInSlice(t *testing.T) { + var cases = []struct { + name string + input []int + inputV []int + expected bool + }{{"TestAllInSlice_NonEmptySliceIn", []int{1, 2, 3}, []int{1, 2}, true}, {"TestAllInSlice_NonEmptySliceNotIn", []int{1, 2, 3}, []int{1, 4}, false}, {"TestAllInSlice_EmptySlice", []int{}, []int{1, 2}, false}, {"TestAllInSlice_NilSlice", nil, []int{1, 2}, false}, {"TestAllInSlice_EmptyValueSlice", []int{1, 2, 3}, []int{}, true}, {"TestAllInSlice_NilValueSlice", []int{1, 2, 3}, nil, true}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AllInSlice(c.input, c.inputV, func(source, target int) bool { + return source == target + }) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func AllInComparableSlice(slice S, values []V) bool > 检查 values 中的所有元素是否均被包含在 slice 中 > - 在所有 values 中的元素都被包含在 slice 中时,返回 true > - 当 values 长度为 0 或为 nil 时,将返回 true + +示例代码: +```go + +func ExampleAllInComparableSlice() { + result := collection.AllInComparableSlice([]int{1, 2, 3}, []int{1, 2}) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAllInComparableSlice(t *testing.T) { + var cases = []struct { + name string + input []int + inputV []int + expected bool + }{{"TestAllInComparableSlice_NonEmptySliceIn", []int{1, 2, 3}, []int{1, 2}, true}, {"TestAllInComparableSlice_NonEmptySliceNotIn", []int{1, 2, 3}, []int{1, 4}, false}, {"TestAllInComparableSlice_EmptySlice", []int{}, []int{1, 2}, false}, {"TestAllInComparableSlice_NilSlice", nil, []int{1, 2}, false}, {"TestAllInComparableSlice_EmptyValueSlice", []int{1, 2, 3}, []int{}, true}, {"TestAllInComparableSlice_NilValueSlice", []int{1, 2, 3}, nil, true}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AllInComparableSlice(c.input, c.inputV) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected") + } + }) + } +} + +``` + + +
+ + *** #### func AnyInSlice(slice S, values []V, handler ComparisonHandler[V]) bool > 检查 values 中的任意一个元素是否被包含在 slice 中,当 handler 返回 true 时,表示 value 中的某个元素和 slice 中的某个元素相匹配 > - 当 values 中的任意一个元素被包含在 slice 中时,返回 true + +示例代码: +```go + +func ExampleAnyInSlice() { + result := collection.AnyInSlice([]int{1, 2, 3}, []int{1, 2}, func(source, target int) bool { + return source == target + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAnyInSlice(t *testing.T) { + var cases = []struct { + name string + input []int + inputV []int + expected bool + }{{"TestAnyInSlice_NonEmptySliceIn", []int{1, 2, 3}, []int{1, 2}, true}, {"TestAnyInSlice_NonEmptySliceNotIn", []int{1, 2, 3}, []int{1, 4}, true}, {"TestAnyInSlice_EmptySlice", []int{}, []int{1, 2}, false}, {"TestAnyInSlice_NilSlice", nil, []int{1, 2}, false}, {"TestAnyInSlice_EmptyValueSlice", []int{1, 2, 3}, []int{}, false}, {"TestAnyInSlice_NilValueSlice", []int{1, 2, 3}, nil, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AnyInSlice(c.input, c.inputV, func(source, target int) bool { + return source == target + }) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func AnyInComparableSlice(slice S, values []V) bool > 检查 values 中的任意一个元素是否被包含在 slice 中 > - 当 values 中的任意一个元素被包含在 slice 中时,返回 true + +示例代码: +```go + +func ExampleAnyInComparableSlice() { + result := collection.AnyInComparableSlice([]int{1, 2, 3}, []int{1, 2}) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAnyInComparableSlice(t *testing.T) { + var cases = []struct { + name string + input []int + inputV []int + expected bool + }{{"TestAnyInComparableSlice_NonEmptySliceIn", []int{1, 2, 3}, []int{1, 2}, true}, {"TestAnyInComparableSlice_NonEmptySliceNotIn", []int{1, 2, 3}, []int{1, 4}, true}, {"TestAnyInComparableSlice_EmptySlice", []int{}, []int{1, 2}, false}, {"TestAnyInComparableSlice_NilSlice", nil, []int{1, 2}, false}, {"TestAnyInComparableSlice_EmptyValueSlice", []int{1, 2, 3}, []int{}, false}, {"TestAnyInComparableSlice_NilValueSlice", []int{1, 2, 3}, nil, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AnyInComparableSlice(c.input, c.inputV) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected") + } + }) + } +} + +``` + + +
+ + *** #### func InSlices(slices []S, v V, handler ComparisonHandler[V]) bool > 通过将多个切片合并后检查 v 是否被包含在 slices 中,当 handler 返回 true 时,表示 v 和 slices 中的某个元素相匹配 > - 当传入的 v 被包含在 slices 的任一成员中时,返回 true + +示例代码: +```go + +func ExampleInSlices() { + result := collection.InSlices([][]int{{1, 2, 3}, {4, 5, 6}}, 2, func(source, target int) bool { + return source == target + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestInSlices(t *testing.T) { + var cases = []struct { + name string + input [][]int + inputV int + expected bool + }{{"TestInSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, 1, true}, {"TestInSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, 5, false}, {"TestInSlices_EmptySlice", [][]int{{}, {}}, 1, false}, {"TestInSlices_NilSlice", nil, 1, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.InSlices(c.input, c.inputV, func(source, target int) bool { + return source == target + }) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func InComparableSlices(slices []S, v V) bool > 通过将多个切片合并后检查 v 是否被包含在 slices 中 > - 当传入的 v 被包含在 slices 的任一成员中时,返回 true + +示例代码: +```go + +func ExampleInComparableSlices() { + result := collection.InComparableSlices([][]int{{1, 2, 3}, {4, 5, 6}}, 2) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestInComparableSlices(t *testing.T) { + var cases = []struct { + name string + input [][]int + inputV int + expected bool + }{{"TestInComparableSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, 1, true}, {"TestInComparableSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, 5, false}, {"TestInComparableSlices_EmptySlice", [][]int{{}, {}}, 1, false}, {"TestInComparableSlices_NilSlice", nil, 1, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.InComparableSlices(c.input, c.inputV) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "") + } + }) + } +} + +``` + + +
+ + *** #### func AllInSlices(slices []S, values []V, handler ComparisonHandler[V]) bool > 通过将多个切片合并后检查 values 中的所有元素是否被包含在 slices 中,当 handler 返回 true 时,表示 values 中的某个元素和 slices 中的某个元素相匹配 > - 当 values 中的所有元素都被包含在 slices 的任一成员中时,返回 true + +示例代码: +```go + +func ExampleAllInSlices() { + result := collection.AllInSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2}, func(source, target int) bool { + return source == target + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAllInSlices(t *testing.T) { + var cases = []struct { + name string + input [][]int + inputValues []int + expected bool + }{{"TestAllInSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, []int{1, 2}, true}, {"TestAllInSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, false}, {"TestAllInSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false}, {"TestAllInSlices_NilSlice", nil, []int{1, 2}, false}, {"TestAllInSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, true}, {"TestAllInSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, true}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AllInSlices(c.input, c.inputValues, func(source, target int) bool { + return source == target + }) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func AllInComparableSlices(slices []S, values []V) bool > 通过将多个切片合并后检查 values 中的所有元素是否被包含在 slices 中 > - 当 values 中的所有元素都被包含在 slices 的任一成员中时,返回 true + +示例代码: +```go + +func ExampleAllInComparableSlices() { + result := collection.AllInComparableSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2}) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAllInComparableSlices(t *testing.T) { + var cases = []struct { + name string + input [][]int + inputValues []int + expected bool + }{{"TestAllInComparableSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, []int{1, 2}, true}, {"TestAllInComparableSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, false}, {"TestAllInComparableSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false}, {"TestAllInComparableSlices_NilSlice", nil, []int{1, 2}, false}, {"TestAllInComparableSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, true}, {"TestAllInComparableSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, true}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AllInComparableSlices(c.input, c.inputValues) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "") + } + }) + } +} + +``` + + +
+ + *** #### func AnyInSlices(slices []S, values []V, handler ComparisonHandler[V]) bool > 通过将多个切片合并后检查 values 中的任意一个元素是否被包含在 slices 中,当 handler 返回 true 时,表示 values 中的某个元素和 slices 中的某个元素相匹配 > - 当 values 中的任意一个元素被包含在 slices 的任一成员中时,返回 true + +示例代码: +```go + +func ExampleAnyInSlices() { + result := collection.AnyInSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2}, func(source, target int) bool { + return source == target + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAnyInSlices(t *testing.T) { + var cases = []struct { + name string + slices [][]int + values []int + expected bool + }{{"TestAnyInSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, []int{1, 2}, true}, {"TestAnyInSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, true}, {"TestAnyInSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false}, {"TestAnyInSlices_NilSlice", nil, []int{1, 2}, false}, {"TestAnyInSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, false}, {"TestAnyInSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AnyInSlices(c.slices, c.values, func(source, target int) bool { + return source == target + }) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func AnyInComparableSlices(slices []S, values []V) bool > 通过将多个切片合并后检查 values 中的任意一个元素是否被包含在 slices 中 > - 当 values 中的任意一个元素被包含在 slices 的任一成员中时,返回 true + +示例代码: +```go + +func ExampleAnyInComparableSlices() { + result := collection.AnyInComparableSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2}) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAnyInComparableSlices(t *testing.T) { + var cases = []struct { + name string + slices [][]int + values []int + expected bool + }{{"TestAnyInComparableSlices_NonEmptySliceIn", [][]int{{1, 2}, {3, 4}}, []int{1, 2}, true}, {"TestAnyInComparableSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, true}, {"TestAnyInComparableSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false}, {"TestAnyInComparableSlices_NilSlice", nil, []int{1, 2}, false}, {"TestAnyInComparableSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, false}, {"TestAnyInComparableSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AnyInComparableSlices(c.slices, c.values) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "") + } + }) + } +} + +``` + + +
+ + *** #### func InAllSlices(slices []S, v V, handler ComparisonHandler[V]) bool > 检查 v 是否被包含在 slices 的每一项元素中,当 handler 返回 true 时,表示 v 和 slices 中的某个元素相匹配 > - 当 v 被包含在 slices 的每一项元素中时,返回 true + +示例代码: +```go + +func ExampleInAllSlices() { + result := collection.InAllSlices([][]int{{1, 2, 3}, {4, 5, 6}}, 2, func(source, target int) bool { + return source == target + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestInAllSlices(t *testing.T) { + var cases = []struct { + name string + slices [][]int + value int + expected bool + }{{"TestInAllSlices_NonEmptySliceIn", [][]int{{1, 2}, {1, 3}}, 1, true}, {"TestInAllSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, 5, false}, {"TestInAllSlices_EmptySlice", [][]int{{}, {}}, 1, false}, {"TestInAllSlices_NilSlice", nil, 1, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.InAllSlices(c.slices, c.value, func(source, target int) bool { + return source == target + }) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func InAllComparableSlices(slices []S, v V) bool > 检查 v 是否被包含在 slices 的每一项元素中 > - 当 v 被包含在 slices 的每一项元素中时,返回 true + +示例代码: +```go + +func ExampleInAllComparableSlices() { + result := collection.InAllComparableSlices([][]int{{1, 2, 3}, {4, 5, 6}}, 2) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestInAllComparableSlices(t *testing.T) { + var cases = []struct { + name string + slices [][]int + value int + expected bool + }{{"TestInAllComparableSlices_NonEmptySliceIn", [][]int{{1, 2}, {1, 3}}, 1, true}, {"TestInAllComparableSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, 5, false}, {"TestInAllComparableSlices_EmptySlice", [][]int{{}, {}}, 1, false}, {"TestInAllComparableSlices_NilSlice", nil, 1, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.InAllComparableSlices(c.slices, c.value) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "") + } + }) + } +} + +``` + + +
+ + *** #### func AnyInAllSlices(slices []S, values []V, handler ComparisonHandler[V]) bool > 检查 slices 中的每一个元素是否均包含至少任意一个 values 中的元素,当 handler 返回 true 时,表示 value 中的某个元素和 slices 中的某个元素相匹配 > - 当 slices 中的每一个元素均包含至少任意一个 values 中的元素时,返回 true + +示例代码: +```go + +func ExampleAnyInAllSlices() { + result := collection.AnyInAllSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2}, func(source, target int) bool { + return source == target + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAnyInAllSlices(t *testing.T) { + var cases = []struct { + name string + slices [][]int + values []int + expected bool + }{{"TestAnyInAllSlices_NonEmptySliceIn", [][]int{{1, 2}, {1, 3}}, []int{1, 2}, true}, {"TestAnyInAllSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, false}, {"TestAnyInAllSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false}, {"TestAnyInAllSlices_NilSlice", nil, []int{1, 2}, false}, {"TestAnyInAllSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, false}, {"TestAnyInAllSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AnyInAllSlices(c.slices, c.values, func(source, target int) bool { + return source == target + }) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func AnyInAllComparableSlices(slices []S, values []V) bool > 检查 slices 中的每一个元素是否均包含至少任意一个 values 中的元素 > - 当 slices 中的每一个元素均包含至少任意一个 values 中的元素时,返回 true + +示例代码: +```go + +func ExampleAnyInAllComparableSlices() { + result := collection.AnyInAllComparableSlices([][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2}) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAnyInAllComparableSlices(t *testing.T) { + var cases = []struct { + name string + slices [][]int + values []int + expected bool + }{{"TestAnyInAllComparableSlices_NonEmptySliceIn", [][]int{{1, 2}, {1, 3}}, []int{1, 2}, true}, {"TestAnyInAllComparableSlices_NonEmptySliceNotIn", [][]int{{1, 2}, {3, 4}}, []int{1, 5}, false}, {"TestAnyInAllComparableSlices_EmptySlice", [][]int{{}, {}}, []int{1, 2}, false}, {"TestAnyInAllComparableSlices_NilSlice", nil, []int{1, 2}, false}, {"TestAnyInAllComparableSlices_EmptyValueSlice", [][]int{{1, 2}, {3, 4}}, []int{}, false}, {"TestAnyInAllComparableSlices_NilValueSlice", [][]int{{1, 2}, {3, 4}}, nil, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AnyInAllComparableSlices(c.slices, c.values) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "") + } + }) + } +} + +``` + + +
+ + *** #### func KeyInMap(m M, key K) bool > 检查 m 中是否包含特定 key + +示例代码: +```go + +func ExampleKeyInMap() { + result := collection.KeyInMap(map[string]int{"a": 1, "b": 2}, "a") + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestKeyInMap(t *testing.T) { + var cases = []struct { + name string + m map[int]int + key int + expected bool + }{{"TestKeyInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, 1, true}, {"TestKeyInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, 3, false}, {"TestKeyInMap_EmptySlice", map[int]int{}, 1, false}, {"TestKeyInMap_NilSlice", nil, 1, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.KeyInMap(c.m, c.key) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func ValueInMap(m M, value V, handler ComparisonHandler[V]) bool > 检查 m 中是否包含特定 value,当 handler 返回 true 时,表示 value 和 m 中的某个元素相匹配 + +示例代码: +```go + +func ExampleValueInMap() { + result := collection.ValueInMap(map[string]int{"a": 1, "b": 2}, 2, func(source, target int) bool { + return source == target + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestValueInMap(t *testing.T) { + var cases = []struct { + name string + m map[int]int + value int + handler collection.ComparisonHandler[int] + expected bool + }{{"TestValueInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, 1, intComparisonHandler, true}, {"TestValueInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, 3, intComparisonHandler, false}, {"TestValueInMap_EmptySlice", map[int]int{}, 1, intComparisonHandler, false}, {"TestValueInMap_NilSlice", nil, 1, intComparisonHandler, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.ValueInMap(c.m, c.value, c.handler) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func AllKeyInMap(m M, keys ...K) bool > 检查 m 中是否包含 keys 中所有的元素 + +示例代码: +```go + +func ExampleAllKeyInMap() { + result := collection.AllKeyInMap(map[string]int{"a": 1, "b": 2}, "a", "b") + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAllKeyInMap(t *testing.T) { + var cases = []struct { + name string + m map[int]int + keys []int + expected bool + }{{"TestAllKeyInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, []int{1, 2}, true}, {"TestAllKeyInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, []int{1, 3}, false}, {"TestAllKeyInMap_EmptySlice", map[int]int{}, []int{1, 2}, false}, {"TestAllKeyInMap_NilSlice", nil, []int{1, 2}, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AllKeyInMap(c.m, c.keys...) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func AllValueInMap(m M, values []V, handler ComparisonHandler[V]) bool > 检查 m 中是否包含 values 中所有的元素,当 handler 返回 true 时,表示 values 中的某个元素和 m 中的某个元素相匹配 + +示例代码: +```go + +func ExampleAllValueInMap() { + result := collection.AllValueInMap(map[string]int{"a": 1, "b": 2}, []int{1}, func(source, target int) bool { + return source == target + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAllValueInMap(t *testing.T) { + var cases = []struct { + name string + m map[int]int + values []int + expected bool + }{{"TestAllValueInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, []int{1, 2}, true}, {"TestAllValueInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, []int{1, 3}, false}, {"TestAllValueInMap_EmptySlice", map[int]int{}, []int{1, 2}, false}, {"TestAllValueInMap_NilSlice", nil, []int{1, 2}, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AllValueInMap(c.m, c.values, intComparisonHandler) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after clone, the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func AnyKeyInMap(m M, keys ...K) bool > 检查 m 中是否包含 keys 中任意一个元素 + +示例代码: +```go + +func ExampleAnyKeyInMap() { + result := collection.AnyKeyInMap(map[string]int{"a": 1, "b": 2}, "a", "b") + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAnyKeyInMap(t *testing.T) { + var cases = []struct { + name string + m map[int]int + keys []int + expected bool + }{{"TestAnyKeyInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, []int{1, 2}, true}, {"TestAnyKeyInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, []int{1, 3}, true}, {"TestAnyKeyInMap_EmptySlice", map[int]int{}, []int{1, 2}, false}, {"TestAnyKeyInMap_NilSlice", nil, []int{1, 2}, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AnyKeyInMap(c.m, c.keys...) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected") + } + }) + } +} + +``` + + +
+ + *** #### func AnyValueInMap(m M, values []V, handler ComparisonHandler[V]) bool > 检查 m 中是否包含 values 中任意一个元素,当 handler 返回 true 时,表示 values 中的某个元素和 m 中的某个元素相匹配 + +示例代码: +```go + +func ExampleAnyValueInMap() { + result := collection.AnyValueInMap(map[string]int{"a": 1, "b": 2}, []int{1}, func(source, target int) bool { + return source == target + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAnyValueInMap(t *testing.T) { + var cases = []struct { + name string + m map[int]int + values []int + expected bool + }{{"TestAnyValueInMap_NonEmptySliceIn", map[int]int{1: 1, 2: 2}, []int{1, 2}, true}, {"TestAnyValueInMap_NonEmptySliceNotIn", map[int]int{1: 1, 2: 2}, []int{1, 3}, true}, {"TestAnyValueInMap_EmptySlice", map[int]int{}, []int{1, 2}, false}, {"TestAnyValueInMap_NilSlice", nil, []int{1, 2}, false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AnyValueInMap(c.m, c.values, intComparisonHandler) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected") + } + }) + } +} + +``` + + +
+ + *** #### func AllKeyInMaps(maps []M, keys ...K) bool > 检查 maps 中的每一个元素是否均包含 keys 中所有的元素 + +示例代码: +```go + +func ExampleAllKeyInMaps() { + result := collection.AllKeyInMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, "a", "b") + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAllKeyInMaps(t *testing.T) { + var cases = []struct { + name string + maps []map[int]int + keys []int + expected bool + }{{"TestAllKeyInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 2}, false}, {"TestAllKeyInMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 3}, false}, {"TestAllKeyInMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, []int{1, 2}, false}, {"TestAllKeyInMaps_NilSlice", []map[int]int{{}, {}}, []int{1, 2}, false}, {"TestAllKeyInMaps_EmptySlice", []map[int]int{}, []int{1, 2}, false}, {"TestAllKeyInMaps_NilSlice", nil, []int{1, 2}, false}, {"TestAllKeyInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, []int{1, 2}, true}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AllKeyInMaps(c.maps, c.keys...) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected") + } + }) + } +} + +``` + + +
+ + *** #### func AllValueInMaps(maps []M, values []V, handler ComparisonHandler[V]) bool > 检查 maps 中的每一个元素是否均包含 value 中所有的元素,当 handler 返回 true 时,表示 value 中的某个元素和 maps 中的某个元素相匹配 + +示例代码: +```go + +func ExampleAllValueInMaps() { + result := collection.AllValueInMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, []int{1}, func(source, target int) bool { + return source == target + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAllValueInMaps(t *testing.T) { + var cases = []struct { + name string + maps []map[int]int + values []int + expected bool + }{{"TestAllValueInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 2}, false}, {"TestAllValueInMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 3}, false}, {"TestAllValueInMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, []int{1, 2}, false}, {"TestAllValueInMaps_NilSlice", []map[int]int{{}, {}}, []int{1, 2}, false}, {"TestAllValueInMaps_EmptySlice", []map[int]int{}, []int{1, 2}, false}, {"TestAllValueInMaps_NilSlice", nil, []int{1, 2}, false}, {"TestAllValueInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, []int{1, 2}, true}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AllValueInMaps(c.maps, c.values, intComparisonHandler) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected") + } + }) + } +} + +``` + + +
+ + *** #### func AnyKeyInMaps(maps []M, keys ...K) bool > 检查 keys 中的任意一个元素是否被包含在 maps 中的任意一个元素中 > - 当 keys 中的任意一个元素被包含在 maps 中的任意一个元素中时,返回 true + +示例代码: +```go + +func ExampleAnyKeyInMaps() { + result := collection.AnyKeyInMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, "a", "b") + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAnyKeyInMaps(t *testing.T) { + var cases = []struct { + name string + maps []map[int]int + keys []int + expected bool + }{{"TestAnyKeyInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 2}, true}, {"TestAnyKeyInMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 3}, true}, {"TestAnyKeyInMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, []int{1, 2}, true}, {"TestAnyKeyInMaps_NilSlice", []map[int]int{{}, {}}, []int{1, 2}, false}, {"TestAnyKeyInMaps_EmptySlice", []map[int]int{}, []int{1, 2}, false}, {"TestAnyKeyInMaps_NilSlice", nil, []int{1, 2}, false}, {"TestAnyKeyInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, []int{1, 2}, true}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AnyKeyInMaps(c.maps, c.keys...) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected") + } + }) + } +} + +``` + + +
+ + *** #### func AnyValueInMaps(maps []M, values []V, handler ComparisonHandler[V]) bool > 检查 maps 中的任意一个元素是否包含 value 中的任意一个元素,当 handler 返回 true 时,表示 value 中的某个元素和 maps 中的某个元素相匹配 > - 当 maps 中的任意一个元素包含 value 中的任意一个元素时,返回 true + +示例代码: +```go + +func ExampleAnyValueInMaps() { + result := collection.AnyValueInMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, []int{1}, func(source, target int) bool { + return source == target + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAnyValueInMaps(t *testing.T) { + var cases = []struct { + name string + maps []map[int]int + values []int + expected bool + }{{"TestAnyValueInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 2}, false}, {"TestAnyValueInMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 3}, true}, {"TestAnyValueInMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, []int{1, 2}, false}, {"TestAnyValueInMaps_NilSlice", []map[int]int{{}, {}}, []int{1, 2}, false}, {"TestAnyValueInMaps_EmptySlice", []map[int]int{}, []int{1, 2}, false}, {"TestAnyValueInMaps_NilSlice", nil, []int{1, 2}, false}, {"TestAnyValueInMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, []int{1, 2}, true}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AnyValueInMaps(c.maps, c.values, intComparisonHandler) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected") + } + }) + } +} + +``` + + +
+ + *** #### func KeyInAllMaps(maps []M, key K) bool > 检查 key 是否被包含在 maps 的每一个元素中 + +示例代码: +```go + +func ExampleKeyInAllMaps() { + result := collection.KeyInAllMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, "a") + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestKeyInAllMaps(t *testing.T) { + var cases = []struct { + name string + maps []map[int]int + key int + expected bool + }{{"TestKeyInAllMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, 1, false}, {"TestKeyInAllMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, 3, false}, {"TestKeyInAllMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, 1, false}, {"TestKeyInAllMaps_NilSlice", []map[int]int{{}, {}}, 1, false}, {"TestKeyInAllMaps_EmptySlice", []map[int]int{}, 1, false}, {"TestKeyInAllMaps_NilSlice", nil, 1, false}, {"TestKeyInAllMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, 1, true}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.KeyInAllMaps(c.maps, c.key) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "not as expected") + } + }) + } +} + +``` + + +
+ + *** #### func AnyKeyInAllMaps(maps []M, keys []K) bool > 检查 maps 中的每一个元素是否均包含 keys 中任意一个元素 > - 当 maps 中的每一个元素均包含 keys 中任意一个元素时,返回 true + +示例代码: +```go + +func ExampleAnyKeyInAllMaps() { + result := collection.AnyKeyInAllMaps([]map[string]int{{"a": 1, "b": 2}, {"a": 1, "b": 2}}, []string{"a"}) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAnyKeyInAllMaps(t *testing.T) { + var cases = []struct { + name string + maps []map[int]int + keys []int + expected bool + }{{"TestAnyKeyInAllMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 2}, false}, {"TestAnyKeyInAllMaps_NonEmptySliceNotIn", []map[int]int{{1: 1, 2: 2}, {3: 3, 4: 4}}, []int{1, 3}, true}, {"TestAnyKeyInAllMaps_EmptySlice", []map[int]int{{1: 1, 2: 2}, {}}, []int{1, 2}, false}, {"TestAnyKeyInAllMaps_NilSlice", []map[int]int{{}, {}}, []int{1, 2}, false}, {"TestAnyKeyInAllMaps_EmptySlice", []map[int]int{}, []int{1, 2}, false}, {"TestAnyKeyInAllMaps_NilSlice", nil, []int{1, 2}, false}, {"TestAnyKeyInAllMaps_NonEmptySliceIn", []map[int]int{{1: 1, 2: 2, 3: 3}, {1: 1, 2: 2, 4: 4}}, []int{1, 2}, true}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var actual = collection.AnyKeyInAllMaps(c.maps, c.keys) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, actual) + } + }) + } +} + +``` + + +
+ + *** #### func ConvertSliceToAny(s S) []any > 将切片转换为任意类型的切片 + +示例代码: +```go + +func ExampleConvertSliceToAny() { + result := collection.ConvertSliceToAny([]int{1, 2, 3}) + fmt.Println(reflect.TypeOf(result).String(), len(result)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestConvertSliceToAny(t *testing.T) { + var cases = []struct { + name string + input []int + expected []interface{} + }{{name: "TestConvertSliceToAny_NonEmpty", input: []int{1, 2, 3}, expected: []any{1, 2, 3}}, {name: "TestConvertSliceToAny_Empty", input: []int{}, expected: []any{}}, {name: "TestConvertSliceToAny_Nil", input: nil, expected: nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.ConvertSliceToAny(c.input) + if len(actual) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + for i := 0; i < len(actual); i++ { + av, ev := actual[i], c.expected[i] + if reflect.TypeOf(av).Kind() != reflect.TypeOf(ev).Kind() { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + if av != ev { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + } + }) + } +} + +``` + + +
+ + *** #### func ConvertSliceToIndexMap(s S) map[int]V > 将切片转换为索引为键的映射 + +示例代码: +```go + +func ExampleConvertSliceToIndexMap() { + slice := []int{1, 2, 3} + result := collection.ConvertSliceToIndexMap(slice) + for i, v := range slice { + fmt.Println(result[i], v) + } +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestConvertSliceToIndexMap(t *testing.T) { + var cases = []struct { + name string + input []int + expected map[int]int + }{{name: "TestConvertSliceToIndexMap_NonEmpty", input: []int{1, 2, 3}, expected: map[int]int{0: 1, 1: 2, 2: 3}}, {name: "TestConvertSliceToIndexMap_Empty", input: []int{}, expected: map[int]int{}}, {name: "TestConvertSliceToIndexMap_Nil", input: nil, expected: nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.ConvertSliceToIndexMap(c.input) + if len(actual) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + for k, v := range actual { + if c.expected[k] != v { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + } + }) + } +} + +``` + + +
+ + *** #### func ConvertSliceToIndexOnlyMap(s S) map[int]struct {} > 将切片转换为索引为键的映射 + +示例代码: +```go + +func ExampleConvertSliceToIndexOnlyMap() { + slice := []int{1, 2, 3} + result := collection.ConvertSliceToIndexOnlyMap(slice) + expected := map[int]bool{0: true, 1: true, 2: true} + for k := range result { + fmt.Println(expected[k]) + } +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestConvertSliceToIndexOnlyMap(t *testing.T) { + var cases = []struct { + name string + input []int + expected map[int]struct{} + }{{name: "TestConvertSliceToIndexOnlyMap_NonEmpty", input: []int{1, 2, 3}, expected: map[int]struct{}{0: {}, 1: {}, 2: {}}}, {name: "TestConvertSliceToIndexOnlyMap_Empty", input: []int{}, expected: map[int]struct{}{}}, {name: "TestConvertSliceToIndexOnlyMap_Nil", input: nil, expected: nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.ConvertSliceToIndexOnlyMap(c.input) + if len(actual) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + for k, v := range actual { + if _, ok := c.expected[k]; !ok { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + if v != struct{}{} { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + } + }) + } +} + +``` + + +
+ + *** #### func ConvertSliceToMap(s S) map[V]struct {} > 将切片转换为值为键的映射 + +示例代码: +```go + +func ExampleConvertSliceToMap() { + slice := []int{1, 2, 3} + result := collection.ConvertSliceToMap(slice) + fmt.Println(collection.AllKeyInMap(result, slice...)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestConvertSliceToMap(t *testing.T) { + var cases = []struct { + name string + input []int + expected map[int]struct{} + }{{name: "TestConvertSliceToMap_NonEmpty", input: []int{1, 2, 3}, expected: map[int]struct{}{1: {}, 2: {}, 3: {}}}, {name: "TestConvertSliceToMap_Empty", input: []int{}, expected: map[int]struct{}{}}, {name: "TestConvertSliceToMap_Nil", input: nil, expected: nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.ConvertSliceToMap(c.input) + if len(actual) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + for k, v := range actual { + if _, ok := c.expected[k]; !ok { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + if v != struct{}{} { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + } + }) + } +} + +``` + + +
+ + *** #### func ConvertSliceToBoolMap(s S) map[V]bool > 将切片转换为值为键的映射 + +示例代码: +```go + +func ExampleConvertSliceToBoolMap() { + slice := []int{1, 2, 3} + result := collection.ConvertSliceToBoolMap(slice) + for _, v := range slice { + fmt.Println(v, result[v]) + } +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestConvertSliceToBoolMap(t *testing.T) { + var cases = []struct { + name string + input []int + expected map[int]bool + }{{name: "TestConvertSliceToBoolMap_NonEmpty", input: []int{1, 2, 3}, expected: map[int]bool{1: true, 2: true, 3: true}}, {name: "TestConvertSliceToBoolMap_Empty", input: []int{}, expected: map[int]bool{}}, {name: "TestConvertSliceToBoolMap_Nil", input: nil, expected: nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.ConvertSliceToBoolMap(c.input) + if len(actual) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + for k, v := range actual { + if c.expected[k] != v { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + } + }) + } +} + +``` + + +
+ + *** #### func ConvertMapKeysToSlice(m M) []K > 将映射的键转换为切片 + +示例代码: +```go + +func ExampleConvertMapKeysToSlice() { + result := collection.ConvertMapKeysToSlice(map[int]int{1: 1, 2: 2, 3: 3}) + for i, v := range result { + fmt.Println(i, v) + } +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestConvertMapKeysToSlice(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected []int + }{{name: "TestConvertMapKeysToSlice_NonEmpty", input: map[int]int{1: 1, 2: 2, 3: 3}, expected: []int{1, 2, 3}}, {name: "TestConvertMapKeysToSlice_Empty", input: map[int]int{}, expected: []int{}}, {name: "TestConvertMapKeysToSlice_Nil", input: nil, expected: nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.ConvertMapKeysToSlice(c.input) + if len(actual) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + var matchCount = 0 + for _, av := range actual { + for _, ev := range c.expected { + if av == ev { + matchCount++ + } + } + } + if matchCount != len(actual) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + }) + } +} + +``` + + +
+ + *** #### func ConvertMapValuesToSlice(m M) []V > 将映射的值转换为切片 + +示例代码: +```go + +func ExampleConvertMapValuesToSlice() { + result := collection.ConvertMapValuesToSlice(map[int]int{1: 1, 2: 2, 3: 3}) + expected := map[int]bool{1: true, 2: true, 3: true} + for _, v := range result { + fmt.Println(expected[v]) + } +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestConvertMapValuesToSlice(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected []int + }{{name: "TestConvertMapValuesToSlice_NonEmpty", input: map[int]int{1: 1, 2: 2, 3: 3}, expected: []int{1, 2, 3}}, {name: "TestConvertMapValuesToSlice_Empty", input: map[int]int{}, expected: []int{}}, {name: "TestConvertMapValuesToSlice_Nil", input: nil, expected: nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.ConvertMapValuesToSlice(c.input) + if len(actual) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + var matchCount = 0 + for _, av := range actual { + for _, ev := range c.expected { + if av == ev { + matchCount++ + } + } + } + if matchCount != len(actual) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + }) + } +} + +``` + + +
+ + *** #### func InvertMap(m M) N > 将映射的键和值互换 + +示例代码: +```go + +func ExampleInvertMap() { + result := collection.InvertMap(map[int]string{1: "a", 2: "b", 3: "c"}) + fmt.Println(collection.AllKeyInMap(result, "a", "b", "c")) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestInvertMap(t *testing.T) { + var cases = []struct { + name string + input map[int]string + expected map[string]int + }{{name: "TestInvertMap_NonEmpty", input: map[int]string{1: "1", 2: "2", 3: "3"}, expected: map[string]int{"1": 1, "2": 2, "3": 3}}, {name: "TestInvertMap_Empty", input: map[int]string{}, expected: map[string]int{}}, {name: "TestInvertMap_Nil", input: nil, expected: nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.InvertMap[map[int]string, map[string]int](c.input) + if len(actual) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + for k, v := range actual { + if c.expected[k] != v { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + } + }) + } +} + +``` + + +
+ + *** #### func ConvertMapValuesToBool(m M) N > 将映射的值转换为布尔值 + +示例代码: +```go + +func ExampleConvertMapValuesToBool() { + result := collection.ConvertMapValuesToBool(map[int]int{1: 1}) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestConvertMapValuesToBool(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{{name: "TestConvertMapValuesToBool_NonEmpty", input: map[int]int{1: 1, 2: 2, 3: 3}, expected: map[int]bool{1: true, 2: true, 3: true}}, {name: "TestConvertMapValuesToBool_Empty", input: map[int]int{}, expected: map[int]bool{}}, {name: "TestConvertMapValuesToBool_Nil", input: nil, expected: nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.ConvertMapValuesToBool[map[int]int, map[int]bool](c.input) + if len(actual) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + for k, v := range actual { + if c.expected[k] != v { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + } + }) + } +} + +``` + + +
+ + *** #### func ReverseSlice(s *S) > 将切片反转 + +示例代码: +```go + +func ExampleReverseSlice() { + var s = []int{1, 2, 3} + collection.ReverseSlice(&s) + fmt.Println(s) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestReverseSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{{name: "TestReverseSlice_NonEmpty", input: []int{1, 2, 3}, expected: []int{3, 2, 1}}, {name: "TestReverseSlice_Empty", input: []int{}, expected: []int{}}, {name: "TestReverseSlice_Nil", input: nil, expected: nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + collection.ReverseSlice(&c.input) + if len(c.input) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, c.input) + } + for i := 0; i < len(c.input); i++ { + av, ev := c.input[i], c.expected[i] + if reflect.TypeOf(av).Kind() != reflect.TypeOf(ev).Kind() { + t.Errorf("expected: %v, actual: %v", c.expected, c.input) + } + if av != ev { + t.Errorf("expected: %v, actual: %v", c.expected, c.input) + } + } + }) + } +} + +``` + + +
+ + *** #### func ClearSlice(slice *S) > 清空切片 + +示例代码: +```go + +func ExampleClearSlice() { + slice := []int{1, 2, 3, 4, 5} + ClearSlice(&slice) + fmt.Println(slice) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestClearSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{{"TestClearSlice_NonEmptySlice", []int{1, 2, 3}, []int{}}, {"TestClearSlice_EmptySlice", []int{}, []int{}}, {"TestClearSlice_NilSlice", nil, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + collection.ClearSlice(&c.input) + if len(c.input) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after clear, the length of input is not equal") + } + for i := 0; i < len(c.input); i++ { + if c.input[i] != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after clear, the inputV of input is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func ClearMap(m M) > 清空 map + +示例代码: +```go + +func ExampleClearMap() { + m := map[int]int{1: 1, 2: 2, 3: 3} + ClearMap(m) + fmt.Println(m) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestClearMap(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]int + }{{"TestClearMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]int{}}, {"TestClearMap_EmptyMap", map[int]int{}, map[int]int{}}, {"TestClearMap_NilMap", nil, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + collection.ClearMap(c.input) + if len(c.input) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after clear, the length of map is not equal") + } + for k, v := range c.input { + if v != c.expected[k] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after clear, the inputV of map is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func DropSliceByIndices(slice *S, indices ...int) > 删除切片中特定索引的元素 + +示例代码: +```go + +func ExampleDropSliceByIndices() { + slice := []int{1, 2, 3, 4, 5} + DropSliceByIndices(&slice, 1, 3) + fmt.Println(slice) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestDropSliceByIndices(t *testing.T) { + var cases = []struct { + name string + input []int + indices []int + expected []int + }{{"TestDropSliceByIndices_NonEmptySlice", []int{1, 2, 3, 4, 5}, []int{1, 3}, []int{1, 3, 5}}, {"TestDropSliceByIndices_EmptySlice", []int{}, []int{1, 3}, []int{}}, {"TestDropSliceByIndices_NilSlice", nil, []int{1, 3}, nil}, {"TestDropSliceByIndices_NonEmptySlice", []int{1, 2, 3, 4, 5}, []int{}, []int{1, 2, 3, 4, 5}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + collection.DropSliceByIndices(&c.input, c.indices...) + if len(c.input) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the length of input is not equal") + } + for i := 0; i < len(c.input); i++ { + if c.input[i] != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the inputV of input is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func DropSliceByCondition(slice *S, condition func (v V) bool) > 删除切片中符合条件的元素 > - condition 的返回值为 true 时,将会删除该元素 + +示例代码: +```go + +func ExampleDropSliceByCondition() { + slice := []int{1, 2, 3, 4, 5} + DropSliceByCondition(&slice, func(v int) bool { + return v%2 == 0 + }) + fmt.Println(slice) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestDropSliceByCondition(t *testing.T) { + var cases = []struct { + name string + input []int + condition func(v int) bool + expected []int + }{{"TestDropSliceByCondition_NonEmptySlice", []int{1, 2, 3, 4, 5}, func(v int) bool { + return v%2 == 0 + }, []int{1, 3, 5}}, {"TestDropSliceByCondition_EmptySlice", []int{}, func(v int) bool { + return v%2 == 0 + }, []int{}}, {"TestDropSliceByCondition_NilSlice", nil, func(v int) bool { + return v%2 == 0 + }, nil}, {"TestDropSliceByCondition_NonEmptySlice", []int{1, 2, 3, 4, 5}, func(v int) bool { + return v%2 == 1 + }, []int{2, 4}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + collection.DropSliceByCondition(&c.input, c.condition) + if len(c.input) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the length of input is not equal") + } + for i := 0; i < len(c.input); i++ { + if c.input[i] != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the inputV of input is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func DropSliceOverlappingElements(slice *S, anotherSlice []V, comparisonHandler ComparisonHandler[V]) > 删除切片中与另一个切片重叠的元素 + +示例代码: +```go + +func ExampleDropSliceOverlappingElements() { + slice := []int{1, 2, 3, 4, 5} + DropSliceOverlappingElements(&slice, []int{1, 3, 5}, func(source, target int) bool { + return source == target + }) + fmt.Println(slice) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestDropSliceOverlappingElements(t *testing.T) { + var cases = []struct { + name string + input []int + anotherSlice []int + comparisonHandler collection.ComparisonHandler[int] + expected []int + expectedAnother []int + expectedComparison []int + }{{"TestDropSliceOverlappingElements_NonEmptySlice", []int{1, 2, 3, 4, 5}, []int{3, 4, 5, 6, 7}, func(v1, v2 int) bool { + return v1 == v2 + }, []int{1, 2}, []int{6, 7}, []int{3, 4, 5}}, {"TestDropSliceOverlappingElements_EmptySlice", []int{}, []int{3, 4, 5, 6, 7}, func(v1, v2 int) bool { + return v1 == v2 + }, []int{}, []int{3, 4, 5, 6, 7}, []int{}}, {"TestDropSliceOverlappingElements_NilSlice", nil, []int{3, 4, 5, 6, 7}, func(v1, v2 int) bool { + return v1 == v2 + }, nil, []int{3, 4, 5, 6, 7}, nil}, {"TestDropSliceOverlappingElements_NonEmptySlice", []int{1, 2, 3, 4, 5}, []int{}, func(v1, v2 int) bool { + return v1 == v2 + }, []int{1, 2, 3, 4, 5}, []int{}, []int{}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + collection.DropSliceOverlappingElements(&c.input, c.anotherSlice, c.comparisonHandler) + if len(c.input) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the length of input is not equal") + } + for i := 0; i < len(c.input); i++ { + if c.input[i] != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, c.input, "after drop, the inputV of input is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func DeduplicateSliceInPlace(s *S) > 去除切片中的重复元素 + +示例代码: +```go + +func ExampleDeduplicateSliceInPlace() { + slice := []int{1, 2, 3, 4, 5, 5, 4, 3, 2, 1} + DeduplicateSliceInPlace(&slice) + fmt.Println(slice) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestDeduplicateSliceInPlace(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{{name: "TestDeduplicateSliceInPlace_NonEmpty", input: []int{1, 2, 3, 1, 2, 3}, expected: []int{1, 2, 3}}, {name: "TestDeduplicateSliceInPlace_Empty", input: []int{}, expected: []int{}}, {name: "TestDeduplicateSliceInPlace_Nil", input: nil, expected: nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + collection.DeduplicateSliceInPlace(&c.input) + if len(c.input) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, c.input) + } + for i := 0; i < len(c.input); i++ { + av, ev := c.input[i], c.expected[i] + if av != ev { + t.Errorf("expected: %v, actual: %v", c.expected, c.input) + } + } + }) + } +} + +``` + + +
+ + *** #### func DeduplicateSlice(s S) S > 去除切片中的重复元素,返回新切片 + +示例代码: +```go + +func ExampleDeduplicateSlice() { + slice := []int{1, 2, 3, 4, 5, 5, 4, 3, 2, 1} + fmt.Println(DeduplicateSlice(slice)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestDeduplicateSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{{name: "TestDeduplicateSlice_NonEmpty", input: []int{1, 2, 3, 1, 2, 3}, expected: []int{1, 2, 3}}, {name: "TestDeduplicateSlice_Empty", input: []int{}, expected: []int{}}, {name: "TestDeduplicateSlice_Nil", input: nil, expected: nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.DeduplicateSlice(c.input) + if len(actual) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + for i := 0; i < len(actual); i++ { + av, ev := actual[i], c.expected[i] + if av != ev { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + } + }) + } +} + +``` + + +
+ + *** #### func DeduplicateSliceInPlaceWithCompare(s *S, compare func (a V) bool) > 去除切片中的重复元素,使用自定义的比较函数 + +示例代码: +```go + +func ExampleDeduplicateSliceInPlaceWithCompare() { + slice := []int{1, 2, 3, 4, 5, 5, 4, 3, 2, 1} + DeduplicateSliceInPlaceWithCompare(&slice, func(a, b int) bool { + return a == b + }) + fmt.Println(slice) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestDeduplicateSliceInPlaceWithCompare(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{{name: "TestDeduplicateSliceInPlaceWithCompare_NonEmpty", input: []int{1, 2, 3, 1, 2, 3}, expected: []int{1, 2, 3}}, {name: "TestDeduplicateSliceInPlaceWithCompare_Empty", input: []int{}, expected: []int{}}, {name: "TestDeduplicateSliceInPlaceWithCompare_Nil", input: nil, expected: nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + collection.DeduplicateSliceInPlaceWithCompare(&c.input, func(a, b int) bool { + return a == b + }) + if len(c.input) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, c.input) + } + for i := 0; i < len(c.input); i++ { + av, ev := c.input[i], c.expected[i] + if av != ev { + t.Errorf("expected: %v, actual: %v", c.expected, c.input) + } + } + }) + } +} + +``` + + +
+ + *** #### func DeduplicateSliceWithCompare(s S, compare func (a V) bool) S > 去除切片中的重复元素,使用自定义的比较函数,返回新的切片 + +示例代码: +```go + +func ExampleDeduplicateSliceWithCompare() { + slice := []int{1, 2, 3, 4, 5, 5, 4, 3, 2, 1} + fmt.Println(DeduplicateSliceWithCompare(slice, func(a, b int) bool { + return a == b + })) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestDeduplicateSliceWithCompare(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{{name: "TestDeduplicateSliceWithCompare_NonEmpty", input: []int{1, 2, 3, 1, 2, 3}, expected: []int{1, 2, 3}}, {name: "TestDeduplicateSliceWithCompare_Empty", input: []int{}, expected: []int{}}, {name: "TestDeduplicateSliceWithCompare_Nil", input: nil, expected: nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.DeduplicateSliceWithCompare(c.input, func(a, b int) bool { + return a == b + }) + if len(actual) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + for i := 0; i < len(actual); i++ { + av, ev := actual[i], c.expected[i] + if av != ev { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + } + }) + } +} + +``` + + +
+ + *** #### func FilterOutByIndices(slice S, indices ...int) S > 过滤切片中特定索引的元素,返回过滤后的切片 + +示例代码: +```go + +func ExampleFilterOutByIndices() { + var slice = []int{1, 2, 3, 4, 5} + var result = collection.FilterOutByIndices(slice, 1, 3) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFilterOutByIndices(t *testing.T) { + var cases = []struct { + name string + input []int + indices []int + expected []int + }{{"TestFilterOutByIndices_NonEmptySlice", []int{1, 2, 3, 4, 5}, []int{1, 3}, []int{1, 3, 5}}, {"TestFilterOutByIndices_EmptySlice", []int{}, []int{1, 3}, []int{}}, {"TestFilterOutByIndices_NilSlice", nil, []int{1, 3}, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FilterOutByIndices(c.input, c.indices...) + if len(actual) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of input is not equal") + } + for i := 0; i < len(actual); i++ { + if actual[i] != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of input is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func FilterOutByCondition(slice S, condition func (v V) bool) S > 过滤切片中符合条件的元素,返回过滤后的切片 > - condition 的返回值为 true 时,将会过滤掉该元素 + +示例代码: +```go + +func ExampleFilterOutByCondition() { + var slice = []int{1, 2, 3, 4, 5} + var result = collection.FilterOutByCondition(slice, func(v int) bool { + return v%2 == 0 + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFilterOutByCondition(t *testing.T) { + var cases = []struct { + name string + input []int + condition func(int) bool + expected []int + }{{"TestFilterOutByCondition_NonEmptySlice", []int{1, 2, 3, 4, 5}, func(v int) bool { + return v%2 == 0 + }, []int{1, 3, 5}}, {"TestFilterOutByCondition_EmptySlice", []int{}, func(v int) bool { + return v%2 == 0 + }, []int{}}, {"TestFilterOutByCondition_NilSlice", nil, func(v int) bool { + return v%2 == 0 + }, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FilterOutByCondition(c.input, c.condition) + if len(actual) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of input is not equal") + } + for i := 0; i < len(actual); i++ { + if actual[i] != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of input is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func FilterOutByKey(m M, key K) M > 过滤 map 中特定的 key,返回过滤后的 map + +示例代码: +```go + +func ExampleFilterOutByKey() { + var m = map[string]int{"a": 1, "b": 2, "c": 3} + var result = collection.FilterOutByKey(m, "b") + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFilterOutByKey(t *testing.T) { + var cases = []struct { + name string + input map[int]int + key int + expected map[int]int + }{{"TestFilterOutByKey_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, 1, map[int]int{2: 2, 3: 3}}, {"TestFilterOutByKey_EmptyMap", map[int]int{}, 1, map[int]int{}}, {"TestFilterOutByKey_NilMap", nil, 1, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FilterOutByKey(c.input, c.key) + if len(actual) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of map is not equal") + } + for k, v := range actual { + if v != c.expected[k] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of map is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func FilterOutByValue(m M, value V, handler ComparisonHandler[V]) M > 过滤 map 中特定的 value,返回过滤后的 map + +示例代码: +```go + +func ExampleFilterOutByValue() { + var m = map[string]int{"a": 1, "b": 2, "c": 3} + var result = collection.FilterOutByValue(m, 2, func(source, target int) bool { + return source == target + }) + fmt.Println(len(result)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFilterOutByValue(t *testing.T) { + var cases = []struct { + name string + input map[int]int + value int + expected map[int]int + }{{"TestFilterOutByValue_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, 1, map[int]int{2: 2, 3: 3}}, {"TestFilterOutByValue_EmptyMap", map[int]int{}, 1, map[int]int{}}, {"TestFilterOutByValue_NilMap", nil, 1, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FilterOutByValue(c.input, c.value, func(source, target int) bool { + return source == target + }) + if len(actual) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of map is not equal") + } + for k, v := range actual { + if v != c.expected[k] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of map is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func FilterOutByKeys(m M, keys ...K) M > 过滤 map 中多个 key,返回过滤后的 map + +示例代码: +```go + +func ExampleFilterOutByKeys() { + var m = map[string]int{"a": 1, "b": 2, "c": 3} + var result = collection.FilterOutByKeys(m, "a", "c") + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFilterOutByKeys(t *testing.T) { + var cases = []struct { + name string + input map[int]int + keys []int + expected map[int]int + }{{"TestFilterOutByKeys_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, []int{1, 3}, map[int]int{2: 2}}, {"TestFilterOutByKeys_EmptyMap", map[int]int{}, []int{1, 3}, map[int]int{}}, {"TestFilterOutByKeys_NilMap", nil, []int{1, 3}, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FilterOutByKeys(c.input, c.keys...) + if len(actual) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of map is not equal") + } + for k, v := range actual { + if v != c.expected[k] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of map is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func FilterOutByValues(m M, values []V, handler ComparisonHandler[V]) M > 过滤 map 中多个 values,返回过滤后的 map + +示例代码: +```go + +func ExampleFilterOutByValues() { + var m = map[string]int{"a": 1, "b": 2, "c": 3} + var result = collection.FilterOutByValues(m, []int{1}, func(source, target int) bool { + return source == target + }) + for i, s := range []string{"a", "b", "c"} { + fmt.Println(i, result[s]) + } +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFilterOutByValues(t *testing.T) { + var cases = []struct { + name string + input map[int]int + values []int + expected map[int]int + }{{"TestFilterOutByValues_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, []int{1, 3}, map[int]int{2: 2}}, {"TestFilterOutByValues_EmptyMap", map[int]int{}, []int{1, 3}, map[int]int{}}, {"TestFilterOutByValues_NilMap", nil, []int{1, 3}, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FilterOutByValues(c.input, c.values, func(source, target int) bool { + return source == target + }) + if len(actual) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of map is not equal") + } + for k, v := range actual { + if v != c.expected[k] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of map is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func FilterOutByMap(m M, condition func (k K, v V) bool) M > 过滤 map 中符合条件的元素,返回过滤后的 map > - condition 的返回值为 true 时,将会过滤掉该元素 + +示例代码: +```go + +func ExampleFilterOutByMap() { + var m = map[string]int{"a": 1, "b": 2, "c": 3} + var result = collection.FilterOutByMap(m, func(k string, v int) bool { + return k == "a" || v == 3 + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFilterOutByMap(t *testing.T) { + var cases = []struct { + name string + input map[int]int + filter map[int]int + expected map[int]int + }{{"TestFilterOutByMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]int{1: 1, 3: 3}, map[int]int{2: 2}}, {"TestFilterOutByMap_EmptyMap", map[int]int{}, map[int]int{1: 1, 3: 3}, map[int]int{}}, {"TestFilterOutByMap_NilMap", nil, map[int]int{1: 1, 3: 3}, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FilterOutByMap(c.input, func(k int, v int) bool { + return c.filter[k] == v + }) + if len(actual) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the length of map is not equal") + } + for k, v := range actual { + if v != c.expected[k] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "after filter, the inputV of map is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func FindLoopedNextInSlice(slice S, i int) (next int, value V) > 返回 i 的下一个数组成员,当 i 达到数组长度时从 0 开始 > - 当 i 为负数时将返回第一个元素 + +示例代码: +```go + +func ExampleFindLoopedNextInSlice() { + next, v := collection.FindLoopedNextInSlice([]int{1, 2, 3}, 1) + fmt.Println(next, v) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindLoopedNextInSlice(t *testing.T) { + var cases = []struct { + name string + input []int + i int + expected int + }{{"TestFindLoopedNextInSlice_NonEmptySlice", []int{1, 2, 3}, 1, 2}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual, _ := collection.FindLoopedNextInSlice(c.input, c.i) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the next index of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindLoopedPrevInSlice(slice S, i int) (prev int, value V) > 返回 i 的上一个数组成员,当 i 为 0 时从数组末尾开始 > - 当 i 为负数时将返回最后一个元素 + +示例代码: +```go + +func ExampleFindLoopedPrevInSlice() { + prev, v := collection.FindLoopedPrevInSlice([]int{1, 2, 3}, 1) + fmt.Println(prev, v) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindLoopedPrevInSlice(t *testing.T) { + var cases = []struct { + name string + input []int + i int + expected int + }{{"TestFindLoopedPrevInSlice_NonEmptySlice", []int{1, 2, 3}, 1, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual, _ := collection.FindLoopedPrevInSlice(c.input, c.i) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the prev index of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindCombinationsInSliceByRange(s S, minSize int, maxSize int) []S > 获取给定数组的所有组合,且每个组合的成员数量限制在指定范围内 + +示例代码: +```go + +func ExampleFindCombinationsInSliceByRange() { + result := collection.FindCombinationsInSliceByRange([]int{1, 2, 3}, 1, 2) + fmt.Println(len(result)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindCombinationsInSliceByRange(t *testing.T) { + var cases = []struct { + name string + input []int + minSize int + maxSize int + expected [][]int + }{{"TestFindCombinationsInSliceByRange_NonEmptySlice", []int{1, 2, 3}, 1, 2, [][]int{{1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FindCombinationsInSliceByRange(c.input, c.minSize, c.maxSize) + if len(actual) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindFirstOrDefaultInSlice(slice S, defaultValue V) V > 判断切片中是否存在元素,返回第一个元素,不存在则返回默认值 + +示例代码: +```go + +func ExampleFindFirstOrDefaultInSlice() { + result := collection.FindFirstOrDefaultInSlice([]int{1, 2, 3}, 0) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindFirstOrDefaultInSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{{"TestFindFirstOrDefaultInSlice_NonEmptySlice", []int{1, 2, 3}, 1}, {"TestFindFirstOrDefaultInSlice_EmptySlice", []int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FindFirstOrDefaultInSlice(c.input, 0) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the first element of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindOrDefaultInSlice(slice S, defaultValue V, handler func (v V) bool) (t V) > 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则返回默认值 + +示例代码: +```go + +func ExampleFindOrDefaultInSlice() { + result := collection.FindOrDefaultInSlice([]int{1, 2, 3}, 0, func(v int) bool { + return v == 2 + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindOrDefaultInSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{{"TestFindOrDefaultInSlice_NonEmptySlice", []int{1, 2, 3}, 2}, {"TestFindOrDefaultInSlice_EmptySlice", []int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FindOrDefaultInSlice(c.input, 0, func(v int) bool { + return v == 2 + }) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the element of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindOrDefaultInComparableSlice(slice S, v V, defaultValue V) (t V) > 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则返回默认值 + +示例代码: +```go + +func ExampleFindOrDefaultInComparableSlice() { + result := collection.FindOrDefaultInComparableSlice([]int{1, 2, 3}, 2, 0) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindOrDefaultInComparableSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{{"TestFindOrDefaultInComparableSlice_NonEmptySlice", []int{1, 2, 3}, 2}, {"TestFindOrDefaultInComparableSlice_EmptySlice", []int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FindOrDefaultInComparableSlice(c.input, 2, 0) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the element of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindInSlice(slice S, handler func (v V) bool) (i int, t V) > 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则索引返回 -1 + +示例代码: +```go + +func ExampleFindInSlice() { + _, result := collection.FindInSlice([]int{1, 2, 3}, func(v int) bool { + return v == 2 + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindInSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{{"TestFindInSlice_NonEmptySlice", []int{1, 2, 3}, 2}, {"TestFindInSlice_EmptySlice", []int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + _, actual := collection.FindInSlice(c.input, func(v int) bool { + return v == 2 + }) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the element of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindIndexInSlice(slice S, handler func (v V) bool) int > 判断切片中是否存在某个元素,返回第一个匹配的索引,不存在则索引返回 -1 + +示例代码: +```go + +func ExampleFindIndexInSlice() { + result := collection.FindIndexInSlice([]int{1, 2, 3}, func(v int) bool { + return v == 2 + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindIndexInSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{{"TestFindIndexInSlice_NonEmptySlice", []int{1, 2, 3}, 1}, {"TestFindIndexInSlice_EmptySlice", []int{}, -1}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FindIndexInSlice(c.input, func(v int) bool { + return v == 2 + }) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the index of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindInComparableSlice(slice S, v V) (i int, t V) > 判断切片中是否存在某个元素,返回第一个匹配的索引和元素,不存在则索引返回 -1 + +示例代码: +```go + +func ExampleFindInComparableSlice() { + index, result := collection.FindInComparableSlice([]int{1, 2, 3}, 2) + fmt.Println(index, result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindInComparableSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{{"TestFindInComparableSlice_NonEmptySlice", []int{1, 2, 3}, 2}, {"TestFindInComparableSlice_EmptySlice", []int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + _, actual := collection.FindInComparableSlice(c.input, 2) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the element of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindIndexInComparableSlice(slice S, v V) int > 判断切片中是否存在某个元素,返回第一个匹配的索引,不存在则索引返回 -1 + +示例代码: +```go + +func ExampleFindIndexInComparableSlice() { + result := collection.FindIndexInComparableSlice([]int{1, 2, 3}, 2) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindIndexInComparableSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{{"TestFindIndexInComparableSlice_NonEmptySlice", []int{1, 2, 3}, 1}, {"TestFindIndexInComparableSlice_EmptySlice", []int{}, -1}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FindIndexInComparableSlice(c.input, 2) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the index of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindMinimumInComparableSlice(slice S) (result V) > 获取切片中的最小值 + +示例代码: +```go + +func ExampleFindMinimumInComparableSlice() { + result := collection.FindMinimumInComparableSlice([]int{1, 2, 3}) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindMinimumInComparableSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{{"TestFindMinimumInComparableSlice_NonEmptySlice", []int{1, 2, 3}, 1}, {"TestFindMinimumInComparableSlice_EmptySlice", []int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FindMinimumInComparableSlice(c.input) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the minimum of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindMinimumInSlice(slice S, handler OrderedValueGetter[V, N]) (result V) > 获取切片中的最小值 + +示例代码: +```go + +func ExampleFindMinimumInSlice() { + result := collection.FindMinimumInSlice([]int{1, 2, 3}, func(v int) int { + return v + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindMinimumInSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{{"TestFindMinimumInSlice_NonEmptySlice", []int{1, 2, 3}, 1}, {"TestFindMinimumInSlice_EmptySlice", []int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FindMinimumInSlice(c.input, func(v int) int { + return v + }) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the minimum of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindMaximumInComparableSlice(slice S) (result V) > 获取切片中的最大值 + +示例代码: +```go + +func ExampleFindMaximumInComparableSlice() { + result := collection.FindMaximumInComparableSlice([]int{1, 2, 3}) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindMaximumInComparableSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{{"TestFindMaximumInComparableSlice_NonEmptySlice", []int{1, 2, 3}, 3}, {"TestFindMaximumInComparableSlice_EmptySlice", []int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FindMaximumInComparableSlice(c.input) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the maximum of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindMaximumInSlice(slice S, handler OrderedValueGetter[V, N]) (result V) > 获取切片中的最大值 + +示例代码: +```go + +func ExampleFindMaximumInSlice() { + result := collection.FindMaximumInSlice([]int{1, 2, 3}, func(v int) int { + return v + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindMaximumInSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{{"TestFindMaximumInSlice_NonEmptySlice", []int{1, 2, 3}, 3}, {"TestFindMaximumInSlice_EmptySlice", []int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FindMaximumInSlice(c.input, func(v int) int { + return v + }) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the maximum of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindMin2MaxInComparableSlice(slice S) (min V, max V) > 获取切片中的最小值和最大值 + +示例代码: +```go + +func ExampleFindMin2MaxInComparableSlice() { + minimum, maximum := collection.FindMin2MaxInComparableSlice([]int{1, 2, 3}) + fmt.Println(minimum, maximum) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindMin2MaxInComparableSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expectedMin int + expectedMax int + }{{"TestFindMin2MaxInComparableSlice_NonEmptySlice", []int{1, 2, 3}, 1, 3}, {"TestFindMin2MaxInComparableSlice_EmptySlice", []int{}, 0, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + minimum, maximum := collection.FindMin2MaxInComparableSlice(c.input) + if minimum != c.expectedMin || maximum != c.expectedMax { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expectedMin, minimum, "the minimum of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindMin2MaxInSlice(slice S, handler OrderedValueGetter[V, N]) (min V, max V) > 获取切片中的最小值和最大值 + +示例代码: +```go + +func ExampleFindMin2MaxInSlice() { + minimum, maximum := collection.FindMin2MaxInSlice([]int{1, 2, 3}, func(v int) int { + return v + }) + fmt.Println(minimum, maximum) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindMin2MaxInSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expectedMin int + expectedMax int + }{{"TestFindMin2MaxInSlice_NonEmptySlice", []int{1, 2, 3}, 1, 3}, {"TestFindMin2MaxInSlice_EmptySlice", []int{}, 0, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + minimum, maximum := collection.FindMin2MaxInSlice(c.input, func(v int) int { + return v + }) + if minimum != c.expectedMin || maximum != c.expectedMax { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expectedMin, minimum, "the minimum of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindMinFromComparableMap(m M) (result V) > 获取 map 中的最小值 + +示例代码: +```go + +func ExampleFindMinFromComparableMap() { + result := collection.FindMinFromComparableMap(map[int]int{1: 1, 2: 2, 3: 3}) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindMinFromComparableMap(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected int + }{{"TestFindMinFromComparableMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, 1}, {"TestFindMinFromComparableMap_EmptyMap", map[int]int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FindMinFromComparableMap(c.input) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the minimum of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindMinFromMap(m M, handler OrderedValueGetter[V, N]) (result V) > 获取 map 中的最小值 + +示例代码: +```go + +func ExampleFindMinFromMap() { + result := collection.FindMinFromMap(map[int]int{1: 1, 2: 2, 3: 3}, func(v int) int { + return v + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindMinFromMap(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected int + }{{"TestFindMinFromMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, 1}, {"TestFindMinFromMap_EmptyMap", map[int]int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FindMinFromMap(c.input, func(v int) int { + return v + }) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the minimum of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindMaxFromComparableMap(m M) (result V) > 获取 map 中的最大值 + +示例代码: +```go + +func ExampleFindMaxFromComparableMap() { + result := collection.FindMaxFromComparableMap(map[int]int{1: 1, 2: 2, 3: 3}) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindMaxFromComparableMap(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected int + }{{"TestFindMaxFromComparableMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, 3}, {"TestFindMaxFromComparableMap_EmptyMap", map[int]int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FindMaxFromComparableMap(c.input) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the maximum of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindMaxFromMap(m M, handler OrderedValueGetter[V, N]) (result V) > 获取 map 中的最大值 + +示例代码: +```go + +func ExampleFindMaxFromMap() { + result := collection.FindMaxFromMap(map[int]int{1: 1, 2: 2, 3: 3}, func(v int) int { + return v + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindMaxFromMap(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected int + }{{"TestFindMaxFromMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, 3}, {"TestFindMaxFromMap_EmptyMap", map[int]int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.FindMaxFromMap(c.input, func(v int) int { + return v + }) + if actual != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, actual, "the maximum of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindMin2MaxFromComparableMap(m M) (min V, max V) > 获取 map 中的最小值和最大值 + +示例代码: +```go + +func ExampleFindMin2MaxFromComparableMap() { + minimum, maximum := collection.FindMin2MaxFromComparableMap(map[int]int{1: 1, 2: 2, 3: 3}) + fmt.Println(minimum, maximum) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindMin2MaxFromComparableMap(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expectedMin int + expectedMax int + }{{"TestFindMin2MaxFromComparableMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, 1, 3}, {"TestFindMin2MaxFromComparableMap_EmptyMap", map[int]int{}, 0, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + minimum, maximum := collection.FindMin2MaxFromComparableMap(c.input) + if minimum != c.expectedMin || maximum != c.expectedMax { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expectedMin, minimum, "the minimum of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func FindMin2MaxFromMap(m M) (min V, max V) > 获取 map 中的最小值和最大值 + +示例代码: +```go + +func ExampleFindMin2MaxFromMap() { + minimum, maximum := collection.FindMin2MaxFromMap(map[int]int{1: 1, 2: 2, 3: 3}) + fmt.Println(minimum, maximum) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestFindMin2MaxFromMap(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expectedMin int + expectedMax int + }{{"TestFindMin2MaxFromMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, 1, 3}, {"TestFindMin2MaxFromMap_EmptyMap", map[int]int{}, 0, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + minimum, maximum := collection.FindMin2MaxFromMap(c.input) + if minimum != c.expectedMin || maximum != c.expectedMax { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expectedMin, minimum, "the minimum of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func SwapSlice(slice *S, i int, j int) > 将切片中的两个元素进行交换 + +示例代码: +```go + +func ExampleSwapSlice() { + var s = []int{1, 2, 3} + collection.SwapSlice(&s, 0, 1) + fmt.Println(s) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestSwapSlice(t *testing.T) { + var cases = []struct { + name string + slice []int + i int + j int + expect []int + }{{"TestSwapSliceNonEmpty", []int{1, 2, 3}, 0, 1, []int{2, 1, 3}}, {"TestSwapSliceEmpty", []int{}, 0, 0, []int{}}, {"TestSwapSliceIndexOutOfBound", []int{1, 2, 3}, 0, 3, []int{1, 2, 3}}, {"TestSwapSliceNil", nil, 0, 0, nil}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + collection.SwapSlice(&c.slice, c.i, c.j) + for i, v := range c.slice { + if v != c.expect[i] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expect, c.slice, "the slice is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func MappingFromSlice(slice S, handler func (value V) N) NS > 将切片中的元素进行转换 + +示例代码: +```go + +func ExampleMappingFromSlice() { + result := collection.MappingFromSlice([]int{1, 2, 3}, func(value int) int { + return value + 1 + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestMappingFromSlice(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{{"TestMappingFromSlice_NonEmptySlice", []int{1, 2, 3}, []int{2, 3, 4}}, {"TestMappingFromSlice_EmptySlice", []int{}, []int{}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.MappingFromSlice[[]int, []int](c.input, func(value int) int { + return value + 1 + }) + if len(result) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + for i := 0; i < len(result); i++ { + if result[i] != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the value of input is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func MappingFromMap(m M, handler func (value V) N) NM > 将 map 中的元素进行转换 + +示例代码: +```go + +func ExampleMappingFromMap() { + result := collection.MappingFromMap(map[int]int{1: 1, 2: 2, 3: 3}, func(value int) int { + return value + 1 + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestMappingFromMap(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]int + }{{"TestMappingFromMap_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]int{1: 2, 2: 3, 3: 4}}, {"TestMappingFromMap_EmptyMap", map[int]int{}, map[int]int{}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.MappingFromMap[map[int]int, map[int]int](c.input, func(value int) int { + return value + 1 + }) + if len(result) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + for k, v := range result { + if v != c.expected[k] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the value of input is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func MergeSlices(slices ...S) (result S) > 合并切片 + +示例代码: +```go + +func ExampleMergeSlices() { + fmt.Println(collection.MergeSlices([]int{1, 2, 3}, []int{4, 5, 6})) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestMergeSlices(t *testing.T) { + var cases = []struct { + name string + input [][]int + expected []int + }{{"TestMergeSlices_NonEmptySlice", [][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2, 3, 4, 5, 6}}, {"TestMergeSlices_EmptySlice", [][]int{{}, {}}, []int{}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.MergeSlices(c.input...) + if len(result) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + for i := 0; i < len(result); i++ { + if result[i] != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the value of input is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func MergeMaps(maps ...M) (result M) > 合并 map,当多个 map 中存在相同的 key 时,后面的 map 中的 key 将会覆盖前面的 map 中的 key + +示例代码: +```go + +func ExampleMergeMaps() { + m := collection.MergeMaps(map[int]int{1: 1, 2: 2, 3: 3}, map[int]int{4: 4, 5: 5, 6: 6}) + fmt.Println(len(m)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestMergeMaps(t *testing.T) { + var cases = []struct { + name string + input []map[int]int + expected int + }{{"TestMergeMaps_NonEmptyMap", []map[int]int{{1: 1, 2: 2, 3: 3}, {4: 4, 5: 5, 6: 6}}, 6}, {"TestMergeMaps_EmptyMap", []map[int]int{{}, {}}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.MergeMaps(c.input...) + if len(result) != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func MergeMapsWithSkip(maps ...M) (result M) > 合并 map,当多个 map 中存在相同的 key 时,后面的 map 中的 key 将会被跳过 + +示例代码: +```go + +func ExampleMergeMapsWithSkip() { + m := collection.MergeMapsWithSkip(map[int]int{1: 1}, map[int]int{1: 2}) + fmt.Println(m[1]) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestMergeMapsWithSkip(t *testing.T) { + var cases = []struct { + name string + input []map[int]int + expected int + }{{"TestMergeMapsWithSkip_NonEmptyMap", []map[int]int{{1: 1}, {1: 2}}, 1}, {"TestMergeMapsWithSkip_EmptyMap", []map[int]int{{}, {}}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.MergeMapsWithSkip(c.input...) + if len(result) != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func ChooseRandomSliceElementRepeatN(slice S, n int) (result []V) > 返回 slice 中的 n 个可重复随机元素 > - 当 slice 长度为 0 或 n 小于等于 0 时将会返回 nil + +示例代码: +```go + +func ExampleChooseRandomSliceElementRepeatN() { + result := collection.ChooseRandomSliceElementRepeatN([]int{1}, 10) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestChooseRandomSliceElementRepeatN(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{{"TestChooseRandomSliceElementRepeatN_NonEmptySlice", []int{1, 2, 3}, 3}, {"TestChooseRandomSliceElementRepeatN_EmptySlice", []int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomSliceElementRepeatN(c.input, 3) + if len(result) != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func ChooseRandomIndexRepeatN(slice S, n int) (result []int) > 返回 slice 中的 n 个可重复随机元素的索引 > - 当 slice 长度为 0 或 n 小于等于 0 时将会返回 nil + +示例代码: +```go + +func ExampleChooseRandomIndexRepeatN() { + result := collection.ChooseRandomIndexRepeatN([]int{1}, 10) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestChooseRandomIndexRepeatN(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{{"TestChooseRandomIndexRepeatN_NonEmptySlice", []int{1, 2, 3}, 3}, {"TestChooseRandomIndexRepeatN_EmptySlice", []int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomIndexRepeatN(c.input, 3) + if len(result) != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func ChooseRandomSliceElement(slice S) (v V) > 返回 slice 中随机一个元素,当 slice 长度为 0 时将会得到 V 的零值 + +示例代码: +```go + +func ExampleChooseRandomSliceElement() { + result := collection.ChooseRandomSliceElement([]int{1}) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestChooseRandomSliceElement(t *testing.T) { + var cases = []struct { + name string + input []int + expected map[int]bool + }{{"TestChooseRandomSliceElement_NonEmptySlice", []int{1, 2, 3}, map[int]bool{1: true, 2: true, 3: true}}, {"TestChooseRandomSliceElement_EmptySlice", []int{}, map[int]bool{0: true}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomSliceElement(c.input) + if !c.expected[result] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func ChooseRandomIndex(slice S) (index int) > 返回 slice 中随机一个元素的索引,当 slice 长度为 0 时将会得到 -1 + +示例代码: +```go + +func ExampleChooseRandomIndex() { + result := collection.ChooseRandomIndex([]int{1}) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestChooseRandomIndex(t *testing.T) { + var cases = []struct { + name string + input []int + expected map[int]bool + }{{"TestChooseRandomIndex_NonEmptySlice", []int{1, 2, 3}, map[int]bool{0: true, 1: true, 2: true}}, {"TestChooseRandomIndex_EmptySlice", []int{}, map[int]bool{-1: true}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomIndex(c.input) + if !c.expected[result] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func ChooseRandomSliceElementN(slice S, n int) (result []V) > 返回 slice 中的 n 个不可重复的随机元素 > - 当 slice 长度为 0 或 n 大于 slice 长度或小于 0 时将会发生 panic + +示例代码: +```go + +func ExampleChooseRandomSliceElementN() { + result := collection.ChooseRandomSliceElementN([]int{1}, 1) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestChooseRandomSliceElementN(t *testing.T) { + var cases = []struct { + name string + input []int + }{{"TestChooseRandomSliceElementN_NonEmptySlice", []int{1, 2, 3}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.ChooseRandomSliceElementN(c.input, 3) + if !collection.AllInComparableSlice(actual, c.input) { + t.Fatalf("%s failed, actual: %v, error: %s", c.name, actual, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func ChooseRandomIndexN(slice S, n int) (result []int) > 获取切片中的 n 个随机元素的索引 > - 如果 n 大于切片长度或小于 0 时将会发生 panic + +示例代码: +```go + +func ExampleChooseRandomIndexN() { + result := collection.ChooseRandomIndexN([]int{1}, 1) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestChooseRandomIndexN(t *testing.T) { + var cases = []struct { + name string + input []int + expected map[int]bool + }{{"TestChooseRandomIndexN_NonEmptySlice", []int{1, 2, 3}, map[int]bool{0: true, 1: true, 2: true}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomIndexN(c.input, 3) + if !c.expected[result[0]] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func ChooseRandomMapKeyRepeatN(m M, n int) (result []K) > 获取 map 中的 n 个随机 key,允许重复 > - 如果 n 大于 map 长度或小于 0 时将会发生 panic + +示例代码: +```go + +func ExampleChooseRandomMapKeyRepeatN() { + result := collection.ChooseRandomMapKeyRepeatN(map[int]int{1: 1}, 1) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestChooseRandomMapKeyRepeatN(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{{"TestChooseRandomMapKeyRepeatN_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomMapKeyRepeatN(c.input, 3) + if !c.expected[result[0]] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func ChooseRandomMapValueRepeatN(m M, n int) (result []V) > 获取 map 中的 n 个随机 n,允许重复 > - 如果 n 大于 map 长度或小于 0 时将会发生 panic + +示例代码: +```go + +func ExampleChooseRandomMapValueRepeatN() { + result := collection.ChooseRandomMapValueRepeatN(map[int]int{1: 1}, 1) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestChooseRandomMapValueRepeatN(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{{"TestChooseRandomMapValueRepeatN_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomMapValueRepeatN(c.input, 3) + if !c.expected[result[0]] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func ChooseRandomMapKeyAndValueRepeatN(m M, n int) M > 获取 map 中的 n 个随机 key 和 v,允许重复 > - 如果 n 大于 map 长度或小于 0 时将会发生 panic + +示例代码: +```go + +func ExampleChooseRandomMapKeyAndValueRepeatN() { + result := collection.ChooseRandomMapKeyAndValueRepeatN(map[int]int{1: 1}, 1) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestChooseRandomMapKeyAndValueRepeatN(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{{"TestChooseRandomMapKeyAndValueRepeatN_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomMapKeyAndValueRepeatN(c.input, 3) + if !c.expected[result[1]] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func ChooseRandomMapKey(m M) (k K) > 获取 map 中的随机 key + +示例代码: +```go + +func ExampleChooseRandomMapKey() { + result := collection.ChooseRandomMapKey(map[int]int{1: 1}) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestChooseRandomMapKey(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{{"TestChooseRandomMapKey_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}, {"TestChooseRandomMapKey_EmptyMap", map[int]int{}, map[int]bool{0: true}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomMapKey(c.input) + if !c.expected[result] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func ChooseRandomMapValue(m M) (v V) > 获取 map 中的随机 value + +示例代码: +```go + +func ExampleChooseRandomMapValue() { + result := collection.ChooseRandomMapValue(map[int]int{1: 1}) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestChooseRandomMapValue(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{{"TestChooseRandomMapValue_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}, {"TestChooseRandomMapValue_EmptyMap", map[int]int{}, map[int]bool{0: true}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomMapValue(c.input) + if !c.expected[result] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func ChooseRandomMapKeyN(m M, n int) (result []K) > 获取 map 中的 inputN 个随机 key > - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic + +示例代码: +```go + +func ExampleChooseRandomMapKeyN() { + result := collection.ChooseRandomMapKeyN(map[int]int{1: 1}, 1) + fmt.Println(result) +} + +``` + *** #### func ChooseRandomMapValueN(m M, n int) (result []V) > 获取 map 中的 n 个随机 value > - 如果 n 大于 map 长度或小于 0 时将会发生 panic + +示例代码: +```go + +func ExampleChooseRandomMapValueN() { + result := collection.ChooseRandomMapValueN(map[int]int{1: 1}, 1) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestChooseRandomMapValueN(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{{"TestChooseRandomMapValueN_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomMapValueN(c.input, 3) + if !c.expected[result[0]] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func ChooseRandomMapKeyAndValue(m M) (k K, v V) > 获取 map 中的随机 key 和 v + +示例代码: +```go + +func ExampleChooseRandomMapKeyAndValue() { + k, v := collection.ChooseRandomMapKeyAndValue(map[int]int{1: 1}) + fmt.Println(k, v) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestChooseRandomMapKeyAndValue(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{{"TestChooseRandomMapKeyAndValue_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}, {"TestChooseRandomMapKeyAndValue_EmptyMap", map[int]int{}, map[int]bool{0: true}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + k, v := collection.ChooseRandomMapKeyAndValue(c.input) + if !c.expected[k] || !c.expected[v] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, k, "the length of input is not equal") + } + }) + } +} + +``` + + +
+ + *** #### func ChooseRandomMapKeyAndValueN(m M, n int) M > 获取 map 中的 inputN 个随机 key 和 v > - 如果 n 大于 map 长度或小于 0 时将会发生 panic + +示例代码: +```go + +func ExampleChooseRandomMapKeyAndValueN() { + result := collection.ChooseRandomMapKeyAndValueN(map[int]int{1: 1}, 1) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestChooseRandomMapKeyAndValueN(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{{"TestChooseRandomMapKeyAndValueN_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + kvm := collection.ChooseRandomMapKeyAndValueN(c.input, 1) + for k := range kvm { + if !c.expected[k] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, k, "the length of input is not equal") + } + } + }) + } +} + +``` + + +
+ + *** #### func DescBy(a Sort, b Sort) bool > 返回降序比较结果 + +示例代码: +```go + +func ExampleDescBy() { + var slice = []int{1, 2, 3} + sort.Slice(slice, func(i, j int) bool { + return collection.DescBy(slice[i], slice[j]) + }) + fmt.Println(slice) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestDescBy(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{{"TestDescBy_NonEmptySlice", []int{1, 2, 3}, []int{3, 2, 1}}, {"TestDescBy_EmptySlice", []int{}, []int{}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + sort.Slice(c.input, func(i, j int) bool { + return collection.DescBy(c.input[i], c.input[j]) + }) + for i, v := range c.input { + if v != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, c.input) + } + } + }) + } +} + +``` + + +
+ + *** #### func AscBy(a Sort, b Sort) bool > 返回升序比较结果 + +示例代码: +```go + +func ExampleAscBy() { + var slice = []int{1, 2, 3} + sort.Slice(slice, func(i, j int) bool { + return collection.AscBy(slice[i], slice[j]) + }) + fmt.Println(slice) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAscBy(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{{"TestAscBy_NonEmptySlice", []int{1, 2, 3}, []int{1, 2, 3}}, {"TestAscBy_EmptySlice", []int{}, []int{}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + sort.Slice(c.input, func(i, j int) bool { + return collection.AscBy(c.input[i], c.input[j]) + }) + for i, v := range c.input { + if v != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, c.input) + } + } + }) + } +} + +``` + + +
+ + *** #### func Desc(slice *S, getter func (index int) Sort) > 对切片进行降序排序 + +示例代码: +```go + +func ExampleDesc() { + var slice = []int{1, 2, 3} + collection.Desc(&slice, func(index int) int { + return slice[index] + }) + fmt.Println(slice) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestDesc(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{{"TestDesc_NonEmptySlice", []int{1, 2, 3}, []int{3, 2, 1}}, {"TestDesc_EmptySlice", []int{}, []int{}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + collection.Desc(&c.input, func(index int) int { + return c.input[index] + }) + for i, v := range c.input { + if v != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, c.input) + } + } + }) + } +} + +``` + + +
+ + *** #### func DescByClone(slice S, getter func (index int) Sort) S > 对切片进行降序排序,返回排序后的切片 + +示例代码: +```go + +func ExampleDescByClone() { + var slice = []int{1, 2, 3} + result := collection.DescByClone(slice, func(index int) int { + return slice[index] + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestDescByClone(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{{"TestDescByClone_NonEmptySlice", []int{1, 2, 3}, []int{3, 2, 1}}, {"TestDescByClone_EmptySlice", []int{}, []int{}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.DescByClone(c.input, func(index int) int { + return c.input[index] + }) + for i, v := range result { + if v != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, result) + } + } + }) + } +} + +``` + + +
+ + *** #### func Asc(slice *S, getter func (index int) Sort) > 对切片进行升序排序 + +示例代码: +```go + +func ExampleAsc() { + var slice = []int{1, 2, 3} + collection.Asc(&slice, func(index int) int { + return slice[index] + }) + fmt.Println(slice) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAsc(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{{"TestAsc_NonEmptySlice", []int{1, 2, 3}, []int{1, 2, 3}}, {"TestAsc_EmptySlice", []int{}, []int{}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + collection.Asc(&c.input, func(index int) int { + return c.input[index] + }) + for i, v := range c.input { + if v != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, c.input) + } + } + }) + } +} + +``` + + +
+ + *** #### func AscByClone(slice S, getter func (index int) Sort) S > 对切片进行升序排序,返回排序后的切片 + +示例代码: +```go + +func ExampleAscByClone() { + var slice = []int{1, 2, 3} + result := collection.AscByClone(slice, func(index int) int { + return slice[index] + }) + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestAscByClone(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{{"TestAscByClone_NonEmptySlice", []int{1, 2, 3}, []int{1, 2, 3}}, {"TestAscByClone_EmptySlice", []int{}, []int{}}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.AscByClone(c.input, func(index int) int { + return c.input[index] + }) + for i, v := range result { + if v != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, result) + } + } + }) + } +} + +``` + + +
+ + *** #### func Shuffle(slice *S) > 对切片进行随机排序 + +示例代码: +```go + +func ExampleShuffle() { + var slice = []int{1, 2, 3} + collection.Shuffle(&slice) + fmt.Println(len(slice)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestShuffle(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{{"TestShuffle_NonEmptySlice", []int{1, 2, 3}, 3}, {"TestShuffle_EmptySlice", []int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + collection.Shuffle(&c.input) + if len(c.input) != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, c.input) + } + }) + } +} + +``` + + +
+ + *** #### func ShuffleByClone(slice S) S > 对切片进行随机排序,返回排序后的切片 + +示例代码: +```go + +func ExampleShuffleByClone() { + var slice = []int{1, 2, 3} + result := collection.ShuffleByClone(slice) + fmt.Println(len(result)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestShuffleByClone(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{{"TestShuffleByClone_NonEmptySlice", []int{1, 2, 3}, 3}, {"TestShuffleByClone_EmptySlice", []int{}, 0}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ShuffleByClone(c.input) + if len(result) != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, result) + } + }) + } +} + +``` + + +
+ + *** -### ComparisonHandler +### ComparisonHandler `STRUCT` ```go -type ComparisonHandler[V any] struct{} +type ComparisonHandler[V any] func(source V) bool ``` -### OrderedValueGetter +### OrderedValueGetter `STRUCT` ```go -type OrderedValueGetter[V any, N generic.Ordered] struct{} +type OrderedValueGetter[V any, N generic.Ordered] func(v V) N ``` diff --git a/utils/collection/listings/README.md b/utils/collection/listings/README.md index c71ab52..061d219 100644 --- a/utils/collection/listings/README.md +++ b/utils/collection/listings/README.md @@ -1,19 +1,20 @@ # Listings - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/listings) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewMatrix](#NewMatrix)|创建一个新的 Matrix 实例。 |[NewPagedSlice](#NewPagedSlice)|创建一个新的 PagedSlice 实例。 @@ -21,35 +22,41 @@ |[NewSyncSlice](#NewSyncSlice)|创建一个 SyncSlice -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Matrix](#matrix)|暂无描述... -|[PagedSlice](#pagedslice)|是一个高效的动态数组,它通过分页管理内存并减少频繁的内存分配来提高性能。 -|[PrioritySlice](#priorityslice)|是一个优先级切片 -|[SyncSlice](#syncslice)|是基于 sync.RWMutex 实现的线程安全的 slice +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Matrix](#matrix)|暂无描述... +|`STRUCT`|[PagedSlice](#pagedslice)|是一个高效的动态数组,它通过分页管理内存并减少频繁的内存分配来提高性能。 +|`STRUCT`|[PrioritySlice](#priorityslice)|是一个优先级切片 +|`STRUCT`|[SyncSlice](#syncslice)|是基于 sync.RWMutex 实现的线程安全的 slice
+*** +## 详情信息 #### func NewMatrix(dimensions ...int) *Matrix[V] > 创建一个新的 Matrix 实例。 + *** #### func NewPagedSlice(pageSize int) *PagedSlice[T] > 创建一个新的 PagedSlice 实例。 + *** #### func NewPrioritySlice(lengthAndCap ...int) *PrioritySlice[V] > 创建一个优先级切片 + *** #### func NewSyncSlice(length int, cap int) *SyncSlice[V] > 创建一个 SyncSlice + *** -### Matrix +### Matrix `STRUCT` ```go type Matrix[V any] struct { @@ -69,7 +76,7 @@ type Matrix[V any] struct { #### func (*Matrix) Clear() > 清空矩阵。 *** -### PagedSlice +### PagedSlice `STRUCT` 是一个高效的动态数组,它通过分页管理内存并减少频繁的内存分配来提高性能。 ```go type PagedSlice[T any] struct { @@ -97,7 +104,7 @@ type PagedSlice[T any] struct { #### func (*PagedSlice) Range(f func (index int, value T) bool) > 迭代 PagedSlice 中的所有元素。 *** -### PrioritySlice +### PrioritySlice `STRUCT` 是一个优先级切片 ```go type PrioritySlice[V any] struct { @@ -115,6 +122,25 @@ type PrioritySlice[V any] struct { *** #### func (*PrioritySlice) Append(v V, p int) > 添加元素 +
+查看 / 收起单元测试 + + +```go + +func TestPrioritySlice_Append(t *testing.T) { + var s = listings.NewPrioritySlice[string]() + s.Append("name_1", 2) + s.Append("name_2", 1) + fmt.Println(s) +} + +``` + + +
+ + *** #### func (*PrioritySlice) Appends(priority int, vs ...V) > 添加元素 @@ -155,7 +181,7 @@ type PrioritySlice[V any] struct { #### func (*PrioritySlice) String() string > 返回切片字符串 *** -### SyncSlice +### SyncSlice `STRUCT` 是基于 sync.RWMutex 实现的线程安全的 slice ```go type SyncSlice[V any] struct { diff --git a/utils/collection/mappings/README.md b/utils/collection/mappings/README.md index 6163363..e5e489f 100644 --- a/utils/collection/mappings/README.md +++ b/utils/collection/mappings/README.md @@ -1,37 +1,41 @@ # Mappings - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/mappings) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewSyncMap](#NewSyncMap)|创建一个 SyncMap -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[SyncMap](#syncmap)|是基于 sync.RWMutex 实现的线程安全的 map +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[SyncMap](#syncmap)|是基于 sync.RWMutex 实现的线程安全的 map
+*** +## 详情信息 #### func NewSyncMap(source ...map[K]V) *SyncMap[K, V] > 创建一个 SyncMap + *** -### SyncMap +### SyncMap `STRUCT` 是基于 sync.RWMutex 实现的线程安全的 map - 适用于要考虑并发读写但是并发读写的频率不高的情况 ```go diff --git a/utils/combination/README.md b/utils/combination/README.md index bb44817..62b26d5 100644 --- a/utils/combination/README.md +++ b/utils/combination/README.md @@ -1,19 +1,20 @@ # Combination -combination 包提供了一些实用的组合函数。 - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/combination) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +combination 包提供了一些实用的组合函数。 + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewCombination](#NewCombination)|创建一个新的组合器 |[WithEvaluation](#WithEvaluation)|设置组合评估函数 @@ -47,24 +48,27 @@ combination 包提供了一些实用的组合函数。 |[WithValidatorHandleNCarryIndependentM](#WithValidatorHandleNCarryIndependentM)|校验组合成员是否匹配 N 携带独立的 M 的组合 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Combination](#combination)|用于从多个匹配器内提取组合的数据结构 -|[Option](#option)|组合器选项 -|[Item](#item)|暂无描述... -|[Matcher](#matcher)|用于从一组数据内提取组合的数据结构 -|[MatcherOption](#matcheroption)|匹配器选项 -|[Validator](#validator)|用于对组合进行验证的校验器 -|[ValidatorOption](#validatoroption)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Combination](#combination)|用于从多个匹配器内提取组合的数据结构 +|`STRUCT`|[Option](#option)|组合器选项 +|`INTERFACE`|[Item](#item)|暂无描述... +|`STRUCT`|[Matcher](#matcher)|用于从一组数据内提取组合的数据结构 +|`STRUCT`|[MatcherOption](#matcheroption)|匹配器选项 +|`STRUCT`|[Validator](#validator)|用于对组合进行验证的校验器 +|`STRUCT`|[ValidatorOption](#validatoroption)|暂无描述...
+*** +## 详情信息 #### func NewCombination(options ...Option[T]) *Combination[T] > 创建一个新的组合器 + *** #### func WithEvaluation(evaluate func (items []T) float64) Option[T] @@ -73,48 +77,57 @@ combination 包提供了一些实用的组合函数。 > - 通过该选项将设置所有匹配器的默认评估函数为该函数 > - 通过匹配器选项 WithMatcherEvaluation 可以覆盖该默认评估函数 > - 默认的评估函数将返回一个随机数 + *** #### func NewMatcher(options ...MatcherOption[T]) *Matcher[T] > 创建一个新的匹配器 + *** #### func WithMatcherEvaluation(evaluate func (items []T) float64) MatcherOption[T] > 设置匹配器评估函数 > - 用于对组合进行评估,返回一个分值的评价函数 > - 通过该选项将覆盖匹配器的默认(WithEvaluation)评估函数 + *** #### func WithMatcherLeastLength(length int) MatcherOption[T] > 通过匹配最小长度的组合创建匹配器 > - length: 组合的长度,表示需要匹配的组合最小数量 + *** #### func WithMatcherLength(length int) MatcherOption[T] > 通过匹配长度的组合创建匹配器 > - length: 组合的长度,表示需要匹配的组合数量 + *** #### func WithMatcherMostLength(length int) MatcherOption[T] > 通过匹配最大长度的组合创建匹配器 > - length: 组合的长度,表示需要匹配的组合最大数量 + *** #### func WithMatcherIntervalLength(min int, max int) MatcherOption[T] > 通过匹配长度区间的组合创建匹配器 > - min: 组合的最小长度,表示需要匹配的组合最小数量 > - max: 组合的最大长度,表示需要匹配的组合最大数量 + *** #### func WithMatcherContinuity(getIndex func (item T) Index) MatcherOption[T] > 通过匹配连续的组合创建匹配器 > - index: 用于获取组合中元素的索引值,用于判断是否连续 + *** #### func WithMatcherSame(count int, getType func (item T) E) MatcherOption[T] > 通过匹配相同的组合创建匹配器 > - count: 组合中相同元素的数量,当 count <= 0 时,表示相同元素的数量不限 > - getType: 用于获取组合中元素的类型,用于判断是否相同 + *** #### func WithMatcherNCarryM(n int, m int, getType func (item T) E) MatcherOption[T] @@ -122,6 +135,7 @@ combination 包提供了一些实用的组合函数。 > - n: 组合中元素的数量,表示需要匹配的组合数量,n 的类型需要全部相同 > - m: 组合中元素的数量,表示需要匹配的组合数量,m 的类型需要全部相同 > - getType: 用于获取组合中元素的类型,用于判断是否相同 + *** #### func WithMatcherNCarryIndependentM(n int, m int, getType func (item T) E) MatcherOption[T] @@ -129,70 +143,87 @@ combination 包提供了一些实用的组合函数。 > - n: 组合中元素的数量,表示需要匹配的组合数量,n 的类型需要全部相同 > - m: 组合中元素的数量,表示需要匹配的组合数量,m 的类型无需全部相同 > - getType: 用于获取组合中元素的类型,用于判断是否相同 + *** #### func NewValidator(options ...ValidatorOption[T]) *Validator[T] > 创建一个新的校验器 + *** #### func WithValidatorHandle(handle func (items []T) bool) ValidatorOption[T] > 通过特定的验证函数对组合进行验证 + *** #### func WithValidatorHandleLength(length int) ValidatorOption[T] > 校验组合的长度是否符合要求 + *** #### func WithValidatorHandleLengthRange(min int, max int) ValidatorOption[T] > 校验组合的长度是否在指定的范围内 + *** #### func WithValidatorHandleLengthMin(min int) ValidatorOption[T] > 校验组合的长度是否大于等于指定的最小值 + *** #### func WithValidatorHandleLengthMax(max int) ValidatorOption[T] > 校验组合的长度是否小于等于指定的最大值 + *** #### func WithValidatorHandleLengthNot(length int) ValidatorOption[T] > 校验组合的长度是否不等于指定的值 + *** #### func WithValidatorHandleTypeLength(length int, getType func (item T) E) ValidatorOption[T] > 校验组合成员类型数量是否为指定的值 + *** #### func WithValidatorHandleTypeLengthRange(min int, max int, getType func (item T) E) ValidatorOption[T] > 校验组合成员类型数量是否在指定的范围内 + *** #### func WithValidatorHandleTypeLengthMin(min int, getType func (item T) E) ValidatorOption[T] > 校验组合成员类型数量是否大于等于指定的最小值 + *** #### func WithValidatorHandleTypeLengthMax(max int, getType func (item T) E) ValidatorOption[T] > 校验组合成员类型数量是否小于等于指定的最大值 + *** #### func WithValidatorHandleTypeLengthNot(length int, getType func (item T) E) ValidatorOption[T] > 校验组合成员类型数量是否不等于指定的值 + *** #### func WithValidatorHandleContinuous(getIndex func (item T) Index) ValidatorOption[T] > 校验组合成员是否连续 + *** #### func WithValidatorHandleContinuousNot(getIndex func (item T) Index) ValidatorOption[T] > 校验组合成员是否不连续 + *** #### func WithValidatorHandleGroupContinuous(getType func (item T) E, getIndex func (item T) Index) ValidatorOption[T] > 校验组合成员是否能够按类型分组并且连续 + *** #### func WithValidatorHandleGroupContinuousN(n int, getType func (item T) E, getIndex func (item T) Index) ValidatorOption[T] > 校验组合成员是否能够按分组为 n 组类型并且连续 + *** #### func WithValidatorHandleNCarryM(n int, m int, getType func (item T) E) ValidatorOption[T] @@ -200,6 +231,7 @@ combination 包提供了一些实用的组合函数。 > - n: 组合中元素的数量,表示需要匹配的组合数量,n 的类型需要全部相同 > - m: 组合中元素的数量,表示需要匹配的组合数量,m 的类型需要全部相同 > - getType: 用于获取组合中元素的类型,用于判断是否相同 + *** #### func WithValidatorHandleNCarryIndependentM(n int, m int, getType func (item T) E) ValidatorOption[T] @@ -207,8 +239,9 @@ combination 包提供了一些实用的组合函数。 > - n: 组合中元素的数量,表示需要匹配的组合数量,n 的类型需要全部相同 > - m: 组合中元素的数量,表示需要匹配的组合数量,m 的类型无需全部相同 > - getType: 用于获取组合中元素的类型,用于判断是否相同 + *** -### Combination +### Combination `STRUCT` 用于从多个匹配器内提取组合的数据结构 ```go type Combination[T Item] struct { @@ -234,21 +267,54 @@ type Combination[T Item] struct { *** #### func (*Combination) Best(items []T) (name string, result []T) > 从一组数据中提取符合匹配器规则的最佳组合 +
+查看 / 收起单元测试 + + +```go + +func TestCombination_Best(t *testing.T) { + combine := combination.NewCombination(combination.WithEvaluation(func(items []*Poker) float64 { + var total float64 + for _, item := range items { + total += float64(item.Point) + } + return total + })) + combine.NewMatcher("炸弹", combination.WithMatcherSame[*Poker, int](4, func(item *Poker) int { + return item.Point + })).NewMatcher("三带一", combination.WithMatcherNCarryM[*Poker, int](3, 1, func(item *Poker) int { + return item.Point + })) + var cards = []*Poker{{Point: 2, Color: 1}, {Point: 2, Color: 2}, {Point: 2, Color: 3}, {Point: 3, Color: 4}, {Point: 4, Color: 1}, {Point: 4, Color: 2}, {Point: 5, Color: 3}, {Point: 6, Color: 4}, {Point: 7, Color: 1}, {Point: 8, Color: 2}, {Point: 9, Color: 3}, {Point: 10, Color: 4}, {Point: 11, Color: 1}, {Point: 12, Color: 2}, {Point: 13, Color: 3}, {Point: 10, Color: 3}, {Point: 11, Color: 2}, {Point: 12, Color: 1}, {Point: 13, Color: 4}, {Point: 10, Color: 2}} + name, result := combine.Worst(cards) + fmt.Println("best:", name) + for _, item := range result { + fmt.Println(item) + } +} + +``` + + +
+ + *** #### func (*Combination) Worst(items []T) (name string, result []T) > 从一组数据中提取符合匹配器规则的最差组合 *** -### Option +### Option `STRUCT` 组合器选项 ```go -type Option[T Item] struct{} +type Option[T Item] func(*Combination[T]) ``` -### Item +### Item `INTERFACE` ```go -type Item struct{} +type Item interface{} ``` -### Matcher +### Matcher `STRUCT` 用于从一组数据内提取组合的数据结构 ```go type Matcher[T Item] struct { @@ -269,12 +335,12 @@ type Matcher[T Item] struct { #### func (*Matcher) Worst(items []T) []T > 从一组数据中提取符筛选器规则的最差组合 *** -### MatcherOption +### MatcherOption `STRUCT` 匹配器选项 ```go -type MatcherOption[T Item] struct{} +type MatcherOption[T Item] func(matcher *Matcher[T]) ``` -### Validator +### Validator `STRUCT` 用于对组合进行验证的校验器 ```go type Validator[T Item] struct { @@ -283,9 +349,41 @@ type Validator[T Item] struct { ``` #### func (*Validator) Validate(items []T) bool > 校验组合是否符合要求 -*** -### ValidatorOption +
+查看 / 收起单元测试 + ```go -type ValidatorOption[T Item] struct{} + +func TestValidator_Validate(t *testing.T) { + v := combination.NewValidator[*Card](combination.WithValidatorHandleContinuous[*Card, int](func(item *Card) int { + switch item.Point { + case "A": + return 1 + case "2", "3", "4", "5", "6", "7", "8", "9", "10": + return super.StringToInt(item.Point) + case "J": + return 11 + case "Q": + return 12 + case "K": + return 13 + } + return -1 + }), combination.WithValidatorHandleLength[*Card](3)) + cards := []*Card{{Point: "2", Color: "Spade"}, {Point: "4", Color: "Heart"}, {Point: "3", Color: "Diamond"}} + fmt.Println(v.Validate(cards)) +} + +``` + + +
+ + +*** +### ValidatorOption `STRUCT` + +```go +type ValidatorOption[T Item] func(validator *Validator[T]) ``` diff --git a/utils/compress/README.md b/utils/compress/README.md index 2e45e71..3b76e37 100644 --- a/utils/compress/README.md +++ b/utils/compress/README.md @@ -1,19 +1,20 @@ # Compress -compress 提供了一些用于压缩和解压缩数据的函数。 - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/compress) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +compress 提供了一些用于压缩和解压缩数据的函数。 + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[GZipCompress](#GZipCompress)|对数据进行GZip压缩,返回bytes.Buffer和错误信息 |[GZipUnCompress](#GZipUnCompress)|对已进行GZip压缩的数据进行解压缩,返回字节数组及错误信息 @@ -23,35 +24,35 @@ compress 提供了一些用于压缩和解压缩数据的函数。 |[ZIPUnCompress](#ZIPUnCompress)|对已进行ZIP压缩的数据进行解压缩,返回字节数组及错误信息 -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 #### func GZipCompress(data []byte) bytes.Buffer, error > 对数据进行GZip压缩,返回bytes.Buffer和错误信息 + *** #### func GZipUnCompress(dataByte []byte) []byte, error > 对已进行GZip压缩的数据进行解压缩,返回字节数组及错误信息 + *** #### func TARCompress(data []byte) bytes.Buffer, error > 对数据进行TAR压缩,返回bytes.Buffer和错误信息 + *** #### func TARUnCompress(dataByte []byte) []byte, error > 对已进行TAR压缩的数据进行解压缩,返回字节数组及错误信息 + *** #### func ZIPCompress(data []byte) bytes.Buffer, error > 对数据进行ZIP压缩,返回bytes.Buffer和错误信息 + *** #### func ZIPUnCompress(dataByte []byte) []byte, error > 对已进行ZIP压缩的数据进行解压缩,返回字节数组及错误信息 + *** diff --git a/utils/crypto/README.md b/utils/crypto/README.md index 3d6498a..f3bba8c 100644 --- a/utils/crypto/README.md +++ b/utils/crypto/README.md @@ -1,19 +1,20 @@ # Crypto - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/crypto) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[EncryptBase64](#EncryptBase64)|对数据进行Base64编码 |[DecodedBase64](#DecodedBase64)|对数据进行Base64解码 @@ -27,51 +28,55 @@ |[DecodedSHA256](#DecodedSHA256)|对字节数组进行SHA256加密并返回其结果。 -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 #### func EncryptBase64(data []byte) string > 对数据进行Base64编码 + *** #### func DecodedBase64(data string) []byte, error > 对数据进行Base64解码 + *** #### func EncryptCRC32(str string) uint32 > 对字符串进行CRC加密并返回其结果。 + *** #### func DecodedCRC32(data []byte) uint32 > 对字节数组进行CRC加密并返回其结果。 + *** #### func EncryptMD5(str string) string > 对字符串进行MD5加密并返回其结果。 + *** #### func DecodedMD5(data []byte) string > 对字节数组进行MD5加密并返回其结果。 + *** #### func EncryptSHA1(str string) string > 对字符串进行SHA1加密并返回其结果。 + *** #### func DecodedSHA1(data []byte) string > 对字节数组进行SHA1加密并返回其结果。 + *** #### func EncryptSHA256(str string) string > 对字符串进行SHA256加密并返回其结果。 + *** #### func DecodedSHA256(data []byte) string > 对字节数组进行SHA256加密并返回其结果。 + *** diff --git a/utils/deck/README.md b/utils/deck/README.md index 1505b51..22ab0c8 100644 --- a/utils/deck/README.md +++ b/utils/deck/README.md @@ -1,44 +1,49 @@ # Deck -deck 包中的内容用于针对一堆内容的管理,适用但不限于牌堆、麻将牌堆等情况。 - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/deck) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +deck 包中的内容用于针对一堆内容的管理,适用但不限于牌堆、麻将牌堆等情况。 + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewDeck](#NewDeck)|创建一个新的甲板 |[NewGroup](#NewGroup)|创建一个新的组 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Deck](#deck)|甲板,用于针对一堆 Group 进行管理的数据结构 -|[Group](#group)|甲板中的组,用于针对一堆内容进行管理的数据结构 -|[Item](#item)|甲板成员 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Deck](#deck)|甲板,用于针对一堆 Group 进行管理的数据结构 +|`STRUCT`|[Group](#group)|甲板中的组,用于针对一堆内容进行管理的数据结构 +|`INTERFACE`|[Item](#item)|甲板成员
+*** +## 详情信息 #### func NewDeck() *Deck[I] > 创建一个新的甲板 + *** #### func NewGroup(guid int64, fillHandle func (guid int64) []I) *Group[I] > 创建一个新的组 + *** -### Deck +### Deck `STRUCT` 甲板,用于针对一堆 Group 进行管理的数据结构 ```go type Deck[I Item] struct { @@ -67,7 +72,7 @@ type Deck[I Item] struct { #### func (*Deck) GetPrev(guid int64) *Group[I] > 获取特定组的上一个组 *** -### Group +### Group `STRUCT` 甲板中的组,用于针对一堆内容进行管理的数据结构 ```go type Group[I Item] struct { @@ -121,8 +126,8 @@ type Group[I Item] struct { #### func (*Group) GetItem(index int) I > 获取组中的指定内容 *** -### Item +### Item `INTERFACE` 甲板成员 ```go -type Item struct{} +type Item interface{} ``` diff --git a/utils/file/README.md b/utils/file/README.md index a2bd5c5..cd74af9 100644 --- a/utils/file/README.md +++ b/utils/file/README.md @@ -1,19 +1,20 @@ # File - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/file) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[PathExist](#PathExist)|路径是否存在 |[IsDir](#IsDir)|路径是否是文件夹 @@ -28,57 +29,60 @@ |[FindLineChunksByOffset](#FindLineChunksByOffset)|该函数与 FindLineChunks 类似,不同的是该函数可以指定 offset 从指定位置开始读取文件 -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 #### func PathExist(path string) bool, error > 路径是否存在 + *** #### func IsDir(path string) bool, error > 路径是否是文件夹 + *** #### func WriterFile(filePath string, content []byte) error > 向特定文件写入内容 + *** #### func ReadOnce(filePath string) []byte, error > 单次读取文件 > - 一次性对整个文件进行读取,小文件读取可以很方便的一次性将文件内容读取出来,而大文件读取会造成性能影响。 + *** #### func ReadBlockHook(filePath string, bufferSize int, hook func (data []byte)) error > 分块读取文件 > - 将filePath路径对应的文件数据并将读到的每一部分传入hook函数中,当过程中如果产生错误则会返回error。 > - 分块读取可以在读取速度和内存消耗之间有一个很好的平衡。 + *** #### func ReadLine(filePath string, hook func (line string)) error > 分行读取文件 > - 将filePath路径对应的文件数据并将读到的每一行传入hook函数中,当过程中如果产生错误则会返回error。 + *** #### func LineCount(filePath string) int > 统计文件行数 + *** #### func Paths(dir string) []string > 获取指定目录下的所有文件路径 > - 包括了子目录下的文件 > - 不包含目录 + *** #### func ReadLineWithParallel(filename string, chunkSize int64, handlerFunc func ( string), start ...int64) (n int64, err error) > 并行的分行读取文件并行处理,处理过程中会将每一行的内容传入 handlerFunc 中进行处理 > - 由于是并行处理,所以处理过程中的顺序是不确定的。 > - 可通过 start 参数指定开始读取的位置,如果不指定则从文件开头开始读取。 + *** #### func FindLineChunks(file *os.File, chunkSize int64) [][2]int64 @@ -86,8 +90,10 @@ > - 使用该函数得到的分块是完整的行,不会出现行被分割的情况 > - 当过程中发生错误将会发生 panic > - 返回值的成员是一个长度为 2 的数组,第一个元素是分块的起始位置,第二个元素是分块的结束位置 + *** #### func FindLineChunksByOffset(file *os.File, offset int64, chunkSize int64) [][2]int64 > 该函数与 FindLineChunks 类似,不同的是该函数可以指定 offset 从指定位置开始读取文件 + *** diff --git a/utils/fsm/README.md b/utils/fsm/README.md index 54c7ae9..a2bdc9d 100644 --- a/utils/fsm/README.md +++ b/utils/fsm/README.md @@ -1,19 +1,20 @@ # Fsm - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/fsm) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewFSM](#NewFSM)|创建一个新的状态机 |[WithEnterBeforeEvent](#WithEnterBeforeEvent)|设置状态进入前的回调 @@ -23,44 +24,52 @@ |[WithExitAfterEvent](#WithExitAfterEvent)|设置状态退出后的回调 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[FSM](#fsm)|状态机 -|[Option](#option)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[FSM](#fsm)|状态机 +|`STRUCT`|[Option](#option)|暂无描述...
+*** +## 详情信息 #### func NewFSM(data Data) *FSM[State, Data] > 创建一个新的状态机 + *** #### func WithEnterBeforeEvent(fn func (state *FSM[State, Data])) Option[State, Data] > 设置状态进入前的回调 > - 在首次设置状态时,状态机本身的当前状态为零值状态 + *** #### func WithEnterAfterEvent(fn func (state *FSM[State, Data])) Option[State, Data] > 设置状态进入后的回调 + *** #### func WithUpdateEvent(fn func (state *FSM[State, Data])) Option[State, Data] > 设置状态内刷新的回调 + *** #### func WithExitBeforeEvent(fn func (state *FSM[State, Data])) Option[State, Data] > 设置状态退出前的回调 > - 该阶段状态机的状态为退出前的状态,而非新的状态 + *** #### func WithExitAfterEvent(fn func (state *FSM[State, Data])) Option[State, Data] > 设置状态退出后的回调 > - 该阶段状态机的状态为新的状态,而非退出前的状态 + *** -### FSM +### FSM `STRUCT` 状态机 ```go type FSM[State comparable, Data any] struct { @@ -75,8 +84,8 @@ type FSM[State comparable, Data any] struct { exitAfterEventHandles map[State][]func(state *FSM[State, Data]) } ``` -### Option +### Option `STRUCT` ```go -type Option[State comparable, Data any] struct{} +type Option[State comparable, Data any] func(fsm *FSM[State, Data], state State) ``` diff --git a/utils/generator/astgo/README.md b/utils/generator/astgo/README.md index de9be3a..2aa131f 100644 --- a/utils/generator/astgo/README.md +++ b/utils/generator/astgo/README.md @@ -1,42 +1,66 @@ # Astgo - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/astgo) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewPackage](#NewPackage)|暂无描述... -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Comment](#comment)|暂无描述... -|[Field](#field)|暂无描述... -|[File](#file)|暂无描述... -|[Function](#function)|暂无描述... -|[Package](#package)|暂无描述... -|[Struct](#struct)|暂无描述... -|[Type](#type)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Comment](#comment)|暂无描述... +|`STRUCT`|[Field](#field)|暂无描述... +|`STRUCT`|[File](#file)|暂无描述... +|`STRUCT`|[Function](#function)|暂无描述... +|`STRUCT`|[Package](#package)|暂无描述... +|`STRUCT`|[Struct](#struct)|暂无描述... +|`STRUCT`|[Type](#type)|暂无描述...
+*** +## 详情信息 #### func NewPackage(dir string) *Package, error + +
+查看 / 收起单元测试 + + +```go + +func TestNewPackage(t *testing.T) { + p, err := astgo.NewPackage(`/Users/kercylan/Coding.localized/Go/minotaur/server`) + if err != nil { + panic(err) + } + fmt.Println(string(super.MarshalIndentJSON(p, "", " "))) +} + +``` + + +
+ + *** -### Comment +### Comment `STRUCT` ```go type Comment struct { @@ -44,7 +68,7 @@ type Comment struct { Clear []string } ``` -### Field +### Field `STRUCT` ```go type Field struct { @@ -54,7 +78,7 @@ type Field struct { Comments *Comment } ``` -### File +### File `STRUCT` ```go type File struct { @@ -68,10 +92,11 @@ type File struct { ``` #### func (*File) Package() string *** -### Function +### Function `STRUCT` ```go type Function struct { + decl *ast.FuncDecl Name string Internal bool Generic []*Field @@ -85,14 +110,17 @@ type Function struct { Test bool } ``` -### Package +#### func (*Function) Code() string +*** +### Package `STRUCT` ```go type Package struct { - Dir string - Name string - Dirs []string - Files []*File + Dir string + Name string + Dirs []string + Files []*File + Functions map[string]*Function } ``` #### func (*Package) StructFunc(name string) []*Function @@ -103,19 +131,27 @@ type Package struct { *** #### func (*Package) FileComments() *Comment *** -### Struct +#### func (*Package) GetUnitTest(f *Function) *Function +*** +#### func (*Package) GetExampleTest(f *Function) *Function +*** +#### func (*Package) GetBenchmarkTest(f *Function) *Function +*** +### Struct `STRUCT` ```go type Struct struct { - Name string - Internal bool - Comments *Comment - Generic []*Field - Fields []*Field - Test bool + Name string + Internal bool + Interface bool + Comments *Comment + Generic []*Field + Fields []*Field + Type *Type + Test bool } ``` -### Type +### Type `STRUCT` ```go type Type struct { diff --git a/utils/generator/astgo/field.go b/utils/generator/astgo/field.go index 9cb741f..1c00789 100644 --- a/utils/generator/astgo/field.go +++ b/utils/generator/astgo/field.go @@ -1,6 +1,7 @@ package astgo import ( + "fmt" "go/ast" ) @@ -14,6 +15,9 @@ func newField(field *ast.Field) []*Field { } else { var fs []*Field for _, name := range field.Names { + if name.String() == "Format" { + fmt.Println() + } fs = append(fs, &Field{ Anonymous: false, Name: name.String(), diff --git a/utils/generator/astgo/function.go b/utils/generator/astgo/function.go index 222b010..feb9799 100644 --- a/utils/generator/astgo/function.go +++ b/utils/generator/astgo/function.go @@ -1,12 +1,16 @@ package astgo import ( + "bytes" "go/ast" + "go/format" + "go/token" "strings" ) func newFunction(astFunc *ast.FuncDecl) *Function { f := &Function{ + decl: astFunc, Name: astFunc.Name.String(), Comments: newComment(astFunc.Doc), } @@ -37,6 +41,7 @@ func newFunction(astFunc *ast.FuncDecl) *Function { } type Function struct { + decl *ast.FuncDecl Name string // 函数名 Internal bool // 内部函数 Generic []*Field // 泛型定义 @@ -49,3 +54,11 @@ type Function struct { IsBenchmark bool // 是否为基准测试 Test bool // 是否为测试函数 } + +func (f *Function) Code() string { + var bb bytes.Buffer + if err := format.Node(&bb, token.NewFileSet(), f.decl); err != nil { + panic(err) + } + return bb.String() + "\n" +} diff --git a/utils/generator/astgo/package.go b/utils/generator/astgo/package.go index 0086feb..681564d 100644 --- a/utils/generator/astgo/package.go +++ b/utils/generator/astgo/package.go @@ -2,12 +2,14 @@ package astgo import ( "errors" + "fmt" + "github.com/kercylan98/minotaur/utils/str" "os" "path/filepath" ) func NewPackage(dir string) (*Package, error) { - pkg := &Package{Dir: dir} + pkg := &Package{Dir: dir, Functions: map[string]*Function{}} fs, err := os.ReadDir(dir) if err != nil { return nil, err @@ -26,7 +28,14 @@ func NewPackage(dir string) (*Package, error) { continue } pkg.Files = append(pkg.Files, f) - + for _, function := range f.Functions { + function := function + key := function.Name + if function.Struct != nil { + key = fmt.Sprintf("%s.%s", function.Struct.Name, key) + } + pkg.Functions[key] = function + } } if len(pkg.Files) == 0 { return nil, errors.New("not found go ext file") @@ -37,10 +46,11 @@ func NewPackage(dir string) (*Package, error) { } type Package struct { - Dir string - Name string - Dirs []string - Files []*File + Dir string + Name string + Dirs []string + Files []*File + Functions map[string]*Function } func (p *Package) StructFunc(name string) []*Function { @@ -92,3 +102,24 @@ func (p *Package) FileComments() *Comment { } return comment } + +func (p *Package) GetUnitTest(f *Function) *Function { + if f.Struct == nil { + return p.Functions[fmt.Sprintf("Test%s", str.FirstUpper(f.Name))] + } + return p.Functions[fmt.Sprintf("Test%s_%s", f.Struct.Type.Name, f.Name)] +} + +func (p *Package) GetExampleTest(f *Function) *Function { + if f.Struct == nil { + return p.Functions[fmt.Sprintf("Example%s", str.FirstUpper(f.Name))] + } + return p.Functions[fmt.Sprintf("Example%s_%s", f.Struct.Type.Name, f.Name)] +} + +func (p *Package) GetBenchmarkTest(f *Function) *Function { + if f.Struct == nil { + return p.Functions[fmt.Sprintf("Benchmark%s", str.FirstUpper(f.Name))] + } + return p.Functions[fmt.Sprintf("Benchmark%s_%s", f.Struct.Type.Name, f.Name)] +} diff --git a/utils/generator/astgo/struct.go b/utils/generator/astgo/struct.go index 56a3553..263e9d1 100644 --- a/utils/generator/astgo/struct.go +++ b/utils/generator/astgo/struct.go @@ -17,20 +17,33 @@ func newStruct(astGen *ast.GenDecl) *Struct { } } - t, ok := astTypeSpec.Type.(*ast.StructType) - if ok && t.Fields != nil { - for _, field := range t.Fields.List { - s.Fields = append(s.Fields, newField(field)...) + switch ts := astTypeSpec.Type.(type) { + case *ast.StructType: + if ts.Fields != nil { + for _, field := range ts.Fields.List { + s.Fields = append(s.Fields, newField(field)...) + } } + case *ast.InterfaceType: + s.Interface = true + if ts.Methods != nil { + for _, field := range ts.Methods.List { + s.Fields = append(s.Fields, newField(field)...) + } + } + default: + s.Type = newType(ts) } return s } type Struct struct { - Name string // 结构体名称 - Internal bool // 内部结构体 - Comments *Comment // 注释 - Generic []*Field // 泛型类型 - Fields []*Field // 结构体字段 - Test bool // 是否是测试结构体 + Name string // 结构体名称 + Internal bool // 内部结构体 + Interface bool // 接口定义 + Comments *Comment // 注释 + Generic []*Field // 泛型类型 + Fields []*Field // 结构体字段 + Type *Type // 和结构体字段二选一,处理类似 type a string 的情况 + Test bool // 是否是测试结构体 } diff --git a/utils/generator/astgo/type.go b/utils/generator/astgo/type.go index a03b498..437bc57 100644 --- a/utils/generator/astgo/type.go +++ b/utils/generator/astgo/type.go @@ -65,7 +65,13 @@ func newType(expr ast.Expr) *Type { return "(" + f + ")" } return f - }(), handler(e.Results)))) + }(), func() string { + f := handler(e.Results) + if e.Results != nil && len(e.Results.List) >= 2 && !strings.HasSuffix(f, "(") { + return "(" + f + ")" + } + return f + }()))) case *ast.InterfaceType: str.WriteString("interface {") if e.Methods != nil && len(e.Methods.List) > 0 { diff --git a/utils/generator/genreadme/README.md b/utils/generator/genreadme/README.md index 138f072..1fb88c4 100644 --- a/utils/generator/genreadme/README.md +++ b/utils/generator/genreadme/README.md @@ -1,36 +1,40 @@ # Genreadme - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/genreadme) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[New](#New)|暂无描述... -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Builder](#builder)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Builder](#builder)|暂无描述...
+*** +## 详情信息 #### func New(pkgDirPath string, output string) *Builder, error + *** -### Builder +### Builder `STRUCT` ```go type Builder struct { @@ -40,4 +44,35 @@ type Builder struct { } ``` #### func (*Builder) Generate() error +
+查看 / 收起单元测试 + + +```go + +func TestBuilder_Generate(t *testing.T) { + filepath.Walk("/Users/kercylan/Coding.localized/Go/minotaur", func(path string, info fs.FileInfo, err error) error { + if !info.IsDir() { + return nil + } + if strings.Contains(strings.TrimPrefix(path, "/Users/kercylan/Coding.localized/Go/minotaur"), ".") { + return nil + } + b, err := New(path, filepath.Join(path, "README.md")) + if err != nil { + return nil + } + if err = b.Generate(); err != nil { + panic(err) + } + return nil + }) +} + +``` + + +
+ + *** diff --git a/utils/generator/genreadme/builder.go b/utils/generator/genreadme/builder.go index c9898b2..3ee24e4 100644 --- a/utils/generator/genreadme/builder.go +++ b/utils/generator/genreadme/builder.go @@ -42,31 +42,15 @@ func (b *Builder) Generate() error { func (b *Builder) genHeader() { b.title(1, str.FirstUpper(b.p.Name)) b.newLine() - b.newLine(b.p.FileComments().Clear...).newLine() b.newLine(fmt.Sprintf(`[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/%s)`, b.p.Name)) b.newLine(fmt.Sprintf(`![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat)`)) + b.newLine().newLine(b.p.FileComments().Clear...).newLine() b.newLine() } func (b *Builder) genMenus() { - b.title(2, "目录") - b.newLine("列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️") - b.detailsStart("展开 / 折叠目录") - - b.quote("包级函数定义").newLine() - b.tableCel("函数", "描述") - for _, function := range b.p.PackageFunc() { - if function.Test || function.Internal { - continue - } - b.tableRow( - fmt.Sprintf("[%s](#%s)", function.Name, function.Name), - collection.FindFirstOrDefaultInSlice(function.Comments.Clear, "暂无描述..."), - ) - } - b.newLine().newLine() - b.quote("结构体定义").newLine() - b.tableCel("结构体", "描述") + packageFunction := b.p.PackageFunc() + var structList []*astgo.Struct for _, f := range b.p.Files { if strings.HasSuffix(f.FilePath, "_test.go") { continue @@ -75,16 +59,53 @@ func (b *Builder) genMenus() { if structInfo.Test || structInfo.Internal { continue } + structList = append(structList, structInfo) + } + } + + if len(packageFunction)+len(structList) > 0 { + b.title(2, "目录导航") + b.newLine("列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️") + b.detailsStart("展开 / 折叠目录导航") + } + + if len(packageFunction) > 0 { + b.quote("包级函数定义").newLine() + b.tableCel("函数名称", "描述") + for _, function := range packageFunction { + if function.Test || function.Internal { + continue + } b.tableRow( + fmt.Sprintf("[%s](#%s)", function.Name, function.Name), + collection.FindFirstOrDefaultInSlice(function.Comments.Clear, "暂无描述..."), + ) + } + b.newLine().newLine() + } + + if len(structList) > 0 { + b.quote("类型定义").newLine() + b.tableCel("类型", "名称", "描述") + for _, structInfo := range structList { + if structInfo.Test || structInfo.Internal { + continue + } + b.tableRow( + super.If(structInfo.Interface, "`INTERFACE`", "`STRUCT`"), fmt.Sprintf("[%s](#%s)", structInfo.Name, strings.ToLower(structInfo.Name)), collection.FindFirstOrDefaultInSlice(structInfo.Comments.Clear, "暂无描述..."), ) } + b.detailsEnd() } - b.detailsEnd() + + b.newLine("***") } func (b *Builder) genStructs() { + b.title(2, "详情信息") + var funcHandler = func(params []*astgo.Field) string { var s string var brackets bool @@ -122,6 +143,20 @@ func (b *Builder) genStructs() { for _, comment := range function.Comments.Clear { b.quote(comment) } + b.newLine() + if example := b.p.GetExampleTest(function); example != nil { + b.newLine("示例代码:", "```go\n", example.Code(), "```\n") + } + if unitTest := b.p.GetUnitTest(function); unitTest != nil { + b.detailsStart("查看 / 收起单元测试") + b.newLine("```go\n", unitTest.Code(), "```\n") + b.detailsEnd() + } + if benchmarkTest := b.p.GetBenchmarkTest(function); benchmarkTest != nil { + b.detailsStart("查看 / 收起基准测试") + b.newLine("```go\n", benchmarkTest.Code(), "```\n") + b.detailsEnd() + } b.newLine("***") } @@ -130,10 +165,10 @@ func (b *Builder) genStructs() { if structInfo.Internal || structInfo.Test { continue } - b.title(3, structInfo.Name) + b.title(3, fmt.Sprintf("%s `%s`", structInfo.Name, super.If(structInfo.Interface, "INTERFACE", "STRUCT"))) b.newLine(structInfo.Comments.Clear...) b.newLine("```go") - structDefine := fmt.Sprintf("type %s struct {%s}", + structDefine := fmt.Sprintf("type %s %s", func() string { var sb strings.Builder sb.WriteString(structInfo.Name) @@ -150,11 +185,27 @@ func (b *Builder) genStructs() { }(), func() string { var sb strings.Builder - if len(structInfo.Fields) > 0 { - sb.WriteString("\n") - } - for _, field := range structInfo.Fields { - sb.WriteString(fmt.Sprintf("\t%s %s\n", field.Name, field.Type.Sign)) + if structInfo.Type == nil { + var head = "struct" + if structInfo.Interface { + head = "interface" + } + sb.WriteString(head + " {") + if len(structInfo.Fields) > 0 { + sb.WriteString("\n") + } + if structInfo.Interface { + for _, field := range structInfo.Fields { + sb.WriteString(fmt.Sprintf("\t%s %s\n", field.Name, strings.TrimPrefix(field.Type.Sign, "func"))) + } + } else { + for _, field := range structInfo.Fields { + sb.WriteString(fmt.Sprintf("\t%s %s\n", field.Name, field.Type.Sign)) + } + } + sb.WriteString("}") + } else { + sb.WriteString(structInfo.Type.Sign) } return sb.String() }()) @@ -183,11 +234,26 @@ func (b *Builder) genStructs() { }(), funcHandler(function.Results), ))) - //b.newLine(fmt.Sprintf(``, function.Name)) b.quote() for _, comment := range function.Comments.Clear { b.quote(comment) } + if function.Name == "Write" { + fmt.Println() + } + if example := b.p.GetExampleTest(function); example != nil { + b.newLine("示例代码:", "```go\n", example.Code(), "```\n") + } + if unitTest := b.p.GetUnitTest(function); unitTest != nil { + b.detailsStart("查看 / 收起单元测试") + b.newLine("```go\n", unitTest.Code(), "```\n") + b.detailsEnd() + } + if benchmarkTest := b.p.GetBenchmarkTest(function); benchmarkTest != nil { + b.detailsStart("查看 / 收起基准测试") + b.newLine("```go\n", benchmarkTest.Code(), "```\n") + b.detailsEnd() + } b.newLine("***") } } @@ -243,7 +309,7 @@ func (b *Builder) tableRow(str ...string) *Builder { } func (b *Builder) detailsStart(title string) *Builder { - return b.newLine("
", ""+title+"", ""+title+"").newLine().newLine() } func (b *Builder) detailsEnd() *Builder { diff --git a/utils/generic/README.md b/utils/generic/README.md index 96431e8..55ef913 100644 --- a/utils/generic/README.md +++ b/utils/generic/README.md @@ -1,134 +1,163 @@ # Generic +[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/generic) +![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) + generic 目的在于提供一组基于泛型的用于处理通用功能的函数和数据结构。该包旨在简化通用功能的实现,并提供一致的接口和易于使用的功能。 主要特性: - 通用功能:generic 包支持处理各种通用功能,如数据结构操作、算法实现和常用工具等。您可以使用这些功能来解决各种通用问题,并提高代码的复用性和可维护性。 -[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/generic) -![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航
> 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[IsNil](#IsNil)|检查指定的值是否为 nil |[IsAllNil](#IsAllNil)|检查指定的值是否全部为 nil |[IsHasNil](#IsHasNil)|检查指定的值是否存在 nil -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[IdR](#idr)|暂无描述... -|[IDR](#idr)|暂无描述... -|[IdW](#idw)|暂无描述... -|[IDW](#idw)|暂无描述... -|[IdR2W](#idr2w)|暂无描述... -|[IDR2W](#idr2w)|暂无描述... -|[Ordered](#ordered)|可排序类型 -|[Number](#number)|数字类型 -|[SignedNumber](#signednumber)|有符号数字类型 -|[Integer](#integer)|整数类型 -|[Signed](#signed)|有符号整数类型 -|[Unsigned](#unsigned)|无符号整数类型 -|[UnsignedNumber](#unsignednumber)|无符号数字类型 -|[Float](#float)|浮点类型 -|[Basic](#basic)|基本类型 +|类型|名称|描述 +|:--|:--|:-- +|`INTERFACE`|[IdR](#idr)|暂无描述... +|`INTERFACE`|[IDR](#idr)|暂无描述... +|`INTERFACE`|[IdW](#idw)|暂无描述... +|`INTERFACE`|[IDW](#idw)|暂无描述... +|`INTERFACE`|[IdR2W](#idr2w)|暂无描述... +|`INTERFACE`|[IDR2W](#idr2w)|暂无描述... +|`INTERFACE`|[Ordered](#ordered)|可排序类型 +|`INTERFACE`|[Number](#number)|数字类型 +|`INTERFACE`|[SignedNumber](#signednumber)|有符号数字类型 +|`INTERFACE`|[Integer](#integer)|整数类型 +|`INTERFACE`|[Signed](#signed)|有符号整数类型 +|`INTERFACE`|[Unsigned](#unsigned)|无符号整数类型 +|`INTERFACE`|[UnsignedNumber](#unsignednumber)|无符号数字类型 +|`INTERFACE`|[Float](#float)|浮点类型 +|`INTERFACE`|[Basic](#basic)|基本类型
+*** +## 详情信息 #### func IsNil(v V) bool > 检查指定的值是否为 nil + *** #### func IsAllNil(v ...V) bool > 检查指定的值是否全部为 nil + *** #### func IsHasNil(v ...V) bool > 检查指定的值是否存在 nil + *** -### IdR +### IdR `INTERFACE` ```go -type IdR[ID comparable] struct{} +type IdR[ID comparable] interface { + GetId() ID +} ``` -### IDR +### IDR `INTERFACE` ```go -type IDR[ID comparable] struct{} +type IDR[ID comparable] interface { + GetID() ID +} ``` -### IdW +### IdW `INTERFACE` ```go -type IdW[ID comparable] struct{} +type IdW[ID comparable] interface { + SetId(id ID) +} ``` -### IDW +### IDW `INTERFACE` ```go -type IDW[ID comparable] struct{} +type IDW[ID comparable] interface { + SetID(id ID) +} ``` -### IdR2W +### IdR2W `INTERFACE` ```go -type IdR2W[ID comparable] struct{} +type IdR2W[ID comparable] interface { + IdR[ID] + IdW[ID] +} ``` -### IDR2W +### IDR2W `INTERFACE` ```go -type IDR2W[ID comparable] struct{} +type IDR2W[ID comparable] interface { + IDR[ID] + IDW[ID] +} ``` -### Ordered +### Ordered `INTERFACE` 可排序类型 ```go -type Ordered struct{} +type Ordered interface { +} ``` -### Number +### Number `INTERFACE` 数字类型 ```go -type Number struct{} +type Number interface { +} ``` -### SignedNumber +### SignedNumber `INTERFACE` 有符号数字类型 ```go -type SignedNumber struct{} +type SignedNumber interface { +} ``` -### Integer +### Integer `INTERFACE` 整数类型 ```go -type Integer struct{} +type Integer interface { +} ``` -### Signed +### Signed `INTERFACE` 有符号整数类型 ```go -type Signed struct{} +type Signed interface { +} ``` -### Unsigned +### Unsigned `INTERFACE` 无符号整数类型 ```go -type Unsigned struct{} +type Unsigned interface { +} ``` -### UnsignedNumber +### UnsignedNumber `INTERFACE` 无符号数字类型 ```go -type UnsignedNumber struct{} +type UnsignedNumber interface { +} ``` -### Float +### Float `INTERFACE` 浮点类型 ```go -type Float struct{} +type Float interface { +} ``` -### Basic +### Basic `INTERFACE` 基本类型 ```go -type Basic struct{} +type Basic interface { +} ``` diff --git a/utils/geometry/README.md b/utils/geometry/README.md index 796462f..cc0cdc7 100644 --- a/utils/geometry/README.md +++ b/utils/geometry/README.md @@ -1,5 +1,8 @@ # Geometry +[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/geometry) +![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) + geometry 旨在提供一组用于处理几何形状和计算几何属性的函数和数据结构。该包旨在简化几何计算的过程,并提供一致的接口和易于使用的功能。 主要特性: - 几何形状:"geometry"包支持处理各种几何形状,如点、线、多边形和圆等。您可以使用这些形状来表示和操作实际世界中的几何对象。 @@ -7,18 +10,16 @@ geometry 旨在提供一组用于处理几何形状和计算几何属性的函 - 坐标转换:"geometry"包还提供了一些函数,用于在不同坐标系之间进行转换。您可以将点从笛卡尔坐标系转换为极坐标系,或者从二维坐标系转换为三维坐标系等。 - 简化接口:该包的设计目标之一是提供简化的接口,使几何计算变得更加直观和易于使用。您可以轻松地创建和操作几何对象,而无需处理繁琐的底层细节。 -[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/geometry) -![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewCircle](#NewCircle)|通过传入圆的半径和需要的点数量,生成一个圆 |[CalcCircleCentroidDistance](#CalcCircleCentroidDistance)|计算两个圆质心距离 @@ -112,410 +113,758 @@ geometry 旨在提供一组用于处理几何形状和计算几何属性的函 |[WithShapeSearchDesc](#WithShapeSearchDesc)|通过降序的方式进行搜索 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Circle](#circle)|圆形 -|[FloorPlan](#floorplan)|平面图 -|[Direction](#direction)|方向 -|[LineSegment](#linesegment)|通过两个点表示一根线段 -|[LineSegmentCap](#linesegmentcap)|可以包含一份额外数据的线段 -|[Point](#point)|表示了一个由 x、y 坐标组成的点 -|[PointCap](#pointcap)|表示了一个由 x、y 坐标组成的点,这个点具有一个数据容量 -|[Shape](#shape)|通过多个点表示了一个形状 -|[ShapeSearchOption](#shapesearchoption)|图形搜索可选项,用于 Shape.ShapeSearch 搜索支持 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Circle](#circle)|圆形 +|`STRUCT`|[FloorPlan](#floorplan)|平面图 +|`STRUCT`|[Direction](#direction)|方向 +|`STRUCT`|[LineSegment](#linesegment)|通过两个点表示一根线段 +|`STRUCT`|[LineSegmentCap](#linesegmentcap)|可以包含一份额外数据的线段 +|`STRUCT`|[Point](#point)|表示了一个由 x、y 坐标组成的点 +|`STRUCT`|[PointCap](#pointcap)|表示了一个由 x、y 坐标组成的点,这个点具有一个数据容量 +|`STRUCT`|[Shape](#shape)|通过多个点表示了一个形状 +|`STRUCT`|[ShapeSearchOption](#shapesearchoption)|图形搜索可选项,用于 Shape.ShapeSearch 搜索支持
+*** +## 详情信息 #### func NewCircle(radius V, points int) Circle[V] > 通过传入圆的半径和需要的点数量,生成一个圆 + +示例代码: +```go + +func ExampleNewCircle() { + fmt.Println(geometry.NewCircle[float64](7, 12)) +} + +``` + *** #### func CalcCircleCentroidDistance(circle1 Circle[V], circle2 Circle[V]) V > 计算两个圆质心距离 + *** #### func GetOppositionDirection(direction Direction) Direction > 获取特定方向的对立方向 + *** #### func GetDirectionNextWithCoordinate(direction Direction, x V, y V) (nx V, ny V) > 获取特定方向上的下一个坐标 + *** #### func GetDirectionNextWithPoint(direction Direction, point Point[V]) Point[V] > 获取特定方向上的下一个坐标 + *** #### func GetDirectionNextWithPos(direction Direction, width V, pos V) V > 获取位置在特定宽度和特定方向上的下一个位置 > - 需要注意的是,在左右方向时,当下一个位置不在矩形区域内时,将会返回上一行的末位置或下一行的首位置 + *** #### func CalcDirection(x1 V, y1 V, x2 V, y2 V) Direction > 计算点2位于点1的方向 + *** #### func CalcDistanceWithCoordinate(x1 V, y1 V, x2 V, y2 V) V > 计算两点之间的距离 + *** #### func CalcDistanceWithPoint(point1 Point[V], point2 Point[V]) V > 计算两点之间的距离 + *** #### func CalcDistanceSquared(x1 V, y1 V, x2 V, y2 V) V > 计算两点之间的平方距离 > - 这个函数的主要用途是在需要计算两点之间距离的情况下,但不需要得到实际的距离值,而只需要比较距离大小。因为平方根运算相对较为耗时,所以在只需要比较大小的情况下,通常会使用平方距离。 + *** #### func CalcAngle(x1 V, y1 V, x2 V, y2 V) V > 计算点2位于点1之间的角度 + *** #### func CalcNewCoordinate(x V, y V, angle V, distance V) (newX V, newY V) > 根据给定的x、y坐标、角度和距离计算新的坐标 + *** #### func CalcRadianWithAngle(angle V) V > 根据角度 angle 计算弧度 + *** #### func CalcAngleDifference(angleA V, angleB V) V > 计算两个角度之间的最小角度差 + *** #### func CalcRayIsIntersect(x V, y V, angle V, shape Shape[V]) bool > 根据给定的位置和角度生成射线,检测射线是否与多边形发生碰撞 + *** #### func NewLineSegment(start Point[V], end Point[V]) LineSegment[V] > 创建一根线段 + *** #### func NewLineSegmentCap(start Point[V], end Point[V], data Data) LineSegmentCap[V, Data] > 创建一根包含数据的线段 + *** #### func NewLineSegmentCapWithLine(line LineSegment[V], data Data) LineSegmentCap[V, Data] > 通过已有线段创建一根包含数据的线段 + *** #### func ConvertLineSegmentGeneric(line LineSegment[V]) LineSegment[TO] > 转换线段的泛型类型为特定类型 + *** #### func PointOnLineSegmentWithCoordinate(x1 V, y1 V, x2 V, y2 V, x V, y V) bool > 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 + *** #### func PointOnLineSegmentWithPos(width V, pos1 V, pos2 V, pos V) bool > 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 + *** #### func PointOnLineSegmentWithPoint(point1 Point[V], point2 Point[V], point Point[V]) bool > 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 + *** #### func PointOnLineSegmentWithCoordinateInBounds(x1 V, y1 V, x2 V, y2 V, x V, y V) bool > 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 > - 与 PointOnLineSegmentWithCoordinate 不同的是, PointOnLineSegmentWithCoordinateInBounds 中会判断线段及点的位置是否正确 + *** #### func PointOnLineSegmentWithPosInBounds(width V, pos1 V, pos2 V, pos V) bool > 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 > - 与 PointOnLineSegmentWithPos 不同的是, PointOnLineSegmentWithPosInBounds 中会判断线段及点的位置是否正确 + *** #### func PointOnLineSegmentWithPointInBounds(point1 Point[V], point2 Point[V], point Point[V]) bool > 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 > - 与 PointOnLineSegmentWithPoint 不同的是, PointOnLineSegmentWithPointInBounds 中会判断线段及点的位置是否正确 + *** #### func CalcLineSegmentIsCollinear(line1 LineSegment[V], line2 LineSegment[V], tolerance V) bool > 检查两条线段在一个误差内是否共线 > - 共线是指两条线段在同一直线上,即它们的延长线可以重合 + *** #### func CalcLineSegmentIsOverlap(line1 LineSegment[V], line2 LineSegment[V]) (line LineSegment[V], overlap bool) > 通过对点进行排序来检查两条共线线段是否重叠,返回重叠线段 + *** #### func CalcLineSegmentIsIntersect(line1 LineSegment[V], line2 LineSegment[V]) bool > 计算两条线段是否相交 + +
+查看 / 收起单元测试 + + +```go + +func TestCalcLineSegmentIsIntersect(t *testing.T) { + line1 := geometry.NewLineSegment(geometry.NewPoint(1, 1), geometry.NewPoint(3, 5)) + line2 := geometry.NewLineSegment(geometry.NewPoint(0, 5), geometry.NewPoint(3, 6)) + fmt.Println(geometry.CalcLineSegmentIsIntersect(line1, line2)) +} + +``` + + +
+ + *** #### func CalcLineSegmentSlope(line LineSegment[V]) V > 计算线段的斜率 + *** #### func CalcLineSegmentIntercept(line LineSegment[V]) V > 计算线段的截距 + *** #### func NewPoint(x V, y V) Point[V] > 创建一个由 x、y 坐标组成的点 + +
+查看 / 收起单元测试 + + +```go + +func TestNewPoint(t *testing.T) { + p := [2]int{1, 1} + fmt.Println(PointToPos(9, p)) +} + +``` + + +
+ + *** #### func NewPointCap(x V, y V) PointCap[V, D] > 创建一个由 x、y 坐标组成的点,这个点具有一个数据容量 + *** #### func NewPointCapWithData(x V, y V, data D) PointCap[V, D] > 通过设置数据的方式创建一个由 x、y 坐标组成的点,这个点具有一个数据容量 + *** #### func NewPointCapWithPoint(point Point[V], data D) PointCap[V, D] > 通过设置数据的方式创建一个由已有坐标组成的点,这个点具有一个数据容量 + *** #### func CoordinateToPoint(x V, y V) Point[V] > 将坐标转换为x、y的坐标数组 + *** #### func CoordinateToPos(width V, x V, y V) V > 将坐标转换为二维数组的顺序位置坐标 > - 需要确保x的取值范围必须小于width,或者将会得到不正确的值 + *** #### func PointToCoordinate(position Point[V]) (x V, y V) > 将坐标数组转换为x和y坐标 + *** #### func PointToPos(width V, xy Point[V]) V > 将坐标转换为二维数组的顺序位置 > - 需要确保x的取值范围必须小于width,或者将会得到不正确的值 + *** #### func PosToCoordinate(width V, pos V) (x V, y V) > 通过宽度将一个二维数组的顺序位置转换为xy坐标 + *** #### func PosToPoint(width V, pos V) Point[V] > 通过宽度将一个二维数组的顺序位置转换为x、y的坐标数组 + *** #### func PosToCoordinateX(width V, pos V) V > 通过宽度将一个二维数组的顺序位置转换为X坐标 + *** #### func PosToCoordinateY(width V, pos V) V > 通过宽度将一个二维数组的顺序位置转换为Y坐标 + *** #### func PointCopy(point Point[V]) Point[V] > 复制一个坐标数组 + *** #### func PointToPosWithMulti(width V, points ...Point[V]) []V > 将一组坐标转换为二维数组的顺序位置 > - 需要确保x的取值范围必须小于width,或者将会得到不正确的值 + *** #### func PosToPointWithMulti(width V, positions ...V) []Point[V] > 将一组二维数组的顺序位置转换为一组数组坐标 + *** #### func PosSameRow(width V, pos1 V, pos2 V) bool > 返回两个顺序位置在同一宽度是否位于同一行 + *** #### func DoublePointToCoordinate(point1 Point[V], point2 Point[V]) (x1 V, y1 V, x2 V, y2 V) > 将两个位置转换为 x1, y1, x2, y2 的坐标进行返回 + *** #### func CalcProjectionPoint(line LineSegment[V], point Point[V]) Point[V] > 计算一个点到一条线段的最近点(即投影点)的。这个函数接收一个点和一条线段作为输入,线段由两个端点组成。 > - 该函数的主要用于需要计算一个点到一条线段的最近点的情况下 + *** #### func GetAdjacentTranslatePos(matrix []T, width P, pos P) (result []P) > 获取一个连续位置的矩阵中,特定位置相邻的最多四个平移方向(上下左右)的位置 + *** #### func GetAdjacentTranslateCoordinateXY(matrix [][]T, x P, y P) (result []Point[P]) > 获取一个基于 x、y 的二维矩阵中,特定位置相邻的最多四个平移方向(上下左右)的位置 + *** #### func GetAdjacentTranslateCoordinateYX(matrix [][]T, x P, y P) (result []Point[P]) > 获取一个基于 y、x 的二维矩阵中,特定位置相邻的最多四个平移方向(上下左右)的位置 + *** #### func GetAdjacentDiagonalsPos(matrix []T, width P, pos P) (result []P) > 获取一个连续位置的矩阵中,特定位置相邻的对角线最多四个方向的位置 + *** #### func GetAdjacentDiagonalsCoordinateXY(matrix [][]T, x P, y P) (result []Point[P]) > 获取一个基于 x、y 的二维矩阵中,特定位置相邻的对角线最多四个方向的位置 + *** #### func GetAdjacentDiagonalsCoordinateYX(matrix [][]T, x P, y P) (result []Point[P]) > 获取一个基于 tx 的二维矩阵中,特定位置相邻的对角线最多四个方向的位置 + *** #### func GetAdjacentPos(matrix []T, width P, pos P) (result []P) > 获取一个连续位置的矩阵中,特定位置相邻的最多八个方向的位置 + *** #### func GetAdjacentCoordinateXY(matrix [][]T, x P, y P) (result []Point[P]) > 获取一个基于 x、y 的二维矩阵中,特定位置相邻的最多八个方向的位置 + *** #### func GetAdjacentCoordinateYX(matrix [][]T, x P, y P) (result []Point[P]) > 获取一个基于 yx 的二维矩阵中,特定位置相邻的最多八个方向的位置 + *** #### func CoordinateMatrixToPosMatrix(matrix [][]V) (width int, posMatrix []V) > 将二维矩阵转换为顺序的二维矩阵 + *** #### func GetShapeCoverageAreaWithPoint(points ...Point[V]) (left V, right V, top V, bottom V) > 通过传入的一组坐标 points 计算一个图形覆盖的矩形范围 + +示例代码: +```go + +func ExampleGetShapeCoverageAreaWithPoint() { + var points []geometry.Point[int] + points = append(points, geometry.NewPoint(1, 1)) + points = append(points, geometry.NewPoint(2, 1)) + points = append(points, geometry.NewPoint(2, 2)) + left, right, top, bottom := geometry.GetShapeCoverageAreaWithPoint(points...) + fmt.Println(fmt.Sprintf("left: %v, right: %v, top: %v, bottom: %v", left, right, top, bottom)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestGetShapeCoverageAreaWithPoint(t *testing.T) { + Convey("TestGetShapeCoverageAreaWithPoint", t, func() { + var points []geometry.Point[int] + points = append(points, geometry.NewPoint(1, 1)) + points = append(points, geometry.NewPoint(2, 1)) + points = append(points, geometry.NewPoint(2, 2)) + left, right, top, bottom := geometry.GetShapeCoverageAreaWithPoint(points...) + So(left, ShouldEqual, 1) + So(right, ShouldEqual, 2) + So(top, ShouldEqual, 1) + So(bottom, ShouldEqual, 2) + }) +} + +``` + + +
+ + *** #### func GetShapeCoverageAreaWithPos(width V, positions ...V) (left V, right V, top V, bottom V) > 通过传入的一组坐标 positions 计算一个图形覆盖的矩形范围 + +示例代码: +```go + +func ExampleGetShapeCoverageAreaWithPos() { + left, right, top, bottom := geometry.GetShapeCoverageAreaWithPos(3, 4, 7, 8) + fmt.Println(fmt.Sprintf("left: %v, right: %v, top: %v, bottom: %v", left, right, top, bottom)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestGetShapeCoverageAreaWithPos(t *testing.T) { + Convey("TestGetShapeCoverageAreaWithPos", t, func() { + left, right, top, bottom := geometry.GetShapeCoverageAreaWithPos(3, 4, 7, 8) + So(left, ShouldEqual, 1) + So(right, ShouldEqual, 2) + So(top, ShouldEqual, 1) + So(bottom, ShouldEqual, 2) + }) +} + +``` + + +
+ + *** #### func CoverageAreaBoundless(l V, r V, t V, b V) (left V, right V, top V, bottom V) > 将一个图形覆盖矩形范围设置为无边的 > - 无边化表示会将多余的部分进行裁剪,例如图形左边从 2 开始的时候,那么左边将会被裁剪到从 0 开始 + +示例代码: +```go + +func ExampleCoverageAreaBoundless() { + left, right, top, bottom := geometry.CoverageAreaBoundless(1, 2, 1, 2) + fmt.Println(fmt.Sprintf("left: %v, right: %v, top: %v, bottom: %v", left, right, top, bottom)) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestCoverageAreaBoundless(t *testing.T) { + Convey("TestCoverageAreaBoundless", t, func() { + left, right, top, bottom := geometry.CoverageAreaBoundless(1, 2, 1, 2) + So(left, ShouldEqual, 0) + So(right, ShouldEqual, 1) + So(top, ShouldEqual, 0) + So(bottom, ShouldEqual, 1) + }) +} + +``` + + +
+ + *** #### func GenerateShapeOnRectangle(points ...Point[V]) (result []PointCap[V, bool]) > 生成一组二维坐标的形状 > - 这个形状将被在一个刚好能容纳形状的矩形中表示 > - 为 true 的位置表示了形状的每一个点 + +
+查看 / 收起单元测试 + + +```go + +func TestGenerateShapeOnRectangle(t *testing.T) { + Convey("TestGenerateShapeOnRectangle", t, func() { + var points geometry.Shape[int] + points = append(points, geometry.NewPoint(1, 1)) + points = append(points, geometry.NewPoint(2, 1)) + points = append(points, geometry.NewPoint(2, 2)) + fmt.Println(points) + ps := geometry.GenerateShapeOnRectangle(points.Points()...) + So(ps[0].GetX(), ShouldEqual, 0) + So(ps[0].GetY(), ShouldEqual, 0) + So(ps[0].GetData(), ShouldEqual, true) + So(ps[1].GetX(), ShouldEqual, 1) + So(ps[1].GetY(), ShouldEqual, 0) + So(ps[1].GetData(), ShouldEqual, true) + So(ps[2].GetX(), ShouldEqual, 0) + So(ps[2].GetY(), ShouldEqual, 1) + So(ps[2].GetData(), ShouldEqual, false) + So(ps[3].GetX(), ShouldEqual, 1) + So(ps[3].GetY(), ShouldEqual, 1) + So(ps[3].GetData(), ShouldEqual, true) + }) +} + +``` + + +
+ + *** #### func GenerateShapeOnRectangleWithCoordinate(points ...Point[V]) (result [][]bool) > 生成一组二维坐标的形状 > - 这个形状将被在一个刚好能容纳形状的矩形中表示 > - 为 true 的位置表示了形状的每一个点 + *** #### func GetExpressibleRectangleBySize(width V, height V, minWidth V, minHeight V) (result []Point[V]) > 获取一个宽高可表达的所有特定尺寸以上的矩形形状 > - 返回值表示了每一个矩形右下角的x,y位置(左上角始终为0, 0) > - 矩形尺寸由大到小 + *** #### func GetExpressibleRectangle(width V, height V) (result []Point[V]) > 获取一个宽高可表达的所有矩形形状 > - 返回值表示了每一个矩形右下角的x,y位置(左上角始终为0, 0) > - 矩形尺寸由大到小 + *** #### func GetRectangleFullPointsByXY(startX V, startY V, endX V, endY V) (result []Point[V]) > 通过开始结束坐标获取一个矩形包含的所有点 > - 例如 1,1 到 2,2 的矩形结果为 1,1 2,1 1,2 2,2 + *** #### func GetRectangleFullPoints(width V, height V) (result []Point[V]) > 获取一个矩形填充满后包含的所有点 + *** #### func GetRectangleFullPos(width V, height V) (result []V) > 获取一个矩形填充满后包含的所有位置 + *** #### func CalcRectangleCentroid(shape Shape[V]) Point[V] > 计算矩形质心 > - 非多边形质心计算,仅为顶点的平均值 - 该区域中多边形因子的适当质心 + *** #### func SetShapeStringHasBorder() > 设置 Shape.String 是拥有边界的 + *** #### func SetShapeStringNotHasBorder() > 设置 Shape.String 是没有边界的 + *** #### func NewShape(points ...Point[V]) Shape[V] > 通过多个点生成一个形状进行返回 + +示例代码: +```go + +func ExampleNewShape() { + shape := geometry.NewShape[int](geometry.NewPoint(3, 0), geometry.NewPoint(3, 1), geometry.NewPoint(3, 2), geometry.NewPoint(3, 3), geometry.NewPoint(4, 3)) + fmt.Println(shape) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestNewShape(t *testing.T) { + Convey("TestNewShape", t, func() { + shape := geometry.NewShape[int](geometry.NewPoint(3, 0), geometry.NewPoint(3, 1), geometry.NewPoint(3, 2), geometry.NewPoint(3, 3), geometry.NewPoint(4, 3)) + fmt.Println(shape) + points := shape.Points() + count := shape.PointCount() + So(count, ShouldEqual, 5) + So(points[0], ShouldEqual, geometry.NewPoint(3, 0)) + So(points[1], ShouldEqual, geometry.NewPoint(3, 1)) + So(points[2], ShouldEqual, geometry.NewPoint(3, 2)) + So(points[3], ShouldEqual, geometry.NewPoint(3, 3)) + So(points[4], ShouldEqual, geometry.NewPoint(4, 3)) + }) +} + +``` + + +
+ + *** #### func NewShapeWithString(rows []string, point rune) (shape Shape[V]) > 通过字符串将指定 rune 转换为点位置生成形状进行返回 > - 每个点的顺序从上到下,从左到右 + +示例代码: +```go + +func ExampleNewShapeWithString() { + shape := geometry.NewShapeWithString[int]([]string{"###X###", "###X###", "###X###", "###XX##"}, 'X') + fmt.Println(shape) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestNewShapeWithString(t *testing.T) { + Convey("TestNewShapeWithString", t, func() { + shape := geometry.NewShapeWithString[int]([]string{"###X###", "###X###", "###X###", "###XX##"}, 'X') + points := shape.Points() + count := shape.PointCount() + So(count, ShouldEqual, 5) + So(points[0], ShouldEqual, geometry.NewPoint(3, 0)) + So(points[1], ShouldEqual, geometry.NewPoint(3, 1)) + So(points[2], ShouldEqual, geometry.NewPoint(3, 2)) + So(points[3], ShouldEqual, geometry.NewPoint(3, 3)) + So(points[4], ShouldEqual, geometry.NewPoint(4, 3)) + }) +} + +``` + + +
+ + *** #### func CalcBoundingRadius(shape Shape[V]) V > 计算多边形转换为圆的半径 + *** #### func CalcBoundingRadiusWithCentroid(shape Shape[V], centroid Point[V]) V > 计算多边形在特定质心下圆的半径 + *** #### func CalcTriangleTwiceArea(a Point[V], b Point[V], c Point[V]) V > 计算由 a、b、c 三个点组成的三角形的面积的两倍 + *** #### func IsPointOnEdge(edges []LineSegment[V], point Point[V]) bool > 检查点是否在 edges 的任意一条边上 + *** #### func ProjectionPointToShape(point Point[V], shape Shape[V]) Point[V], V > 将一个点投影到一个多边形上,找到离该点最近的投影点,并返回投影点和距离 + *** #### func WithShapeSearchRectangleLowerLimit(minWidth int, minHeight int) ShapeSearchOption > 通过矩形宽高下限的方式搜索 + *** #### func WithShapeSearchRectangleUpperLimit(maxWidth int, maxHeight int) ShapeSearchOption > 通过矩形宽高上限的方式搜索 + *** #### func WithShapeSearchRightAngle() ShapeSearchOption > 通过直角的方式进行搜索 + *** #### func WithShapeSearchOppositionDirection(direction Direction) ShapeSearchOption > 通过限制对立方向的方式搜索 > - 对立方向例如上不能与下共存 + *** #### func WithShapeSearchDirectionCount(count int) ShapeSearchOption > 通过限制方向数量的方式搜索 + *** #### func WithShapeSearchDirectionCountLowerLimit(direction Direction, count int) ShapeSearchOption > 通过限制特定方向数量下限的方式搜索 + *** #### func WithShapeSearchDirectionCountUpperLimit(direction Direction, count int) ShapeSearchOption > 通过限制特定方向数量上限的方式搜索 + *** #### func WithShapeSearchDeduplication() ShapeSearchOption > 通过去重的方式进行搜索 > - 去重方式中每个点仅会被使用一次 + *** #### func WithShapeSearchPointCountLowerLimit(lowerLimit int) ShapeSearchOption > 通过限制图形构成的最小点数进行搜索 > - 当搜索到的图形的点数量低于 lowerLimit 时,将被忽略 + *** #### func WithShapeSearchPointCountUpperLimit(upperLimit int) ShapeSearchOption > 通过限制图形构成的最大点数进行搜索 > - 当搜索到的图形的点数量大于 upperLimit 时,将被忽略 + *** #### func WithShapeSearchAsc() ShapeSearchOption > 通过升序的方式进行搜索 + *** #### func WithShapeSearchDesc() ShapeSearchOption > 通过降序的方式进行搜索 + *** -### Circle +### Circle `STRUCT` 圆形 ```go type Circle[V generic.SignedNumber] struct { @@ -540,10 +889,10 @@ type Circle[V generic.SignedNumber] struct { #### func (Circle) CentroidDistance(circle Circle[V]) V > 计算与另一个圆的质心距离 *** -### FloorPlan +### FloorPlan `STRUCT` 平面图 ```go -type FloorPlan struct{} +type FloorPlan []string ``` #### func (FloorPlan) IsFree(point Point[int]) bool > 检查位置是否为空格 @@ -557,15 +906,15 @@ type FloorPlan struct{} #### func (FloorPlan) String() string > 获取平面图结果 *** -### Direction +### Direction `STRUCT` 方向 ```go -type Direction struct{} +type Direction uint8 ``` -### LineSegment +### LineSegment `STRUCT` 通过两个点表示一根线段 ```go -type LineSegment[V generic.SignedNumber] struct{} +type LineSegment[V generic.SignedNumber] [2]Point[V] ``` #### func (LineSegment) GetPoints() [2]Point[V] > 获取该线段的两个点 @@ -579,7 +928,7 @@ type LineSegment[V generic.SignedNumber] struct{} #### func (LineSegment) GetLength() V > 获取该线段的长度 *** -### LineSegmentCap +### LineSegmentCap `STRUCT` 可以包含一份额外数据的线段 ```go type LineSegmentCap[V generic.SignedNumber, Data any] struct { @@ -587,10 +936,10 @@ type LineSegmentCap[V generic.SignedNumber, Data any] struct { Data Data } ``` -### Point +### Point `STRUCT` 表示了一个由 x、y 坐标组成的点 ```go -type Point[V generic.SignedNumber] struct{} +type Point[V generic.SignedNumber] [2]V ``` #### func (Point) GetX() V > 返回该点的 x 坐标 @@ -640,7 +989,7 @@ type Point[V generic.SignedNumber] struct{} #### func (Point) Min(point Point[V]) Point[V] > 返回两个位置中每个维度的最小值组成的新的位置 *** -### PointCap +### PointCap `STRUCT` 表示了一个由 x、y 坐标组成的点,这个点具有一个数据容量 ```go type PointCap[V generic.SignedNumber, D any] struct { @@ -648,16 +997,79 @@ type PointCap[V generic.SignedNumber, D any] struct { Data D } ``` -### Shape +### Shape `STRUCT` 通过多个点表示了一个形状 ```go -type Shape[V generic.SignedNumber] struct{} +type Shape[V generic.SignedNumber] []Point[V] ``` #### func (Shape) Points() []Point[V] > 获取这个形状的所有点 +示例代码: +```go + +func ExampleShape_Points() { + shape := geometry.NewShapeWithString[int]([]string{"###X###", "##XXX##"}, 'X') + points := shape.Points() + fmt.Println(points) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestShape_Points(t *testing.T) { + Convey("TestShape_Points", t, func() { + shape := geometry.NewShapeWithString[int]([]string{"###X###", "##XXX##"}, 'X') + points := shape.Points() + So(points[0], ShouldEqual, geometry.NewPoint(3, 0)) + So(points[1], ShouldEqual, geometry.NewPoint(2, 1)) + So(points[2], ShouldEqual, geometry.NewPoint(3, 1)) + So(points[3], ShouldEqual, geometry.NewPoint(4, 1)) + }) +} + +``` + + +
+ + *** #### func (Shape) PointCount() int > 获取这个形状的点数量 +示例代码: +```go + +func ExampleShape_PointCount() { + shape := geometry.NewShapeWithString[int]([]string{"###X###", "##XXX##"}, 'X') + fmt.Println(shape.PointCount()) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestShape_PointCount(t *testing.T) { + Convey("TestShape_PointCount", t, func() { + shape := geometry.NewShapeWithString[int]([]string{"###X###", "##XXX##"}, 'X') + So(shape.PointCount(), ShouldEqual, 4) + }) +} + +``` + + +
+ + *** #### func (Shape) Contains(point Point[V]) bool > 返回该形状中是否包含点 @@ -668,6 +1080,36 @@ type Shape[V generic.SignedNumber] struct{} *** #### func (Shape) String() string > 将该形状转换为可视化的字符串进行返回 +示例代码: +```go + +func ExampleShape_String() { + shape := geometry.NewShapeWithString[int]([]string{"###X###", "##XXX##"}, 'X') + fmt.Println(shape) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestShape_String(t *testing.T) { + Convey("TestShape_String", t, func() { + shape := geometry.NewShapeWithString[int]([]string{"###X###", "##XXX##"}, 'X') + str := shape.String() + So(str, ShouldEqual, "[[3 0] [2 1] [3 1] [4 1]]\n# X #\nX X X") + }) +} + +``` + + +
+ + *** #### func (Shape) ShapeSearch(options ...ShapeSearchOption) (result []Shape[V]) > 获取该形状中包含的所有图形组合及其位置 @@ -676,6 +1118,19 @@ type Shape[V generic.SignedNumber] struct{} > - 返回的坐标为原始形状的坐标 > > 可通过可选项对搜索结果进行过滤 +示例代码: +```go + +func ExampleShape_ShapeSearch() { + shape := geometry.NewShapeWithString[int]([]string{"###X###", "##XXX##", "###X###"}, 'X') + shapes := shape.ShapeSearch(geometry.WithShapeSearchDeduplication(), geometry.WithShapeSearchDesc()) + for _, shape := range shapes { + fmt.Println(shape) + } +} + +``` + *** #### func (Shape) Edges() (edges []LineSegment[V]) > 获取该形状每一条边 @@ -684,8 +1139,8 @@ type Shape[V generic.SignedNumber] struct{} #### func (Shape) IsPointOnEdge(point Point[V]) bool > 检查点是否在该形状的一条边上 *** -### ShapeSearchOption +### ShapeSearchOption `STRUCT` 图形搜索可选项,用于 Shape.ShapeSearch 搜索支持 ```go -type ShapeSearchOption struct{} +type ShapeSearchOption func(options *shapeSearchOptions) ``` diff --git a/utils/geometry/astar/README.md b/utils/geometry/astar/README.md index 000adc9..8debe7f 100644 --- a/utils/geometry/astar/README.md +++ b/utils/geometry/astar/README.md @@ -1,5 +1,8 @@ # Astar +[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/astar) +![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) + astar 提供用于实现 A* 算法的函数和数据结构。A* 算法是一种常用的路径搜索算法,用于在图形或网络中找到最短路径。该包旨在简化 A* 算法的实现过程,并提供一致的接口和易于使用的功能。 主要特性: - 图形表示:astar 包支持使用图形或网络来表示路径搜索的环境。您可以定义节点和边,以构建图形,并在其中执行路径搜索。 @@ -7,31 +10,31 @@ astar 提供用于实现 A* 算法的函数和数据结构。A* 算法是一种 - 自定义启发式函数:您可以根据具体问题定义自己的启发式函数,以指导 A* 算法的搜索过程。启发式函数用于估计从当前节点到目标节点的代价,以帮助算法选择最佳路径。 - 可定制性:astar 包提供了一些可定制的选项,以满足不同场景下的需求。您可以设置节点的代价、边的权重等参数,以调整算法的行为。 -[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/astar) -![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[Find](#Find)|使用 A* 算法在导航网格上查找从起点到终点的最短路径,并返回路径上的节点序列。 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Graph](#graph)|适用于 A* 算法的图数据结构接口定义,表示导航网格,其中包含了节点和连接节点的边。 +|类型|名称|描述 +|:--|:--|:-- +|`INTERFACE`|[Graph](#graph)|适用于 A* 算法的图数据结构接口定义,表示导航网格,其中包含了节点和连接节点的边。
+*** +## 详情信息 #### func Find(graph Graph[Node], start Node, end Node, cost func (a Node) V, heuristic func (a Node) V) []Node > 使用 A* 算法在导航网格上查找从起点到终点的最短路径,并返回路径上的节点序列。 @@ -54,11 +57,32 @@ astar 提供用于实现 A* 算法的函数和数据结构。A* 算法是一种 > - 函数使用了 A* 算法来搜索最短路径。 > - 函数内部使用了堆数据结构来管理待处理的节点。 > - 函数返回一个节点序列,表示从起点到终点的最短路径。如果找不到路径,则返回空序列。 + +示例代码: +```go + +func ExampleFind() { + graph := Graph{FloorPlan: geometry.FloorPlan{"===========", "X XX X X", "X X XX X", "X XX X", "X XXX X", "X XX X X", "X XX X X", "==========="}} + paths := astar.Find[geometry.Point[int], int](graph, geometry.NewPoint(1, 1), geometry.NewPoint(8, 6), func(a, b geometry.Point[int]) int { + return geometry.CalcDistanceWithCoordinate(geometry.DoublePointToCoordinate(a, b)) + }, func(a, b geometry.Point[int]) int { + return geometry.CalcDistanceWithCoordinate(geometry.DoublePointToCoordinate(a, b)) + }) + for _, path := range paths { + graph.Put(path, '.') + } + fmt.Println(graph) +} + +``` + *** -### Graph +### Graph `INTERFACE` 适用于 A* 算法的图数据结构接口定义,表示导航网格,其中包含了节点和连接节点的边。 ```go -type Graph[Node comparable] struct{} +type Graph[Node comparable] interface { + Neighbours(node Node) []Node +} ``` #### func (Graph) Neighbours(point geometry.Point[int]) []geometry.Point[int] *** diff --git a/utils/geometry/dp/README.md b/utils/geometry/dp/README.md index cb6888c..c0477d6 100644 --- a/utils/geometry/dp/README.md +++ b/utils/geometry/dp/README.md @@ -1,42 +1,75 @@ # Dp +[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/dp) +![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) + dp (DistributionPattern) 提供用于在二维数组中根据不同的特征标记为数组成员建立分布链接的函数和数据结构。该包的目标是实现快速查找与给定位置成员具有相同特征且位置紧邻的其他成员。 主要特性: - 分布链接机制:dp 包提供了一种分布链接的机制,可以根据成员的特征将它们链接在一起。这样,可以快速查找与给定成员具有相同特征且位置紧邻的其他成员。 - 二维数组支持:该包支持在二维数组中建立分布链接。可以将二维数组中的成员视为节点,并根据其特征进行链接。 - 快速查找功能:使用 dp 包提供的函数,可以快速查找与给定位置成员具有相同特征且位置紧邻的其他成员。这有助于在二维数组中进行相关性分析或查找相邻成员。 -[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/dp) -![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewDistributionPattern](#NewDistributionPattern)|构建一个分布图实例 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[DistributionPattern](#distributionpattern)|分布图 -|[Link](#link)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[DistributionPattern](#distributionpattern)|分布图 +|`STRUCT`|[Link](#link)|暂无描述...
+*** +## 详情信息 #### func NewDistributionPattern(sameKindVerifyHandle func (itemA Item) bool) *DistributionPattern[Item] > 构建一个分布图实例 + +
+查看 / 收起单元测试 + + +```go + +func TestNewDistributionPattern(t *testing.T) { + dp := NewDistributionPattern[int](func(itemA, itemB int) bool { + return itemA == itemB + }) + matrix := []int{1, 1, 2, 2, 2, 2, 1, 2, 2} + dp.LoadMatrixWithPos(3, matrix) + for pos, link := range dp.links { + fmt.Println(pos, link, fmt.Sprintf("%p", link)) + } + fmt.Println() + matrix[6] = 2 + dp.Refresh(6) + for pos, link := range dp.links { + fmt.Println(pos, link, fmt.Sprintf("%p", link)) + } +} + +``` + + +
+ + *** -### DistributionPattern +### DistributionPattern `STRUCT` 分布图 ```go type DistributionPattern[Item any] struct { @@ -71,7 +104,7 @@ type DistributionPattern[Item any] struct { > 通过特定的成员刷新特定位置的分布关系 > - 如果矩阵通过 LoadMatrixWithPos 加载,将会重定向至 Refresh *** -### Link +### Link `STRUCT` ```go type Link[V any] struct { diff --git a/utils/geometry/matrix/README.md b/utils/geometry/matrix/README.md index d28059e..1fe3cc8 100644 --- a/utils/geometry/matrix/README.md +++ b/utils/geometry/matrix/README.md @@ -1,39 +1,43 @@ # Matrix -matrix 提供了一个简单的二维数组的实现 - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/matrix) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +matrix 提供了一个简单的二维数组的实现 + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewMatrix](#NewMatrix)|生成特定宽高的二维矩阵 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Matrix](#matrix)|二维矩阵 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Matrix](#matrix)|二维矩阵
+*** +## 详情信息 #### func NewMatrix(width int, height int) *Matrix[T] > 生成特定宽高的二维矩阵 > - 虽然提供了通过x、y坐标的操作函数,但是建议无论如何使用pos进行处理 > - 该矩阵为XY,而非YX + *** -### Matrix +### Matrix `STRUCT` 二维矩阵 ```go type Matrix[T any] struct { diff --git a/utils/geometry/navmesh/README.md b/utils/geometry/navmesh/README.md index 08d7a99..b998a84 100644 --- a/utils/geometry/navmesh/README.md +++ b/utils/geometry/navmesh/README.md @@ -1,35 +1,38 @@ # Navmesh +[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/navmesh) +![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) + navmesh 提供了用于导航网格处理的函数和数据结构。导航网格是一种常用的数据结构,用于在游戏开发和虚拟环境中进行路径规划和导航。该包旨在简化导航网格的创建、查询和操作过程,并提供高效的导航功能。 主要特性: - 导航网格表示:navmesh 包支持使用导航网格来表示虚拟环境中的可行走区域和障碍物。您可以定义多边形区域和连接关系,以构建导航网格,并在其中执行路径规划和导航。 - 导航算法:采用了 A* 算法作为导航算法,用于在导航网格中找到最短路径或最优路径。这些算法使用启发式函数和代价评估来指导路径搜索,并提供高效的路径规划能力。 -[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/navmesh) -![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewNavMesh](#NewNavMesh)|创建一个新的导航网格,并返回一个指向该导航网格的指针。 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[NavMesh](#navmesh)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[NavMesh](#navmesh)|暂无描述...
+*** +## 详情信息 #### func NewNavMesh(shapes []geometry.Shape[V], meshShrinkAmount V) *NavMesh[V] > 创建一个新的导航网格,并返回一个指向该导航网格的指针。 @@ -49,8 +52,9 @@ navmesh 提供了用于导航网格处理的函数和数据结构。导航网格 > > 使用建议: > - 确保 NavMesh 计算精度的情况下,V 建议使用 float64 类型 + *** -### NavMesh +### NavMesh `STRUCT` ```go type NavMesh[V generic.SignedNumber] struct { @@ -94,4 +98,32 @@ type NavMesh[V generic.SignedNumber] struct { > - 如果起点或终点不在任何形状内部,且 NavMesh 的 meshShrinkAmount 大于0,则会考虑缩小的形状。 > - 使用 A* 算法在 NavMesh 上搜索从起点形状到终点形状的最短路径。 > - 使用漏斗算法对路径进行优化,以得到最终的路径点序列。 +示例代码: +```go + +func ExampleNavMesh_FindPath() { + fp := geometry.FloorPlan{"=================================", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "X X", "================================="} + var walkable []geometry.Shape[int] + walkable = append(walkable, geometry.NewShape(geometry.NewPoint(5, 5), geometry.NewPoint(15, 5), geometry.NewPoint(15, 15), geometry.NewPoint(5, 15)), geometry.NewShape(geometry.NewPoint(15, 5), geometry.NewPoint(25, 5), geometry.NewPoint(25, 15), geometry.NewPoint(15, 15)), geometry.NewShape(geometry.NewPoint(15, 15), geometry.NewPoint(25, 15), geometry.NewPoint(25, 25), geometry.NewPoint(15, 25))) + for _, shape := range walkable { + for _, edge := range shape.Edges() { + sx, bx := maths.MinMax(edge.GetStart().GetX(), edge.GetEnd().GetX()) + sy, by := maths.MinMax(edge.GetStart().GetY(), edge.GetEnd().GetY()) + for x := sx; x <= bx; x++ { + for y := sy; y <= by; y++ { + fp.Put(geometry.NewPoint[int](x, y), '+') + } + } + } + } + nm := navmesh.NewNavMesh(walkable, 0) + path := nm.FindPath(geometry.NewPoint(6, 6), geometry.NewPoint(18, 24)) + for _, point := range path { + fp.Put(geometry.NewPoint(point.GetX(), point.GetY()), 'G') + } + fmt.Println(fp) +} + +``` + *** diff --git a/utils/hub/README.md b/utils/hub/README.md index 30d65b8..7264472 100644 --- a/utils/hub/README.md +++ b/utils/hub/README.md @@ -1,37 +1,95 @@ # Hub - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/hub) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewObjectPool](#NewObjectPool)|创建一个 ObjectPool -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[ObjectPool](#objectpool)|基于 sync.Pool 实现的线程安全的对象池 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[ObjectPool](#objectpool)|基于 sync.Pool 实现的线程安全的对象池
+*** +## 详情信息 #### func NewObjectPool(generator func () *T, releaser func (data *T)) *ObjectPool[*T] > 创建一个 ObjectPool + +示例代码: +```go + +func ExampleNewObjectPool() { + var p = hub.NewObjectPool[map[int]int](func() *map[int]int { + return &map[int]int{} + }, func(data *map[int]int) { + collection.ClearMap(*data) + }) + m := *p.Get() + m[1] = 1 + p.Release(&m) + fmt.Println(m) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestNewObjectPool(t *testing.T) { + var cases = []struct { + name string + generator func() *map[string]int + releaser func(data *map[string]int) + shouldPanic bool + }{{name: "TestNewObjectPool_NilGenerator", generator: nil, releaser: func(data *map[string]int) { + }, shouldPanic: true}, {name: "TestNewObjectPool_NilReleaser", generator: func() *map[string]int { + return &map[string]int{} + }, releaser: nil, shouldPanic: true}, {name: "TestNewObjectPool_NilGeneratorAndReleaser", generator: nil, releaser: nil, shouldPanic: true}, {name: "TestNewObjectPool_Normal", generator: func() *map[string]int { + return &map[string]int{} + }, releaser: func(data *map[string]int) { + }, shouldPanic: false}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + defer func() { + if err := recover(); c.shouldPanic && err == nil { + t.Error("TestNewObjectPool should panic") + } + }() + _ = hub.NewObjectPool[map[string]int](c.generator, c.releaser) + }) + } +} + +``` + + +
+ + *** -### ObjectPool +### ObjectPool `STRUCT` 基于 sync.Pool 实现的线程安全的对象池 - 一些高频临时生成使用的对象可以通过 ObjectPool 进行管理,例如属性计算等 ```go @@ -42,7 +100,79 @@ type ObjectPool[T any] struct { ``` #### func (*ObjectPool) Get() T > 获取一个对象 +
+查看 / 收起单元测试 + + +```go + +func TestObjectPool_Get(t *testing.T) { + var cases = []struct { + name string + generator func() *map[string]int + releaser func(data *map[string]int) + }{{name: "TestObjectPool_Get_Normal", generator: func() *map[string]int { + return &map[string]int{} + }, releaser: func(data *map[string]int) { + for k := range *data { + delete(*data, k) + } + }}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + pool := hub.NewObjectPool[map[string]int](c.generator, c.releaser) + if actual := pool.Get(); len(*actual) != 0 { + t.Error("TestObjectPool_Get failed") + } + }) + } +} + +``` + + +
+ + *** #### func (*ObjectPool) Release(data T) > 将使用完成的对象放回缓冲区 +
+查看 / 收起单元测试 + + +```go + +func TestObjectPool_Release(t *testing.T) { + var cases = []struct { + name string + generator func() *map[string]int + releaser func(data *map[string]int) + }{{name: "TestObjectPool_Release_Normal", generator: func() *map[string]int { + return &map[string]int{} + }, releaser: func(data *map[string]int) { + for k := range *data { + delete(*data, k) + } + }}} + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + pool := hub.NewObjectPool[map[string]int](c.generator, c.releaser) + msg := pool.Get() + m := *msg + m["test"] = 1 + pool.Release(msg) + if len(m) != 0 { + t.Error("TestObjectPool_Release failed") + } + }) + } +} + +``` + + +
+ + *** diff --git a/utils/huge/README.md b/utils/huge/README.md index e0ddf0d..8086a18 100644 --- a/utils/huge/README.md +++ b/utils/huge/README.md @@ -1,19 +1,20 @@ # Huge - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/huge) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewFloat](#NewFloat)|创建一个 Float |[NewFloatByString](#NewFloatByString)|通过字符串创建一个 Float @@ -21,38 +22,44 @@ |[NewIntByString](#NewIntByString)|通过字符串创建一个 Int -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Float](#float)|暂无描述... -|[Int](#int)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Float](#float)|暂无描述... +|`STRUCT`|[Int](#int)|暂无描述...
+*** +## 详情信息 #### func NewFloat(x T) *Float > 创建一个 Float + *** #### func NewFloatByString(i string) *Float > 通过字符串创建一个 Float > - 如果字符串不是一个合法的数字,则返回 0 + *** #### func NewInt(x T) *Int > 创建一个 Int + *** #### func NewIntByString(i string) *Int > 通过字符串创建一个 Int > - 如果字符串不是一个合法的数字,则返回 0 + *** -### Float +### Float `STRUCT` ```go -type Float struct{} +type Float big.Float ``` #### func (*Float) Copy() *Float *** @@ -110,10 +117,10 @@ type Float struct{} #### func (*Float) IsNegative() bool > 是否为负数 *** -### Int +### Int `STRUCT` ```go -type Int struct{} +type Int big.Int ``` #### func (*Int) Copy() *Int *** diff --git a/utils/leaderboard/README.md b/utils/leaderboard/README.md index 0a6de09..84eca10 100644 --- a/utils/leaderboard/README.md +++ b/utils/leaderboard/README.md @@ -1,51 +1,67 @@ # Leaderboard - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/leaderboard) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewBinarySearch](#NewBinarySearch)|创建一个基于内存的二分查找排行榜 |[WithBinarySearchCount](#WithBinarySearchCount)|通过限制排行榜竞争者数量来创建排行榜 |[WithBinarySearchASC](#WithBinarySearchASC)|通过升序的方式创建排行榜 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[BinarySearch](#binarysearch)|暂无描述... -|[BinarySearchRankChangeEventHandle](#binarysearchrankchangeeventhandle)|暂无描述... -|[BinarySearchOption](#binarysearchoption)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[BinarySearch](#binarysearch)|暂无描述... +|`STRUCT`|[BinarySearchRankChangeEventHandle](#binarysearchrankchangeeventhandle)|暂无描述... +|`STRUCT`|[BinarySearchOption](#binarysearchoption)|暂无描述...
+*** +## 详情信息 #### func NewBinarySearch(options ...BinarySearchOption[CompetitorID, Score]) *BinarySearch[CompetitorID, Score] > 创建一个基于内存的二分查找排行榜 + +示例代码: +```go + +func ExampleNewBinarySearch() { + bs := leaderboard2.NewBinarySearch[string, int](leaderboard2.WithBinarySearchCount[string, int](10)) + fmt.Println(bs != nil) +} + +``` + *** #### func WithBinarySearchCount(rankCount int) BinarySearchOption[CompetitorID, Score] > 通过限制排行榜竞争者数量来创建排行榜 > - 默认情况下允许100位竞争者 + *** #### func WithBinarySearchASC() BinarySearchOption[CompetitorID, Score] > 通过升序的方式创建排行榜 > - 默认情况下为降序 + *** -### BinarySearch +### BinarySearch `STRUCT` ```go type BinarySearch[CompetitorID comparable, Score generic.Ordered] struct { @@ -58,13 +74,13 @@ type BinarySearch[CompetitorID comparable, Score generic.Ordered] struct { rankClearBeforeEventHandles []BinarySearchRankClearBeforeEventHandle[CompetitorID, Score] } ``` -### BinarySearchRankChangeEventHandle +### BinarySearchRankChangeEventHandle `STRUCT` ```go -type BinarySearchRankChangeEventHandle[CompetitorID comparable, Score generic.Ordered] struct{} +type BinarySearchRankChangeEventHandle[CompetitorID comparable, Score generic.Ordered] func(leaderboard *BinarySearch[CompetitorID, Score], competitorId CompetitorID, oldRank int, oldScore Score) ``` -### BinarySearchOption +### BinarySearchOption `STRUCT` ```go -type BinarySearchOption[CompetitorID comparable, Score generic.Ordered] struct{} +type BinarySearchOption[CompetitorID comparable, Score generic.Ordered] func(list *BinarySearch[CompetitorID, Score]) ``` diff --git a/utils/log/README.md b/utils/log/README.md index 8e43f1c..90755be 100644 --- a/utils/log/README.md +++ b/utils/log/README.md @@ -1,19 +1,20 @@ # Log - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/log) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[CallerBasicFormat](#CallerBasicFormat)|返回调用者的基本格式 |[Println](#Println)|暂无描述... @@ -72,255 +73,339 @@ |[NewOptions](#NewOptions)|创建一个新的日志选项 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Field](#field)|暂无描述... -|[Logger](#logger)|暂无描述... -|[MultiHandler](#multihandler)|暂无描述... -|[Option](#option)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Field](#field)|暂无描述... +|`STRUCT`|[Logger](#logger)|暂无描述... +|`STRUCT`|[MultiHandler](#multihandler)|暂无描述... +|`STRUCT`|[Option](#option)|暂无描述...
+*** +## 详情信息 #### func CallerBasicFormat(file string, line int) (repFile string, refLine string) > 返回调用者的基本格式 + *** #### func Println(str string, color string, desc string) + *** #### func Default() *Logger > 获取默认的日志记录器 + *** #### func SetDefault(l *Logger) > 设置默认的日志记录器 + *** #### func SetDefaultBySlog(l *slog.Logger) > 设置默认的日志记录器 + *** #### func Debug(msg string, args ...any) > 在 DebugLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 + *** #### func Info(msg string, args ...any) > 在 InfoLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 + *** #### func Warn(msg string, args ...any) > 在 WarnLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 + *** #### func Error(msg string, args ...any) > 在 ErrorLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 + *** #### func DPanic(msg string, args ...any) > 在 DPanicLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 > - 如果记录器处于开发模式,它就会出现 panic(DPanic 的意思是“development panic”)。这对于捕获可恢复但不应该发生的错误很有用 > - 该 panic 仅在 NewHandler 中创建的处理器会生效 + *** #### func Panic(msg string, args ...any) > 在 PanicLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 > - 即使禁用了 PanicLevel 的日志记录,记录器也会出现 panic + *** #### func Fatal(msg string, args ...any) > 在 FatalLevel 记录一条消息。该消息包括在日志站点传递的任何字段以及记录器上累积的任何字段 > - 然后记录器调用 os.Exit(1),即使 FatalLevel 的日志记录被禁用 + *** #### func Skip(vs ...any) slog.Attr > 构造一个无操作字段,这在处理其他 Field 构造函数中的无效输入时通常很有用 > - 该函数还支持将其他字段快捷的转换为 Skip 字段 + *** #### func Duration(key string, val time.Duration) slog.Attr > 使用给定的键和值构造一个字段。编码器控制持续时间的序列化方式 + *** #### func DurationP(key string, val *time.Duration) slog.Attr > 构造一个带有 time.Duration 的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Bool(key string, val bool) slog.Attr > 构造一个带有布尔值的字段 + *** #### func BoolP(key string, val *bool) slog.Attr > 构造一个带有布尔值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func String(key string, val string) slog.Attr > 构造一个带有字符串值的字段 + *** #### func StringP(key string, val *string) slog.Attr > 构造一个带有字符串值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Int(key string, val I) slog.Attr > 构造一个带有整数值的字段 + *** #### func IntP(key string, val *I) slog.Attr > 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Int8(key string, val I) slog.Attr > 构造一个带有整数值的字段 + *** #### func Int8P(key string, val *I) slog.Attr > 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Int16(key string, val I) slog.Attr > 构造一个带有整数值的字段 + *** #### func Int16P(key string, val *I) slog.Attr > 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Int32(key string, val I) slog.Attr > 构造一个带有整数值的字段 + *** #### func Int32P(key string, val *I) slog.Attr > 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Int64(key string, val I) slog.Attr > 构造一个带有整数值的字段 + *** #### func Int64P(key string, val *I) slog.Attr > 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Uint(key string, val I) slog.Attr > 构造一个带有整数值的字段 + *** #### func UintP(key string, val *I) slog.Attr > 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Uint8(key string, val I) slog.Attr > 构造一个带有整数值的字段 + *** #### func Uint8P(key string, val *I) slog.Attr > 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Uint16(key string, val I) slog.Attr > 构造一个带有整数值的字段 + *** #### func Uint16P(key string, val *I) slog.Attr > 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Uint32(key string, val I) slog.Attr > 构造一个带有整数值的字段 + *** #### func Uint32P(key string, val *I) slog.Attr > 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Uint64(key string, val I) slog.Attr > 构造一个带有整数值的字段 + *** #### func Uint64P(key string, val *I) slog.Attr > 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Float(key string, val F) slog.Attr > 构造一个带有浮点值的字段 + *** #### func FloatP(key string, val *F) slog.Attr > 构造一个带有浮点值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Float32(key string, val F) slog.Attr > 构造一个带有浮点值的字段 + *** #### func Float32P(key string, val *F) slog.Attr > 构造一个带有浮点值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Float64(key string, val F) slog.Attr > 构造一个带有浮点值的字段 + *** #### func Float64P(key string, val *F) slog.Attr > 构造一个带有浮点值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Time(key string, val time.Time) slog.Attr > 构造一个带有时间值的字段 + *** #### func TimeP(key string, val *time.Time) slog.Attr > 构造一个带有时间值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null" + *** #### func Any(key string, val any) slog.Attr > 构造一个带有任意值的字段 + *** #### func Group(key string, args ...any) slog.Attr > 返回分组字段 + *** #### func Stack(key string) slog.Attr > 返回堆栈字段 + +
+查看 / 收起单元测试 + + +```go + +func TestStack(t *testing.T) { + var i int + for { + time.Sleep(time.Second) + Debug("TestStack") + Info("TestStack") + Warn("TestStack") + Error("TestStack") + i++ + if i == 3 { + Default().Logger.Handler().(*handler).opts.GerRuntimeHandler().ChangeLevel(slog.LevelInfo) + } + } +} + +``` + + +
+ + *** #### func Err(err error) slog.Attr > 构造一个带有错误值的字段 + *** #### func NewHandler(w io.Writer, opts *Options) slog.Handler > 创建一个更偏向于人类可读的处理程序,该处理程序也是默认的处理程序 + *** #### func NewLogger(handlers ...slog.Handler) *Logger > 创建一个新的日志记录器 + *** #### func NewMultiHandler(handlers ...slog.Handler) slog.Handler > 创建一个新的多处理程序 + *** #### func NewOptions() *Options > 创建一个新的日志选项 + *** -### Field +### Field `STRUCT` ```go -type Field struct{} +type Field slog.Attr ``` -### Logger +### Logger `STRUCT` ```go type Logger struct { *slog.Logger } ``` -### MultiHandler +### MultiHandler `STRUCT` ```go type MultiHandler struct { @@ -335,8 +420,8 @@ type MultiHandler struct { *** #### func (MultiHandler) WithGroup(name string) slog.Handler *** -### Option +### Option `STRUCT` ```go -type Option struct{} +type Option func(opts *Options) ``` diff --git a/utils/log/survey/README.md b/utils/log/survey/README.md index 45a3d87..7e63221 100644 --- a/utils/log/survey/README.md +++ b/utils/log/survey/README.md @@ -1,19 +1,20 @@ # Survey - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/survey) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewFileFlusher](#NewFileFlusher)|创建一个文件刷新器 |[WithFlushInterval](#WithFlushInterval)|设置日志文件刷新间隔 @@ -27,67 +28,132 @@ |[IncrementAnalyze](#IncrementAnalyze)|增量分析,返回一个函数,每次调用该函数都会分析文件中新增的内容 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Analyzer](#analyzer)|分析器 -|[Flusher](#flusher)|用于刷新缓冲区的接口 -|[FileFlusher](#fileflusher)|暂无描述... -|[Option](#option)|选项 -|[Result](#result)|暂无描述... -|[R](#r)|记录器所记录的一条数据 -|[Report](#report)|分析报告 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Analyzer](#analyzer)|分析器 +|`INTERFACE`|[Flusher](#flusher)|用于刷新缓冲区的接口 +|`STRUCT`|[FileFlusher](#fileflusher)|暂无描述... +|`STRUCT`|[Option](#option)|选项 +|`STRUCT`|[Result](#result)|暂无描述... +|`STRUCT`|[R](#r)|记录器所记录的一条数据 +|`STRUCT`|[Report](#report)|分析报告
+*** +## 详情信息 #### func NewFileFlusher(filePath string, layout ...string) *FileFlusher > 创建一个文件刷新器 > - layout 为日志文件名的时间戳格式 (默认为 time.DateOnly) + *** #### func WithFlushInterval(interval time.Duration) Option > 设置日志文件刷新间隔 > - 默认为 3s,当日志文件刷新间隔 <= 0 时,将会在每次写入日志时刷新日志文件 + *** #### func Reg(name string, flusher Flusher, options ...Option) > 注册一个运营日志记录器 + *** #### func Record(name string, data map[string]any) > 记录一条运营日志 + *** #### func RecordBytes(name string, data []byte) > 记录一条运营日志 + *** #### func Flush(names ...string) > 将运营日志记录器的缓冲区数据写入到文件 > - name 为空时,将所有记录器的缓冲区数据写入到文件 + *** #### func Close(names ...string) > 关闭运营日志记录器 + *** #### func Analyze(filePath string, handle func (analyzer *Analyzer, record R)) *Report > 分析特定文件的记录,当发生错误时,会发生 panic > - handle 为并行执行的,需要自行处理并发安全 > - 适用于外部进程对于日志文件的读取,但是需要注意的是,此时日志文件可能正在被写入,所以可能会读取到错误的数据 + *** #### func AnalyzeMulti(filePaths []string, handle func (analyzer *Analyzer, record R)) *Report > 与 Analyze 类似,但是可以分析多个文件 + *** -#### func IncrementAnalyze(filePath string, handle func (analyzer *Analyzer, record R)) func () *Report, error +#### func IncrementAnalyze(filePath string, handle func (analyzer *Analyzer, record R)) func () ( *Report, error) > 增量分析,返回一个函数,每次调用该函数都会分析文件中新增的内容 + +
+查看 / 收起单元测试 + + +```go + +func TestIncrementAnalyze(t *testing.T) { + path := `./test/day.2023-09-06.log` + reader := survey.IncrementAnalyze(path, func(analyzer *survey.Analyzer, record survey.R) { + switch record.GetString("type") { + case "open_conn": + analyzer.IncreaseValueNonRepeat("开播人数", record, 1, "live_id") + case "report_rank": + analyzer.IncreaseValue("开始游戏次数", 1) + analyzer.Increase("开播时长", record, "game_time") + analyzer.Sub(record.GetString("live_id")).IncreaseValue("开始游戏次数", 1) + analyzer.Sub(record.GetString("live_id")).Increase("开播时长", record, "game_time") + case "statistics": + analyzer.IncreaseValueNonRepeat("活跃人数", record, 1, "active_player") + analyzer.IncreaseValueNonRepeat("评论人数", record, 1, "comment_player") + analyzer.IncreaseValueNonRepeat("点赞人数", record, 1, "like_player") + analyzer.Sub(record.GetString("live_id")).IncreaseValueNonRepeat("活跃人数", record, 1, "active_player") + analyzer.Sub(record.GetString("live_id")).IncreaseValueNonRepeat("评论人数", record, 1, "comment_player") + analyzer.Sub(record.GetString("live_id")).IncreaseValueNonRepeat("点赞人数", record, 1, "like_player") + giftId := record.GetString("gift.gift_id") + if len(giftId) > 0 { + giftPrice := record.GetFloat64("gift.price") + giftCount := record.GetFloat64("gift.count") + giftSender := record.GetString("gift.gift_senders") + analyzer.IncreaseValue("礼物总价值", giftPrice*giftCount) + analyzer.IncreaseValueNonRepeat(fmt.Sprintf("送礼人数_%s", giftId), record, 1, giftSender) + analyzer.IncreaseValue(fmt.Sprintf("礼物总数_%s", giftId), giftCount) + analyzer.Sub(record.GetString("live_id")).IncreaseValue("礼物总价值", giftPrice*giftCount) + analyzer.Sub(record.GetString("live_id")).IncreaseValueNonRepeat(fmt.Sprintf("送礼人数_%s", giftId), record, 1, giftSender) + analyzer.Sub(record.GetString("live_id")).IncreaseValue(fmt.Sprintf("礼物总数_%s", giftId), giftCount) + } + } + }) + for i := 0; i < 10; i++ { + report, err := reader() + if err != nil { + t.Fatal(err) + } + fmt.Println(report.FilterSub("warzone0009")) + } +} + +``` + + +
+ + *** -### Analyzer +### Analyzer `STRUCT` 分析器 ```go type Analyzer struct { @@ -148,12 +214,15 @@ type Analyzer struct { #### func (*Analyzer) GetValueString(key string) string > 获取当前记录的值 *** -### Flusher +### Flusher `INTERFACE` 用于刷新缓冲区的接口 ```go -type Flusher struct{} +type Flusher interface { + Flush(records []string) + Info() string +} ``` -### FileFlusher +### FileFlusher `STRUCT` ```go type FileFlusher struct { @@ -168,20 +237,20 @@ type FileFlusher struct { *** #### func (*FileFlusher) Info() string *** -### Option +### Option `STRUCT` 选项 ```go -type Option struct{} +type Option func(logger *logger) ``` -### Result +### Result `STRUCT` ```go -type Result struct{} +type Result gjson.Result ``` -### R +### R `STRUCT` 记录器所记录的一条数据 ```go -type R struct{} +type R string ``` #### func (R) GetTime(layout string) time.Time > 获取该记录的时间 @@ -211,7 +280,7 @@ type R struct{} *** #### func (R) String() string *** -### Report +### Report `STRUCT` 分析报告 ```go type Report struct { diff --git a/utils/maths/README.md b/utils/maths/README.md index 6d3cc6a..36bc432 100644 --- a/utils/maths/README.md +++ b/utils/maths/README.md @@ -1,19 +1,20 @@ # Maths - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/maths) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[Compare](#Compare)|根据特定表达式比较两个值 |[IsContinuity](#IsContinuity)|检查一组值是否连续 @@ -40,114 +41,180 @@ |[MakeLastDigitsZero](#MakeLastDigitsZero)|返回一个新的数,其中 num 的最后 digits 位数被设为零。 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[CompareExpression](#compareexpression)|比较表达式 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[CompareExpression](#compareexpression)|比较表达式
+*** +## 详情信息 #### func Compare(a V, expression CompareExpression, b V) bool > 根据特定表达式比较两个值 + *** #### func IsContinuity(values S) bool > 检查一组值是否连续 + +示例代码: +```go + +func ExampleIsContinuity() { + fmt.Println(maths.IsContinuity([]int{1, 2, 3, 4, 5, 6, 7})) + fmt.Println(maths.IsContinuity([]int{1, 2, 3, 5, 5, 6, 7})) +} + +``` + *** #### func IsContinuityWithSort(values S) bool > 检查一组值排序后是否连续 + *** #### func GetDefaultTolerance() float64 > 获取默认误差范围 + *** #### func Pow(a int, n int) int > 整数幂运算 + *** #### func PowInt64(a int64, n int64) int64 > 整数幂运算 + *** #### func Min(a V, b V) V > 返回两个数之中较小的值 + *** #### func Max(a V, b V) V > 返回两个数之中较大的值 + *** #### func MinMax(a V, b V) (min V, max V) > 将两个数按照较小的和较大的顺序进行返回 + *** #### func MaxMin(a V, b V) (max V, min V) > 将两个数按照较大的和较小的顺序进行返回 + *** #### func Clamp(value V, min V, max V) V > 将给定值限制在最小值和最大值之间 + *** #### func Tolerance(value1 V, value2 V, tolerance V) bool > 检查两个值是否在一个误差范围内 + *** #### func Merge(refer V, a V, b V) V > 通过一个参考值合并两个数字 + *** #### func UnMerge(refer V, num V) (a V, b V) > 通过一个参考值取消合并的两个数字 + *** #### func MergeToInt64(v1 V, v2 V) int64 > 将两个数字合并为一个 int64 数字 + *** #### func UnMergeInt64(n int64) V, V > 将一个 int64 数字拆分为两个数字 + *** #### func ToContinuous(nums S) map[V]V > 将一组非连续的数字转换为从1开始的连续数字 > - 返回值是一个 map,key 是从 1 开始的连续数字,value 是原始数字 + +示例代码: +```go + +func ExampleToContinuous() { + var nums = []int{1, 2, 3, 5, 6, 7, 9, 10, 11} + var continuous = maths.ToContinuous(nums) + fmt.Println(nums) + fmt.Println(continuous) +} + +``` + *** #### func CountDigits(num V) int > 接收一个整数 num 作为输入,并返回该数字的位数 + *** #### func GetDigitValue(num int64, digit int) int64 > 接收一个整数 num 和一个表示目标位数的整数 digit 作为输入,并返 > 回数字 num 在指定位数上的数值。我们使用 math.Abs() 函数获取 num 的绝对值,并通 > 过除以10的操作将 num 移动到目标位数上。然后,通过取余运算得到位数上的数值 + *** #### func JoinNumbers(num1 V, n ...V) V > 将一组数字连接起来 + *** #### func IsOdd(n V) bool > 返回 n 是否为奇数 + *** #### func IsEven(n V) bool > 返回 n 是否为偶数 + *** #### func MakeLastDigitsZero(num T, digits int) T > 返回一个新的数,其中 num 的最后 digits 位数被设为零。 > - 函数首先创建一个 10 的 digits 次方的遮罩,然后通过整除和乘以这个遮罩来使 num 的最后 digits 位归零。 > - 当 T 类型为浮点型时,将被向下取整后再进行转换 + +
+查看 / 收起单元测试 + + +```go + +func TestMakeLastDigitsZero(t *testing.T) { + for i := 0; i < 20; i++ { + n := float64(random.Int64(100, 999999)) + t.Log(n, 3, maths.MakeLastDigitsZero(n, 3)) + } +} + +``` + + +
+ + *** -### CompareExpression +### CompareExpression `STRUCT` 比较表达式 ```go -type CompareExpression struct{} +type CompareExpression int ``` diff --git a/utils/memory/README.md b/utils/memory/README.md index 593fb3a..3035a1c 100644 --- a/utils/memory/README.md +++ b/utils/memory/README.md @@ -1,19 +1,20 @@ # Memory - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/memory) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[Run](#Run)|运行持久化缓存程序 |[BindPersistCacheProgram](#BindPersistCacheProgram)|绑定持久化缓存程序 @@ -21,18 +22,21 @@ |[NewOption](#NewOption)|暂无描述... -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Option](#option)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Option](#option)|暂无描述...
+*** +## 详情信息 #### func Run() > 运行持久化缓存程序 + *** #### func BindPersistCacheProgram(name string, handler OutputParamHandlerFunc, option ...*Option) func () @@ -46,6 +50,7 @@ > - 持久化程序处理函数参数类型必须与绑定的缓存程序输出参数类型一致,并且相同 name 的持久化程序必须在 BindAction 之后进行绑定 > - 默认情况下只有执行该函数返回的函数才会进行持久化,如果需要持久化策略,可以设置 option 参数或者自行实现策略调用返回的函数 > - 所有持久化程序绑定完成后,应该主动调用 Run 函数运行 + *** #### func BindAction(name string, handler Func) Func @@ -60,11 +65,38 @@ > > 使用场景: > - 例如在游戏中,需要根据玩家 ID 查询玩家信息,可以使用该函数进行绑定,当查询玩家信息时,如果缓存中存在该玩家信息,将直接返回缓存中的数据,否则将执行 handler 函数进行查询并缓存 + +
+查看 / 收起单元测试 + + +```go + +func TestBindAction(t *testing.T) { + var player *Player + player = QueryPlayer(1) + fmt.Println(player.ID) + player.ID = 666 + player = QueryPlayer(1) + fmt.Println(player.ID) + player = QueryPlayer(2) + fmt.Println(player.ID) + QueryPlayerPersist() + time.Sleep(times.Week) +} + +``` + + +
+ + *** #### func NewOption() *Option + *** -### Option +### Option `STRUCT` ```go type Option struct { diff --git a/utils/moving/README.md b/utils/moving/README.md index d109314..4be2d5f 100644 --- a/utils/moving/README.md +++ b/utils/moving/README.md @@ -1,19 +1,20 @@ # Moving - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/moving) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewTwoDimensional](#NewTwoDimensional)|创建一个用于2D对象移动的实例(TwoDimensional) |[WithTwoDimensionalTimeUnit](#WithTwoDimensionalTimeUnit)|通过特定时间单位创建 @@ -22,40 +23,79 @@ |[NewEntity](#NewEntity)|暂无描述... -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[TwoDimensional](#twodimensional)|用于2D对象移动的数据结构 -|[TwoDimensionalEntity](#twodimensionalentity)|2D移动对象接口定义 -|[Position2DChangeEventHandle](#position2dchangeeventhandle)|暂无描述... -|[TwoDimensionalOption](#twodimensionaloption)|暂无描述... +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[TwoDimensional](#twodimensional)|用于2D对象移动的数据结构 +|`INTERFACE`|[TwoDimensionalEntity](#twodimensionalentity)|2D移动对象接口定义 +|`STRUCT`|[Position2DChangeEventHandle](#position2dchangeeventhandle)|暂无描述... +|`STRUCT`|[TwoDimensionalOption](#twodimensionaloption)|暂无描述...
+*** +## 详情信息 #### func NewTwoDimensional(options ...TwoDimensionalOption[EID, PosType]) *TwoDimensional[EID, PosType] > 创建一个用于2D对象移动的实例(TwoDimensional) + +示例代码: +```go + +func ExampleNewTwoDimensional() { + m := moving2.NewTwoDimensional[int64, float64]() + defer func() { + m.Release() + }() + fmt.Println(m != nil) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestNewTwoDimensional(t *testing.T) { + m := moving2.NewTwoDimensional[int64, float64]() + defer func() { + m.Release() + }() +} + +``` + + +
+ + *** #### func WithTwoDimensionalTimeUnit(duration time.Duration) TwoDimensionalOption[EID, PosType] > 通过特定时间单位创建 > - 默认单位为1毫秒,最小单位也为1毫秒 + *** #### func WithTwoDimensionalIdleWaitTime(duration time.Duration) TwoDimensionalOption[EID, PosType] > 通过特定的空闲等待时间创建 > - 默认情况下在没有新的移动计划时将限制 100毫秒 + 移动间隔事件(默认100毫秒) + *** #### func WithTwoDimensionalInterval(duration time.Duration) TwoDimensionalOption[EID, PosType] > 通过特定的移动间隔时间创建 + *** #### func NewEntity(guid int64, speed float64) *MoveEntity + *** -### TwoDimensional +### TwoDimensional `STRUCT` 用于2D对象移动的数据结构 - 通过对象调用 MoveTo 方法后将开始执行该对象的移动 - 移动将在根据设置的每次移动间隔时间(WithTwoDimensionalInterval)进行移动,当无对象移动需要移动时将会进入短暂的休眠 @@ -73,18 +113,23 @@ type TwoDimensional[EID generic.Basic, PosType generic.SignedNumber] struct { position2DStopMoveEventHandles []Position2DStopMoveEventHandle[EID, PosType] } ``` -### TwoDimensionalEntity +### TwoDimensionalEntity `INTERFACE` 2D移动对象接口定义 ```go -type TwoDimensionalEntity[EID generic.Basic, PosType generic.SignedNumber] struct{} +type TwoDimensionalEntity[EID generic.Basic, PosType generic.SignedNumber] interface { + GetTwoDimensionalEntityID() EID + GetSpeed() float64 + GetPosition() geometry.Point[PosType] + SetPosition(geometry.Point[PosType]) +} ``` -### Position2DChangeEventHandle +### Position2DChangeEventHandle `STRUCT` ```go -type Position2DChangeEventHandle[EID generic.Basic, PosType generic.SignedNumber] struct{} +type Position2DChangeEventHandle[EID generic.Basic, PosType generic.SignedNumber] func(moving *TwoDimensional[EID, PosType], entity TwoDimensionalEntity[EID, PosType], oldX PosType) ``` -### TwoDimensionalOption +### TwoDimensionalOption `STRUCT` ```go -type TwoDimensionalOption[EID generic.Basic, PosType generic.SignedNumber] struct{} +type TwoDimensionalOption[EID generic.Basic, PosType generic.SignedNumber] func(moving *TwoDimensional[EID, PosType]) ``` diff --git a/utils/network/README.md b/utils/network/README.md index e0af33f..0d4120c 100644 --- a/utils/network/README.md +++ b/utils/network/README.md @@ -1,32 +1,28 @@ # Network - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/network) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[IP](#IP)|返回本机出站地址 -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 #### func IP() (ip net.IP, err error) > 返回本机出站地址 + *** diff --git a/utils/offset/README.md b/utils/offset/README.md index d565543..added2d 100644 --- a/utils/offset/README.md +++ b/utils/offset/README.md @@ -1,19 +1,20 @@ # Offset - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/offset) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewTime](#NewTime)|新建一个包含偏移的时间 |[SetGlobal](#SetGlobal)|设置全局偏移时间 @@ -22,36 +23,43 @@ |[Since](#Since)|获取当前时间偏移后的时间自从 t 以来经过的时间 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Time](#time)|带有偏移量的时间 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Time](#time)|带有偏移量的时间
+*** +## 详情信息 #### func NewTime(offset time.Duration) *Time > 新建一个包含偏移的时间 + *** #### func SetGlobal(offset time.Duration) > 设置全局偏移时间 + *** #### func GetGlobal() *Time > 获取全局偏移时间 + *** #### func Now() time.Time > 获取当前时间偏移后的时间 + *** #### func Since(t time.Time) time.Duration > 获取当前时间偏移后的时间自从 t 以来经过的时间 + *** -### Time +### Time `STRUCT` 带有偏移量的时间 ```go type Time struct { diff --git a/utils/random/README.md b/utils/random/README.md index 4e657de..b996a86 100644 --- a/utils/random/README.md +++ b/utils/random/README.md @@ -1,19 +1,20 @@ # Random - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/random) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[Dice](#Dice)|掷骰子 |[DiceN](#DiceN)|掷骰子 @@ -45,129 +46,173 @@ |[WeightMapKey](#WeightMapKey)|按权重随机从map中产生一个数据并返回数据和对应 key -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 #### func Dice() int > 掷骰子 > - 常规掷骰子将返回 1-6 的随机数 + *** #### func DiceN(n int) int > 掷骰子 > - 与 Dice 不同的是,将返回 1-N 的随机数 + *** #### func NetIP() net.IP > 返回一个随机的IP地址 + *** #### func Port() int > 返回一个随机的端口号 + *** #### func IPv4() string > 返回一个随机产生的IPv4地址。 + *** #### func IPv4Port() string > 返回一个随机产生的IPv4地址和端口。 + *** #### func Int64(min int64, max int64) int64 > 返回一个介于min和max之间的int64类型的随机数。 + *** #### func Int(min int, max int) int > 返回一个介于min和max之间的的int类型的随机数。 + *** #### func Duration(min int64, max int64) time.Duration > 返回一个介于min和max之间的的Duration类型的随机数。 + *** #### func Float64() float64 > 返回一个0~1的浮点数 + *** #### func Float32() float32 > 返回一个0~1的浮点数 + *** #### func IntN(n int) int > 返回一个0~n的整数 + *** #### func Bool() bool > 返回一个随机的布尔值 + *** #### func ProbabilitySlice(getProbabilityHandle func (data T) float64, data ...T) (hit T, miss bool) > 按概率随机从切片中产生一个数据并返回命中的对象及是否未命中 > - 当总概率小于 1 将会发生未命中的情况 + +
+查看 / 收起单元测试 + + +```go + +func TestProbabilitySlice(t *testing.T) { + var awards = []int{1, 2, 3, 4, 5, 6, 7} + var probability = []float64{0.1, 2, 0.1, 0.1, 0.1, 0.1, 0.1} + for i := 0; i < 50; i++ { + t.Log(random.ProbabilitySlice(func(data int) float64 { + return probability[data-1] + }, awards...)) + } +} + +``` + + +
+ + *** #### func ProbabilitySliceIndex(getProbabilityHandle func (data T) float64, data ...T) (hit T, index int, miss bool) > 按概率随机从切片中产生一个数据并返回命中的对象及对象索引以及是否未命中 > - 当总概率小于 1 将会发生未命中的情况 + *** #### func Probability(p int, full ...int) bool > 输入一个概率,返回是否命中 > - 当 full 不为空时,将以 full 为基数,p 为分子,计算命中概率 + *** #### func ProbabilityChooseOne(ps ...int) int > 输入一组概率,返回命中的索引 + *** #### func RefreshSeed(seed ...int64) + *** #### func ChineseName() string > 返回一个随机组成的中文姓名。 + *** #### func EnglishName() string > 返回一个随机组成的英文姓名。 + *** #### func Name() string > 返回一个随机组成的中文或英文姓名 > - 以1/2的概率决定生产的是中文还是英文姓名。 + *** #### func NumberString(min int, max int) string > 返回一个介于min和max之间的string类型的随机数。 + *** #### func NumberStringRepair(min int, max int) string > 返回一个介于min和max之间的string类型的随机数 > - 通过Int64生成一个随机数,当结果的字符串长度小于max的字符串长度的情况下,使用0在开头补齐。 + *** #### func HostName() string > 返回一个随机产生的hostname。 + *** #### func WeightSlice(getWeightHandle func (data T) int64, data ...T) T > 按权重随机从切片中产生一个数据并返回 + *** #### func WeightSliceIndex(getWeightHandle func (data T) int64, data ...T) (item T, index int) > 按权重随机从切片中产生一个数据并返回数据和对应索引 + *** #### func WeightMap(getWeightHandle func (data T) int64, data map[K]T) T > 按权重随机从map中产生一个数据并返回 + *** #### func WeightMapKey(getWeightHandle func (data T) int64, data map[K]T) (item T, key K) > 按权重随机从map中产生一个数据并返回数据和对应 key + *** diff --git a/utils/reflects/README.md b/utils/reflects/README.md index 47b9c3c..adb6763 100644 --- a/utils/reflects/README.md +++ b/utils/reflects/README.md @@ -1,19 +1,20 @@ # Reflects - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/reflects) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[WrapperFunc](#WrapperFunc)|包装函数 |[WrapperFuncBefore2After](#WrapperFuncBefore2After)|包装函数,前置函数执行前,后置函数执行后 @@ -25,43 +26,45 @@ |[GetPointer](#GetPointer)|获取指针 -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 #### func WrapperFunc(f any, wrapper func (call func ( []reflect.Value) []reflect.Value) func (args []reflect.Value) []reflect.Value) (wf Func, err error) > 包装函数 + *** #### func WrapperFuncBefore2After(f Func, before func (), after func ()) (wf Func, err error) > 包装函数,前置函数执行前,后置函数执行后 + *** #### func WrapperFuncBefore(f Func, before func ()) (wf Func, err error) > 包装函数,前置函数执行前 + *** #### func WrapperFuncAfter(f Func, after func ()) (wf Func, err error) > 包装函数,后置函数执行后 + *** #### func GetPtrUnExportFiled(s reflect.Value, filedIndex int) reflect.Value > 获取指针类型的未导出字段 + *** #### func SetPtrUnExportFiled(s reflect.Value, filedIndex int, val reflect.Value) > 设置指针类型的未导出字段 + *** #### func Copy(s reflect.Value) reflect.Value > 拷贝 + *** #### func GetPointer(src T) reflect.Value > 获取指针 + *** diff --git a/utils/runtimes/README.md b/utils/runtimes/README.md index e88b073..b381dc9 100644 --- a/utils/runtimes/README.md +++ b/utils/runtimes/README.md @@ -1,19 +1,20 @@ # Runtimes - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/runtimes) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[GetWorkingDir](#GetWorkingDir)|获取工作目录绝对路径 |[GetTempDir](#GetTempDir)|获取系统临时目录 @@ -22,31 +23,30 @@ |[CurrentRunningFuncName](#CurrentRunningFuncName)|获取正在运行的函数名 -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 #### func GetWorkingDir() string > 获取工作目录绝对路径 + *** #### func GetTempDir() string > 获取系统临时目录 + *** #### func GetExecutablePathByBuild() string > 获取当前执行文件绝对路径(go build) + *** #### func GetExecutablePathByCaller() string > 获取当前执行文件绝对路径(go run) + *** #### func CurrentRunningFuncName(skip ...int) string > 获取正在运行的函数名 + *** diff --git a/utils/sole/README.md b/utils/sole/README.md index 00b3daf..09594fc 100644 --- a/utils/sole/README.md +++ b/utils/sole/README.md @@ -1,19 +1,20 @@ # Sole - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/sole) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[RegNameSpace](#RegNameSpace)|注册特定命名空间的唯一标识符 |[UnRegNameSpace](#UnRegNameSpace)|解除注销特定命名空间的唯一标识符 @@ -33,80 +34,98 @@ |[AutoIncrementString](#AutoIncrementString)|获取一个自增的字符串 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[Once](#once)|用于数据取值去重的结构体 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[Once](#once)|用于数据取值去重的结构体
+*** +## 详情信息 #### func RegNameSpace(name any) > 注册特定命名空间的唯一标识符 + *** #### func UnRegNameSpace(name any) > 解除注销特定命名空间的唯一标识符 + *** #### func Get() int64 > 获取全局唯一标识符 + *** #### func Reset() > 重置全局唯一标识符 + *** #### func GetWith(name any) int64 > 获取特定命名空间的唯一标识符 + *** #### func ResetWith(name any) > 重置特定命名空间的唯一标识符 + *** #### func NewOnce() *Once[V] > 创建一个用于数据取值去重的结构实例 + *** #### func SonyflakeIDE() int64, error > 获取一个雪花id + *** #### func SonyflakeID() int64 > 获取一个雪花id + *** #### func SonyflakeSetting(settings sonyflake.Settings) > 配置雪花id生成策略 + *** #### func AutoIncrementUint32() uint32 > 获取一个自增的 uint32 值 + *** #### func AutoIncrementUint64() uint64 > 获取一个自增的 uint64 值 + *** #### func AutoIncrementInt32() int32 > 获取一个自增的 int32 值 + *** #### func AutoIncrementInt64() int64 > 获取一个自增的 int64 值 + *** #### func AutoIncrementInt() int > 获取一个自增的 int 值 + *** #### func AutoIncrementString() string > 获取一个自增的字符串 + *** -### Once +### Once `STRUCT` 用于数据取值去重的结构体 ```go type Once[V any] struct { diff --git a/utils/sorts/README.md b/utils/sorts/README.md index a7d3a42..ff779d9 100644 --- a/utils/sorts/README.md +++ b/utils/sorts/README.md @@ -1,31 +1,26 @@ # Sorts - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/sorts) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[Topological](#Topological)|拓扑排序是一种对有向图进行排序的算法,它可以用来解决一些依赖关系的问题,比如计算字段的依赖关系。拓扑排序会将存在依赖关系的元素进行排序,使得依赖关系的元素总是排在被依赖的元素之前。 -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 #### func Topological(slice S, queryIndexHandler func (item V) Index, queryDependsHandler func (item V) []Index) S, error > 拓扑排序是一种对有向图进行排序的算法,它可以用来解决一些依赖关系的问题,比如计算字段的依赖关系。拓扑排序会将存在依赖关系的元素进行排序,使得依赖关系的元素总是排在被依赖的元素之前。 @@ -34,4 +29,93 @@ > - queryDependsHandler: 用于查询切片中每个元素的依赖关系,返回的是一个索引切片,如果没有依赖关系,那么返回空切片 > > 该函数在存在循环依赖的情况下将会返回 ErrCircularDependencyDetected 错误 + +示例代码: +```go + +func ExampleTopological() { + type Item struct { + ID int + Depends []int + } + var items = []Item{{ID: 2, Depends: []int{4}}, {ID: 1, Depends: []int{2, 3}}, {ID: 3, Depends: []int{4}}, {ID: 4, Depends: []int{5}}, {ID: 5, Depends: []int{}}} + var sorted, err = sorts.Topological(items, func(item Item) int { + return item.ID + }, func(item Item) []int { + return item.Depends + }) + if err != nil { + return + } + for _, item := range sorted { + fmt.Println(item.ID, "|", item.Depends) + } +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestTopological(t *testing.T) { + type Item struct { + ID int + Depends []int + } + var items = []Item{{ID: 2, Depends: []int{4}}, {ID: 1, Depends: []int{2, 3}}, {ID: 3, Depends: []int{4}}, {ID: 4, Depends: []int{5}}, {ID: 5, Depends: []int{}}} + var sorted, err = sorts.Topological(items, func(item Item) int { + return item.ID + }, func(item Item) []int { + return item.Depends + }) + if err != nil { + t.Error(err) + return + } + for _, item := range sorted { + t.Log(item.ID, "|", item.Depends) + } +} + +``` + + +
+ + +
+查看 / 收起基准测试 + + +```go + +func BenchmarkTopological(b *testing.B) { + type Item struct { + ID int + Depends []int + } + var items = []Item{{ID: 2, Depends: []int{4}}, {ID: 1, Depends: []int{2, 3}}, {ID: 3, Depends: []int{4}}, {ID: 4, Depends: []int{5}}, {ID: 5, Depends: []int{}}} + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := Topological(items, func(item Item) int { + return item.ID + }, func(item Item) []int { + return item.Depends + }) + if err != nil { + b.Error(err) + return + } + } +} + +``` + + +
+ + *** diff --git a/utils/str/README.md b/utils/str/README.md index 69dd2f0..0d9846e 100644 --- a/utils/str/README.md +++ b/utils/str/README.md @@ -1,19 +1,20 @@ # Str - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/str) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[RangeLine](#RangeLine)|对传入的eachString进行按行切片后再进行遍历 |[SplitTrimSpace](#SplitTrimSpace)|按照空格分割字符串并去除空格 @@ -39,91 +40,103 @@ |[FormatSpeedyFloat64](#FormatSpeedyFloat64)|返回numberStr经过格式化后去除空格和“,”分隔符的结果 -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 #### func RangeLine(eachString string, eachFunc func (index int, line string) error) error > 对传入的eachString进行按行切片后再进行遍历 > - 该函数会预先对“\r\n”进行处理替换为“\n”。 > - 在遍历到每一行的时候会将结果index和line作为入参传入eachFunc中进行调用。 > - index表示了当前行的行号(由0开始),line表示了当前行的内容。 + *** #### func SplitTrimSpace(str string, sep string) []string > 按照空格分割字符串并去除空格 + *** #### func FirstUpper(str string) string > 首字母大写 + *** #### func FirstLower(str string) string > 首字母小写 + *** #### func FirstUpperBytes(str []byte) []byte > 首字母大写 + *** #### func FirstLowerBytes(str []byte) []byte > 首字母小写 + *** #### func IsEmpty(str string) bool > 判断字符串是否为空 + *** #### func IsEmptyBytes(str []byte) bool > 判断字符串是否为空 + *** #### func IsNotEmpty(str string) bool > 判断字符串是否不为空 + *** #### func IsNotEmptyBytes(str []byte) bool > 判断字符串是否不为空 + *** #### func SnakeString(str string) string > 蛇形字符串 + *** #### func SnakeStringBytes(str []byte) []byte > 蛇形字符串 + *** #### func CamelString(str string) string > 驼峰字符串 + *** #### func CamelStringBytes(str []byte) []byte > 驼峰字符串 + *** #### func SortJoin(delimiter string, s ...string) string > 将多个字符串排序后拼接 + *** #### func HideSensitivity(str string) (result string) > 返回防敏感化后的字符串 > - 隐藏身份证、邮箱、手机号等敏感信息用*号替代 + *** #### func ThousandsSeparator(str string) string > 返回将str进行千位分隔符处理后的字符串。 + *** #### func KV(str string, tag ...string) string, string > 返回str经过转换后形成的key、value > - 这里tag表示使用什么字符串来区分key和value的分隔符。 > - 默认情况即不传入tag的情况下分隔符为“=”。 + *** #### func FormatSpeedyInt(numberStr string) int, error @@ -131,6 +144,7 @@ > - 当字符串为“123,456,789”的时候,返回结果为“123456789”。 > - 当字符串为“123 456 789”的时候,返回结果为“123456789”。 > - 当字符串为“1 23, 45 6, 789”的时候,返回结果为“123456789”。 + *** #### func FormatSpeedyInt64(numberStr string) int64, error @@ -138,6 +152,7 @@ > - 当字符串为“123,456,789”的时候,返回结果为“123456789”。 > - 当字符串为“123 456 789”的时候,返回结果为“123456789”。 > - 当字符串为“1 23, 45 6, 789”的时候,返回结果为“123456789”。 + *** #### func FormatSpeedyFloat32(numberStr string) float64, error @@ -145,6 +160,7 @@ > - 当字符串为“123,456,789.123”的时候,返回结果为“123456789.123”。 > - 当字符串为“123 456 789.123”的时候,返回结果为“123456789.123”。 > - 当字符串为“1 23, 45 6, 789.123”的时候,返回结果为“123456789.123”。 + *** #### func FormatSpeedyFloat64(numberStr string) float64, error @@ -152,4 +168,5 @@ > - 当字符串为“123,456,789.123”的时候,返回结果为“123456789.123”。 > - 当字符串为“123 456 789.123”的时候,返回结果为“123456789.123”。 > - 当字符串为“1 23, 45 6, 789.123”的时候,返回结果为“123456789.123”。 + *** diff --git a/utils/super/README.md b/utils/super/README.md index cf5c3c7..e999ee3 100644 --- a/utils/super/README.md +++ b/utils/super/README.md @@ -1,19 +1,20 @@ # Super - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/super) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[NewBitSet](#NewBitSet)|通过指定的 Bit 位创建一个 BitSet |[TryWriteChannel](#TryWriteChannel)|尝试写入 channel,如果 channel 无法写入则忽略,返回是否写入成功 @@ -87,249 +88,417 @@ |[CompareVersion](#CompareVersion)|返回一个整数,用于表示两个版本号的比较结果: -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[BitSet](#bitset)|是一个可以动态增长的比特位集合 -|[LossCounter](#losscounter)|暂无描述... -|[Matcher](#matcher)|匹配器 -|[Permission](#permission)|暂无描述... -|[StackGo](#stackgo)|用于获取上一个协程调用的堆栈信息 -|[VerifyHandle](#verifyhandle)|校验句柄 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[BitSet](#bitset)|是一个可以动态增长的比特位集合 +|`STRUCT`|[LossCounter](#losscounter)|暂无描述... +|`STRUCT`|[Matcher](#matcher)|匹配器 +|`STRUCT`|[Permission](#permission)|暂无描述... +|`STRUCT`|[StackGo](#stackgo)|用于获取上一个协程调用的堆栈信息 +|`STRUCT`|[VerifyHandle](#verifyhandle)|校验句柄
+*** +## 详情信息 #### func NewBitSet(bits ...Bit) *BitSet[Bit] > 通过指定的 Bit 位创建一个 BitSet + *** #### func TryWriteChannel(ch chan T, data T) bool > 尝试写入 channel,如果 channel 无法写入则忽略,返回是否写入成功 > - 无法写入的情况包括:channel 已满、channel 已关闭 + *** #### func TryWriteChannelByHandler(ch chan T, data T, handler func ()) > 尝试写入 channel,如果 channel 无法写入则执行 handler > - 无法写入的情况包括:channel 已满、channel 已关闭 + *** #### func RegError(code int, message string) error > 通过错误码注册错误,返回错误的引用 + *** #### func RegErrorRef(code int, message string, ref error) error > 通过错误码注册错误,返回错误的引用 > - 引用将会被重定向到注册的错误信息 + *** #### func GetError(err error) int, error > 通过错误引用获取错误码和真实错误信息,如果错误不存在则返回 0,如果错误引用不存在则返回原本的错误 + +
+查看 / 收起单元测试 + + +```go + +func TestGetError(t *testing.T) { + var ErrNotFound = errors.New("not found") + var _ = super.RegErrorRef(100, "test error", ErrNotFound) + t.Log(super.GetError(ErrNotFound)) +} + +``` + + +
+ + *** #### func RecoverTransform(a any) error > recover 错误转换 + +示例代码: +```go + +func ExampleRecoverTransform() { + defer func() { + if err := super.RecoverTransform(recover()); err != nil { + fmt.Println(err) + } + }() + panic("test") +} + +``` + *** #### func Handle(f func ()) > 执行 f 函数,如果 f 为 nil,则不执行 + *** #### func HandleErr(f func () error) error > 执行 f 函数,如果 f 为 nil,则不执行 + *** #### func HandleV(v V, f func (v V)) > 执行 f 函数,如果 f 为 nil,则不执行 + *** #### func GoFormat(filePath string) > go 代码格式化 + *** #### func If(expression bool, t T, f T) T + *** #### func MarshalJSON(v interface {}) []byte > 将对象转换为 json > - 当转换失败时,将返回 json 格式的空对象 + *** #### func MarshalJSONE(v interface {}) []byte, error > 将对象转换为 json > - 当转换失败时,将返回错误信息 + *** #### func UnmarshalJSON(data []byte, v interface {}) error > 将 json 转换为对象 + *** #### func MarshalIndentJSON(v interface {}, prefix string, indent string) []byte > 将对象转换为 json + *** #### func MarshalToTargetWithJSON(src interface {}, dest interface {}) error > 将对象转换为目标对象 + *** #### func StartLossCounter() *LossCounter > 开始损耗计数 + *** #### func Match(value Value) *Matcher[Value, Result] > 匹配 + +
+查看 / 收起单元测试 + + +```go + +func TestMatch(t *testing.T) { + Convey("TestMatch", t, func() { + So(super.Match[int, string](1).Case(1, "a").Case(2, "b").Default("c"), ShouldEqual, "a") + So(super.Match[int, string](2).Case(1, "a").Case(2, "b").Default("c"), ShouldEqual, "b") + So(super.Match[int, string](3).Case(1, "a").Case(2, "b").Default("c"), ShouldEqual, "c") + }) +} + +``` + + +
+ + *** #### func IsNumber(v any) bool > 判断是否为数字 + *** #### func NumberToRome(num int) string > 将数字转换为罗马数字 + +
+查看 / 收起单元测试 + + +```go + +func TestNumberToRome(t *testing.T) { + tests := []struct { + input int + output string + }{{input: 1, output: "I"}, {input: 5, output: "V"}, {input: 10, output: "X"}, {input: 50, output: "L"}, {input: 100, output: "C"}, {input: 500, output: "D"}, {input: 1000, output: "M"}} + for _, test := range tests { + result := super.NumberToRome(test.input) + if result != test.output { + t.Errorf("NumberToRome(%d) = %s; want %s", test.input, result, test.output) + } + } +} + +``` + + +
+ + *** #### func StringToInt(value string) int > 字符串转换为整数 + *** #### func StringToFloat64(value string) float64 > 字符串转换为 float64 + *** #### func StringToBool(value string) bool > 字符串转换为 bool + *** #### func StringToUint64(value string) uint64 > 字符串转换为 uint64 + *** #### func StringToUint(value string) uint > 字符串转换为 uint + *** #### func StringToFloat32(value string) float32 > 字符串转换为 float32 + *** #### func StringToInt64(value string) int64 > 字符串转换为 int64 + *** #### func StringToUint32(value string) uint32 > 字符串转换为 uint32 + *** #### func StringToInt32(value string) int32 > 字符串转换为 int32 + *** #### func StringToUint16(value string) uint16 > 字符串转换为 uint16 + *** #### func StringToInt16(value string) int16 > 字符串转换为 int16 + *** #### func StringToUint8(value string) uint8 > 字符串转换为 uint8 + *** #### func StringToInt8(value string) int8 > 字符串转换为 int8 + *** #### func StringToByte(value string) byte > 字符串转换为 byte + *** #### func StringToRune(value string) rune > 字符串转换为 rune + *** #### func IntToString(value int) string > 整数转换为字符串 + *** #### func Float64ToString(value float64) string > float64 转换为字符串 + *** #### func BoolToString(value bool) string > bool 转换为字符串 + *** #### func Uint64ToString(value uint64) string > uint64 转换为字符串 + *** #### func UintToString(value uint) string > uint 转换为字符串 + *** #### func Float32ToString(value float32) string > float32 转换为字符串 + *** #### func Int64ToString(value int64) string > int64 转换为字符串 + *** #### func Uint32ToString(value uint32) string > uint32 转换为字符串 + *** #### func Int32ToString(value int32) string > int32 转换为字符串 + *** #### func Uint16ToString(value uint16) string > uint16 转换为字符串 + *** #### func Int16ToString(value int16) string > int16 转换为字符串 + *** #### func Uint8ToString(value uint8) string > uint8 转换为字符串 + *** #### func Int8ToString(value int8) string > int8 转换为字符串 + *** #### func ByteToString(value byte) string > byte 转换为字符串 + *** #### func RuneToString(value rune) string > rune 转换为字符串 + *** #### func StringToSlice(value string) []string > 字符串转换为切片 + *** #### func SliceToString(value []string) string > 切片转换为字符串 + *** #### func NewPermission() *Permission[Code, EntityID] > 创建权限 + +
+查看 / 收起单元测试 + + +```go + +func TestNewPermission(t *testing.T) { + const ( + Read = 1 << iota + Write + Execute + ) + p := super.NewPermission[int, int]() + p.AddPermission(1, Read, Write) + t.Log(p.HasPermission(1, Read)) + t.Log(p.HasPermission(1, Write)) + p.SetPermission(2, Read|Write) + t.Log(p.HasPermission(2, Read)) + t.Log(p.HasPermission(2, Execute)) + p.SetPermission(2, Execute) + t.Log(p.HasPermission(2, Execute)) + t.Log(p.HasPermission(2, Read)) + t.Log(p.HasPermission(2, Write)) + p.RemovePermission(2, Execute) + t.Log(p.HasPermission(2, Execute)) +} + +``` + + +
+ + *** #### func Retry(count int, interval time.Duration, f func () error) error > 根据提供的 count 次数尝试执行 f 函数,如果 f 函数返回错误,则在 interval 后重试,直到成功或者达到 count 次数 + *** #### func RetryByRule(f func () error, rule func (count int) time.Duration) error > 根据提供的规则尝试执行 f 函数,如果 f 函数返回错误,则根据 rule 的返回值进行重试 > - rule 将包含一个入参,表示第几次重试,返回值表示下一次重试的时间间隔,当返回值为 0 时,表示不再重试 > - rule 的 count 将在 f 首次失败后变为 1,因此 rule 的入参将从 1 开始 + *** #### func RetryByExponentialBackoff(f func () error, maxRetries int, baseDelay time.Duration, maxDelay time.Duration, multiplier float64, randomization float64, ignoreErrors ...error) error @@ -340,6 +509,7 @@ > - multiplier:延迟时间的乘数,通常为 2 > - randomization:延迟时间的随机化因子,通常为 0.5 > - ignoreErrors:忽略的错误,当 f 返回的错误在 ignoreErrors 中时,将不会进行重试 + *** #### func ConditionalRetryByExponentialBackoff(f func () error, cond func () bool, maxRetries int, baseDelay time.Duration, maxDelay time.Duration, multiplier float64, randomization float64, ignoreErrors ...error) error @@ -348,54 +518,164 @@ > > 该函数通常用于在重试过程中,需要中断重试的场景,例如: > - 用户请求开始游戏,由于网络等情况,进入重试状态。此时用户再次发送开始游戏请求,此时需要中断之前的重试,避免重复进入游戏 + *** #### func RetryAsync(count int, interval time.Duration, f func () error, callback func (err error)) > 与 Retry 类似,但是是异步执行 > - 传入的 callback 函数会在执行完毕后被调用,如果执行成功,则 err 为 nil,否则为错误信息 > - 如果 callback 为 nil,则不会在执行完毕后被调用 + *** #### func RetryForever(interval time.Duration, f func () error) > 根据提供的 interval 时间间隔尝试执行 f 函数,如果 f 函数返回错误,则在 interval 后重试,直到成功 + *** #### func NewStackGo() *StackGo > 返回一个用于获取上一个协程调用的堆栈信息的收集器 + *** #### func LaunchTime() time.Time > 获取程序启动时间 + *** #### func Hostname() string > 获取主机名 + *** #### func PID() int > 获取进程 PID + *** #### func StringToBytes(s string) []byte > 以零拷贝的方式将字符串转换为字节切片 + *** #### func BytesToString(b []byte) string > 以零拷贝的方式将字节切片转换为字符串 + *** #### func Convert(src A) B > 以零拷贝的方式将一个对象转换为另一个对象 > - 两个对象字段必须完全一致 > - 该函数可以绕过私有字段的访问限制 + +
+查看 / 收起单元测试 + + +```go + +func TestConvert(t *testing.T) { + type B struct { + nocmp [0]func() + v atomic.Value + } + var a = atomic.NewString("hello") + var b = super.Convert[*atomic.String, *B](a) + fmt.Println(b.v.Load()) +} + +``` + + +
+ + *** #### func Verify(handle func ( V)) *VerifyHandle[V] > 对特定表达式进行校验,当表达式不成立时,将执行 handle + +示例代码: +```go + +func ExampleVerify() { + var getId = func() int { + return 1 + } + var n *super.VerifyHandle[int] + super.Verify(func(err error) { + fmt.Println(err) + }).Case(getId() == 1, errors.New("id can't be 1")).Do() + super.Verify(func(err error) { + fmt.Println(err) + }).PreCase(func() bool { + return n == nil + }, errors.New("n can't be nil"), func(verify *super.VerifyHandle[error]) bool { + return verify.Do() + }) +} + +``` + *** #### func OldVersion(version1 string, version2 string) bool > 检查 version2 对于 version1 来说是不是旧版本 + +示例代码: +```go + +func ExampleOldVersion() { + result := super.OldVersion("1.2.3", "1.2.2") + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestOldVersion(t *testing.T) { + testCases := []struct { + version1 string + version2 string + want bool + }{{"1.2.3", "1.2.2", true}, {"1.2.1", "1.2.2", false}, {"1.2.3", "1.2.3", false}, {"v1.2.3", "v1.2.2", true}, {"v1.2.3", "v1.2.4", false}, {"v1.2.3", "1.2.3", false}, {"vxx2faf.d2ad5.dd3", "gga2faf.d2ad5.dd2", true}, {"awd2faf.d2ad4.dd3", "vsd2faf.d2ad5.dd3", false}, {"vxd2faf.d2ad5.dd3", "qdq2faf.d2ad5.dd3", false}, {"1.2.3", "vdafe2faf.d2ad5.dd3", false}, {"v1.2.3", "vdafe2faf.d2ad5.dd3", false}} + for _, tc := range testCases { + got := super.OldVersion(tc.version1, tc.version2) + if got != tc.want { + t.Errorf("OldVersion(%q, %q) = %v; want %v", tc.version1, tc.version2, got, tc.want) + } + } +} + +``` + + +
+ + +
+查看 / 收起基准测试 + + +```go + +func BenchmarkOldVersion(b *testing.B) { + for i := 0; i < b.N; i++ { + super.OldVersion("vfe2faf.d2ad5.dd3", "vda2faf.d2ad5.dd2") + } +} + +``` + + +
+ + *** #### func CompareVersion(version1 string, version2 string) int @@ -403,8 +683,63 @@ > - 如果 version1 大于 version2,它将返回 1 > - 如果 version1 小于 version2,它将返回 -1 > - 如果 version1 和 version2 相等,它将返回 0 + +示例代码: +```go + +func ExampleCompareVersion() { + result := super.CompareVersion("1.2.3", "1.2.2") + fmt.Println(result) +} + +``` + +
+查看 / 收起单元测试 + + +```go + +func TestCompareVersion(t *testing.T) { + testCases := []struct { + version1 string + version2 string + want int + }{{"1.2.3", "1.2.2", 1}, {"1.2.1", "1.2.2", -1}, {"1.2.3", "1.2.3", 0}, {"v1.2.3", "v1.2.2", 1}, {"v1.2.3", "v1.2.4", -1}, {"v1.2.3", "1.2.3", 0}, {"vde2faf.d2ad5.dd3", "e2faf.d2ad5.dd2", 1}, {"vde2faf.d2ad4.dd3", "vde2faf.d2ad5.dd3", -1}, {"vfe2faf.d2ad5.dd3", "ve2faf.d2ad5.dd3", 0}, {"1.2.3", "vdafe2faf.d2ad5.dd3", -1}, {"v1.2.3", "vdafe2faf.d2ad5.dd3", -1}} + for _, tc := range testCases { + got := super.CompareVersion(tc.version1, tc.version2) + if got != tc.want { + t.Errorf("CompareVersion(%q, %q) = %v; want %v", tc.version1, tc.version2, got, tc.want) + } + } +} + +``` + + +
+ + +
+查看 / 收起基准测试 + + +```go + +func BenchmarkCompareVersion(b *testing.B) { + for i := 0; i < b.N; i++ { + super.CompareVersion("vfe2faf.d2ad5.dd3", "afe2faf.d2ad5.dd2") + } +} + +``` + + +
+ + *** -### BitSet +### BitSet `STRUCT` 是一个可以动态增长的比特位集合 - 默认情况下将使用 64 位无符号整数来表示比特位,当需要表示的比特位超过 64 位时,将自动增长 ```go @@ -414,13 +749,76 @@ type BitSet[Bit generic.Integer] struct { ``` #### func (*BitSet) Set(bit Bit) *BitSet[Bit] > 将指定的位 bit 设置为 1 +
+查看 / 收起单元测试 + + +```go + +func TestBitSet_Set(t *testing.T) { + bs := super.NewBitSet(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + bs.Set(11) + bs.Set(12) + bs.Set(13) + t.Log(bs) +} + +``` + + +
+ + *** #### func (*BitSet) Del(bit Bit) *BitSet[Bit] > 将指定的位 bit 设置为 0 +
+查看 / 收起单元测试 + + +```go + +func TestBitSet_Del(t *testing.T) { + bs := super.NewBitSet(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + bs.Del(11) + bs.Del(12) + bs.Del(13) + bs.Del(10) + t.Log(bs) +} + +``` + + +
+ + *** #### func (*BitSet) Shrink() *BitSet[Bit] > 将 BitSet 中的比特位集合缩小到最小 > - 正常情况下当 BitSet 中的比特位超出 64 位时,将自动增长,当 BitSet 中的比特位数量减少时,可以使用该方法将 BitSet 中的比特位集合缩小到最小 +
+查看 / 收起单元测试 + + +```go + +func TestBitSet_Shrink(t *testing.T) { + bs := super.NewBitSet(63) + t.Log(bs.Cap()) + bs.Set(200) + t.Log(bs.Cap()) + bs.Del(200) + bs.Shrink() + t.Log(bs.Cap()) +} + +``` + + +
+ + *** #### func (*BitSet) Cap() int > 返回当前 BitSet 中可以表示的最大比特位数量 @@ -509,7 +907,7 @@ type BitSet[Bit generic.Integer] struct { #### func (*BitSet) UnmarshalJSON(data []byte) error > 实现 json.Unmarshaler 接口 *** -### LossCounter +### LossCounter `STRUCT` ```go type LossCounter struct { @@ -526,7 +924,7 @@ type LossCounter struct { *** #### func (*LossCounter) String() string *** -### Matcher +### Matcher `STRUCT` 匹配器 ```go type Matcher[Value any, Result any] struct { @@ -535,7 +933,7 @@ type Matcher[Value any, Result any] struct { d bool } ``` -### Permission +### Permission `STRUCT` ```go type Permission[Code generic.Integer, EntityID comparable] struct { @@ -543,7 +941,7 @@ type Permission[Code generic.Integer, EntityID comparable] struct { l sync.RWMutex } ``` -### StackGo +### StackGo `STRUCT` 用于获取上一个协程调用的堆栈信息 - 应当最先运行 Wait 函数,然后在其他协程中调用 Stack 函数或者 GiveUp 函数 - 适用于跨协程同步通讯,例如单线程的消息处理统计耗时打印堆栈信息 @@ -567,7 +965,7 @@ type StackGo struct { > - 在调用 Wait 函数后调用该函数,将会放弃收集消息堆栈并且释放资源 > - 在调用 GiveUp 函数后调用 Stack 函数,将会 panic *** -### VerifyHandle +### VerifyHandle `STRUCT` 校验句柄 ```go type VerifyHandle[V any] struct { diff --git a/utils/timer/README.md b/utils/timer/README.md index 6967b86..b59ae25 100644 --- a/utils/timer/README.md +++ b/utils/timer/README.md @@ -1,19 +1,20 @@ # Timer - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/timer) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[RegSystemNewDayEvent](#RegSystemNewDayEvent)|注册系统新的一天事件 |[OnSystemNewDayEvent](#OnSystemNewDayEvent)|系统新的一天事件 @@ -26,71 +27,82 @@ |[GetTicker](#GetTicker)|获取标准池中的一个定时器 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[SystemNewDayEventHandle](#systemnewdayeventhandle)|暂无描述... -|[Option](#option)|暂无描述... -|[Pool](#pool)|定时器池 -|[Scheduler](#scheduler)|调度器 -|[Ticker](#ticker)|定时器 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[SystemNewDayEventHandle](#systemnewdayeventhandle)|暂无描述... +|`STRUCT`|[Option](#option)|暂无描述... +|`STRUCT`|[Pool](#pool)|定时器池 +|`STRUCT`|[Scheduler](#scheduler)|调度器 +|`STRUCT`|[Ticker](#ticker)|定时器
+*** +## 详情信息 #### func RegSystemNewDayEvent(ticker *Ticker, name string, trigger bool, handle SystemNewDayEventHandle) > 注册系统新的一天事件 > - 建议全局注册一个事件后再另行拓展 > - 将特定 name 的定时任务注册到 ticker 中,在系统时间到达每天的 00:00:00 时触发,如果 trigger 为 true,则立即触发一次 + *** #### func OnSystemNewDayEvent(name string) > 系统新的一天事件 + *** #### func RegOffsetTimeNewDayEvent(ticker *Ticker, name string, offset *offset.Time, trigger bool, handle OffsetTimeNewDayEventHandle) > 注册偏移时间新的一天事件 > - 建议全局注册一个事件后再另行拓展 > - 与 RegSystemNewDayEvent 类似,但是触发时间为 offset 时间到达每天的 00:00:00 + *** #### func OnOffsetTimeNewDayEvent(name string) > 偏移时间新的一天事件 + *** #### func WithCaller(handle func (name string, caller func ())) Option > 通过其他的 handle 执行 Caller + *** #### func WithMark(mark string) Option > 通过特定的标记创建定时器 + *** #### func NewPool(tickerPoolSize int) *Pool > 创建一个定时器池,当 tickerPoolSize 小于等于 0 时,将会引发 panic,可指定为 DefaultTickerPoolSize + *** #### func SetPoolSize(size int) > 设置标准池定时器池大小 > - 默认值为 DefaultTickerPoolSize,当定时器池中的定时器不足时,会自动创建新的定时器,当定时器释放时,会将多余的定时器进行释放,否则将放入定时器池中 + *** #### func GetTicker(size int, options ...Option) *Ticker > 获取标准池中的一个定时器 + *** -### SystemNewDayEventHandle +### SystemNewDayEventHandle `STRUCT` ```go -type SystemNewDayEventHandle struct{} +type SystemNewDayEventHandle func() ``` -### Option +### Option `STRUCT` ```go -type Option struct{} +type Option func(ticker *Ticker) ``` -### Pool +### Pool `STRUCT` 定时器池 ```go type Pool struct { @@ -111,7 +123,7 @@ type Pool struct { > 释放定时器池的资源,释放后由其产生的 Ticker 在 Ticker.Release 后将不再回到池中,而是直接释放 > - 虽然定时器池已被释放,但是依旧可以产出 Ticker *** -### Scheduler +### Scheduler `STRUCT` 调度器 ```go type Scheduler struct { @@ -138,7 +150,7 @@ type Scheduler struct { #### func (*Scheduler) Caller() > 可由外部发起调用的执行函数 *** -### Ticker +### Ticker `STRUCT` 定时器 ```go type Ticker struct { @@ -168,6 +180,32 @@ type Ticker struct { *** #### func (*Ticker) Cron(name string, expression string, handleFunc interface {}, args ...interface {}) > 通过 cron 表达式设置一个调度器,当 cron 表达式错误时,将会引发 panic +
+查看 / 收起单元测试 + + +```go + +func TestTicker_Cron(t *testing.T) { + ticker := timer.GetTicker(10) + ticker.After("1_sec", time.Second, func() { + t.Log(time.Now().Format(time.DateTime), "1_sec") + }) + ticker.Loop("1_sec_loop_3", 0, time.Second, 3, func() { + t.Log(time.Now().Format(time.DateTime), "1_sec_loop_3") + }) + ticker.Cron("5_sec_cron", "0/5 * * * * * ?", func() { + t.Log(time.Now().Format(time.DateTime), "5_sec_cron") + }) + time.Sleep(times.Week) +} + +``` + + +
+ + *** #### func (*Ticker) CronByInstantly(name string, expression string, handleFunc interface {}, args ...interface {}) > 与 Cron 相同,但是会立即执行一次 diff --git a/utils/times/README.md b/utils/times/README.md index 00a9ab7..039774f 100644 --- a/utils/times/README.md +++ b/utils/times/README.md @@ -1,19 +1,20 @@ # Times - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/times) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[CalcNextSec](#CalcNextSec)|计算下一个N秒在多少秒之后 |[CalcNextSecWithTime](#CalcNextSecWithTime)|计算下一个N秒在多少秒之后 @@ -65,23 +66,37 @@ |[NewPeriodWithNanosecond](#NewPeriodWithNanosecond)|创建一个时间段,从 t 开始,持续 nanosecond 纳秒 -> 结构体定义 +> 类型定义 -|结构体|描述 -|:--|:-- -|[StateLine](#stateline)|状态时间线 -|[Period](#period)|表示一个时间段 +|类型|名称|描述 +|:--|:--|:-- +|`STRUCT`|[StateLine](#stateline)|状态时间线 +|`STRUCT`|[Period](#period)|表示一个时间段
+*** +## 详情信息 #### func CalcNextSec(sec int) int > 计算下一个N秒在多少秒之后 + *** #### func CalcNextSecWithTime(t time.Time, sec int) int > 计算下一个N秒在多少秒之后 + +示例代码: +```go + +func ExampleCalcNextSecWithTime() { + now := time.Date(2023, 9, 20, 0, 0, 3, 0, time.Local) + fmt.Println(times.CalcNextSecWithTime(now, 10)) +} + +``` + *** #### func CalcNextTimeWithRefer(now time.Time, refer time.Duration) time.Time @@ -90,192 +105,301 @@ > - 假设当 now 为 14:15:16 , 参考时间为 20 分钟, 则返回 14:20:00 > > 当 refer 小于 1 分钟时,将会返回当前时间 + *** #### func IntervalFormatSet(intervalType int, str string) > 针对 IntervalFormat 函数设置格式化内容 + *** #### func IntervalFormat(time1 time.Time, time2 time.Time) string > 返回指定时间戳之间的间隔 > - 使用传入的时间进行计算换算,将结果体现为几年前、几天前、几小时前、几分钟前、几秒前。 + *** #### func GetMonthDays(t time.Time) int > 获取一个时间当月共有多少天 + *** #### func WeekDay(t time.Time) int > 获取一个时间是星期几 > - 1 ~ 7 + *** #### func GetNextDayInterval(t time.Time) time.Duration > 获取一个时间到下一天间隔多少秒 + *** #### func GetToday(t time.Time) time.Time > 获取一个时间的今天 + *** #### func GetSecond(d time.Duration) int > 获取共有多少秒 + *** #### func IsSameDay(t1 time.Time, t2 time.Time) bool > 两个时间是否是同一天 + *** #### func IsSameHour(t1 time.Time, t2 time.Time) bool > 两个时间是否是同一小时 + *** #### func GetMondayZero(t time.Time) time.Time > 获取本周一零点 + *** #### func Date(year int, month time.Month, day int) time.Time > 返回一个特定日期的时间 + *** #### func DateWithHMS(year int, month time.Month, day int, hour int, min int, sec int) time.Time > 返回一个精确到秒的时间 + *** #### func GetDeltaDay(t1 time.Time, t2 time.Time) int > 获取两个时间需要加减的天数 + *** #### func GetDeltaWeek(t1 time.Time, t2 time.Time) int > 获取两个时间需要加减的周数 + *** #### func GetHSMFromString(timeStr string, layout string) (hour int, min int, sec int) > 从时间字符串中获取时分秒 + *** #### func GetTimeFromString(timeStr string, layout string) time.Time > 将时间字符串转化为时间 + *** #### func GetDayZero(t time.Time, day int) time.Time > 获取 t 增加 day 天后的零点时间 + *** #### func GetYesterday(t time.Time) time.Time > 获取昨天 + *** #### func GetDayLast(t time.Time) time.Time > 获取某天的最后一刻 > - 最后一刻即 23:59:59 + *** #### func GetYesterdayLast(t time.Time) time.Time > 获取昨天最后一刻 + *** #### func GetMinuteStart(t time.Time) time.Time > 获取一个时间的 0 秒 + *** #### func GetMinuteEnd(t time.Time) time.Time > 获取一个时间的 59 秒 + *** #### func GetHourStart(t time.Time) time.Time > 获取一个时间的 0 分 0 秒 + *** #### func GetHourEnd(t time.Time) time.Time > 获取一个时间的 59 分 59 秒 + *** #### func GetMonthStart(t time.Time) time.Time > 获取一个时间的月初 + *** #### func GetMonthEnd(t time.Time) time.Time > 获取一个时间的月末 + *** #### func GetYearStart(t time.Time) time.Time > 获取一个时间的年初 + *** #### func GetYearEnd(t time.Time) time.Time > 获取一个时间的年末 + *** #### func NewStateLine(zero State) *StateLine[State] > 创建一个从左向右由早到晚的状态时间线 + +
+查看 / 收起单元测试 + + +```go + +func TestNewStateLine(t *testing.T) { + sl := times.NewStateLine(0) + sl.AddState(1, time.Now()) + sl.AddState(2, time.Now().Add(-times.Hour)) + sl.Range(func(index int, state int, ts time.Time) bool { + t.Log(index, state, ts) + return true + }) + t.Log(sl.GetStateByTime(time.Now())) +} + +``` + + +
+ + *** #### func SetGlobalTimeOffset(offset time.Duration) > 设置全局时间偏移量 + +
+查看 / 收起单元测试 + + +```go + +func TestSetGlobalTimeOffset(t *testing.T) { + fmt.Println(time.Now()) + times.SetGlobalTimeOffset(-times.Hour) + fmt.Println(time.Now()) + times.SetGlobalTimeOffset(times.Hour) + fmt.Println(time.Now()) + fmt.Println(times.NowByNotOffset()) +} + +``` + + +
+ + *** #### func NowByNotOffset() time.Time > 获取未偏移的当前时间 + *** #### func GetGlobalTimeOffset() time.Duration > 获取全局时间偏移量 + *** #### func ResetGlobalTimeOffset() > 重置全局时间偏移量 + *** #### func NewPeriod(start time.Time, end time.Time) Period > 创建一个时间段 > - 如果 start 比 end 晚,则会自动交换两个时间 + *** #### func NewPeriodWindow(t time.Time, size time.Duration) Period > 创建一个特定长度的时间窗口 + +
+查看 / 收起单元测试 + + +```go + +func TestNewPeriodWindow(t *testing.T) { + cur := time.Now() + fmt.Println(cur) + window := times.NewPeriodWindow(cur, times.Day) + fmt.Println(window) +} + +``` + + +
+ + *** #### func NewPeriodWindowWeek(t time.Time) Period > 创建一周长度的时间窗口,从周一零点开始至周日 23:59:59 结束 + *** #### func NewPeriodWithTimeArray(times [2]time.Time) Period > 创建一个时间段 + *** #### func NewPeriodWithDayZero(t time.Time, day int) Period > 创建一个时间段,从 t 开始,持续到 day 天后的 0 点 + *** #### func NewPeriodWithDay(t time.Time, day int) Period > 创建一个时间段,从 t 开始,持续 day 天 + *** #### func NewPeriodWithHour(t time.Time, hour int) Period > 创建一个时间段,从 t 开始,持续 hour 小时 + *** #### func NewPeriodWithMinute(t time.Time, minute int) Period > 创建一个时间段,从 t 开始,持续 minute 分钟 + *** #### func NewPeriodWithSecond(t time.Time, second int) Period > 创建一个时间段,从 t 开始,持续 second 秒 + *** #### func NewPeriodWithMillisecond(t time.Time, millisecond int) Period > 创建一个时间段,从 t 开始,持续 millisecond 毫秒 + *** #### func NewPeriodWithMicrosecond(t time.Time, microsecond int) Period > 创建一个时间段,从 t 开始,持续 microsecond 微秒 + *** #### func NewPeriodWithNanosecond(t time.Time, nanosecond int) Period > 创建一个时间段,从 t 开始,持续 nanosecond 纳秒 + *** -### StateLine +### StateLine `STRUCT` 状态时间线 ```go type StateLine[State generic.Basic] struct { @@ -362,10 +486,10 @@ type StateLine[State generic.Basic] struct { #### func (*StateLine) RangeReverse(handler func (index int, state State, t time.Time) bool) > 按照时间逆序遍历时间线 *** -### Period +### Period `STRUCT` 表示一个时间段 ```go -type Period struct{} +type Period [2]time.Time ``` #### func (Period) Start() time.Time > 返回时间段的开始时间 diff --git a/utils/xlsxtool/README.md b/utils/xlsxtool/README.md index 778723f..b54becc 100644 --- a/utils/xlsxtool/README.md +++ b/utils/xlsxtool/README.md @@ -1,32 +1,28 @@ # Xlsxtool - - [![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/xlsxtool) ![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat) -## 目录 -列出了该 `package` 下所有的函数,可通过目录进行快捷跳转 ❤️ + + + +## 目录导航 +列出了该 `package` 下所有的函数及类型定义,可通过目录导航进行快捷跳转 ❤️
-展开 / 折叠目录展开 / 折叠目录导航 > 包级函数定义 -|函数|描述 +|函数名称|描述 |:--|:-- |[GetSheetMatrix](#GetSheetMatrix)|将sheet转换为二维矩阵 -> 结构体定义 - -|结构体|描述 -|:--|:-- - -
- - +*** +## 详情信息 #### func GetSheetMatrix(sheet *xlsx.Sheet) *matrix.Matrix[*xlsx.Cell] > 将sheet转换为二维矩阵 + ***