Merge branch 'develop'

This commit is contained in:
kercylan98 2024-01-24 11:13:11 +08:00
commit eeefbff585
110 changed files with 10891 additions and 1528 deletions

BIN
.github/images/pod.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
.github/images/yc-cpu.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
.github/images/yc-event.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
.github/images/yc-memory.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
.github/images/yc1.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
.github/images/yc2.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1,11 +1,53 @@
# Minotaur
Minotaur 是一个用于服务端开发的支持库,其中采用了大量泛型设计,主要被用于游戏服务器开发,但由于拥有大量通用的功能,也常被用于 WEB 开发。
***
[![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)
![](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat)
![](https://komarev.com/ghpvc/?username=kercylan98)
![license](https://img.shields.io/github/license/kercylan98/minotaur)
<a target="_blank" href="https://goreportcard.com/report/github.com/kercylan98/minotaur"><img src="https://goreportcard.com/badge/github.com/kercylan98/minotaur?style=flat-square" /></a>
Minotaur 是一个基于 Golang 1.20 编写的服务端开发支持库,其中采用了大量泛型设计,主要被用于游戏服务器开发,但由于拥有大量通用的功能,也常被用于 WEB 开发。
![go version](https://img.shields.io/github/go-mod/go-version/kercylan98/minotaur?logo=go&style=flat)
![tag](https://img.shields.io/github/v/tag/kercylan98/minotaur?logo=github&style=flat)
![views](https://komarev.com/ghpvc/?username=kercylan98&color=blue&style=flat)
![email](https://img.shields.io/badge/Email-kercylan@gmail.com-green.svg?style=flat&logo=gmail&link=mailto:kercylan@gmail.com)
![qq-group](https://img.shields.io/badge/QQ%20Group-758219443-green.svg?style=flat&logo=tencent-qq&link=https://qm.qq.com/cgi-bin/qm/qr?k=WzRWJIDLzuJbH6-VjdFiTCd1_qA_Ug-D&jump_from=webapi&authKey=ktLEw3XyY9yO+i9rPbI6Fk0UA0uEhACcUidOFdblaiToZtbHcXyU7sFb31FEc9JJ&noverify=0)
![telegram](https://img.shields.io/badge/Telegram-ziv__siren-green.svg?style=flat&logo=telegram&link=https://telegram.me/ziv_siren)
> - 这是支持快速搭建多功能游戏服务器及 HTTP 服务器的 `Golang` 服务端框架;
> - 网络传输基于 [`gorilla/websocket`](https://github.com/gorilla/websocket)、[`gin-gonic/gin`](https://github.com/gin-gonic/gin)、[`grpc/grpc-go`](https://github.com/grpc/grpc-go)、[`panjf2000/gnet`](https://github.com/panjf2000/gnet)、[`xtaci/kcp-go`](https://github.com/xtaci/kcp-go) 构建;
> - 该项目的目标是提供一个简单、高效、可扩展的游戏服务器框架,让开发者可以专注于游戏逻辑的开发,而不用花费大量时间在网络传输、配置导表、日志、监控等基础功能的开发上;
***
在 Minotaur 中不包括任何跨服实现,但支持基于多级路由器快速实现跨服功能。推荐使用 [`NATS.io`](https://nats.io/) 作为跨服消息中间件。
- 目前已实践的弹幕游戏项目以 `NATS.io` 作为消息队列,实现了跨服、埋点日志收集等功能,部署在 `Kubernetes` 集群中;
- 该项目客户端与服务端采用 `WebSocket` 进行通讯,服务端暴露 `HTTP` 接口接收互动数据消息回调,通过负载均衡器进入 `Kubernetes` 集群中的 `Minotaur` 服务,最终通过 `NATS.io` 消息队列转发至对应所在的 `Pod` 中进行处理;
<details>
<summary>关于 Pod 配置参数及非极限压测数据</summary>
> 本次压测 `Pod` 扩容数量为 1但由于压测连接是最开始就建立好的所以该扩容的 `Pod` 并没有接受到压力。
> 理论上来说该 `Pod` 也应该接受 `HTTP` 回调压力,实测过程中,这个扩容的 `Pod` 没有接受到任何压力
**Pod 配置参数**
![pod](.github/images/pod.png)
**压测结果**
![压测数据](.github/images/yc1.png)
![压测数据](.github/images/yc2.png)
**监控数据**
![事件](./.github/images/yc-event.png)
![CPU](./.github/images/yc-cpu.png)
![内存](./.github/images/yc-memory.png)
</details>
***
## 特色内容
```mermaid

View File

@ -29,8 +29,8 @@ configuration 基于配置导表功能实现的配置加载及刷新功能
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[RefreshEventHandle](#refresheventhandle)|配置刷新事件处理函数
|`INTERFACE`|[Loader](#loader)|配置加载器
|`STRUCT`|[RefreshEventHandle](#struct_RefreshEventHandle)|配置刷新事件处理函数
|`INTERFACE`|[Loader](#struct_Loader)|配置加载器
</details>
@ -75,11 +75,13 @@ configuration 基于配置导表功能实现的配置加载及刷新功能
<span id="OnConfigRefreshEvent"></span>
***
<span id="struct_RefreshEventHandle"></span>
### RefreshEventHandle `STRUCT`
配置刷新事件处理函数
```go
type RefreshEventHandle func()
```
<span id="struct_Loader"></span>
### Loader `INTERFACE`
配置加载器
```go

View File

@ -43,17 +43,17 @@ activity 活动状态管理
|类型|名称|描述
|:--|:--|:--
|`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)|活动选项
|`STRUCT`|[Activity](#struct_Activity)|活动描述
|`STRUCT`|[Controller](#struct_Controller)|活动控制器
|`INTERFACE`|[BasicActivityController](#struct_BasicActivityController)|暂无描述...
|`INTERFACE`|[NoneDataActivityController](#struct_NoneDataActivityController)|无数据活动控制器
|`INTERFACE`|[GlobalDataActivityController](#struct_GlobalDataActivityController)|全局数据活动控制器
|`INTERFACE`|[EntityDataActivityController](#struct_EntityDataActivityController)|实体数据活动控制器
|`INTERFACE`|[GlobalAndEntityDataActivityController](#struct_GlobalAndEntityDataActivityController)|全局数据和实体数据活动控制器
|`STRUCT`|[DataMeta](#struct_DataMeta)|全局活动数据
|`STRUCT`|[EntityDataMeta](#struct_EntityDataMeta)|活动实体数据
|`STRUCT`|[UpcomingEventHandler](#struct_UpcomingEventHandler)|暂无描述...
|`STRUCT`|[Options](#struct_Options)|活动选项
</details>
@ -75,97 +75,98 @@ activity 活动状态管理
> 加载所有活动实体数据
***
#### func LoadOrRefreshActivity(activityType Type, activityId ID, options ...*Options) error
#### func LoadOrRefreshActivity\[Type generic.Basic, ID generic.Basic\](activityType Type, activityId ID, options ...*Options) error
<span id="LoadOrRefreshActivity"></span>
> 加载或刷新活动
> - 通常在活动配置刷新时候将活动通过该方法注册或刷新
***
#### func DefineNoneDataActivity(activityType Type) NoneDataActivityController[Type, ID, *none, none, *none]
#### func DefineNoneDataActivity\[Type generic.Basic, ID generic.Basic\](activityType Type) NoneDataActivityController[Type, ID, *none, none, *none]
<span id="DefineNoneDataActivity"></span>
> 声明无数据的活动类型
***
#### func DefineGlobalDataActivity(activityType Type) GlobalDataActivityController[Type, ID, Data, none, *none]
#### func DefineGlobalDataActivity\[Type generic.Basic, ID generic.Basic, Data any\](activityType Type) GlobalDataActivityController[Type, ID, Data, none, *none]
<span id="DefineGlobalDataActivity"></span>
> 声明拥有全局数据的活动类型
***
#### func DefineEntityDataActivity(activityType Type) EntityDataActivityController[Type, ID, *none, EntityID, EntityData]
#### func DefineEntityDataActivity\[Type generic.Basic, ID generic.Basic, EntityID generic.Basic, EntityData any\](activityType Type) EntityDataActivityController[Type, ID, *none, EntityID, EntityData]
<span id="DefineEntityDataActivity"></span>
> 声明拥有实体数据的活动类型
***
#### func DefineGlobalAndEntityDataActivity(activityType Type) GlobalAndEntityDataActivityController[Type, ID, Data, EntityID, EntityData]
#### func DefineGlobalAndEntityDataActivity\[Type generic.Basic, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any\](activityType Type) GlobalAndEntityDataActivityController[Type, ID, Data, EntityID, EntityData]
<span id="DefineGlobalAndEntityDataActivity"></span>
> 声明拥有全局数据和实体数据的活动类型
***
#### func RegUpcomingEvent(activityType Type, handler UpcomingEventHandler[ID], priority ...int)
#### func RegUpcomingEvent\[Type generic.Basic, ID generic.Basic\](activityType Type, handler UpcomingEventHandler[ID], priority ...int)
<span id="RegUpcomingEvent"></span>
> 注册即将开始的活动事件处理器
***
#### func OnUpcomingEvent(activity *Activity[Type, ID])
#### func OnUpcomingEvent\[Type generic.Basic, ID generic.Basic\](activity *Activity[Type, ID])
<span id="OnUpcomingEvent"></span>
> 即将开始的活动事件
***
#### func RegStartedEvent(activityType Type, handler StartedEventHandler[ID], priority ...int)
#### func RegStartedEvent\[Type generic.Basic, ID generic.Basic\](activityType Type, handler StartedEventHandler[ID], priority ...int)
<span id="RegStartedEvent"></span>
> 注册活动开始事件处理器
***
#### func OnStartedEvent(activity *Activity[Type, ID])
#### func OnStartedEvent\[Type generic.Basic, ID generic.Basic\](activity *Activity[Type, ID])
<span id="OnStartedEvent"></span>
> 活动开始事件
***
#### func RegEndedEvent(activityType Type, handler EndedEventHandler[ID], priority ...int)
#### func RegEndedEvent\[Type generic.Basic, ID generic.Basic\](activityType Type, handler EndedEventHandler[ID], priority ...int)
<span id="RegEndedEvent"></span>
> 注册活动结束事件处理器
***
#### func OnEndedEvent(activity *Activity[Type, ID])
#### func OnEndedEvent\[Type generic.Basic, ID generic.Basic\](activity *Activity[Type, ID])
<span id="OnEndedEvent"></span>
> 活动结束事件
***
#### func RegExtendedShowStartedEvent(activityType Type, handler ExtendedShowStartedEventHandler[ID], priority ...int)
#### func RegExtendedShowStartedEvent\[Type generic.Basic, ID generic.Basic\](activityType Type, handler ExtendedShowStartedEventHandler[ID], priority ...int)
<span id="RegExtendedShowStartedEvent"></span>
> 注册活动结束后延长展示开始事件处理器
***
#### func OnExtendedShowStartedEvent(activity *Activity[Type, ID])
#### func OnExtendedShowStartedEvent\[Type generic.Basic, ID generic.Basic\](activity *Activity[Type, ID])
<span id="OnExtendedShowStartedEvent"></span>
> 活动结束后延长展示开始事件
***
#### func RegExtendedShowEndedEvent(activityType Type, handler ExtendedShowEndedEventHandler[ID], priority ...int)
#### func RegExtendedShowEndedEvent\[Type generic.Basic, ID generic.Basic\](activityType Type, handler ExtendedShowEndedEventHandler[ID], priority ...int)
<span id="RegExtendedShowEndedEvent"></span>
> 注册活动结束后延长展示结束事件处理器
***
#### func OnExtendedShowEndedEvent(activity *Activity[Type, ID])
#### func OnExtendedShowEndedEvent\[Type generic.Basic, ID generic.Basic\](activity *Activity[Type, ID])
<span id="OnExtendedShowEndedEvent"></span>
> 活动结束后延长展示结束事件
***
#### func RegNewDayEvent(activityType Type, handler NewDayEventHandler[ID], priority ...int)
#### func RegNewDayEvent\[Type generic.Basic, ID generic.Basic\](activityType Type, handler NewDayEventHandler[ID], priority ...int)
<span id="RegNewDayEvent"></span>
> 注册新的一天事件处理器
***
#### func OnNewDayEvent(activity *Activity[Type, ID])
#### func OnNewDayEvent\[Type generic.Basic, ID generic.Basic\](activity *Activity[Type, ID])
<span id="OnNewDayEvent"></span>
> 新的一天事件
***
#### func NewOptions() *Options
#### func NewOptions() *Options
<span id="NewOptions"></span>
> 创建活动选项
***
<span id="struct_Activity"></span>
### Activity `STRUCT`
活动描述
```go
@ -185,6 +186,7 @@ type Activity[Type generic.Basic, ID generic.Basic] struct {
initializeData func()
}
```
<span id="struct_Controller"></span>
### Controller `STRUCT`
活动控制器
```go
@ -199,6 +201,63 @@ type Controller[Type generic.Basic, ID generic.Basic, Data any, EntityID generic
mutex sync.RWMutex
}
```
<span id="struct_Controller_GetGlobalData"></span>
#### func (*Controller) GetGlobalData(activityId ID) Data
> 获取特定活动全局数据
***
<span id="struct_Controller_GetEntityData"></span>
#### func (*Controller) GetEntityData(activityId ID, entityId EntityID) EntityData
> 获取特定活动实体数据
***
<span id="struct_Controller_IsOpen"></span>
#### func (*Controller) IsOpen(activityId ID) bool
> 活动是否开启
***
<span id="struct_Controller_IsShow"></span>
#### func (*Controller) IsShow(activityId ID) bool
> 活动是否展示
***
<span id="struct_Controller_IsOpenOrShow"></span>
#### func (*Controller) IsOpenOrShow(activityId ID) bool
> 活动是否开启或展示
***
<span id="struct_Controller_Refresh"></span>
#### func (*Controller) Refresh(activityId ID)
> 刷新活动
***
<span id="struct_Controller_InitializeNoneData"></span>
#### func (*Controller) InitializeNoneData(handler func (activityId ID, data *DataMeta[Data])) NoneDataActivityController[Type, ID, Data, EntityID, EntityData]
***
<span id="struct_Controller_InitializeGlobalData"></span>
#### func (*Controller) InitializeGlobalData(handler func (activityId ID, data *DataMeta[Data])) GlobalDataActivityController[Type, ID, Data, EntityID, EntityData]
***
<span id="struct_Controller_InitializeEntityData"></span>
#### func (*Controller) InitializeEntityData(handler func (activityId ID, entityId EntityID, data *EntityDataMeta[EntityData])) EntityDataActivityController[Type, ID, Data, EntityID, EntityData]
***
<span id="struct_Controller_InitializeGlobalAndEntityData"></span>
#### func (*Controller) InitializeGlobalAndEntityData(handler func (activityId ID, data *DataMeta[Data]), entityHandler func (activityId ID, entityId EntityID, data *EntityDataMeta[EntityData])) GlobalAndEntityDataActivityController[Type, ID, Data, EntityID, EntityData]
***
<span id="struct_BasicActivityController"></span>
### BasicActivityController `INTERFACE`
```go
@ -209,6 +268,7 @@ type BasicActivityController[Type generic.Basic, ID generic.Basic, Data any, Ent
Refresh(activityId ID)
}
```
<span id="struct_NoneDataActivityController"></span>
### NoneDataActivityController `INTERFACE`
无数据活动控制器
```go
@ -217,6 +277,7 @@ type NoneDataActivityController[Type generic.Basic, ID generic.Basic, Data any,
InitializeNoneData(handler func(activityId ID, data *DataMeta[Data])) NoneDataActivityController[Type, ID, Data, EntityID, EntityData]
}
```
<span id="struct_GlobalDataActivityController"></span>
### GlobalDataActivityController `INTERFACE`
全局数据活动控制器
```go
@ -226,6 +287,7 @@ type GlobalDataActivityController[Type generic.Basic, ID generic.Basic, Data any
InitializeGlobalData(handler func(activityId ID, data *DataMeta[Data])) GlobalDataActivityController[Type, ID, Data, EntityID, EntityData]
}
```
<span id="struct_EntityDataActivityController"></span>
### EntityDataActivityController `INTERFACE`
实体数据活动控制器
```go
@ -235,6 +297,7 @@ type EntityDataActivityController[Type generic.Basic, ID generic.Basic, Data any
InitializeEntityData(handler func(activityId ID, entityId EntityID, data *EntityDataMeta[EntityData])) EntityDataActivityController[Type, ID, Data, EntityID, EntityData]
}
```
<span id="struct_GlobalAndEntityDataActivityController"></span>
### GlobalAndEntityDataActivityController `INTERFACE`
全局数据和实体数据活动控制器
```go
@ -245,6 +308,7 @@ type GlobalAndEntityDataActivityController[Type generic.Basic, ID generic.Basic,
InitializeGlobalAndEntityData(handler func(activityId ID, data *DataMeta[Data]), entityHandler func(activityId ID, entityId EntityID, data *EntityDataMeta[EntityData])) GlobalAndEntityDataActivityController[Type, ID, Data, EntityID, EntityData]
}
```
<span id="struct_DataMeta"></span>
### DataMeta `STRUCT`
全局活动数据
```go
@ -254,6 +318,7 @@ type DataMeta[Data any] struct {
LastNewDay time.Time
}
```
<span id="struct_EntityDataMeta"></span>
### EntityDataMeta `STRUCT`
活动实体数据
```go
@ -263,11 +328,13 @@ type EntityDataMeta[Data any] struct {
LastNewDay time.Time
}
```
<span id="struct_UpcomingEventHandler"></span>
### UpcomingEventHandler `STRUCT`
```go
type UpcomingEventHandler[ID generic.Basic] func(activityId ID)
```
<span id="struct_Options"></span>
### Options `STRUCT`
活动选项
```go
@ -276,19 +343,34 @@ type Options struct {
Loop time.Duration
}
```
<span id="struct_Options_WithUpcomingTime"></span>
#### func (*Options) WithUpcomingTime(t time.Time) *Options
> 设置活动预告时间
***
<span id="struct_Options_WithStartTime"></span>
#### func (*Options) WithStartTime(t time.Time) *Options
> 设置活动开始时间
***
<span id="struct_Options_WithEndTime"></span>
#### func (*Options) WithEndTime(t time.Time) *Options
> 设置活动结束时间
***
<span id="struct_Options_WithExtendedShowTime"></span>
#### func (*Options) WithExtendedShowTime(t time.Time) *Options
> 设置延长展示时间
***
<span id="struct_Options_WithLoop"></span>
#### func (*Options) WithLoop(interval time.Duration) *Options
> 设置活动循环,时间间隔小于等于 0 表示不循环
> - 当活动状态展示结束后,会根据该选项设置的时间间隔重新开始
***

View File

@ -16,13 +16,14 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[DemoActivityData](#demoactivitydata)|暂无描述...
|`STRUCT`|[DemoActivityData](#struct_DemoActivityData)|暂无描述...
</details>
***
## 详情信息
<span id="struct_DemoActivityData"></span>
### DemoActivityData `STRUCT`
```go

View File

@ -23,23 +23,24 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[TurnBased](#turnbased)|回合制
|`INTERFACE`|[TurnBasedControllerInfo](#turnbasedcontrollerinfo)|暂无描述...
|`INTERFACE`|[TurnBasedControllerAction](#turnbasedcontrolleraction)|暂无描述...
|`STRUCT`|[TurnBasedController](#turnbasedcontroller)|回合制控制器
|`STRUCT`|[TurnBasedEntitySwitchEventHandler](#turnbasedentityswitcheventhandler)|暂无描述...
|`STRUCT`|[TurnBased](#struct_TurnBased)|回合制
|`INTERFACE`|[TurnBasedControllerInfo](#struct_TurnBasedControllerInfo)|暂无描述...
|`INTERFACE`|[TurnBasedControllerAction](#struct_TurnBasedControllerAction)|暂无描述...
|`STRUCT`|[TurnBasedController](#struct_TurnBasedController)|回合制控制器
|`STRUCT`|[TurnBasedEntitySwitchEventHandler](#struct_TurnBasedEntitySwitchEventHandler)|暂无描述...
</details>
***
## 详情信息
#### func NewTurnBased(calcNextTurnDuration func ( Camp, Entity) time.Duration) *TurnBased[CampID, EntityID, Camp, Entity]
#### func NewTurnBased\[CampID comparable, EntityID comparable, Camp generic.IdR[CampID], Entity generic.IdR[EntityID]\](calcNextTurnDuration func ( Camp, Entity) time.Duration) *TurnBased[CampID, EntityID, Camp, Entity]
<span id="NewTurnBased"></span>
> 创建一个新的回合制
> - calcNextTurnDuration 将返回下一次行动时间间隔,适用于按照速度计算下一次行动时间间隔的情况。当返回 0 时,将使用默认的行动超时时间
***
<span id="struct_TurnBased"></span>
### TurnBased `STRUCT`
回合制
```go
@ -64,6 +65,76 @@ type TurnBased[CampID comparable, EntityID comparable, Camp generic.IdR[CampID],
closed bool
}
```
<span id="struct_TurnBased_Close"></span>
#### func (*TurnBased) Close()
> 关闭回合制
***
<span id="struct_TurnBased_AddCamp"></span>
#### func (*TurnBased) AddCamp(camp Camp, entity Entity, entities ...Entity)
> 添加阵营
***
<span id="struct_TurnBased_SetActionTimeout"></span>
#### func (*TurnBased) SetActionTimeout(actionTimeoutHandler func ( Camp, Entity) time.Duration)
> 设置行动超时时间处理函数
> - 默认情况下行动超时时间函数将始终返回 0
***
<span id="struct_TurnBased_Run"></span>
#### func (*TurnBased) Run()
> 运行
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestTurnBased_Run(t *testing.T) {
tbi := fight.NewTurnBased[string, string, *Camp, *Entity](func(camp *Camp, entity *Entity) time.Duration {
return time.Duration(float64(time.Second) / entity.speed)
})
tbi.SetActionTimeout(func(camp *Camp, entity *Entity) time.Duration {
return time.Second * 5
})
tbi.RegTurnBasedEntityActionTimeoutEvent(func(controller fight.TurnBasedControllerInfo[string, string, *Camp, *Entity]) {
t.Log("时间", time.Now().Unix(), "回合", controller.GetRound(), "阵营", controller.GetCamp().GetId(), "实体", controller.GetEntity().GetId(), "超时")
})
tbi.RegTurnBasedRoundChangeEvent(func(controller fight.TurnBasedControllerInfo[string, string, *Camp, *Entity]) {
t.Log("时间", time.Now().Unix(), "回合", controller.GetRound(), "回合切换")
})
tbi.RegTurnBasedEntitySwitchEvent(func(controller fight.TurnBasedControllerAction[string, string, *Camp, *Entity]) {
switch controller.GetEntity().GetId() {
case "1":
go func() {
time.Sleep(time.Second * 2)
controller.Finish()
}()
case "2":
controller.Refresh(time.Second)
case "4":
controller.Stop()
}
t.Log("时间", time.Now().Unix(), "回合", controller.GetRound(), "阵营", controller.GetCamp().GetId(), "实体", controller.GetEntity().GetId(), "开始行动")
})
tbi.AddCamp(&Camp{id: "1"}, &Entity{id: "1", speed: 1}, &Entity{id: "2", speed: 1})
tbi.AddCamp(&Camp{id: "2"}, &Entity{id: "3", speed: 1}, &Entity{id: "4", speed: 1})
tbi.Run()
}
```
</details>
***
<span id="struct_TurnBasedControllerInfo"></span>
### TurnBasedControllerInfo `INTERFACE`
```go
@ -77,6 +148,7 @@ type TurnBasedControllerInfo[CampID comparable, EntityID comparable, Camp generi
Stop()
}
```
<span id="struct_TurnBasedControllerAction"></span>
### TurnBasedControllerAction `INTERFACE`
```go
@ -86,6 +158,7 @@ type TurnBasedControllerAction[CampID comparable, EntityID comparable, Camp gene
Refresh(duration time.Duration) time.Time
}
```
<span id="struct_TurnBasedController"></span>
### TurnBasedController `STRUCT`
回合制控制器
```go
@ -93,6 +166,62 @@ type TurnBasedController[CampID comparable, EntityID comparable, Camp generic.Id
tb *TurnBased[CampID, EntityID, Camp, Entity]
}
```
<span id="struct_TurnBasedController_GetRound"></span>
#### func (*TurnBasedController) GetRound() int
> 获取当前回合数
***
<span id="struct_TurnBasedController_GetCamp"></span>
#### func (*TurnBasedController) GetCamp() Camp
> 获取当前操作阵营
***
<span id="struct_TurnBasedController_GetEntity"></span>
#### func (*TurnBasedController) GetEntity() Entity
> 获取当前操作实体
***
<span id="struct_TurnBasedController_GetActionTimeoutDuration"></span>
#### func (*TurnBasedController) GetActionTimeoutDuration() time.Duration
> 获取当前行动超时时长
***
<span id="struct_TurnBasedController_GetActionStartTime"></span>
#### func (*TurnBasedController) GetActionStartTime() time.Time
> 获取当前行动开始时间
***
<span id="struct_TurnBasedController_GetActionEndTime"></span>
#### func (*TurnBasedController) GetActionEndTime() time.Time
> 获取当前行动结束时间
***
<span id="struct_TurnBasedController_Finish"></span>
#### func (*TurnBasedController) Finish()
> 结束当前操作,将立即切换到下一个操作实体
***
<span id="struct_TurnBasedController_Stop"></span>
#### func (*TurnBasedController) Stop()
> 在当前回合执行完毕后停止回合进程
***
<span id="struct_TurnBasedController_Refresh"></span>
#### func (*TurnBasedController) Refresh(duration time.Duration) time.Time
> 刷新当前操作实体的行动超时时间
> - 当不在行动阶段时,将返回 time.Time 零值
***
<span id="struct_TurnBasedEntitySwitchEventHandler"></span>
### TurnBasedEntitySwitchEventHandler `STRUCT`
```go

View File

@ -24,21 +24,22 @@ space 游戏中常见的空间设计,例如房间、地图等
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[RoomController](#roomcontroller)|对房间进行操作的控制器,由 RoomManager 接管后返回
|`STRUCT`|[RoomManager](#roommanager)|房间管理器是用于对房间进行管理的基本单元,通过该实例可以对房间进行增删改查等操作
|`STRUCT`|[RoomAssumeControlEventHandle](#roomassumecontroleventhandle)|暂无描述...
|`STRUCT`|[RoomControllerOptions](#roomcontrolleroptions)|暂无描述...
|`STRUCT`|[RoomController](#struct_RoomController)|对房间进行操作的控制器,由 RoomManager 接管后返回
|`STRUCT`|[RoomManager](#struct_RoomManager)|房间管理器是用于对房间进行管理的基本单元,通过该实例可以对房间进行增删改查等操作
|`STRUCT`|[RoomAssumeControlEventHandle](#struct_RoomAssumeControlEventHandle)|暂无描述...
|`STRUCT`|[RoomControllerOptions](#struct_RoomControllerOptions)|暂无描述...
</details>
***
## 详情信息
#### func NewRoomManager() *RoomManager[EntityID, RoomID, Entity, Room]
#### func NewRoomManager\[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]\]() *RoomManager[EntityID, RoomID, Entity, Room]
<span id="NewRoomManager"></span>
> 创建房间管理器 RoomManager 的实例
示例代码:
**示例代码:**
```go
func ExampleNewRoomManager() {
@ -49,11 +50,12 @@ func ExampleNewRoomManager() {
```
***
#### func NewRoomControllerOptions() *RoomControllerOptions[EntityID, RoomID, Entity, Room]
#### func NewRoomControllerOptions\[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]\]() *RoomControllerOptions[EntityID, RoomID, Entity, Room]
<span id="NewRoomControllerOptions"></span>
> 创建房间控制器选项
***
<span id="struct_RoomController"></span>
### RoomController `STRUCT`
对房间进行操作的控制器,由 RoomManager 接管后返回
```go
@ -68,6 +70,247 @@ type RoomController[EntityID comparable, RoomID comparable, Entity generic.IdR[E
owner *EntityID
}
```
<span id="struct_RoomController_HasOwner"></span>
#### func (*RoomController) HasOwner() bool
> 判断是否有房主
***
<span id="struct_RoomController_IsOwner"></span>
#### func (*RoomController) IsOwner(entityId EntityID) bool
> 判断是否为房主
***
<span id="struct_RoomController_GetOwner"></span>
#### func (*RoomController) GetOwner() Entity
> 获取房主
***
<span id="struct_RoomController_GetOwnerID"></span>
#### func (*RoomController) GetOwnerID() EntityID
> 获取房主 ID
***
<span id="struct_RoomController_GetOwnerExist"></span>
#### func (*RoomController) GetOwnerExist() ( Entity, bool)
> 获取房间,并返回房主是否存在的状态
***
<span id="struct_RoomController_SetOwner"></span>
#### func (*RoomController) SetOwner(entityId EntityID)
> 设置房主
***
<span id="struct_RoomController_DelOwner"></span>
#### func (*RoomController) DelOwner()
> 删除房主,将房间设置为无主的状态
***
<span id="struct_RoomController_JoinSeat"></span>
#### func (*RoomController) JoinSeat(entityId EntityID, seat ...int) error
> 设置特定对象加入座位,当具体的座位不存在的时候,将会自动分配座位
> - 当目标座位存在玩家或未添加到房间中的时候,将会返回错误
***
<span id="struct_RoomController_LeaveSeat"></span>
#### func (*RoomController) LeaveSeat(entityId EntityID)
> 离开座位
***
<span id="struct_RoomController_GetSeat"></span>
#### func (*RoomController) GetSeat(entityId EntityID) int
> 获取座位
***
<span id="struct_RoomController_GetFirstNotEmptySeat"></span>
#### func (*RoomController) GetFirstNotEmptySeat() int
> 获取第一个非空座位号,如果没有非空座位,将返回 UnknownSeat
***
<span id="struct_RoomController_GetFirstEmptySeatEntity"></span>
#### func (*RoomController) GetFirstEmptySeatEntity() (entity Entity)
> 获取第一个空座位上的实体,如果没有空座位,将返回空实体
***
<span id="struct_RoomController_GetRandomEntity"></span>
#### func (*RoomController) GetRandomEntity() (entity Entity)
> 获取随机实体,如果房间中没有实体,将返回空实体
***
<span id="struct_RoomController_GetNotEmptySeat"></span>
#### func (*RoomController) GetNotEmptySeat() []int
> 获取非空座位
***
<span id="struct_RoomController_GetEmptySeat"></span>
#### func (*RoomController) GetEmptySeat() []int
> 获取空座位
> - 空座位需要在有对象离开座位后才可能出现
***
<span id="struct_RoomController_HasSeat"></span>
#### func (*RoomController) HasSeat(entityId EntityID) bool
> 判断是否有座位
***
<span id="struct_RoomController_GetSeatEntityCount"></span>
#### func (*RoomController) GetSeatEntityCount() int
> 获取座位上的实体数量
***
<span id="struct_RoomController_GetSeatEntities"></span>
#### func (*RoomController) GetSeatEntities() map[EntityID]Entity
> 获取座位上的实体
***
<span id="struct_RoomController_GetSeatEntitiesByOrdered"></span>
#### func (*RoomController) GetSeatEntitiesByOrdered() []Entity
> 有序的获取座位上的实体
***
<span id="struct_RoomController_GetSeatEntitiesByOrderedAndContainsEmpty"></span>
#### func (*RoomController) GetSeatEntitiesByOrderedAndContainsEmpty() []Entity
> 获取有序的座位上的实体,包含空座位
***
<span id="struct_RoomController_GetSeatEntity"></span>
#### func (*RoomController) GetSeatEntity(seat int) (entity Entity)
> 获取座位上的实体
***
<span id="struct_RoomController_ContainEntity"></span>
#### func (*RoomController) ContainEntity(id EntityID) bool
> 房间内是否包含实体
***
<span id="struct_RoomController_GetRoom"></span>
#### func (*RoomController) GetRoom() Room
> 获取原始房间实例,该实例为被接管的房间的原始实例
***
<span id="struct_RoomController_GetEntities"></span>
#### func (*RoomController) GetEntities() map[EntityID]Entity
> 获取所有实体
***
<span id="struct_RoomController_HasEntity"></span>
#### func (*RoomController) HasEntity(id EntityID) bool
> 判断是否有实体
***
<span id="struct_RoomController_GetEntity"></span>
#### func (*RoomController) GetEntity(id EntityID) Entity
> 获取实体
***
<span id="struct_RoomController_GetEntityExist"></span>
#### func (*RoomController) GetEntityExist(id EntityID) ( Entity, bool)
> 获取实体,并返回实体是否存在的状态
***
<span id="struct_RoomController_GetEntityIDs"></span>
#### func (*RoomController) GetEntityIDs() []EntityID
> 获取所有实体ID
***
<span id="struct_RoomController_GetEntityCount"></span>
#### func (*RoomController) GetEntityCount() int
> 获取实体数量
***
<span id="struct_RoomController_ChangePassword"></span>
#### func (*RoomController) ChangePassword(password *string)
> 修改房间密码
> - 当房间密码为 nil 时,将会取消密码
***
<span id="struct_RoomController_AddEntity"></span>
#### func (*RoomController) AddEntity(entity Entity) error
> 添加实体,如果房间存在密码,应使用 AddEntityByPassword 函数进行添加,否则将始终返回 ErrRoomPasswordNotMatch 错误
> - 当房间已满时,将会返回 ErrRoomFull 错误
***
<span id="struct_RoomController_AddEntityByPassword"></span>
#### func (*RoomController) AddEntityByPassword(entity Entity, password string) error
> 通过房间密码添加实体到该房间中
> - 当未设置房间密码时password 参数将会被忽略
> - 当房间密码不匹配时,将会返回 ErrRoomPasswordNotMatch 错误
> - 当房间已满时,将会返回 ErrRoomFull 错误
***
<span id="struct_RoomController_RemoveEntity"></span>
#### func (*RoomController) RemoveEntity(id EntityID)
> 移除实体
> - 当实体被移除时如果实体在座位上,将会自动离开座位
> - 如果实体为房主,将会根据 RoomControllerOptions.WithOwnerInherit 函数的设置进行继承
***
<span id="struct_RoomController_RemoveAllEntities"></span>
#### func (*RoomController) RemoveAllEntities()
> 移除该房间中的所有实体
> - 当实体被移除时如果实体在座位上,将会自动离开座位
> - 如果实体为房主,将会根据 RoomControllerOptions.WithOwnerInherit 函数的设置进行继承
***
<span id="struct_RoomController_Destroy"></span>
#### func (*RoomController) Destroy()
> 销毁房间,房间会从 RoomManager 中移除,同时所有房间的实体、座位等数据都会被清空
> - 该函数与 RoomManager.DestroyRoom 相同RoomManager.DestroyRoom 函数为该函数的快捷方式
***
<span id="struct_RoomController_GetRoomManager"></span>
#### func (*RoomController) GetRoomManager() *RoomManager[EntityID, RoomID, Entity, Room]
> 获取该房间控制器所属的房间管理器
***
<span id="struct_RoomController_GetRoomID"></span>
#### func (*RoomController) GetRoomID() RoomID
> 获取房间 ID
***
<span id="struct_RoomController_Broadcast"></span>
#### func (*RoomController) Broadcast(handler func ( Entity), conditions ...func ( Entity) bool)
> 广播,该函数会将所有房间中满足 conditions 的对象传入 handler 中进行处理
***
<span id="struct_RoomManager"></span>
### RoomManager `STRUCT`
房间管理器是用于对房间进行管理的基本单元,通过该实例可以对房间进行增删改查等操作
- 该实例是线程安全的
@ -78,11 +321,87 @@ type RoomManager[EntityID comparable, RoomID comparable, Entity generic.IdR[Enti
rooms map[RoomID]*RoomController[EntityID, RoomID, Entity, Room]
}
```
<span id="struct_RoomManager_AssumeControl"></span>
#### func (*RoomManager) AssumeControl(room Room, options ...*RoomControllerOptions[EntityID, RoomID, Entity, Room]) *RoomController[EntityID, RoomID, Entity, Room]
> 将房间控制权交由 RoomManager 接管,返回 RoomController 实例
> - 当任何房间需要被 RoomManager 管理时,都应该调用该方法获取到 RoomController 实例后进行操作
> - 房间被接管后需要在释放房间控制权时调用 RoomController.Destroy 方法,否则将会导致 RoomManager 一直持有房间资源
**示例代码:**
```go
func ExampleRoomManager_AssumeControl() {
var rm = space.NewRoomManager[string, int64, *Player, *Room]()
var room = &Room{Id: 1}
var controller = rm.AssumeControl(room)
if err := controller.AddEntity(&Player{Id: "1"}); err != nil {
panic(err)
}
fmt.Println(controller.GetEntityCount())
}
```
***
<span id="struct_RoomManager_DestroyRoom"></span>
#### func (*RoomManager) DestroyRoom(id RoomID)
> 销毁房间,该函数为 RoomController.Destroy 的快捷方式
***
<span id="struct_RoomManager_GetRoom"></span>
#### func (*RoomManager) GetRoom(id RoomID) *RoomController[EntityID, RoomID, Entity, Room]
> 通过房间 ID 获取对应房间的控制器 RoomController当房间不存在时将返回 nil
***
<span id="struct_RoomManager_GetRooms"></span>
#### func (*RoomManager) GetRooms() map[RoomID]*RoomController[EntityID, RoomID, Entity, Room]
> 获取包含所有房间 ID 到对应控制器 RoomController 的映射
> - 返回值的 map 为拷贝对象,可安全的对其进行增删等操作
***
<span id="struct_RoomManager_GetRoomCount"></span>
#### func (*RoomManager) GetRoomCount() int
> 获取房间管理器接管的房间数量
***
<span id="struct_RoomManager_GetRoomIDs"></span>
#### func (*RoomManager) GetRoomIDs() []RoomID
> 获取房间管理器接管的所有房间 ID
***
<span id="struct_RoomManager_HasEntity"></span>
#### func (*RoomManager) HasEntity(entityId EntityID) bool
> 判断特定对象是否在任一房间中,当对象不在任一房间中时将返回 false
***
<span id="struct_RoomManager_GetEntityRooms"></span>
#### func (*RoomManager) GetEntityRooms(entityId EntityID) map[RoomID]*RoomController[EntityID, RoomID, Entity, Room]
> 获取特定对象所在的房间,返回值为房间 ID 到对应控制器 RoomController 的映射
> - 由于一个对象可能在多个房间中,因此返回值为 map 类型
***
<span id="struct_RoomManager_Broadcast"></span>
#### func (*RoomManager) Broadcast(handler func ( Entity), conditions ...func ( Entity) bool)
> 向所有房间对象广播消息,该方法将会遍历所有房间控制器并调用 RoomController.Broadcast 方法
***
<span id="struct_RoomAssumeControlEventHandle"></span>
### RoomAssumeControlEventHandle `STRUCT`
```go
type RoomAssumeControlEventHandle[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]] func(controller *RoomController[EntityID, RoomID, Entity, Room])
```
<span id="struct_RoomControllerOptions"></span>
### RoomControllerOptions `STRUCT`
```go
@ -93,3 +412,23 @@ type RoomControllerOptions[EntityID comparable, RoomID comparable, Entity generi
ownerInheritHandler func(controller *RoomController[EntityID, RoomID, Entity, Room]) *EntityID
}
```
<span id="struct_RoomControllerOptions_WithOwnerInherit"></span>
#### func (*RoomControllerOptions) WithOwnerInherit(inherit bool, inheritHandler ...func (controller *RoomController[EntityID, RoomID, Entity, Room]) *EntityID) *RoomControllerOptions[EntityID, RoomID, Entity, Room]
> 设置房间所有者是否继承,默认为 false
> - inherit: 是否继承,当未设置 inheritHandler 且 inherit 为 true 时,将会按照随机或根据座位号顺序继承房间所有者
> - inheritHandler: 继承处理函数,当 inherit 为 true 时,该函数将会被调用,传入当前房间中的所有实体,返回值为新的房间所有者
***
<span id="struct_RoomControllerOptions_WithMaxEntityCount"></span>
#### func (*RoomControllerOptions) WithMaxEntityCount(maxEntityCount int) *RoomControllerOptions[EntityID, RoomID, Entity, Room]
> 设置房间最大实体数量
***
<span id="struct_RoomControllerOptions_WithPassword"></span>
#### func (*RoomControllerOptions) WithPassword(password string) *RoomControllerOptions[EntityID, RoomID, Entity, Room]
> 设置房间密码
***

View File

@ -34,18 +34,18 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Condition](#condition)|任务条件
|`STRUCT`|[RefreshTaskCounterEventHandler](#refreshtaskcountereventhandler)|暂无描述...
|`STRUCT`|[Option](#option)|任务选项
|`STRUCT`|[Status](#status)|暂无描述...
|`STRUCT`|[Task](#task)|是对任务信息进行描述和处理的结构体
|`STRUCT`|[Condition](#struct_Condition)|任务条件
|`STRUCT`|[RefreshTaskCounterEventHandler](#struct_RefreshTaskCounterEventHandler)|暂无描述...
|`STRUCT`|[Option](#struct_Option)|任务选项
|`STRUCT`|[Status](#struct_Status)|暂无描述...
|`STRUCT`|[Task](#struct_Task)|是对任务信息进行描述和处理的结构体
</details>
***
## 详情信息
#### func Cond(k any, v any) Condition
#### func Cond(k any, v any) Condition
<span id="Cond"></span>
> 创建任务条件
@ -88,7 +88,7 @@ func TestCond(t *testing.T) {
***
#### func RegisterRefreshTaskCounterEvent(taskType string, handler RefreshTaskCounterEventHandler[Trigger])
#### func RegisterRefreshTaskCounterEvent\[Trigger any\](taskType string, handler RefreshTaskCounterEventHandler[Trigger])
<span id="RegisterRefreshTaskCounterEvent"></span>
> 注册特定任务类型的刷新任务计数器事件处理函数
@ -98,7 +98,7 @@ func TestCond(t *testing.T) {
> 触发特定任务类型的刷新任务计数器事件
***
#### func RegisterRefreshTaskConditionEvent(taskType string, handler RefreshTaskConditionEventHandler[Trigger])
#### func RegisterRefreshTaskConditionEvent\[Trigger any\](taskType string, handler RefreshTaskConditionEventHandler[Trigger])
<span id="RegisterRefreshTaskConditionEvent"></span>
> 注册特定任务类型的刷新任务条件事件处理函数
@ -108,19 +108,19 @@ func TestCond(t *testing.T) {
> 触发特定任务类型的刷新任务条件事件
***
#### func WithType(taskType string) Option
#### func WithType(taskType string) Option
<span id="WithType"></span>
> 设置任务类型
***
#### func WithCondition(condition Condition) Option
#### func WithCondition(condition Condition) Option
<span id="WithCondition"></span>
> 设置任务完成条件,当满足条件时,任务状态为完成
> - 任务条件值需要变更时可通过 Task.AssignConditionValueAndRefresh 方法变更
> - 当多次设置该选项时,后面的设置会覆盖之前的设置
***
#### func WithCounter(counter int64, initCount ...int64) Option
#### func WithCounter(counter int64, initCount ...int64) Option
<span id="WithCounter"></span>
> 设置任务计数器,当计数器达到要求时,任务状态为完成
> - 一些场景下,任务计数器可能会溢出,此时可通过 WithOverflowCounter 设置可溢出的任务计数器
@ -128,116 +128,190 @@ func TestCond(t *testing.T) {
> - 如果需要初始化计数器的值,可通过 initCount 参数设置
***
#### func WithOverflowCounter(counter int64, initCount ...int64) Option
#### func WithOverflowCounter(counter int64, initCount ...int64) Option
<span id="WithOverflowCounter"></span>
> 设置可溢出的任务计数器,当计数器达到要求时,任务状态为完成
> - 当多次设置该选项时,后面的设置会覆盖之前的设置
> - 如果需要初始化计数器的值,可通过 initCount 参数设置
***
#### func WithDeadline(deadline time.Time) Option
#### func WithDeadline(deadline time.Time) Option
<span id="WithDeadline"></span>
> 设置任务截止时间,超过截至时间并且任务未完成时,任务状态为失败
***
#### func WithLimitedDuration(start time.Time, duration time.Duration) Option
#### func WithLimitedDuration(start time.Time, duration time.Duration) Option
<span id="WithLimitedDuration"></span>
> 设置任务限时,超过限时时间并且任务未完成时,任务状态为失败
***
#### func NewTask(options ...Option) *Task
#### func NewTask(options ...Option) *Task
<span id="NewTask"></span>
> 生成任务
***
<span id="struct_Condition"></span>
### Condition `STRUCT`
任务条件
```go
type Condition map[any]any
```
<span id="struct_Condition_Cond"></span>
#### func (Condition) Cond(k any, v any) Condition
> 创建任务条件
***
<span id="struct_Condition_GetString"></span>
#### func (Condition) GetString(key any) string
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetInt"></span>
#### func (Condition) GetInt(key any) int
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetInt8"></span>
#### func (Condition) GetInt8(key any) int8
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetInt16"></span>
#### func (Condition) GetInt16(key any) int16
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetInt32"></span>
#### func (Condition) GetInt32(key any) int32
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetInt64"></span>
#### func (Condition) GetInt64(key any) int64
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetUint"></span>
#### func (Condition) GetUint(key any) uint
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetUint8"></span>
#### func (Condition) GetUint8(key any) uint8
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetUint16"></span>
#### func (Condition) GetUint16(key any) uint16
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetUint32"></span>
#### func (Condition) GetUint32(key any) uint32
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetUint64"></span>
#### func (Condition) GetUint64(key any) uint64
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetFloat32"></span>
#### func (Condition) GetFloat32(key any) float32
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetFloat64"></span>
#### func (Condition) GetFloat64(key any) float64
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetBool"></span>
#### func (Condition) GetBool(key any) bool
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetTime"></span>
#### func (Condition) GetTime(key any) time.Time
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetDuration"></span>
#### func (Condition) GetDuration(key any) time.Duration
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetByte"></span>
#### func (Condition) GetByte(key any) byte
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetBytes"></span>
#### func (Condition) GetBytes(key any) []byte
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetRune"></span>
#### func (Condition) GetRune(key any) rune
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetRunes"></span>
#### func (Condition) GetRunes(key any) []rune
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_Condition_GetAny"></span>
#### func (Condition) GetAny(key any) any
> 获取特定类型的任务条件值,该值必须与预期类型一致,否则返回零值
***
<span id="struct_RefreshTaskCounterEventHandler"></span>
### RefreshTaskCounterEventHandler `STRUCT`
```go
type RefreshTaskCounterEventHandler[Trigger any] func(taskType string, trigger Trigger, count int64)
```
<span id="struct_Option"></span>
### Option `STRUCT`
任务选项
```go
type Option func(task *Task)
```
<span id="struct_Status"></span>
### Status `STRUCT`
```go
type Status byte
```
<span id="struct_Status_String"></span>
#### func (Status) String() string
***
<span id="struct_Task"></span>
### Task `STRUCT`
是对任务信息进行描述和处理的结构体
```go
@ -254,33 +328,60 @@ type Task struct {
LimitedDuration time.Duration
}
```
<span id="struct_Task_IsComplete"></span>
#### func (*Task) IsComplete() bool
> 判断任务是否已完成
***
<span id="struct_Task_IsFailed"></span>
#### func (*Task) IsFailed() bool
> 判断任务是否已失败
***
<span id="struct_Task_IsReward"></span>
#### func (*Task) IsReward() bool
> 判断任务是否已领取奖励
***
<span id="struct_Task_ReceiveReward"></span>
#### func (*Task) ReceiveReward() bool
> 领取任务奖励,当任务状态为已完成时,才能领取奖励,此时返回 true并且任务状态变更为已领取奖励
***
<span id="struct_Task_IncrementCounter"></span>
#### func (*Task) IncrementCounter(incr int64) *Task
> 增加计数器的值,当 incr 为负数时,计数器的值不会发生变化
> - 如果需要溢出计数器,可通过 WithOverflowCounter 设置可溢出的任务计数器
***
<span id="struct_Task_DecrementCounter"></span>
#### func (*Task) DecrementCounter(decr int64) *Task
> 减少计数器的值,当 decr 为负数时,计数器的值不会发生变化
***
<span id="struct_Task_AssignConditionValueAndRefresh"></span>
#### func (*Task) AssignConditionValueAndRefresh(key any, value any) *Task
> 分配条件值并刷新任务状态
***
<span id="struct_Task_AssignConditionValueAndRefreshByCondition"></span>
#### func (*Task) AssignConditionValueAndRefreshByCondition(condition Condition) *Task
> 分配条件值并刷新任务状态
***
<span id="struct_Task_ResetStatus"></span>
#### func (*Task) ResetStatus() *Task
> 重置任务状态
> - 该函数会将任务状态重置为已接受状态后,再刷新任务状态
> - 当任务条件变更,例如任务计数要求为 10已经完成的情况下将任务计数要求变更为 5 或 20此时任务状态由于是已完成或已领取状态不会自动刷新需要调用该函数刷新任务状态
***

45
go.mod
View File

@ -4,26 +4,26 @@ go 1.21
require (
github.com/RussellLuo/timingwheel v0.0.0-20220218152713-54845bda3108
github.com/alphadose/haxmap v1.3.0
github.com/alphadose/haxmap v1.3.1
github.com/gin-contrib/pprof v1.4.0
github.com/gin-gonic/gin v1.9.1
github.com/go-resty/resty/v2 v2.7.0
github.com/go-resty/resty/v2 v2.11.0
github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75
github.com/gorilla/websocket v1.5.0
github.com/gorilla/websocket v1.5.1
github.com/json-iterator/go v1.1.12
github.com/panjf2000/ants/v2 v2.8.1
github.com/panjf2000/ants/v2 v2.9.0
github.com/panjf2000/gnet v1.6.7
github.com/pkg/errors v0.9.1
github.com/smartystreets/goconvey v1.8.1
github.com/sony/sonyflake v1.2.0
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.3
github.com/spf13/cobra v1.8.0
github.com/stretchr/testify v1.8.4
github.com/tealeg/xlsx v1.0.5
github.com/tidwall/gjson v1.16.0
github.com/xtaci/kcp-go/v5 v5.6.3
go.uber.org/atomic v1.10.0
golang.org/x/crypto v0.17.0
google.golang.org/grpc v1.59.0
github.com/tidwall/gjson v1.17.0
github.com/xtaci/kcp-go/v5 v5.6.7
go.uber.org/atomic v1.11.0
golang.org/x/crypto v0.18.0
google.golang.org/grpc v1.60.1
)
require (
@ -40,11 +40,10 @@ require (
github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/klauspost/reedsolomon v1.11.8 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/klauspost/reedsolomon v1.12.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
@ -59,16 +58,16 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.25.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.19.1 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/term v0.15.0 // indirect
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/term v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

129
go.sum
View File

@ -1,12 +1,12 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/RussellLuo/timingwheel v0.0.0-20220218152713-54845bda3108 h1:iPugyBI7oFtbDZXC4dnY093M1kZx6k/95sen92gafbY=
github.com/RussellLuo/timingwheel v0.0.0-20220218152713-54845bda3108/go.mod h1:WAMLHwunr1hi3u7OjGV6/VWG9QbdMhGpEKjROiSFd10=
github.com/alphadose/haxmap v1.3.0 h1:C/2LboOnPCZP27GmmSXOcwx360st0P8N0fTJ3voefKc=
github.com/alphadose/haxmap v1.3.0/go.mod h1:rjHw1IAqbxm0S3U5tD16GoKsiAd8FWx5BJ2IYqXwgmM=
github.com/alphadose/haxmap v1.3.1 h1:KmZh75duO1tC8pt3LmUwoTYiZ9sh4K52FX8p7/yrlqU=
github.com/alphadose/haxmap v1.3.1/go.mod h1:rjHw1IAqbxm0S3U5tD16GoKsiAd8FWx5BJ2IYqXwgmM=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
@ -16,7 +16,7 @@ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhD
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -45,8 +45,8 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8=
github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
@ -76,8 +76,8 @@ github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25d
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 h1:f0n1xnMSmBLzVfsMMvriDyA75NB/oBgILX2GcHXIQzY=
github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@ -85,15 +85,14 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/reedsolomon v1.11.8 h1:s8RpUW5TK4hjr+djiOpbZJB4ksx+TdYbRH7vHQpwPOY=
github.com/klauspost/reedsolomon v1.11.8/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/reedsolomon v1.12.0 h1:I5FEp3xSwVCcEh3F5A7dofEfhXdF/bWhQWPH+XwBFno=
github.com/klauspost/reedsolomon v1.12.0/go.mod h1:EPLZJeh4l27pUGC3aXOjheaoh1I9yut7xTURiW3LQ9Y=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -102,16 +101,16 @@ github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ic
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/panjf2000/ants/v2 v2.4.7/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A=
github.com/panjf2000/ants/v2 v2.8.1 h1:C+n/f++aiW8kHCExKlpX6X+okmxKXP7DWLutxuAPuwQ=
github.com/panjf2000/ants/v2 v2.8.1/go.mod h1:KIBmYG9QQX5U2qzFP/yQJaq/nSb6rahS9iEHkrCMgM8=
github.com/panjf2000/ants/v2 v2.9.0 h1:SztCLkVxBRigbg+vt0S5QvF5vxAbxbKt09/YfAJ0tEo=
github.com/panjf2000/ants/v2 v2.9.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
github.com/panjf2000/gnet v1.6.7 h1:zv1k6kw80sG5ZQrLpbbFDheNCm50zm3z2e3ck5GwMOM=
github.com/panjf2000/gnet v1.6.7/go.mod h1:KcOU7QsCaCBjeD5kyshBIamG3d9kAQtlob4Y0v0E+sc=
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
@ -125,9 +124,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
@ -135,8 +133,8 @@ github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sS
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
github.com/sony/sonyflake v1.2.0 h1:Pfr3A+ejSg+0SPqpoAmQgEtNDAhc2G1SUYk205qVMLQ=
github.com/sony/sonyflake v1.2.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -150,16 +148,17 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM=
github.com/templexxx/cpu v0.1.0 h1:wVM+WIJP2nYaxVxqgHPD4wGA2aJ9rvrQRV8CvFzNb40=
github.com/templexxx/cpu v0.1.0/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/xorsimd v0.4.2 h1:ocZZ+Nvu65LGHmCLZ7OoCtg8Fx8jnHKK37SjvngUoVI=
github.com/templexxx/xorsimd v0.4.2/go.mod h1:HgwaPoDREdi6OnULpSfxhzaiiSUY4Fi3JPn1wpt28NI=
github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg=
github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
@ -174,25 +173,23 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/xtaci/kcp-go/v5 v5.6.3 h1:yd59SKXdJ0PBxeMBy3apalxFCEmBLGgQmL6nP46tU0g=
github.com/xtaci/kcp-go/v5 v5.6.3/go.mod h1:uIuw2KEg3FcmEdS4PeXHaGty9Ui7NYb1WKIrSDwpMg4=
github.com/xtaci/kcp-go/v5 v5.6.7 h1:7+rnxNFIsjEwTXQk4cSZpXM4pO0hqtpwE1UFFoJBffA=
github.com/xtaci/kcp-go/v5 v5.6.7/go.mod h1:oE9j2NVqAkuKO5o8ByKGch3vgVX3BNf8zqP8JiGq0bM=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@ -201,16 +198,20 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pBIwikq0AI1evdm+FksE=
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -220,41 +221,59 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211204120058-94396e421777/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -262,6 +281,8 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -270,14 +291,14 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 h1:6GQBEOdGkX6MMTLT9V+TjtIRZCw9VPD5Z+yHY9wMgS0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -295,12 +316,12 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -23,20 +23,21 @@ notify 包含了对外部第三方通知的实现,如机器人消息等
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Manager](#manager)|通知管理器,可用于将通知同时发送至多个渠道
|`INTERFACE`|[Notify](#notify)|通用通知接口定义
|`INTERFACE`|[Sender](#sender)|通知发送器接口声明
|`STRUCT`|[Manager](#struct_Manager)|通知管理器,可用于将通知同时发送至多个渠道
|`INTERFACE`|[Notify](#struct_Notify)|通用通知接口定义
|`INTERFACE`|[Sender](#struct_Sender)|通知发送器接口声明
</details>
***
## 详情信息
#### func NewManager(senders ...Sender) *Manager
#### func NewManager(senders ...Sender) *Manager
<span id="NewManager"></span>
> 通过指定的 Sender 创建一个通知管理器, senders 包中提供了一些内置的 Sender
***
<span id="struct_Manager"></span>
### Manager `STRUCT`
通知管理器,可用于将通知同时发送至多个渠道
```go
@ -46,12 +47,19 @@ type Manager struct {
closeChannel chan struct{}
}
```
<span id="struct_Manager_PushNotify"></span>
#### func (*Manager) PushNotify(notify Notify)
> 推送通知
***
<span id="struct_Manager_Release"></span>
#### func (*Manager) Release()
> 释放通知管理器
***
<span id="struct_Notify"></span>
### Notify `INTERFACE`
通用通知接口定义
```go
@ -59,6 +67,7 @@ type Notify interface {
Format() (string, error)
}
```
<span id="struct_Sender"></span>
### Sender `INTERFACE`
通知发送器接口声明
```go

View File

@ -35,22 +35,22 @@ notifies 包含了内置通知内容的实现
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[FeiShu](#feishu)|飞书通知消息
|`STRUCT`|[FeiShuMessage](#feishumessage)|暂无描述...
|`STRUCT`|[FeiShuRichText](#feishurichtext)|飞书富文本结构
|`STRUCT`|[FeiShuRichTextContent](#feishurichtextcontent)|飞书富文本内容体
|`STRUCT`|[FeiShu](#struct_FeiShu)|飞书通知消息
|`STRUCT`|[FeiShuMessage](#struct_FeiShuMessage)|暂无描述...
|`STRUCT`|[FeiShuRichText](#struct_FeiShuRichText)|飞书富文本结构
|`STRUCT`|[FeiShuRichTextContent](#struct_FeiShuRichTextContent)|飞书富文本内容体
</details>
***
## 详情信息
#### func NewFeiShu(message FeiShuMessage) *FeiShu
#### func NewFeiShu(message FeiShuMessage) *FeiShu
<span id="NewFeiShu"></span>
> 创建飞书通知消息
***
#### func FeiShuMessageWithText(text string) FeiShuMessage
#### func FeiShuMessageWithText(text string) FeiShuMessage
<span id="FeiShuMessageWithText"></span>
> 飞书文本消息
> - 支持通过换行符进行消息换行
@ -68,24 +68,24 @@ notifies 包含了内置通知内容的实现
> - 请确保链接是合法的,否则会以原始内容发送消息。
***
#### func FeiShuMessageWithRichText(richText *FeiShuRichText) FeiShuMessage
#### func FeiShuMessageWithRichText(richText *FeiShuRichText) FeiShuMessage
<span id="FeiShuMessageWithRichText"></span>
> 飞书富文本消息
***
#### func FeiShuMessageWithImage(imageKey string) FeiShuMessage
#### func FeiShuMessageWithImage(imageKey string) FeiShuMessage
<span id="FeiShuMessageWithImage"></span>
> 飞书图片消息
> - imageKey 可通过上传图片接口获取
***
#### func FeiShuMessageWithInteractive(json string) FeiShuMessage
#### func FeiShuMessageWithInteractive(json string) FeiShuMessage
<span id="FeiShuMessageWithInteractive"></span>
> 飞书卡片消息
> - json 表示卡片的 json 数据或者消息模板的 json 数据
***
#### func FeiShuMessageWithShareChat(chatId string) FeiShuMessage
#### func FeiShuMessageWithShareChat(chatId string) FeiShuMessage
<span id="FeiShuMessageWithShareChat"></span>
> 飞书分享群名片
> - chatId 群ID获取方式请参见群ID说明
@ -93,7 +93,7 @@ notifies 包含了内置通知内容的实现
> 群ID说明https://open.feishu.cn/document/server-docs/group/chat/chat-id-description
***
#### func FeiShuMessageWithShareUser(userId string) FeiShuMessage
#### func FeiShuMessageWithShareUser(userId string) FeiShuMessage
<span id="FeiShuMessageWithShareUser"></span>
> 飞书分享个人名片
> - userId 表示用户的 OpenID 获取方式请参见了解更多:如何获取 Open ID
@ -101,7 +101,7 @@ notifies 包含了内置通知内容的实现
> 如何获取 Open IDhttps://open.feishu.cn/document/faq/trouble-shooting/how-to-obtain-openid
***
#### func FeiShuMessageWithAudio(fileKey string) FeiShuMessage
#### func FeiShuMessageWithAudio(fileKey string) FeiShuMessage
<span id="FeiShuMessageWithAudio"></span>
> 飞书语音消息
> - fileKey 语音文件Key可通过上传文件接口获取
@ -109,7 +109,7 @@ notifies 包含了内置通知内容的实现
> 上传文件https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/file/create
***
#### func FeiShuMessageWithMedia(fileKey string) FeiShuMessage
#### func FeiShuMessageWithMedia(fileKey string) FeiShuMessage
<span id="FeiShuMessageWithMedia"></span>
> 飞书视频消息
> - fileKey 视频文件Key可通过上传文件接口获取
@ -117,7 +117,7 @@ notifies 包含了内置通知内容的实现
> 上传文件https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/file/create
***
#### func FeiShuMessageWithMediaAndCover(fileKey string, imageKey string) FeiShuMessage
#### func FeiShuMessageWithMediaAndCover(fileKey string, imageKey string) FeiShuMessage
<span id="FeiShuMessageWithMediaAndCover"></span>
> 飞书带封面的视频消息
> - fileKey 视频文件Key可通过上传文件接口获取
@ -126,7 +126,7 @@ notifies 包含了内置通知内容的实现
> 上传文件https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/file/create
***
#### func FeiShuMessageWithFile(fileKey string) FeiShuMessage
#### func FeiShuMessageWithFile(fileKey string) FeiShuMessage
<span id="FeiShuMessageWithFile"></span>
> 飞书文件消息
> - fileKey 文件Key可通过上传文件接口获取
@ -134,7 +134,7 @@ notifies 包含了内置通知内容的实现
> 上传文件https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/file/create
***
#### func FeiShuMessageWithSticker(fileKey string) FeiShuMessage
#### func FeiShuMessageWithSticker(fileKey string) FeiShuMessage
<span id="FeiShuMessageWithSticker"></span>
> 飞书表情包消息
> - fileKey 表情包文件Key目前仅支持发送机器人收到的表情包可通过接收消息事件的推送获取表情包 file_key。
@ -142,11 +142,12 @@ notifies 包含了内置通知内容的实现
> 接收消息事件https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/events/receive
***
#### func NewFeiShuRichText() *FeiShuRichText
#### func NewFeiShuRichText() *FeiShuRichText
<span id="NewFeiShuRichText"></span>
> 创建一个飞书富文本
***
<span id="struct_FeiShu"></span>
### FeiShu `STRUCT`
飞书通知消息
```go
@ -155,14 +156,19 @@ type FeiShu struct {
MsgType string
}
```
#### func (*FeiShu) Format() string, error
<span id="struct_FeiShu_Format"></span>
#### func (*FeiShu) Format() ( string, error)
> 格式化通知内容
***
<span id="struct_FeiShuMessage"></span>
### FeiShuMessage `STRUCT`
```go
type FeiShuMessage func(feishu *FeiShu)
```
<span id="struct_FeiShuRichText"></span>
### FeiShuRichText `STRUCT`
飞书富文本结构
```go
@ -170,9 +176,13 @@ type FeiShuRichText struct {
content map[string]*FeiShuRichTextContent
}
```
<span id="struct_FeiShuRichText_Create"></span>
#### func (*FeiShuRichText) Create(lang string, title string) *FeiShuRichTextContent
> 创建一个特定语言和标题的富文本内容
***
<span id="struct_FeiShuRichTextContent"></span>
### FeiShuRichTextContent `STRUCT`
飞书富文本内容体
```go
@ -182,45 +192,75 @@ type FeiShuRichTextContent struct {
Content [][]map[string]any
}
```
<span id="struct_FeiShuRichTextContent_AddText"></span>
#### func (*FeiShuRichTextContent) AddText(text string, styles ...string) *FeiShuRichTextContent
> 添加文本
***
<span id="struct_FeiShuRichTextContent_AddUnescapeText"></span>
#### func (*FeiShuRichTextContent) AddUnescapeText(text string, styles ...string) *FeiShuRichTextContent
> 添加 unescape 解码的文本
***
<span id="struct_FeiShuRichTextContent_AddLink"></span>
#### func (*FeiShuRichTextContent) AddLink(text string, href string, styles ...string) *FeiShuRichTextContent
> 添加超链接文本
> - 请确保链接地址的合法性,否则消息会发送失败
***
<span id="struct_FeiShuRichTextContent_AddAt"></span>
#### func (*FeiShuRichTextContent) AddAt(userId string, styles ...string) *FeiShuRichTextContent
> 添加@的用户
> - @单个用户时userId 字段必须是有效值
> - @所有人填"all"。
***
<span id="struct_FeiShuRichTextContent_AddAtWithUsername"></span>
#### func (*FeiShuRichTextContent) AddAtWithUsername(userId string, username string, styles ...string) *FeiShuRichTextContent
> 添加包含用户名的@用户
> - @单个用户时userId 字段必须是有效值
> - @所有人填"all"。
***
<span id="struct_FeiShuRichTextContent_AddImg"></span>
#### func (*FeiShuRichTextContent) AddImg(imageKey string) *FeiShuRichTextContent
> 添加图片
> - imageKey 表示图片的唯一标识,可通过上传图片接口获取
***
<span id="struct_FeiShuRichTextContent_AddMedia"></span>
#### func (*FeiShuRichTextContent) AddMedia(fileKey string) *FeiShuRichTextContent
> 添加视频
> - fileKey 表示视频文件的唯一标识,可通过上传文件接口获取
***
<span id="struct_FeiShuRichTextContent_AddMediaWithCover"></span>
#### func (*FeiShuRichTextContent) AddMediaWithCover(fileKey string, imageKey string) *FeiShuRichTextContent
> 添加包含封面的视频
> - fileKey 表示视频文件的唯一标识,可通过上传文件接口获取
> - imageKey 表示图片的唯一标识,可通过上传图片接口获取
***
<span id="struct_FeiShuRichTextContent_AddEmotion"></span>
#### func (*FeiShuRichTextContent) AddEmotion(emojiType string) *FeiShuRichTextContent
> 添加表情
> - emojiType 表示表情类型,部分可选值请参见表情文案。
>
> 表情文案https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce
***
<span id="struct_FeiShuRichTextContent_Ok"></span>
#### func (*FeiShuRichTextContent) Ok() *FeiShuRichText
> 确认完成,将返回 FeiShuRichText 可继续创建多语言富文本
***

View File

@ -23,18 +23,19 @@ senders Package 包含了内置通知发送器的实现
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[FeiShu](#feishu)|飞书发送器
|`STRUCT`|[FeiShu](#struct_FeiShu)|飞书发送器
</details>
***
## 详情信息
#### func NewFeiShu(webhook string) *FeiShu
#### func NewFeiShu(webhook string) *FeiShu
<span id="NewFeiShu"></span>
> 根据特定的 webhook 地址创建飞书发送器
***
<span id="struct_FeiShu"></span>
### FeiShu `STRUCT`
飞书发送器
```go
@ -43,8 +44,11 @@ type FeiShu struct {
webhook string
}
```
<span id="struct_FeiShu_Push"></span>
#### func (*FeiShu) Push(notify notify.Notify) error
> 推送通知
<details>
<summary>查看 / 收起单元测试</summary>
@ -52,7 +56,7 @@ type FeiShu struct {
```go
func TestFeiShu_Push(t *testing.T) {
fs := NewFeiShu("https://open.feishu.cn/open-apis/bot/v2/hook/d886f30f-814c-47b1-aeb0-b508da0f7f22")
fs := NewFeiShu("https://open.feishu.cn/open-apis/bot/v2/hook/bid")
rt := notifies.NewFeiShu(notifies.FeiShuMessageWithRichText(notifies.NewFeiShuRichText().Create("zh_cn", "标题咯").AddText("哈哈哈").Ok()))
if err := fs.Push(rt); err != nil {
panic(err)

View File

@ -6,7 +6,7 @@ import (
)
func TestFeiShu_Push(t *testing.T) {
fs := NewFeiShu("https://open.feishu.cn/open-apis/bot/v2/hook/d886f30f-814c-47b1-aeb0-b508da0f7f22")
fs := NewFeiShu("https://open.feishu.cn/open-apis/bot/v2/hook/bid")
rt := notifies.NewFeiShu(notifies.FeiShuMessageWithRichText(notifies.NewFeiShuRichText().Create("zh_cn", "标题咯").AddText("哈哈哈").Ok()))
if err := fs.Push(rt); err != nil {

View File

@ -26,55 +26,55 @@
|类型|名称|描述
|:--|:--|:--
|`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)|模板结构
|`INTERFACE`|[Config](#struct_Config)|配置解析接口
|`INTERFACE`|[DataTmpl](#struct_DataTmpl)|数据导出模板
|`STRUCT`|[Exporter](#struct_Exporter)|导出器
|`INTERFACE`|[Field](#struct_Field)|基本字段类型接口
|`STRUCT`|[Int](#struct_Int)|暂无描述...
|`STRUCT`|[Int8](#struct_Int8)|暂无描述...
|`STRUCT`|[Int16](#struct_Int16)|暂无描述...
|`STRUCT`|[Int32](#struct_Int32)|暂无描述...
|`STRUCT`|[Int64](#struct_Int64)|暂无描述...
|`STRUCT`|[Uint](#struct_Uint)|暂无描述...
|`STRUCT`|[Uint8](#struct_Uint8)|暂无描述...
|`STRUCT`|[Uint16](#struct_Uint16)|暂无描述...
|`STRUCT`|[Uint32](#struct_Uint32)|暂无描述...
|`STRUCT`|[Uint64](#struct_Uint64)|暂无描述...
|`STRUCT`|[Float32](#struct_Float32)|暂无描述...
|`STRUCT`|[Float64](#struct_Float64)|暂无描述...
|`STRUCT`|[String](#struct_String)|暂无描述...
|`STRUCT`|[Bool](#struct_Bool)|暂无描述...
|`STRUCT`|[Byte](#struct_Byte)|暂无描述...
|`STRUCT`|[Rune](#struct_Rune)|暂无描述...
|`STRUCT`|[Complex64](#struct_Complex64)|暂无描述...
|`STRUCT`|[Complex128](#struct_Complex128)|暂无描述...
|`STRUCT`|[Uintptr](#struct_Uintptr)|暂无描述...
|`STRUCT`|[Double](#struct_Double)|暂无描述...
|`STRUCT`|[Float](#struct_Float)|暂无描述...
|`STRUCT`|[Long](#struct_Long)|暂无描述...
|`STRUCT`|[Short](#struct_Short)|暂无描述...
|`STRUCT`|[Char](#struct_Char)|暂无描述...
|`STRUCT`|[Number](#struct_Number)|暂无描述...
|`STRUCT`|[Integer](#struct_Integer)|暂无描述...
|`STRUCT`|[Boolean](#struct_Boolean)|暂无描述...
|`STRUCT`|[Loader](#struct_Loader)|配置加载器
|`STRUCT`|[DataInfo](#struct_DataInfo)|配置数据
|`STRUCT`|[DataField](#struct_DataField)|配置数据字段
|`INTERFACE`|[Tmpl](#struct_Tmpl)|配置结构模板接口
|`STRUCT`|[TmplField](#struct_TmplField)|模板字段
|`STRUCT`|[TmplStruct](#struct_TmplStruct)|模板结构
</details>
***
## 详情信息
#### func NewExporter() *Exporter
#### func NewExporter() *Exporter
<span id="NewExporter"></span>
> 创建导出器
***
#### func GetFieldGolangType(field Field) string
#### func GetFieldGolangType(field Field) string
<span id="GetFieldGolangType"></span>
> 获取字段的 Golang 类型
@ -95,17 +95,18 @@ func TestGetFieldGolangType(t *testing.T) {
***
#### func GetFields() []Field
#### func GetFields() []Field
<span id="GetFields"></span>
> 获取所有内置支持的字段
***
#### func NewLoader(fields []Field) *Loader
#### func NewLoader(fields []Field) *Loader
<span id="NewLoader"></span>
> 创建加载器
> - 加载器被用于加载配置表的数据和结构信息
***
<span id="struct_Config"></span>
### Config `INTERFACE`
配置解析接口
- 用于将配置文件解析为可供分析的数据结构
@ -120,6 +121,7 @@ type Config interface {
GetData() [][]DataInfo
}
```
<span id="struct_DataTmpl"></span>
### DataTmpl `INTERFACE`
数据导出模板
```go
@ -127,17 +129,25 @@ type DataTmpl interface {
Render(data map[any]any) (string, error)
}
```
<span id="struct_Exporter"></span>
### Exporter `STRUCT`
导出器
```go
type Exporter struct{}
```
#### func (*Exporter) ExportStruct(tmpl Tmpl, tmplStruct ...*TmplStruct) []byte, error
<span id="struct_Exporter_ExportStruct"></span>
#### func (*Exporter) ExportStruct(tmpl Tmpl, tmplStruct ...*TmplStruct) ( []byte, error)
> 导出结构
***
#### func (*Exporter) ExportData(tmpl DataTmpl, data map[any]any) []byte, error
<span id="struct_Exporter_ExportData"></span>
#### func (*Exporter) ExportData(tmpl DataTmpl, data map[any]any) ( []byte, error)
> 导出数据
***
<span id="struct_Field"></span>
### Field `INTERFACE`
基本字段类型接口
```go
@ -147,303 +157,574 @@ type Field interface {
Parse(value string) any
}
```
<span id="struct_Int"></span>
### Int `STRUCT`
```go
type Int int
```
<span id="struct_Int_TypeName"></span>
#### func (Int) TypeName() string
***
<span id="struct_Int_Zero"></span>
#### func (Int) Zero() any
***
<span id="struct_Int_Parse"></span>
#### func (Int) Parse(value string) any
***
<span id="struct_Int8"></span>
### Int8 `STRUCT`
```go
type Int8 int8
```
<span id="struct_Int8_TypeName"></span>
#### func (Int8) TypeName() string
***
<span id="struct_Int8_Zero"></span>
#### func (Int8) Zero() any
***
<span id="struct_Int8_Parse"></span>
#### func (Int8) Parse(value string) any
***
<span id="struct_Int16"></span>
### Int16 `STRUCT`
```go
type Int16 int16
```
<span id="struct_Int16_TypeName"></span>
#### func (Int16) TypeName() string
***
<span id="struct_Int16_Zero"></span>
#### func (Int16) Zero() any
***
<span id="struct_Int16_Parse"></span>
#### func (Int16) Parse(value string) any
***
<span id="struct_Int32"></span>
### Int32 `STRUCT`
```go
type Int32 int32
```
<span id="struct_Int32_TypeName"></span>
#### func (Int32) TypeName() string
***
<span id="struct_Int32_Zero"></span>
#### func (Int32) Zero() any
***
<span id="struct_Int32_Parse"></span>
#### func (Int32) Parse(value string) any
***
<span id="struct_Int64"></span>
### Int64 `STRUCT`
```go
type Int64 int64
```
<span id="struct_Int64_TypeName"></span>
#### func (Int64) TypeName() string
***
<span id="struct_Int64_Zero"></span>
#### func (Int64) Zero() any
***
<span id="struct_Int64_Parse"></span>
#### func (Int64) Parse(value string) any
***
<span id="struct_Uint"></span>
### Uint `STRUCT`
```go
type Uint uint
```
<span id="struct_Uint_TypeName"></span>
#### func (Uint) TypeName() string
***
<span id="struct_Uint_Zero"></span>
#### func (Uint) Zero() any
***
<span id="struct_Uint_Parse"></span>
#### func (Uint) Parse(value string) any
***
<span id="struct_Uint8"></span>
### Uint8 `STRUCT`
```go
type Uint8 uint8
```
<span id="struct_Uint8_TypeName"></span>
#### func (Uint8) TypeName() string
***
<span id="struct_Uint8_Zero"></span>
#### func (Uint8) Zero() any
***
<span id="struct_Uint8_Parse"></span>
#### func (Uint8) Parse(value string) any
***
<span id="struct_Uint16"></span>
### Uint16 `STRUCT`
```go
type Uint16 uint16
```
<span id="struct_Uint16_TypeName"></span>
#### func (Uint16) TypeName() string
***
<span id="struct_Uint16_Zero"></span>
#### func (Uint16) Zero() any
***
<span id="struct_Uint16_Parse"></span>
#### func (Uint16) Parse(value string) any
***
<span id="struct_Uint32"></span>
### Uint32 `STRUCT`
```go
type Uint32 uint32
```
<span id="struct_Uint32_TypeName"></span>
#### func (Uint32) TypeName() string
***
<span id="struct_Uint32_Zero"></span>
#### func (Uint32) Zero() any
***
<span id="struct_Uint32_Parse"></span>
#### func (Uint32) Parse(value string) any
***
<span id="struct_Uint64"></span>
### Uint64 `STRUCT`
```go
type Uint64 uint64
```
<span id="struct_Uint64_TypeName"></span>
#### func (Uint64) TypeName() string
***
<span id="struct_Uint64_Zero"></span>
#### func (Uint64) Zero() any
***
<span id="struct_Uint64_Parse"></span>
#### func (Uint64) Parse(value string) any
***
<span id="struct_Float32"></span>
### Float32 `STRUCT`
```go
type Float32 float32
```
<span id="struct_Float32_TypeName"></span>
#### func (Float32) TypeName() string
***
<span id="struct_Float32_Zero"></span>
#### func (Float32) Zero() any
***
<span id="struct_Float32_Parse"></span>
#### func (Float32) Parse(value string) any
***
<span id="struct_Float64"></span>
### Float64 `STRUCT`
```go
type Float64 float64
```
<span id="struct_Float64_TypeName"></span>
#### func (Float64) TypeName() string
***
<span id="struct_Float64_Zero"></span>
#### func (Float64) Zero() any
***
<span id="struct_Float64_Parse"></span>
#### func (Float64) Parse(value string) any
***
<span id="struct_String"></span>
### String `STRUCT`
```go
type String string
```
<span id="struct_String_TypeName"></span>
#### func (String) TypeName() string
***
<span id="struct_String_Zero"></span>
#### func (String) Zero() any
***
<span id="struct_String_Parse"></span>
#### func (String) Parse(value string) any
***
<span id="struct_Bool"></span>
### Bool `STRUCT`
```go
type Bool bool
```
<span id="struct_Bool_TypeName"></span>
#### func (Bool) TypeName() string
***
<span id="struct_Bool_Zero"></span>
#### func (Bool) Zero() any
***
<span id="struct_Bool_Parse"></span>
#### func (Bool) Parse(value string) any
***
<span id="struct_Byte"></span>
### Byte `STRUCT`
```go
type Byte byte
```
<span id="struct_Byte_TypeName"></span>
#### func (Byte) TypeName() string
***
<span id="struct_Byte_Zero"></span>
#### func (Byte) Zero() any
***
<span id="struct_Byte_Parse"></span>
#### func (Byte) Parse(value string) any
***
<span id="struct_Rune"></span>
### Rune `STRUCT`
```go
type Rune rune
```
<span id="struct_Rune_TypeName"></span>
#### func (Rune) TypeName() string
***
<span id="struct_Rune_Zero"></span>
#### func (Rune) Zero() any
***
<span id="struct_Rune_Parse"></span>
#### func (Rune) Parse(value string) any
***
<span id="struct_Complex64"></span>
### Complex64 `STRUCT`
```go
type Complex64 complex64
```
<span id="struct_Complex64_TypeName"></span>
#### func (Complex64) TypeName() string
***
<span id="struct_Complex64_Zero"></span>
#### func (Complex64) Zero() any
***
<span id="struct_Complex64_Parse"></span>
#### func (Complex64) Parse(value string) any
***
<span id="struct_Complex128"></span>
### Complex128 `STRUCT`
```go
type Complex128 complex128
```
<span id="struct_Complex128_TypeName"></span>
#### func (Complex128) TypeName() string
***
<span id="struct_Complex128_Zero"></span>
#### func (Complex128) Zero() any
***
<span id="struct_Complex128_Parse"></span>
#### func (Complex128) Parse(value string) any
***
<span id="struct_Uintptr"></span>
### Uintptr `STRUCT`
```go
type Uintptr uintptr
```
<span id="struct_Uintptr_TypeName"></span>
#### func (Uintptr) TypeName() string
***
<span id="struct_Uintptr_Zero"></span>
#### func (Uintptr) Zero() any
***
<span id="struct_Uintptr_Parse"></span>
#### func (Uintptr) Parse(value string) any
***
<span id="struct_Double"></span>
### Double `STRUCT`
```go
type Double float64
```
<span id="struct_Double_TypeName"></span>
#### func (Double) TypeName() string
***
<span id="struct_Double_Zero"></span>
#### func (Double) Zero() any
***
<span id="struct_Double_Parse"></span>
#### func (Double) Parse(value string) any
***
<span id="struct_Float"></span>
### Float `STRUCT`
```go
type Float float32
```
<span id="struct_Float_TypeName"></span>
#### func (Float) TypeName() string
***
<span id="struct_Float_Zero"></span>
#### func (Float) Zero() any
***
<span id="struct_Float_Parse"></span>
#### func (Float) Parse(value string) any
***
<span id="struct_Long"></span>
### Long `STRUCT`
```go
type Long int64
```
<span id="struct_Long_TypeName"></span>
#### func (Long) TypeName() string
***
<span id="struct_Long_Zero"></span>
#### func (Long) Zero() any
***
<span id="struct_Long_Parse"></span>
#### func (Long) Parse(value string) any
***
<span id="struct_Short"></span>
### Short `STRUCT`
```go
type Short int16
```
<span id="struct_Short_TypeName"></span>
#### func (Short) TypeName() string
***
<span id="struct_Short_Zero"></span>
#### func (Short) Zero() any
***
<span id="struct_Short_Parse"></span>
#### func (Short) Parse(value string) any
***
<span id="struct_Char"></span>
### Char `STRUCT`
```go
type Char int8
```
<span id="struct_Char_TypeName"></span>
#### func (Char) TypeName() string
***
<span id="struct_Char_Zero"></span>
#### func (Char) Zero() any
***
<span id="struct_Char_Parse"></span>
#### func (Char) Parse(value string) any
***
<span id="struct_Number"></span>
### Number `STRUCT`
```go
type Number float64
```
<span id="struct_Number_TypeName"></span>
#### func (Number) TypeName() string
***
<span id="struct_Number_Zero"></span>
#### func (Number) Zero() any
***
<span id="struct_Number_Parse"></span>
#### func (Number) Parse(value string) any
***
<span id="struct_Integer"></span>
### Integer `STRUCT`
```go
type Integer int64
```
<span id="struct_Integer_TypeName"></span>
#### func (Integer) TypeName() string
***
<span id="struct_Integer_Zero"></span>
#### func (Integer) Zero() any
***
<span id="struct_Integer_Parse"></span>
#### func (Integer) Parse(value string) any
***
<span id="struct_Boolean"></span>
### Boolean `STRUCT`
```go
type Boolean bool
```
<span id="struct_Boolean_TypeName"></span>
#### func (Boolean) TypeName() string
***
<span id="struct_Boolean_Zero"></span>
#### func (Boolean) Zero() any
***
<span id="struct_Boolean_Parse"></span>
#### func (Boolean) Parse(value string) any
***
<span id="struct_Loader"></span>
### Loader `STRUCT`
配置加载器
```go
@ -451,12 +732,19 @@ type Loader struct {
fields map[string]Field
}
```
<span id="struct_Loader_LoadStruct"></span>
#### func (*Loader) LoadStruct(config Config) *TmplStruct
> 加载结构
***
<span id="struct_Loader_LoadData"></span>
#### func (*Loader) LoadData(config Config) map[any]any
> 加载配置并得到配置数据
***
<span id="struct_DataInfo"></span>
### DataInfo `STRUCT`
配置数据
```go
@ -465,6 +753,7 @@ type DataInfo struct {
Value string
}
```
<span id="struct_DataField"></span>
### DataField `STRUCT`
配置数据字段
```go
@ -476,6 +765,7 @@ type DataField struct {
ExportType string
}
```
<span id="struct_Tmpl"></span>
### Tmpl `INTERFACE`
配置结构模板接口
```go
@ -483,6 +773,7 @@ type Tmpl interface {
Render(templates ...*TmplStruct) (string, error)
}
```
<span id="struct_TmplField"></span>
### TmplField `STRUCT`
模板字段
```go
@ -496,15 +787,25 @@ type TmplField struct {
isIndex bool
}
```
<span id="struct_TmplField_IsIndex"></span>
#### func (*TmplField) IsIndex() bool
> 是否是索引字段
***
<span id="struct_TmplField_IsStruct"></span>
#### func (*TmplField) IsStruct() bool
> 是否是结构类型
***
<span id="struct_TmplField_IsSlice"></span>
#### func (*TmplField) IsSlice() bool
> 是否是切片类型
***
<span id="struct_TmplStruct"></span>
### TmplStruct `STRUCT`
模板结构
```go
@ -515,6 +816,9 @@ type TmplStruct struct {
IndexCount int
}
```
<span id="struct_TmplStruct_AllChildren"></span>
#### func (*TmplStruct) AllChildren() []*TmplStruct
> 获取所有子结构
***

View File

@ -23,23 +23,25 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[XlsxExportType](#xlsxexporttype)|暂无描述...
|`STRUCT`|[Xlsx](#xlsx)|内置的 Xlsx 配置
|`STRUCT`|[XlsxExportType](#struct_XlsxExportType)|暂无描述...
|`STRUCT`|[Xlsx](#struct_Xlsx)|内置的 Xlsx 配置
</details>
***
## 详情信息
#### func NewXlsx(sheet *xlsx.Sheet, exportType XlsxExportType) *Xlsx
#### func NewXlsx(sheet *xlsx.Sheet, exportType XlsxExportType) *Xlsx
<span id="NewXlsx"></span>
***
<span id="struct_XlsxExportType"></span>
### XlsxExportType `STRUCT`
```go
type XlsxExportType int
```
<span id="struct_Xlsx"></span>
### Xlsx `STRUCT`
内置的 Xlsx 配置
```go
@ -48,15 +50,33 @@ type Xlsx struct {
exportType XlsxExportType
}
```
<span id="struct_Xlsx_GetConfigName"></span>
#### func (*Xlsx) GetConfigName() string
***
<span id="struct_Xlsx_GetDisplayName"></span>
#### func (*Xlsx) GetDisplayName() string
***
<span id="struct_Xlsx_GetDescription"></span>
#### func (*Xlsx) GetDescription() string
***
<span id="struct_Xlsx_GetIndexCount"></span>
#### func (*Xlsx) GetIndexCount() int
***
<span id="struct_Xlsx_GetFields"></span>
#### func (*Xlsx) GetFields() []pce.DataField
***
<span id="struct_Xlsx_GetData"></span>
#### func (*Xlsx) GetData() [][]pce.DataInfo
***

View File

@ -24,23 +24,24 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Golang](#golang)|配置导出模板
|`STRUCT`|[JSON](#json)|暂无描述...
|`STRUCT`|[Golang](#struct_Golang)|配置导出模板
|`STRUCT`|[JSON](#struct_JSON)|暂无描述...
</details>
***
## 详情信息
#### func NewGolang(packageName string) *Golang
#### func NewGolang(packageName string) *Golang
<span id="NewGolang"></span>
> 创建一个 Golang 配置导出模板
***
#### func NewJSON() *JSON
#### func NewJSON() *JSON
<span id="NewJSON"></span>
***
<span id="struct_Golang"></span>
### Golang `STRUCT`
配置导出模板
```go
@ -49,14 +50,27 @@ type Golang struct {
Templates []*pce.TmplStruct
}
```
#### func (*Golang) Render(templates ...*pce.TmplStruct) string, error
<span id="struct_Golang_Render"></span>
#### func (*Golang) Render(templates ...*pce.TmplStruct) ( string, error)
***
<span id="struct_Golang_GetVariable"></span>
#### func (*Golang) GetVariable(config *pce.TmplStruct) string
***
<span id="struct_Golang_GetConfigName"></span>
#### func (*Golang) GetConfigName(config *pce.TmplStruct) string
***
<span id="struct_Golang_HasIndex"></span>
#### func (*Golang) HasIndex(config *pce.TmplStruct) bool
***
<span id="struct_JSON"></span>
### JSON `STRUCT`
```go
@ -64,5 +78,8 @@ type JSON struct {
jsonIter.API
}
```
#### func (*JSON) Render(data map[any]any) string, error
<span id="struct_JSON_Render"></span>
#### func (*JSON) Render(data map[any]any) ( string, error)
***

File diff suppressed because it is too large Load Diff

View File

@ -27,42 +27,43 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Client](#client)|客户端
|`INTERFACE`|[Core](#core)|暂无描述...
|`STRUCT`|[ConnectionClosedEventHandle](#connectionclosedeventhandle)|暂无描述...
|`STRUCT`|[Packet](#packet)|暂无描述...
|`STRUCT`|[TCP](#tcp)|暂无描述...
|`STRUCT`|[UnixDomainSocket](#unixdomainsocket)|暂无描述...
|`STRUCT`|[Websocket](#websocket)|websocket 客户端
|`STRUCT`|[Client](#struct_Client)|客户端
|`INTERFACE`|[Core](#struct_Core)|暂无描述...
|`STRUCT`|[ConnectionClosedEventHandle](#struct_ConnectionClosedEventHandle)|暂无描述...
|`STRUCT`|[Packet](#struct_Packet)|暂无描述...
|`STRUCT`|[TCP](#struct_TCP)|暂无描述...
|`STRUCT`|[UnixDomainSocket](#struct_UnixDomainSocket)|暂无描述...
|`STRUCT`|[Websocket](#struct_Websocket)|websocket 客户端
</details>
***
## 详情信息
#### func NewClient(core Core) *Client
#### func NewClient(core Core) *Client
<span id="NewClient"></span>
> 创建客户端
***
#### func CloneClient(client *Client) *Client
#### func CloneClient(client *Client) *Client
<span id="CloneClient"></span>
> 克隆客户端
***
#### func NewTCP(addr string) *Client
#### func NewTCP(addr string) *Client
<span id="NewTCP"></span>
***
#### func NewUnixDomainSocket(addr string) *Client
#### func NewUnixDomainSocket(addr string) *Client
<span id="NewUnixDomainSocket"></span>
***
#### func NewWebsocket(addr string) *Client
#### func NewWebsocket(addr string) *Client
<span id="NewWebsocket"></span>
> 创建 websocket 客户端
***
<span id="struct_Client"></span>
### Client `STRUCT`
客户端
```go
@ -77,23 +78,38 @@ type Client struct {
block chan struct{}
}
```
<span id="struct_Client_Run"></span>
#### func (*Client) Run(block ...bool) error
> 运行客户端,当客户端已运行时,会先关闭客户端再重新运行
> - block 以阻塞方式运行
***
<span id="struct_Client_RunByBufferSize"></span>
#### func (*Client) RunByBufferSize(size int, block ...bool) error
> 指定写入循环缓冲区大小运行客户端,当客户端已运行时,会先关闭客户端再重新运行
> - block 以阻塞方式运行
***
<span id="struct_Client_IsConnected"></span>
#### func (*Client) IsConnected() bool
> 是否已连接
***
<span id="struct_Client_Close"></span>
#### func (*Client) Close(err ...error)
> 关闭
***
<span id="struct_Client_WriteWS"></span>
#### func (*Client) WriteWS(wst int, packet []byte, callback ...func (err error))
> 向连接中写入指定 websocket 数据类型
> - wst: websocket模式中指定消息类型
<details>
<summary>查看 / 收起单元测试</summary>
@ -132,12 +148,19 @@ func TestClient_WriteWS(t *testing.T) {
***
<span id="struct_Client_Write"></span>
#### func (*Client) Write(packet []byte, callback ...func (err error))
> 向连接中写入数据
***
<span id="struct_Client_GetServerAddr"></span>
#### func (*Client) GetServerAddr() string
> 获取服务器地址
***
<span id="struct_Core"></span>
### Core `INTERFACE`
```go
@ -149,11 +172,13 @@ type Core interface {
Clone() Core
}
```
<span id="struct_ConnectionClosedEventHandle"></span>
### ConnectionClosedEventHandle `STRUCT`
```go
type ConnectionClosedEventHandle func(conn *Client, err any)
```
<span id="struct_Packet"></span>
### Packet `STRUCT`
```go
@ -163,6 +188,7 @@ type Packet struct {
callback func(err error)
}
```
<span id="struct_TCP"></span>
### TCP `STRUCT`
```go
@ -172,16 +198,32 @@ type TCP struct {
closed bool
}
```
<span id="struct_TCP_Run"></span>
#### func (*TCP) Run(runState chan error, receive func (wst int, packet []byte))
***
<span id="struct_TCP_Write"></span>
#### func (*TCP) Write(packet *Packet) error
***
<span id="struct_TCP_Close"></span>
#### func (*TCP) Close()
***
<span id="struct_TCP_GetServerAddr"></span>
#### func (*TCP) GetServerAddr() string
***
<span id="struct_TCP_Clone"></span>
#### func (*TCP) Clone() Core
***
<span id="struct_UnixDomainSocket"></span>
### UnixDomainSocket `STRUCT`
```go
@ -191,9 +233,15 @@ type UnixDomainSocket struct {
closed bool
}
```
<span id="struct_UnixDomainSocket_Run"></span>
#### func (*UnixDomainSocket) Run(runState chan error, receive func (wst int, packet []byte))
***
<span id="struct_UnixDomainSocket_Write"></span>
#### func (*UnixDomainSocket) Write(packet *Packet) error
<details>
<summary>查看 / 收起单元测试</summary>
@ -237,12 +285,22 @@ func TestUnixDomainSocket_Write(t *testing.T) {
***
<span id="struct_UnixDomainSocket_Close"></span>
#### func (*UnixDomainSocket) Close()
***
<span id="struct_UnixDomainSocket_GetServerAddr"></span>
#### func (*UnixDomainSocket) GetServerAddr() string
***
<span id="struct_UnixDomainSocket_Clone"></span>
#### func (*UnixDomainSocket) Clone() Core
***
<span id="struct_Websocket"></span>
### Websocket `STRUCT`
websocket 客户端
```go
@ -253,13 +311,28 @@ type Websocket struct {
mu sync.Mutex
}
```
<span id="struct_Websocket_Run"></span>
#### func (*Websocket) Run(runState chan error, receive func (wst int, packet []byte))
***
<span id="struct_Websocket_Write"></span>
#### func (*Websocket) Write(packet *Packet) error
***
<span id="struct_Websocket_Close"></span>
#### func (*Websocket) Close()
***
<span id="struct_Websocket_GetServerAddr"></span>
#### func (*Websocket) GetServerAddr() string
***
<span id="struct_Websocket_Clone"></span>
#### func (*Websocket) Clone() Core
***

View File

@ -32,30 +32,30 @@ gateway 是用于处理服务器消息的网关模块,适用于对客户端消
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Endpoint](#endpoint)|网关端点
|`STRUCT`|[EndpointOption](#endpointoption)|网关端点选项
|`STRUCT`|[ConnectionOpenedEventHandle](#connectionopenedeventhandle)|暂无描述...
|`STRUCT`|[EndpointSelector](#endpointselector)|暂无描述...
|`STRUCT`|[Gateway](#gateway)|基于 server.Server 实现的网关服务器
|`STRUCT`|[Option](#option)|网关选项
|`INTERFACE`|[Scanner](#scanner)|端点扫描器
|`STRUCT`|[Endpoint](#struct_Endpoint)|网关端点
|`STRUCT`|[EndpointOption](#struct_EndpointOption)|网关端点选项
|`STRUCT`|[ConnectionOpenedEventHandle](#struct_ConnectionOpenedEventHandle)|暂无描述...
|`STRUCT`|[EndpointSelector](#struct_EndpointSelector)|暂无描述...
|`STRUCT`|[Gateway](#struct_Gateway)|基于 server.Server 实现的网关服务器
|`STRUCT`|[Option](#struct_Option)|网关选项
|`INTERFACE`|[Scanner](#struct_Scanner)|端点扫描器
</details>
***
## 详情信息
#### func NewEndpoint(name string, cli *client.Client, options ...EndpointOption) *Endpoint
#### func NewEndpoint(name string, cli *client.Client, options ...EndpointOption) *Endpoint
<span id="NewEndpoint"></span>
> 创建网关端点
***
#### func WithEndpointStateEvaluator(evaluator func (costUnixNano float64) float64) EndpointOption
#### func WithEndpointStateEvaluator(evaluator func (costUnixNano float64) float64) EndpointOption
<span id="WithEndpointStateEvaluator"></span>
> 设置端点健康值评估函数
***
#### func WithEndpointConnectionPoolSize(size int) EndpointOption
#### func WithEndpointConnectionPoolSize(size int) EndpointOption
<span id="WithEndpointConnectionPoolSize"></span>
> 设置端点连接池大小
> - 默认为 DefaultEndpointConnectionPoolSize
@ -63,25 +63,25 @@ gateway 是用于处理服务器消息的网关模块,适用于对客户端消
> - 在网关服务器中,多个客户端在发送消息到端点服务器时,会共用一个连接,适当的增大连接池大小可以提高网关服务器的承载能力
***
#### func WithEndpointReconnectInterval(interval time.Duration) EndpointOption
#### func WithEndpointReconnectInterval(interval time.Duration) EndpointOption
<span id="WithEndpointReconnectInterval"></span>
> 设置端点重连间隔
> - 默认为 DefaultEndpointReconnectInterval
> - 端点在连接失败后会在该间隔后重连,如果 <= 0 则不会重连
***
#### func NewGateway(srv *server.Server, scanner Scanner, options ...Option) *Gateway
#### func NewGateway(srv *server.Server, scanner Scanner, options ...Option) *Gateway
<span id="NewGateway"></span>
> 基于 server.Server 创建 Gateway 网关服务器
***
#### func WithEndpointSelector(selector EndpointSelector) Option
#### func WithEndpointSelector(selector EndpointSelector) Option
<span id="WithEndpointSelector"></span>
> 设置端点选择器
> - 默认情况下,网关会随机选择一个端点作为目标,如果需要自定义端点选择器,可以通过该选项设置
***
#### func MarshalGatewayOutPacket(addr string, packet []byte) []byte, error
#### func MarshalGatewayOutPacket(addr string, packet []byte) ([]byte, error)
<span id="MarshalGatewayOutPacket"></span>
> 将数据包转换为网关出网数据包
> - | identifier(4) | ipv4(4) | port(2) | packet |
@ -93,7 +93,7 @@ gateway 是用于处理服务器消息的网关模块,适用于对客户端消
> - | identifier(4) | ipv4(4) | port(2) | packet |
***
#### func MarshalGatewayInPacket(addr string, currentTime int64, packet []byte) []byte, error
#### func MarshalGatewayInPacket(addr string, currentTime int64, packet []byte) ([]byte, error)
<span id="MarshalGatewayInPacket"></span>
> 将数据包转换为网关入网数据包
> - | ipv4(4) | port(2) | cost(4) | packet |
@ -105,6 +105,7 @@ gateway 是用于处理服务器消息的网关模块,适用于对客户端消
> - | ipv4(4) | port(2) | cost(4) | packet |
***
<span id="struct_Endpoint"></span>
### Endpoint `STRUCT`
网关端点
- 每一个端点均表示了一个目标服务,网关会将数据包转发到该端点,由该端点负责将数据包转发到目标服务。
@ -128,34 +129,50 @@ type Endpoint struct {
cps int
}
```
<span id="struct_Endpoint_GetName"></span>
#### func (*Endpoint) GetName() string
> 获取端点名称
***
<span id="struct_Endpoint_GetAddress"></span>
#### func (*Endpoint) GetAddress() string
> 获取端点地址
***
<span id="struct_Endpoint_GetState"></span>
#### func (*Endpoint) GetState() float64
> 获取端点健康值
***
<span id="struct_Endpoint_Forward"></span>
#### func (*Endpoint) Forward(conn *server.Conn, packet []byte, callback ...func (err error))
> 转发数据包到该端点
> - 端点在处理数据包时,应区分数据包为普通直连数据包还是网关数据包。可通过 UnmarshalGatewayOutPacket 进行数据包解析,当解析失败且无其他数据包协议时,可认为该数据包为普通直连数据包。
***
<span id="struct_EndpointOption"></span>
### EndpointOption `STRUCT`
网关端点选项
```go
type EndpointOption func(endpoint *Endpoint)
```
<span id="struct_ConnectionOpenedEventHandle"></span>
### ConnectionOpenedEventHandle `STRUCT`
```go
type ConnectionOpenedEventHandle func(gateway *Gateway, conn *server.Conn)
```
<span id="struct_EndpointSelector"></span>
### EndpointSelector `STRUCT`
```go
type EndpointSelector func(endpoints []*Endpoint) *Endpoint
```
<span id="struct_Gateway"></span>
### Gateway `STRUCT`
基于 server.Server 实现的网关服务器
- 网关服务器是一个特殊的服务器,它会通过扫描器扫描端点列表,然后连接到端点列表中的所有端点,当端点连接成功后,网关服务器会将客户端的连接数据转发到端点服务器
@ -181,8 +198,11 @@ type Gateway struct {
cceLock sync.RWMutex
}
```
<span id="struct_Gateway_Run"></span>
#### func (*Gateway) Run(addr string) error
> 运行网关
<details>
<summary>查看 / 收起单元测试</summary>
@ -212,29 +232,46 @@ func TestGateway_Run(t *testing.T) {
***
<span id="struct_Gateway_Shutdown"></span>
#### func (*Gateway) Shutdown()
> 关闭网关
***
<span id="struct_Gateway_Server"></span>
#### func (*Gateway) Server() *server.Server
> 获取网关服务器核心
***
#### func (*Gateway) GetEndpoint(name string) *Endpoint, error
<span id="struct_Gateway_GetEndpoint"></span>
#### func (*Gateway) GetEndpoint(name string) ( *Endpoint, error)
> 获取一个可用的端点
> - name: 端点名称
***
#### func (*Gateway) GetConnEndpoint(name string, conn *server.Conn) *Endpoint, error
<span id="struct_Gateway_GetConnEndpoint"></span>
#### func (*Gateway) GetConnEndpoint(name string, conn *server.Conn) ( *Endpoint, error)
> 获取一个可用的端点,如果客户端已经连接到了某个端点,将优先返回该端点
> - 当连接到的端点不可用或没有连接记录时,效果同 GetEndpoint 相同
> - 当连接行为为有状态时,推荐使用该方法
***
<span id="struct_Gateway_SwitchEndpoint"></span>
#### func (*Gateway) SwitchEndpoint(source *Endpoint, dest *Endpoint)
> 将端点端点的所有连接切换到另一个端点
***
<span id="struct_Option"></span>
### Option `STRUCT`
网关选项
```go
type Option func(gateway *Gateway)
```
<span id="struct_Scanner"></span>
### Scanner `INTERFACE`
端点扫描器
```go
@ -243,7 +280,13 @@ type Scanner interface {
GetInterval() time.Duration
}
```
#### func (*Scanner) GetEndpoints() []*gateway.Endpoint, error
<span id="struct_Scanner_GetEndpoints"></span>
#### func (*Scanner) GetEndpoints() ( []*gateway.Endpoint, error)
***
<span id="struct_Scanner_GetInterval"></span>
#### func (*Scanner) GetInterval() time.Duration
***

View File

@ -24,23 +24,24 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Action](#action)|消息分发器操作器,用于暴露外部可操作的消息分发器函数
|`STRUCT`|[Handler](#handler)|消息处理器
|`STRUCT`|[Dispatcher](#dispatcher)|用于服务器消息处理的消息分发器
|`STRUCT`|[Manager](#manager)|消息分发器管理器
|`INTERFACE`|[Message](#message)|暂无描述...
|`INTERFACE`|[Producer](#producer)|暂无描述...
|`STRUCT`|[Action](#struct_Action)|消息分发器操作器,用于暴露外部可操作的消息分发器函数
|`STRUCT`|[Handler](#struct_Handler)|消息处理器
|`STRUCT`|[Dispatcher](#struct_Dispatcher)|用于服务器消息处理的消息分发器
|`STRUCT`|[Manager](#struct_Manager)|消息分发器管理器
|`INTERFACE`|[Message](#struct_Message)|暂无描述...
|`INTERFACE`|[Producer](#struct_Producer)|暂无描述...
</details>
***
## 详情信息
#### func NewDispatcher(bufferSize int, name string, handler Handler[P, M]) *Dispatcher[P, M]
#### func NewDispatcher\[P Producer, M Message[P]\](bufferSize int, name string, handler Handler[P, M]) *Dispatcher[P, M]
<span id="NewDispatcher"></span>
> 创建一个新的消息分发器 Dispatcher 实例
示例代码:
**示例代码:**
```go
func ExampleNewDispatcher() {
@ -107,11 +108,12 @@ func TestNewDispatcher(t *testing.T) {
***
#### func NewManager(bufferSize int, handler Handler[P, M]) *Manager[P, M]
#### func NewManager\[P Producer, M Message[P]\](bufferSize int, handler Handler[P, M]) *Manager[P, M]
<span id="NewManager"></span>
> 生成消息分发器管理器
示例代码:
**示例代码:**
```go
func ExampleNewManager() {
@ -165,6 +167,7 @@ func TestNewManager(t *testing.T) {
***
<span id="struct_Action"></span>
### Action `STRUCT`
消息分发器操作器,用于暴露外部可操作的消息分发器函数
```go
@ -173,11 +176,31 @@ type Action[P Producer, M Message[P]] struct {
d *Dispatcher[P, M]
}
```
<span id="struct_Action_Name"></span>
#### func (*Action) Name() string
> 获取消息分发器名称
***
<span id="struct_Action_UnExpel"></span>
#### func (*Action) UnExpel()
> 取消特定生产者的驱逐计划
***
<span id="struct_Action_Expel"></span>
#### func (*Action) Expel()
> 设置该消息分发器即将被驱逐,当消息分发器中没有任何消息时,会自动关闭
***
<span id="struct_Handler"></span>
### Handler `STRUCT`
消息处理器
```go
type Handler[P Producer, M Message[P]] func(dispatcher *Dispatcher[P, M], message M)
```
<span id="struct_Dispatcher"></span>
### Dispatcher `STRUCT`
用于服务器消息处理的消息分发器
@ -207,6 +230,394 @@ type Dispatcher[P Producer, M Message[P]] struct {
abort chan struct{}
}
```
<span id="struct_Dispatcher_SetProducerDoneHandler"></span>
#### func (*Dispatcher) SetProducerDoneHandler(p P, handler func (p P, dispatcher *Action[P, M])) *Dispatcher[P, M]
> 设置特定生产者的所有消息处理完成时的回调函数
> - 如果 handler 为 nil则会删除该生产者的回调函数
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_SetProducerDoneHandler(t *testing.T) {
var cases = []struct {
name string
producer string
messageFinish *atomic.Bool
cancel bool
}{{name: "TestDispatcher_SetProducerDoneHandlerNotCancel", producer: "producer", cancel: false}, {name: "TestDispatcher_SetProducerDoneHandlerCancel", producer: "producer", cancel: true}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
c.messageFinish = &atomic.Bool{}
w := new(sync.WaitGroup)
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
w.Done()
})
d.Put(&TestMessage{producer: c.producer})
d.SetProducerDoneHandler(c.producer, func(p string, dispatcher *dispatcher.Action[string, *TestMessage]) {
c.messageFinish.Store(true)
})
if c.cancel {
d.SetProducerDoneHandler(c.producer, nil)
}
w.Add(1)
d.Start()
w.Wait()
if c.cancel && c.messageFinish.Load() {
t.Errorf("%s should cancel, but not", c.name)
}
})
}
}
```
</details>
***
<span id="struct_Dispatcher_SetClosedHandler"></span>
#### func (*Dispatcher) SetClosedHandler(handler func (dispatcher *Action[P, M])) *Dispatcher[P, M]
> 设置消息分发器关闭时的回调函数
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_SetClosedHandler(t *testing.T) {
var cases = []struct {
name string
handlerFinishMsgCount *atomic.Int64
msgTime time.Duration
msgCount int
}{{name: "TestDispatcher_SetClosedHandler_Normal", msgTime: 0, msgCount: 1}, {name: "TestDispatcher_SetClosedHandler_MessageCount1024", msgTime: 0, msgCount: 1024}, {name: "TestDispatcher_SetClosedHandler_MessageTime1sMessageCount3", msgTime: 1 * time.Second, msgCount: 3}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
c.handlerFinishMsgCount = &atomic.Int64{}
w := new(sync.WaitGroup)
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
time.Sleep(c.msgTime)
c.handlerFinishMsgCount.Add(1)
})
d.SetClosedHandler(func(dispatcher *dispatcher.Action[string, *TestMessage]) {
w.Done()
})
for i := 0; i < c.msgCount; i++ {
d.Put(&TestMessage{producer: "producer"})
}
w.Add(1)
d.Start()
d.Expel()
w.Wait()
if c.handlerFinishMsgCount.Load() != int64(c.msgCount) {
t.Errorf("%s should finish %d messages, but finish %d", c.name, c.msgCount, c.handlerFinishMsgCount.Load())
}
})
}
}
```
</details>
***
<span id="struct_Dispatcher_Name"></span>
#### func (*Dispatcher) Name() string
> 获取消息分发器名称
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_Name(t *testing.T) {
var cases = []struct{ name string }{{name: "TestDispatcher_Name_Normal"}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
if d.Name() != c.name {
t.Errorf("%s should equal %s, but not", c.name, c.name)
}
})
}
}
```
</details>
***
<span id="struct_Dispatcher_Unique"></span>
#### func (*Dispatcher) Unique(name string) bool
> 设置唯一消息键,返回是否已存在
***
<span id="struct_Dispatcher_AntiUnique"></span>
#### func (*Dispatcher) AntiUnique(name string)
> 取消唯一消息键
***
<span id="struct_Dispatcher_Expel"></span>
#### func (*Dispatcher) Expel()
> 设置该消息分发器即将被驱逐,当消息分发器中没有任何消息时,会自动关闭
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_Expel(t *testing.T) {
var cases = []struct {
name string
handlerFinishMsgCount *atomic.Int64
msgTime time.Duration
msgCount int
}{{name: "TestDispatcher_Expel_Normal", msgTime: 0, msgCount: 1}, {name: "TestDispatcher_Expel_MessageCount1024", msgTime: 0, msgCount: 1024}, {name: "TestDispatcher_Expel_MessageTime1sMessageCount3", msgTime: 1 * time.Second, msgCount: 3}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
c.handlerFinishMsgCount = &atomic.Int64{}
w := new(sync.WaitGroup)
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
time.Sleep(c.msgTime)
c.handlerFinishMsgCount.Add(1)
})
d.SetClosedHandler(func(dispatcher *dispatcher.Action[string, *TestMessage]) {
w.Done()
})
for i := 0; i < c.msgCount; i++ {
d.Put(&TestMessage{producer: "producer"})
}
w.Add(1)
d.Start()
d.Expel()
w.Wait()
if c.handlerFinishMsgCount.Load() != int64(c.msgCount) {
t.Errorf("%s should finish %d messages, but finish %d", c.name, c.msgCount, c.handlerFinishMsgCount.Load())
}
})
}
}
```
</details>
***
<span id="struct_Dispatcher_UnExpel"></span>
#### func (*Dispatcher) UnExpel()
> 取消特定生产者的驱逐计划
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_UnExpel(t *testing.T) {
var cases = []struct {
name string
closed *atomic.Bool
isUnExpel bool
expect bool
}{{name: "TestDispatcher_UnExpel_Normal", isUnExpel: true, expect: false}, {name: "TestDispatcher_UnExpel_NotExpel", isUnExpel: false, expect: true}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
c.closed = &atomic.Bool{}
w := new(sync.WaitGroup)
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
w.Done()
})
d.SetClosedHandler(func(dispatcher *dispatcher.Action[string, *TestMessage]) {
c.closed.Store(true)
})
d.Put(&TestMessage{producer: "producer"})
w.Add(1)
if c.isUnExpel {
d.Expel()
d.UnExpel()
} else {
d.Expel()
}
d.Start()
w.Wait()
if c.closed.Load() != c.expect {
t.Errorf("%s should %v, but %v", c.name, c.expect, c.closed.Load())
}
})
}
}
```
</details>
***
<span id="struct_Dispatcher_IncrCount"></span>
#### func (*Dispatcher) IncrCount(producer P, i int64)
> 主动增量设置特定生产者的消息计数,这在等待异步消息完成后再关闭消息分发器时非常有用
> - 如果 i 为负数,则会减少消息计数
***
<span id="struct_Dispatcher_Put"></span>
#### func (*Dispatcher) Put(message M)
> 将消息放入分发器
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_Put(t *testing.T) {
var cases = []struct {
name string
producer string
messageDone *atomic.Bool
}{{name: "TestDispatcher_Put_Normal", producer: "producer"}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
c.messageDone = &atomic.Bool{}
w := new(sync.WaitGroup)
w.Add(1)
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
c.messageDone.Store(true)
w.Done()
})
d.Start()
d.Put(&TestMessage{producer: c.producer})
d.Expel()
w.Wait()
if !c.messageDone.Load() {
t.Errorf("%s should done, but not", c.name)
}
})
}
}
```
</details>
***
<span id="struct_Dispatcher_Start"></span>
#### func (*Dispatcher) Start() *Dispatcher[P, M]
> 以非阻塞的方式开始进行消息分发,当消息分发器中没有任何消息并且处于驱逐计划 Expel 时,将会自动关闭
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_Start(t *testing.T) {
var cases = []struct {
name string
producer string
messageDone *atomic.Bool
}{{name: "TestDispatcher_Start_Normal", producer: "producer"}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
c.messageDone = &atomic.Bool{}
w := new(sync.WaitGroup)
w.Add(1)
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
c.messageDone.Store(true)
w.Done()
})
d.Start()
d.Put(&TestMessage{producer: c.producer})
d.Expel()
w.Wait()
if !c.messageDone.Load() {
t.Errorf("%s should done, but not", c.name)
}
})
}
}
```
</details>
***
<span id="struct_Dispatcher_Closed"></span>
#### func (*Dispatcher) Closed() bool
> 判断消息分发器是否已关闭
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_Closed(t *testing.T) {
var cases = []struct{ name string }{{name: "TestDispatcher_Closed_Normal"}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
w := new(sync.WaitGroup)
w.Add(1)
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
d.SetClosedHandler(func(dispatcher *dispatcher.Action[string, *TestMessage]) {
w.Done()
})
d.Start()
d.Expel()
w.Wait()
if !d.Closed() {
t.Errorf("%s should closed, but not", c.name)
}
})
}
}
```
</details>
***
<span id="struct_Manager"></span>
### Manager `STRUCT`
消息分发器管理器
```go
@ -223,6 +634,323 @@ type Manager[P Producer, M Message[P]] struct {
createdHandler func(name string)
}
```
<span id="struct_Manager_Wait"></span>
#### func (*Manager) Wait()
> 等待所有消息分发器关闭
***
<span id="struct_Manager_SetDispatcherClosedHandler"></span>
#### func (*Manager) SetDispatcherClosedHandler(handler func (name string)) *Manager[P, M]
> 设置消息分发器关闭时的回调函数
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_SetDispatcherClosedHandler(t *testing.T) {
var cases = []struct {
name string
setCloseHandler bool
}{{name: "TestManager_SetDispatcherClosedHandler_Set", setCloseHandler: true}, {name: "TestManager_SetDispatcherClosedHandler_NotSet", setCloseHandler: false}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var closed atomic.Bool
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
if c.setCloseHandler {
m.SetDispatcherClosedHandler(func(name string) {
closed.Store(true)
})
}
m.BindProducer(c.name, c.name)
m.UnBindProducer(c.name)
m.Wait()
if c.setCloseHandler && !closed.Load() {
t.Errorf("SetDispatcherClosedHandler() should be called")
}
})
}
}
```
</details>
***
<span id="struct_Manager_SetDispatcherCreatedHandler"></span>
#### func (*Manager) SetDispatcherCreatedHandler(handler func (name string)) *Manager[P, M]
> 设置消息分发器创建时的回调函数
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_SetDispatcherCreatedHandler(t *testing.T) {
var cases = []struct {
name string
setCreatedHandler bool
}{{name: "TestManager_SetDispatcherCreatedHandler_Set", setCreatedHandler: true}, {name: "TestManager_SetDispatcherCreatedHandler_NotSet", setCreatedHandler: false}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var created atomic.Bool
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
if c.setCreatedHandler {
m.SetDispatcherCreatedHandler(func(name string) {
created.Store(true)
})
}
m.BindProducer(c.name, c.name)
m.UnBindProducer(c.name)
m.Wait()
if c.setCreatedHandler && !created.Load() {
t.Errorf("SetDispatcherCreatedHandler() should be called")
}
})
}
}
```
</details>
***
<span id="struct_Manager_HasDispatcher"></span>
#### func (*Manager) HasDispatcher(name string) bool
> 检查是否存在指定名称的消息分发器
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_HasDispatcher(t *testing.T) {
var cases = []struct {
name string
bindName string
has bool
}{{name: "TestManager_HasDispatcher_Has", bindName: "TestManager_HasDispatcher_Has", has: true}, {name: "TestManager_HasDispatcher_NotHas", bindName: "TestManager_HasDispatcher_NotHas", has: false}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
m.BindProducer(c.bindName, c.bindName)
var cond string
if c.has {
cond = c.bindName
}
if m.HasDispatcher(cond) != c.has {
t.Errorf("HasDispatcher() should return %v", c.has)
}
})
}
}
```
</details>
***
<span id="struct_Manager_GetDispatcherNum"></span>
#### func (*Manager) GetDispatcherNum() int
> 获取当前正在工作的消息分发器数量
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_GetDispatcherNum(t *testing.T) {
var cases = []struct {
name string
num int
}{{name: "TestManager_GetDispatcherNum_N1", num: -1}, {name: "TestManager_GetDispatcherNum_0", num: 0}, {name: "TestManager_GetDispatcherNum_1", num: 1}, {name: "TestManager_GetDispatcherNum_2", num: 2}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
switch {
case c.num <= 0:
return
case c.num == 1:
if m.GetDispatcherNum() != 1 {
t.Errorf("GetDispatcherNum() should return 1")
}
return
default:
for i := 0; i < c.num-1; i++ {
m.BindProducer(fmt.Sprintf("%s_%d", c.name, i), fmt.Sprintf("%s_%d", c.name, i))
}
if m.GetDispatcherNum() != c.num {
t.Errorf("GetDispatcherNum() should return %v", c.num)
}
}
})
}
}
```
</details>
***
<span id="struct_Manager_GetSystemDispatcher"></span>
#### func (*Manager) GetSystemDispatcher() *Dispatcher[P, M]
> 获取系统消息分发器
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_GetSystemDispatcher(t *testing.T) {
var cases = []struct{ name string }{{name: "TestManager_GetSystemDispatcher"}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
if m.GetSystemDispatcher() == nil {
t.Errorf("GetSystemDispatcher() should not return nil")
}
})
}
}
```
</details>
***
<span id="struct_Manager_GetDispatcher"></span>
#### func (*Manager) GetDispatcher(p P) *Dispatcher[P, M]
> 获取生产者正在使用的消息分发器,如果生产者没有绑定消息分发器,则会返回系统消息分发器
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_GetDispatcher(t *testing.T) {
var cases = []struct {
name string
bindName string
}{{name: "TestManager_GetDispatcher", bindName: "TestManager_GetDispatcher"}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
m.BindProducer(c.bindName, c.bindName)
if m.GetDispatcher(c.bindName) == nil {
t.Errorf("GetDispatcher() should not return nil")
}
})
}
}
```
</details>
***
<span id="struct_Manager_BindProducer"></span>
#### func (*Manager) BindProducer(p P, name string)
> 绑定生产者使用特定的消息分发器,如果生产者已经绑定了消息分发器,则会先解绑
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_BindProducer(t *testing.T) {
var cases = []struct {
name string
bindName string
}{{name: "TestManager_BindProducer", bindName: "TestManager_BindProducer"}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
m.BindProducer(c.bindName, c.bindName)
if m.GetDispatcher(c.bindName) == nil {
t.Errorf("GetDispatcher() should not return nil")
}
})
}
}
```
</details>
***
<span id="struct_Manager_UnBindProducer"></span>
#### func (*Manager) UnBindProducer(p P)
> 解绑生产者使用特定的消息分发器
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_UnBindProducer(t *testing.T) {
var cases = []struct {
name string
bindName string
}{{name: "TestManager_UnBindProducer", bindName: "TestManager_UnBindProducer"}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
m.BindProducer(c.bindName, c.bindName)
m.UnBindProducer(c.bindName)
if m.GetDispatcher(c.bindName) != m.GetSystemDispatcher() {
t.Errorf("GetDispatcher() should return SystemDispatcher")
}
})
}
}
```
</details>
***
<span id="struct_Message"></span>
### Message `INTERFACE`
```go
@ -230,6 +958,7 @@ type Message[P comparable] interface {
GetProducer() P
}
```
<span id="struct_Producer"></span>
### Producer `INTERFACE`
```go

View File

@ -59,10 +59,8 @@ type Dispatcher[P Producer, M Message[P]] struct {
abort chan struct{}
}
// SetProducerDoneHandler 设置特定生产者所有消息处理完成时的回调函数
// SetProducerDoneHandler 设置特定生产者所有消息处理完成时的回调函数
// - 如果 handler 为 nil则会删除该生产者的回调函数
//
// 需要注意的是,该 handler 中
func (d *Dispatcher[P, M]) SetProducerDoneHandler(p P, handler func(p P, dispatcher *Action[P, M])) *Dispatcher[P, M] {
d.lock.Lock()
if handler == nil {

View File

@ -16,33 +16,53 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Ants](#ants)|暂无描述...
|`STRUCT`|[GNet](#gnet)|暂无描述...
|`STRUCT`|[Ants](#struct_Ants)|暂无描述...
|`STRUCT`|[GNet](#struct_GNet)|暂无描述...
</details>
***
## 详情信息
<span id="struct_Ants"></span>
### Ants `STRUCT`
```go
type Ants struct{}
```
<span id="struct_Ants_Printf"></span>
#### func (*Ants) Printf(format string, args ...interface {})
***
<span id="struct_GNet"></span>
### GNet `STRUCT`
```go
type GNet struct{}
```
<span id="struct_GNet_Debugf"></span>
#### func (*GNet) Debugf(format string, args ...interface {})
***
<span id="struct_GNet_Infof"></span>
#### func (*GNet) Infof(format string, args ...interface {})
***
<span id="struct_GNet_Warnf"></span>
#### func (*GNet) Warnf(format string, args ...interface {})
***
<span id="struct_GNet_Errorf"></span>
#### func (*GNet) Errorf(format string, args ...interface {})
***
<span id="struct_GNet_Fatalf"></span>
#### func (*GNet) Fatalf(format string, args ...interface {})
***

View File

@ -27,17 +27,17 @@
|类型|名称|描述
|:--|:--|:--
|`INTERFACE`|[Client](#client)|帧同步客户端接口定义
|`STRUCT`|[StoppedEventHandle](#stoppedeventhandle)|暂无描述...
|`STRUCT`|[Lockstep](#lockstep)|锁步(帧)同步默认实现
|`STRUCT`|[Option](#option)|暂无描述...
|`INTERFACE`|[Client](#struct_Client)|帧同步客户端接口定义
|`STRUCT`|[StoppedEventHandle](#struct_StoppedEventHandle)|暂无描述...
|`STRUCT`|[Lockstep](#struct_Lockstep)|锁步(帧)同步默认实现
|`STRUCT`|[Option](#struct_Option)|暂无描述...
</details>
***
## 详情信息
#### func NewLockstep(options ...Option[ClientID, Command]) *Lockstep[ClientID, Command]
#### func NewLockstep\[ClientID comparable, Command any\](options ...Option[ClientID, Command]) *Lockstep[ClientID, Command]
<span id="NewLockstep"></span>
> 创建一个锁步(帧)同步默认实现的组件(Lockstep)进行返回
@ -78,19 +78,19 @@ func TestNewLockstep(t *testing.T) {
***
#### func WithFrameLimit(frameLimit int64) Option[ClientID, Command]
#### func WithFrameLimit\[ClientID comparable, Command any\](frameLimit int64) Option[ClientID, Command]
<span id="WithFrameLimit"></span>
> 通过特定逻辑帧上限创建锁步(帧)同步组件
> - 当达到上限时将停止广播
***
#### func WithFrameRate(frameRate int64) Option[ClientID, Command]
#### func WithFrameRate\[ClientID comparable, Command any\](frameRate int64) Option[ClientID, Command]
<span id="WithFrameRate"></span>
> 通过特定逻辑帧率创建锁步(帧)同步组件
> - 默认情况下为 15/s
***
#### func WithSerialization(handle func (frame int64, commands []Command) []byte) Option[ClientID, Command]
#### func WithSerialization\[ClientID comparable, Command any\](handle func (frame int64, commands []Command) []byte) Option[ClientID, Command]
<span id="WithSerialization"></span>
> 通过特定的序列化方式将每一帧的数据进行序列化
>
@ -102,12 +102,13 @@ func TestNewLockstep(t *testing.T) {
> }
***
#### func WithInitFrame(initFrame int64) Option[ClientID, Command]
#### func WithInitFrame\[ClientID comparable, Command any\](initFrame int64) Option[ClientID, Command]
<span id="WithInitFrame"></span>
> 通过特定的初始帧创建锁步(帧)同步组件
> - 默认情况下为 0即第一帧索引为 0
***
<span id="struct_Client"></span>
### Client `INTERFACE`
帧同步客户端接口定义
- 客户端应该具备ID及写入数据包的实现
@ -117,11 +118,13 @@ type Client[ID comparable] interface {
Write(packet []byte, callback ...func(err error))
}
```
<span id="struct_StoppedEventHandle"></span>
### StoppedEventHandle `STRUCT`
```go
type StoppedEventHandle[ClientID comparable, Command any] func(lockstep *Lockstep[ClientID, Command])
```
<span id="struct_Lockstep"></span>
### Lockstep `STRUCT`
锁步(帧)同步默认实现
- 支持最大帧上限 WithFrameLimit
@ -149,6 +152,108 @@ type Lockstep[ClientID comparable, Command any] struct {
lockstepStoppedEventHandles []StoppedEventHandle[ClientID, Command]
}
```
<span id="struct_Lockstep_JoinClient"></span>
#### func (*Lockstep) JoinClient(client Client[ClientID])
> 将客户端加入到广播队列中,通常在开始广播前使用
> - 如果客户端在开始广播后加入,将丢失之前的帧数据,如要从特定帧开始追帧请使用 JoinClientWithFrame
***
<span id="struct_Lockstep_JoinClientWithFrame"></span>
#### func (*Lockstep) JoinClientWithFrame(client Client[ClientID], frameIndex int64)
> 加入客户端到广播队列中,并从特定帧开始追帧
> - 可用于重连及状态同步、帧同步混用的情况
> - 混用:服务端记录指令时同时做一次状态计算,新客户端加入时直接同步当前状态,之后从特定帧开始广播
***
<span id="struct_Lockstep_GetClientCount"></span>
#### func (*Lockstep) GetClientCount() int
> 获取客户端数量
***
<span id="struct_Lockstep_DropCache"></span>
#### func (*Lockstep) DropCache(handler func (frame int64) bool)
> 丢弃特定帧的缓存,当 handler 返回 true 时将丢弃缓存
***
<span id="struct_Lockstep_LeaveClient"></span>
#### func (*Lockstep) LeaveClient(clientId ClientID)
> 将客户端从广播队列中移除
***
<span id="struct_Lockstep_StartBroadcast"></span>
#### func (*Lockstep) StartBroadcast()
> 开始广播
> - 在开始广播后将持续按照设定的帧率进行帧数推进,并在每一帧推进时向客户端进行同步,需提前将客户端加入广播队列 JoinClient
> - 广播过程中使用 AddCommand 将该帧数据追加到当前帧中
***
<span id="struct_Lockstep_StopBroadcast"></span>
#### func (*Lockstep) StopBroadcast()
> 停止广播
***
<span id="struct_Lockstep_IsRunning"></span>
#### func (*Lockstep) IsRunning() bool
> 是否正在广播
***
<span id="struct_Lockstep_AddCommand"></span>
#### func (*Lockstep) AddCommand(command Command)
> 添加命令到当前帧
***
<span id="struct_Lockstep_AddCommands"></span>
#### func (*Lockstep) AddCommands(commands []Command)
> 添加命令到当前帧
***
<span id="struct_Lockstep_GetCurrentFrame"></span>
#### func (*Lockstep) GetCurrentFrame() int64
> 获取当前帧
***
<span id="struct_Lockstep_GetClientCurrentFrame"></span>
#### func (*Lockstep) GetClientCurrentFrame(clientId ClientID) int64
> 获取客户端当前帧
***
<span id="struct_Lockstep_GetFrameLimit"></span>
#### func (*Lockstep) GetFrameLimit() int64
> 获取帧上限
> - 未设置时将返回0
***
<span id="struct_Lockstep_GetCurrentCommands"></span>
#### func (*Lockstep) GetCurrentCommands() []Command
> 获取当前帧还未结束时的所有指令
***
<span id="struct_Lockstep_RegLockstepStoppedEvent"></span>
#### func (*Lockstep) RegLockstepStoppedEvent(handle StoppedEventHandle[ClientID, Command])
> 当广播停止时将触发被注册的事件处理函数
***
<span id="struct_Lockstep_OnLockstepStoppedEvent"></span>
#### func (*Lockstep) OnLockstepStoppedEvent()
***
<span id="struct_Option"></span>
### Option `STRUCT`
```go

View File

@ -16,6 +16,22 @@ import (
"time"
)
// Network 服务器运行的网络模式
// - 根据不同的网络模式,服务器将会产生不同的行为,该类型将在服务器创建时候指定
//
// 服务器支持的网络模式如下:
// - NetworkNone 该模式下不监听任何网络端口,仅开启消息队列,适用于纯粹的跨服服务器等情况
// - NetworkTcp 该模式下将会监听 TCP 协议的所有地址,包括 IPv4 和 IPv6
// - NetworkTcp4 该模式下将会监听 TCP 协议的 IPv4 地址
// - NetworkTcp6 该模式下将会监听 TCP 协议的 IPv6 地址
// - NetworkUdp 该模式下将会监听 UDP 协议的所有地址,包括 IPv4 和 IPv6
// - NetworkUdp4 该模式下将会监听 UDP 协议的 IPv4 地址
// - NetworkUdp6 该模式下将会监听 UDP 协议的 IPv6 地址
// - NetworkUnix 该模式下将会监听 Unix 协议的地址
// - NetworkHttp 该模式下将会监听 HTTP 协议的地址
// - NetworkWebsocket 该模式下将会监听 Websocket 协议的地址
// - NetworkKcp 该模式下将会监听 KCP 协议的地址
// - NetworkGRPC 该模式下将会监听 GRPC 协议的地址
type Network string
const (
@ -103,6 +119,7 @@ func (n Network) adaptation(srv *Server) <-chan error {
switch n {
case NetworkNone:
srv.addr = "-"
state <- nil
case NetworkTcp:
n.gNetMode(state, srv)
case NetworkTcp4:
@ -125,6 +142,8 @@ func (n Network) adaptation(srv *Server) <-chan error {
n.kcpMode(state, srv)
case NetworkGRPC:
n.grpcMode(state, srv)
default:
state <- fmt.Errorf("unsupported network mode: %s", n)
}
return state
}
@ -248,7 +267,8 @@ func (n Network) websocketMode(state chan<- error, srv *Server) {
if srv.websocketUpgrader == nil {
srv.websocketUpgrader = DefaultWebsocketUpgrader()
}
http.HandleFunc(pattern, func(writer http.ResponseWriter, request *http.Request) {
mux := http.NewServeMux()
mux.HandleFunc(pattern, func(writer http.ResponseWriter, request *http.Request) {
ip := request.Header.Get("X-Real-IP")
ws, err := srv.websocketUpgrader.Upgrade(writer, request, nil)
if err != nil {
@ -304,20 +324,29 @@ func (n Network) websocketMode(state chan<- error, srv *Server) {
srv.PushPacketMessage(conn, messageType, packet)
}
})
go func(lis *listener) {
go func(lis *listener, mux *http.ServeMux) {
var err error
if len(lis.srv.certFile)+len(lis.srv.keyFile) > 0 {
err = http.ServeTLS(lis, nil, lis.srv.certFile, lis.srv.keyFile)
err = http.ServeTLS(lis, mux, lis.srv.certFile, lis.srv.keyFile)
} else {
err = http.Serve(lis, nil)
err = http.Serve(lis, mux)
}
if err != nil {
super.TryWriteChannel(lis.state, err)
}
}((&listener{srv: srv, Listener: l, state: state}).init())
}((&listener{srv: srv, Listener: l, state: state}).init(), mux)
}
// IsSocket 返回当前服务器的网络模式是否为 Socket 模式
// IsSocket 返回当前服务器的网络模式是否为 Socket 模式,目前为止仅有如下几种模式为 Socket 模式:
// - NetworkTcp
// - NetworkTcp4
// - NetworkTcp6
// - NetworkUdp
// - NetworkUdp4
// - NetworkUdp6
// - NetworkUnix
// - NetworkKcp
// - NetworkWebsocket
func (n Network) IsSocket() bool {
return collection.KeyInMap(socketNetworks, n)
}

View File

@ -0,0 +1,51 @@
package server_test
import (
"fmt"
"github.com/kercylan98/minotaur/server"
"github.com/kercylan98/minotaur/utils/times"
"time"
)
// 服务器在启动时将阻塞 1s模拟了慢消息的过程这时候如果通过 RegMessageLowExecEvent 函数注册过慢消息事件,将会收到该事件的消息
// - 该示例中,将在收到慢消息时关闭服务器
func ExampleWithLowMessageDuration() {
srv := server.New(server.NetworkNone,
server.WithLowMessageDuration(time.Second),
)
srv.RegStartFinishEvent(func(srv *server.Server) {
time.Sleep(time.Second)
})
srv.RegMessageLowExecEvent(func(srv *server.Server, message *server.Message, cost time.Duration) {
srv.Shutdown()
fmt.Println(times.GetSecond(cost))
})
if err := srv.RunNone(); err != nil {
panic(err)
}
// Output:
// 1
}
// 服务器在启动时将发布一条阻塞 1s 的异步消息,模拟了慢消息的过程,这时候如果通过 RegMessageLowExecEvent 函数注册过慢消息事件,将会收到该事件的消息
// - 该示例中,将在收到慢消息时关闭服务器
func ExampleWithAsyncLowMessageDuration() {
srv := server.New(server.NetworkNone,
server.WithAsyncLowMessageDuration(time.Second),
)
srv.RegStartFinishEvent(func(srv *server.Server) {
srv.PushAsyncMessage(func() error {
time.Sleep(time.Second)
return nil
}, nil)
})
srv.RegMessageLowExecEvent(func(srv *server.Server, message *server.Message, cost time.Duration) {
srv.Shutdown()
fmt.Println(times.GetSecond(cost))
})
if err := srv.RunNone(); err != nil {
panic(err)
}
// Output:
// 1
}

109
server/options_test.go Normal file
View File

@ -0,0 +1,109 @@
package server_test
import (
"fmt"
"github.com/kercylan98/minotaur/server"
"github.com/kercylan98/minotaur/utils/random"
"testing"
"time"
)
func TestWithLowMessageDuration(t *testing.T) {
var cases = []struct {
name string
duration time.Duration
}{
{name: "TestWithLowMessageDuration", duration: server.DefaultLowMessageDuration},
{name: "TestWithLowMessageDuration_Zero", duration: 0},
{name: "TestWithLowMessageDuration_Negative", duration: -server.DefaultAsyncLowMessageDuration},
}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
networks := server.GetNetworks()
for i := 0; i < len(networks); i++ {
low := false
network := networks[i]
srv := server.New(network,
server.WithLowMessageDuration(c.duration),
)
srv.RegMessageLowExecEvent(func(srv *server.Server, message *server.Message, cost time.Duration) {
low = true
srv.Shutdown()
})
srv.RegStartFinishEvent(func(srv *server.Server) {
if c.duration <= 0 {
srv.Shutdown()
return
}
time.Sleep(server.DefaultLowMessageDuration)
})
var lis string
switch network {
case server.NetworkNone, server.NetworkUnix:
lis = "addr"
default:
lis = fmt.Sprintf(":%d", random.UsablePort())
}
if err := srv.Run(lis); err != nil {
t.Fatalf("%s run error: %s", network, err)
}
if !low && c.duration > 0 {
t.Fatalf("%s low message not exec", network)
}
}
})
}
}
func TestWithAsyncLowMessageDuration(t *testing.T) {
var cases = []struct {
name string
duration time.Duration
}{
{name: "TestWithAsyncLowMessageDuration", duration: time.Millisecond * 100},
{name: "TestWithAsyncLowMessageDuration_Zero", duration: 0},
{name: "TestWithAsyncLowMessageDuration_Negative", duration: -server.DefaultAsyncLowMessageDuration},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
networks := server.GetNetworks()
for i := 0; i < len(networks); i++ {
low := false
network := networks[i]
srv := server.New(network,
server.WithAsyncLowMessageDuration(c.duration),
)
srv.RegMessageLowExecEvent(func(srv *server.Server, message *server.Message, cost time.Duration) {
low = true
srv.Shutdown()
})
srv.RegStartFinishEvent(func(srv *server.Server) {
if c.duration <= 0 {
srv.Shutdown()
return
}
srv.PushAsyncMessage(func() error {
time.Sleep(c.duration)
return nil
}, nil)
})
var lis string
switch network {
case server.NetworkNone, server.NetworkUnix:
lis = fmt.Sprintf("%s%d", "addr", random.Int(0, 9999))
default:
lis = fmt.Sprintf(":%d", random.UsablePort())
}
if err := srv.Run(lis); err != nil {
t.Fatalf("%s run error: %s", network, err)
}
if !low && c.duration > 0 {
t.Fatalf("%s low message not exec", network)
}
}
})
}
}

View File

@ -24,20 +24,21 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[MultistageBind](#multistagebind)|多级分类路由绑定函数
|`STRUCT`|[Multistage](#multistage)|支持多级分类的路由器
|`STRUCT`|[MultistageOption](#multistageoption)|路由器选项
|`STRUCT`|[MultistageBind](#struct_MultistageBind)|多级分类路由绑定函数
|`STRUCT`|[Multistage](#struct_Multistage)|支持多级分类的路由器
|`STRUCT`|[MultistageOption](#struct_MultistageOption)|路由器选项
</details>
***
## 详情信息
#### func NewMultistage(options ...MultistageOption[HandleFunc]) *Multistage[HandleFunc]
#### func NewMultistage\[HandleFunc any\](options ...MultistageOption[HandleFunc]) *Multistage[HandleFunc]
<span id="NewMultistage"></span>
> 创建一个支持多级分类的路由器
示例代码:
**示例代码:**
```go
func ExampleNewMultistage() {
@ -47,20 +48,25 @@ func ExampleNewMultistage() {
```
***
#### func WithRouteTrim(handle func (route any) any) MultistageOption[HandleFunc]
#### func WithRouteTrim\[HandleFunc any\](handle func (route any) any) MultistageOption[HandleFunc]
<span id="WithRouteTrim"></span>
> 路由修剪选项
> - 将在路由注册前对路由进行对应处理
***
<span id="struct_MultistageBind"></span>
### MultistageBind `STRUCT`
多级分类路由绑定函数
```go
type MultistageBind[HandleFunc any] func(HandleFunc)
```
<span id="struct_MultistageBind_Bind"></span>
#### func (MultistageBind) Bind(handleFunc HandleFunc)
> 将处理函数绑定到预设的路由中
***
<span id="struct_Multistage"></span>
### Multistage `STRUCT`
支持多级分类的路由器
```go
@ -71,10 +77,14 @@ type Multistage[HandleFunc any] struct {
trim func(route any) any
}
```
<span id="struct_Multistage_Register"></span>
#### func (*Multistage) Register(routes ...any) MultistageBind[HandleFunc]
> 注册路由是结合 Sub 和 Route 的快捷方式,用于一次性注册多级路由
> - 该函数将返回一个注册函数可通过调用其将路由绑定到特定处理函数例如router.Register("a", "b").Bind(onExec())
示例代码:
**示例代码:**
```go
func ExampleMultistage_Register() {
@ -86,9 +96,13 @@ func ExampleMultistage_Register() {
```
***
<span id="struct_Multistage_Route"></span>
#### func (*Multistage) Route(route any, handleFunc HandleFunc)
> 为特定路由绑定处理函数,被绑定的处理函数将可以通过 Match 函数进行匹配
示例代码:
**示例代码:**
```go
func ExampleMultistage_Route() {
@ -100,10 +114,14 @@ func ExampleMultistage_Route() {
```
***
<span id="struct_Multistage_Match"></span>
#### func (*Multistage) Match(routes ...any) HandleFunc
> 匹配已绑定处理函数的路由,返回处理函数
> - 如果未找到将会返回空指针
示例代码:
**示例代码:**
```go
func ExampleMultistage_Match() {
@ -150,9 +168,13 @@ func TestMultistage_Match(t *testing.T) {
***
<span id="struct_Multistage_Sub"></span>
#### func (*Multistage) Sub(route any) *Multistage[HandleFunc]
> 获取子路由器
示例代码:
**示例代码:**
```go
func ExampleMultistage_Sub() {
@ -164,6 +186,7 @@ func ExampleMultistage_Sub() {
```
***
<span id="struct_MultistageOption"></span>
### MultistageOption `STRUCT`
路由器选项
```go

View File

@ -84,6 +84,7 @@ type Server struct {
systemSignal chan os.Signal // 系统信号
closeChannel chan struct{} // 关闭信号
multipleRuntimeErrorChan chan error // 多服务器模式下的运行时错误
data map[string]any // 服务器全局数据
messageCounter atomic.Int64 // 消息计数器
addr string // 侦听地址
@ -92,6 +93,33 @@ type Server struct {
services []func() // 服务
}
// LoadData 加载绑定的服务器数据
func LoadData[T any](srv *Server, name string, data any) T {
return srv.data[name].(T)
}
// BindData 绑定数据到特定服务器
func BindData(srv *Server, name string, data any) {
srv.BindData(name, data)
}
// LoadData 加载绑定的服务器数据
func (srv *Server) LoadData(name string, data any) any {
return srv.data[name]
}
// BindData 绑定数据到特定服务器
func (srv *Server) BindData(name string, data any) {
if srv.data == nil {
srv.data = map[string]any{}
}
_, exist := srv.data[name]
if exist {
panic(fmt.Errorf("data with duplicate names is bound, got: %s", name))
}
srv.data[name] = data
}
// preCheckAndAdaptation 预检查及适配
func (srv *Server) preCheckAndAdaptation(addr string) (startState <-chan error, err error) {
if srv.event == nil {
@ -149,7 +177,7 @@ func (srv *Server) Run(addr string) (err error) {
return nil
}
// IsSocket 是否是 Socket 模式
// IsSocket 通过执行 Network.IsSocket 函数检查该服务器是否是 Socket 模式
func (srv *Server) IsSocket() bool {
return srv.network.IsSocket()
}

View File

@ -1,30 +1,53 @@
package server_test
import (
"fmt"
"github.com/kercylan98/minotaur/server"
"time"
)
// 该案例将创建一个简单的 WebSocket 服务器,如果需要更多的服务器类型可参考 [` Network `](#struct_Network) 部分
// - server.WithLimitLife(time.Millisecond) 通常不是在正常开发应该使用的,在这里只是为了让服务器在启动完成后的 1 毫秒后自动关闭
//
// 该案例的输出结果为 true
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)
}
fmt.Println(srv != nil)
// Output:
// true
}
// 该案例将创建两个不同类型的服务器,其中 WebSocket 是一个 Socket 服务器,而 Http 是一个非 Socket 服务器
//
// 可知案例输出结果为:
// - true
// - false
func ExampleServer_IsSocket() {
srv1 := server.New(server.NetworkWebsocket)
fmt.Println(srv1.IsSocket())
srv2 := server.New(server.NetworkHttp)
fmt.Println(srv2.IsSocket())
// Output:
// true
// false
}
// 该案例将创建一个简单的 WebSocket 服务器并启动监听 `:9999/` 作为 WebSocket 监听地址,如果需要更多的服务器类型可参考 [` Network `](#struct_Network) 部分
// - 当服务器启动失败后,将会返回错误信息并触发 panic
// - server.WithLimitLife(time.Millisecond) 通常不是在正常开发应该使用的,在这里只是为了让服务器在启动完成后的 1 毫秒后自动关闭
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)
}
// Output:
}
// RunNone 函数并没有特殊的意义,该函数内部调用了 `srv.Run("")` 函数,仅是一个语法糖,用来表示服务器不需要监听任何地址
func ExampleServer_RunNone() {
srv := server.New(server.NetworkNone)
if err := srv.RunNone(); err != nil {
panic(err)
}
// Output:
}

View File

@ -1,81 +1,101 @@
package server_test
import (
"fmt"
"github.com/kercylan98/minotaur/server"
"github.com/kercylan98/minotaur/server/client"
"github.com/kercylan98/minotaur/utils/times"
"github.com/kercylan98/minotaur/utils/super"
"runtime/debug"
"testing"
"time"
)
// 该单元测试用于测试以不同的基本参数创建服务器是否存在异常
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)
var cases = []struct {
name string
network server.Network
addr string
shouldPanic bool
}{
{name: "TestNew_Unknown", addr: "", network: "Unknown", shouldPanic: true},
{name: "TestNew_None", addr: "", network: server.NetworkNone, shouldPanic: false},
{name: "TestNew_None_Addr", addr: "addr", network: server.NetworkNone, shouldPanic: false},
{name: "TestNew_Tcp_AddrEmpty", addr: "", network: server.NetworkTcp, shouldPanic: true},
{name: "TestNew_Tcp_AddrIllegal", addr: "addr", network: server.NetworkTcp, shouldPanic: true},
{name: "TestNew_Tcp_Addr", addr: ":9999", network: server.NetworkTcp, shouldPanic: false},
{name: "TestNew_Tcp4_AddrEmpty", addr: "", network: server.NetworkTcp4, shouldPanic: true},
{name: "TestNew_Tcp4_AddrIllegal", addr: "addr", network: server.NetworkTcp4, shouldPanic: true},
{name: "TestNew_Tcp4_Addr", addr: ":9999", network: server.NetworkTcp4, shouldPanic: false},
{name: "TestNew_Tcp6_AddrEmpty", addr: "", network: server.NetworkTcp6, shouldPanic: true},
{name: "TestNew_Tcp6_AddrIllegal", addr: "addr", network: server.NetworkTcp6, shouldPanic: true},
{name: "TestNew_Tcp6_Addr", addr: ":9999", network: server.NetworkTcp6, shouldPanic: false},
{name: "TestNew_Udp_AddrEmpty", addr: "", network: server.NetworkUdp, shouldPanic: true},
{name: "TestNew_Udp_AddrIllegal", addr: "addr", network: server.NetworkUdp, shouldPanic: true},
{name: "TestNew_Udp_Addr", addr: ":9999", network: server.NetworkUdp, shouldPanic: false},
{name: "TestNew_Udp4_AddrEmpty", addr: "", network: server.NetworkUdp4, shouldPanic: true},
{name: "TestNew_Udp4_AddrIllegal", addr: "addr", network: server.NetworkUdp4, shouldPanic: true},
{name: "TestNew_Udp4_Addr", addr: ":9999", network: server.NetworkUdp4, shouldPanic: false},
{name: "TestNew_Udp6_AddrEmpty", addr: "", network: server.NetworkUdp6, shouldPanic: true},
{name: "TestNew_Udp6_AddrIllegal", addr: "addr", network: server.NetworkUdp6, shouldPanic: true},
{name: "TestNew_Udp6_Addr", addr: ":9999", network: server.NetworkUdp6, shouldPanic: false},
{name: "TestNew_Unix_AddrEmpty", addr: "", network: server.NetworkUnix, shouldPanic: true},
{name: "TestNew_Unix_AddrIllegal", addr: "addr", network: server.NetworkUnix, shouldPanic: true},
{name: "TestNew_Unix_Addr", addr: "addr", network: server.NetworkUnix, shouldPanic: false},
{name: "TestNew_Websocket_AddrEmpty", addr: "", network: server.NetworkWebsocket, shouldPanic: true},
{name: "TestNew_Websocket_AddrIllegal", addr: "addr", network: server.NetworkWebsocket, shouldPanic: true},
{name: "TestNew_Websocket_Addr", addr: ":9999/ws", network: server.NetworkWebsocket, shouldPanic: false},
{name: "TestNew_Http_AddrEmpty", addr: "", network: server.NetworkHttp, shouldPanic: true},
{name: "TestNew_Http_AddrIllegal", addr: "addr", network: server.NetworkHttp, shouldPanic: true},
{name: "TestNew_Http_Addr", addr: ":9999", network: server.NetworkHttp, shouldPanic: false},
{name: "TestNew_Kcp_AddrEmpty", addr: "", network: server.NetworkKcp, shouldPanic: true},
{name: "TestNew_Kcp_AddrIllegal", addr: "addr", network: server.NetworkKcp, shouldPanic: true},
{name: "TestNew_Kcp_Addr", addr: ":9999", network: server.NetworkKcp, shouldPanic: false},
{name: "TestNew_GRPC_AddrEmpty", addr: "", network: server.NetworkGRPC, shouldPanic: true},
{name: "TestNew_GRPC_AddrIllegal", addr: "addr", network: server.NetworkGRPC, shouldPanic: true},
{name: "TestNew_GRPC_Addr", addr: ":9999", network: server.NetworkGRPC, shouldPanic: false},
}
}
func TestNew2(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 TestNewClient(t *testing.T) {
count := 500
for i := 0; i < count; i++ {
fmt.Println("启动", i+1)
cli := client.NewWebsocket("ws://172.29.5.138:9999")
cli.RegConnectionReceivePacketEvent(func(conn *client.Client, wst int, packet []byte) {
fmt.Println(time.Now().Unix(), "收到", string(packet))
})
cli.RegConnectionClosedEvent(func(conn *client.Client, err any) {
fmt.Println("关闭", err)
})
cli.RegConnectionOpenedEvent(func(conn *client.Client) {
go func() {
for i < count {
time.Sleep(time.Second)
}
for {
for i := 0; i < 10; i++ {
cli.WriteWS(2, []byte("hello"))
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
defer func() {
if err := super.RecoverTransform(recover()); err != nil && !c.shouldPanic {
debug.PrintStack()
t.Fatal("not should panic, err:", err)
}
}()
if err := server.New(c.network, server.WithLimitLife(time.Millisecond*10)).Run(""); err != nil {
panic(err)
}
})
if err := cli.Run(); err != nil {
panic(err)
}
}
}
// 这个测试检查了各个类型的服务器是否为 Socket 模式。如需查看为 Socket 模式的网络类型,请参考 [` Network.IsSocket` ](#struct_Network_IsSocket)
func TestServer_IsSocket(t *testing.T) {
var cases = []struct {
name string
network server.Network
expect bool
}{
{name: "TestServer_IsSocket_None", network: server.NetworkNone, expect: false},
{name: "TestServer_IsSocket_Tcp", network: server.NetworkTcp, expect: true},
{name: "TestServer_IsSocket_Tcp4", network: server.NetworkTcp4, expect: true},
{name: "TestServer_IsSocket_Tcp6", network: server.NetworkTcp6, expect: true},
{name: "TestServer_IsSocket_Udp", network: server.NetworkUdp, expect: true},
{name: "TestServer_IsSocket_Udp4", network: server.NetworkUdp4, expect: true},
{name: "TestServer_IsSocket_Udp6", network: server.NetworkUdp6, expect: true},
{name: "TestServer_IsSocket_Unix", network: server.NetworkUnix, expect: true},
{name: "TestServer_IsSocket_Http", network: server.NetworkHttp, expect: false},
{name: "TestServer_IsSocket_Websocket", network: server.NetworkWebsocket, expect: true},
{name: "TestServer_IsSocket_Kcp", network: server.NetworkKcp, expect: true},
{name: "TestServer_IsSocket_GRPC", network: server.NetworkGRPC, expect: false},
}
time.Sleep(times.Week)
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
s := server.New(c.network)
if s.IsSocket() != c.expect {
t.Fatalf("expect: %v, got: %v", c.expect, s.IsSocket())
}
})
}
}

View File

@ -5,7 +5,8 @@ import (
"reflect"
)
// Service 兼容传统 service 设计模式的接口
// Service 兼容传统 service 设计模式的接口,通过该接口可以实现更简洁、更具有可读性的服务绑定
// - 在这之前,我们在实现功能上会将 Server 进行全局存储,之后通过 init 函数进行初始化,这样的顺序是不可控的。
type Service interface {
// OnInit 初始化服务,该方法将会在 Server 初始化时执行
// - 通常来说,该阶段发生任何错误都应该 panic 以阻止 Server 启动

View File

@ -0,0 +1,44 @@
package server_test
import (
"github.com/kercylan98/minotaur/server"
"time"
)
// 这个案例中我们将 `TestService` 绑定到了 `srv` 服务器中,当服务器启动时,将会对 `TestService` 进行初始化
//
// 其中 `TestService` 的定义如下:
// ```go
//
// type TestService struct{}
//
// func (ts *TestService) OnInit(srv *server.Server) {
// srv.RegStartFinishEvent(onStartFinish)
//
// srv.RegStopEvent(func(srv *server.Server) {
// fmt.Println("server stop")
// })
// }
//
// func (ts *TestService) onStartFinish(srv *server.Server) {
// fmt.Println("server start finish")
// }
//
// ```
//
// 可以看出,在服务初始化时,该服务向服务器注册了启动完成事件及停止事件。这是我们推荐的编码方式,这样编码有以下好处:
// - 具备可控制的初始化顺序,避免 init 产生的各种顺序导致的问题,如配置还未加载完成,即开始进行数据库连接等操作
// - 可以方便的将不同的服务拆分到不同的包中进行管理
// - 当不需要某个服务时,可以直接删除该服务的绑定,而不需要修改其他代码
// - ...
func ExampleBindService() {
srv := server.New(server.NetworkNone, server.WithLimitLife(time.Second))
server.BindService(srv, new(TestService))
if err := srv.RunNone(); err != nil {
panic(err)
}
// Output:
// server start finish
// server stop
}

View File

@ -20,24 +20,19 @@ func (ts *TestService) OnInit(srv *server.Server) {
}
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)
}
}
func ExampleBindService() {
srv := server.New(server.NetworkNone, server.WithLimitLife(time.Second))
server.BindService(srv, new(TestService))
if err := srv.RunNone(); err != nil {
panic(err)
var cases = []struct {
name string
}{
{name: "TestBindService"},
}
// Output:
// server start finish
// server stop
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
srv := server.New(server.NetworkNone, server.WithLimitLife(time.Millisecond))
server.BindService(srv, new(TestService))
if err := srv.RunNone(); err != nil {
t.Fatal(err)
}
})
}
}

View File

@ -24,16 +24,16 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Channel](#channel)|基于 chan 的写循环,与 Unbounded 相同,但是使用 Channel 实现
|`STRUCT`|[Unbounded](#unbounded)|写循环
|`INTERFACE`|[WriteLoop](#writeloop)|暂无描述...
|`STRUCT`|[Channel](#struct_Channel)|基于 chan 的写循环,与 Unbounded 相同,但是使用 Channel 实现
|`STRUCT`|[Unbounded](#struct_Unbounded)|写循环
|`INTERFACE`|[WriteLoop](#struct_WriteLoop)|暂无描述...
</details>
***
## 详情信息
#### func NewChannel(pool *hub.ObjectPool[Message], channelSize int, writeHandler func (message Message) error, errorHandler func (err any)) *Channel[Message]
#### func NewChannel\[Message any\](pool *hub.ObjectPool[Message], channelSize int, writeHandler func (message Message) error, errorHandler func (err any)) *Channel[Message]
<span id="NewChannel"></span>
> 创建基于 Channel 的写循环
> - pool 用于管理 Message 对象的缓冲池,在创建 Message 对象时也应该使用该缓冲池,以便复用 Message 对象。 Channel 会在写入完成后将 Message 对象放回缓冲池
@ -44,7 +44,7 @@
> 传入 writeHandler 的消息对象是从 Channel 中获取的,因此 writeHandler 不应该持有消息对象的引用,同时也不应该主动释放消息对象
***
#### func NewUnbounded(pool *hub.ObjectPool[Message], writeHandler func (message Message) error, errorHandler func (err any)) *Unbounded[Message]
#### func NewUnbounded\[Message any\](pool *hub.ObjectPool[Message], writeHandler func (message Message) error, errorHandler func (err any)) *Unbounded[Message]
<span id="NewUnbounded"></span>
> 创建写循环
> - pool 用于管理 Message 对象的缓冲池,在创建 Message 对象时也应该使用该缓冲池,以便复用 Message 对象。 Unbounded 会在写入完成后将 Message 对象放回缓冲池
@ -53,7 +53,8 @@
>
> 传入 writeHandler 的消息对象是从 pool 中获取的,并且在 writeHandler 执行完成后会被放回 pool 中,因此 writeHandler 不应该持有消息对象的引用,同时也不应该主动释放消息对象
示例代码:
**示例代码:**
```go
func ExampleNewUnbounded() {
@ -106,6 +107,7 @@ func TestNewUnbounded(t *testing.T) {
***
<span id="struct_Channel"></span>
### Channel `STRUCT`
基于 chan 的写循环,与 Unbounded 相同,但是使用 Channel 实现
```go
@ -113,12 +115,19 @@ type Channel[T any] struct {
c chan T
}
```
<span id="struct_Channel_Put"></span>
#### func (*Channel) Put(message T)
> 将数据放入写循环message 应该来源于 hub.ObjectPool
***
<span id="struct_Channel_Close"></span>
#### func (*Channel) Close()
> 关闭写循环
***
<span id="struct_Unbounded"></span>
### Unbounded `STRUCT`
写循环
- 用于将数据并发安全的写入到底层连接
@ -127,8 +136,11 @@ type Unbounded[Message any] struct {
buf *buffer.Unbounded[Message]
}
```
<span id="struct_Unbounded_Put"></span>
#### func (*Unbounded) Put(message Message)
> 将数据放入写循环message 应该来源于 hub.ObjectPool
<details>
<summary>查看 / 收起单元测试</summary>
@ -186,8 +198,11 @@ func BenchmarkUnbounded_Put(b *testing.B) {
***
<span id="struct_Unbounded_Close"></span>
#### func (*Unbounded) Close()
> 关闭写循环
<details>
<summary>查看 / 收起单元测试</summary>
@ -217,6 +232,7 @@ func TestUnbounded_Close(t *testing.T) {
***
<span id="struct_WriteLoop"></span>
### WriteLoop `INTERFACE`
```go

View File

@ -28,16 +28,16 @@ AOI 问题是在大规模多人在线游戏中常见的问题,它涉及到确
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[TwoDimensional](#twodimensional)|暂无描述...
|`INTERFACE`|[TwoDimensionalEntity](#twodimensionalentity)|基于2D定义的AOI对象功能接口
|`STRUCT`|[EntityJoinVisionEventHandle](#entityjoinvisioneventhandle)|暂无描述...
|`STRUCT`|[TwoDimensional](#struct_TwoDimensional)|暂无描述...
|`INTERFACE`|[TwoDimensionalEntity](#struct_TwoDimensionalEntity)|基于2D定义的AOI对象功能接口
|`STRUCT`|[EntityJoinVisionEventHandle](#struct_EntityJoinVisionEventHandle)|暂无描述...
</details>
***
## 详情信息
#### func NewTwoDimensional(width int, height int, areaWidth int, areaHeight int) *TwoDimensional[EID, PosType, E]
#### func NewTwoDimensional\[EID generic.Basic, PosType generic.SignedNumber, E TwoDimensionalEntity[EID, PosType]\](width int, height int, areaWidth int, areaHeight int) *TwoDimensional[EID, PosType, E]
<span id="NewTwoDimensional"></span>
<details>
@ -65,6 +65,7 @@ func TestNewTwoDimensional(t *testing.T) {
***
<span id="struct_TwoDimensional"></span>
### TwoDimensional `STRUCT`
```go
@ -82,6 +83,37 @@ type TwoDimensional[EID generic.Basic, PosType generic.SignedNumber, E TwoDimens
repartitionQueue []func()
}
```
<span id="struct_TwoDimensional_AddEntity"></span>
#### func (*TwoDimensional) AddEntity(entity E)
***
<span id="struct_TwoDimensional_DeleteEntity"></span>
#### func (*TwoDimensional) DeleteEntity(entity E)
***
<span id="struct_TwoDimensional_Refresh"></span>
#### func (*TwoDimensional) Refresh(entity E)
***
<span id="struct_TwoDimensional_GetFocus"></span>
#### func (*TwoDimensional) GetFocus(id EID) map[EID]E
***
<span id="struct_TwoDimensional_SetSize"></span>
#### func (*TwoDimensional) SetSize(width int, height int)
***
<span id="struct_TwoDimensional_SetAreaSize"></span>
#### func (*TwoDimensional) SetAreaSize(width int, height int)
***
<span id="struct_TwoDimensionalEntity"></span>
### TwoDimensionalEntity `INTERFACE`
基于2D定义的AOI对象功能接口
- AOI 对象提供了 AOI 系统中常用的属性,诸如位置坐标和视野范围等
@ -92,6 +124,7 @@ type TwoDimensionalEntity[EID generic.Basic, PosType generic.SignedNumber] inter
GetPosition() geometry.Point[PosType]
}
```
<span id="struct_EntityJoinVisionEventHandle"></span>
### EntityJoinVisionEventHandle `STRUCT`
```go

View File

@ -34,70 +34,70 @@ arrangement 包提供了一些有用的函数来处理数组的排列。
|类型|名称|描述
|:--|:--|:--
|`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)|暂无描述...
|`STRUCT`|[Area](#struct_Area)|编排区域
|`STRUCT`|[AreaOption](#struct_AreaOption)|编排区域选项
|`STRUCT`|[AreaConstraintHandle](#struct_AreaConstraintHandle)|暂无描述...
|`STRUCT`|[Arrangement](#struct_Arrangement)|用于针对多条数据进行合理编排的数据结构
|`STRUCT`|[Editor](#struct_Editor)|提供了大量辅助函数的编辑器
|`INTERFACE`|[Item](#struct_Item)|编排成员
|`STRUCT`|[ItemOption](#struct_ItemOption)|编排成员选项
|`STRUCT`|[ItemFixedAreaHandle](#struct_ItemFixedAreaHandle)|暂无描述...
|`STRUCT`|[Option](#struct_Option)|编排选项
|`STRUCT`|[ConstraintHandle](#struct_ConstraintHandle)|暂无描述...
</details>
***
## 详情信息
#### func WithAreaConstraint(constraint AreaConstraintHandle[ID, AreaInfo]) AreaOption[ID, AreaInfo]
#### func WithAreaConstraint\[ID comparable, AreaInfo any\](constraint AreaConstraintHandle[ID, AreaInfo]) AreaOption[ID, AreaInfo]
<span id="WithAreaConstraint"></span>
> 设置编排区域的约束条件
> - 该约束用于判断一个成员是否可以被添加到该编排区域中
> - 与 WithAreaConflict 不同的是,约束通常用于非成员关系导致的硬性约束,例如:成员的等级过滤、成员的性别等
***
#### func WithAreaConflict(conflict AreaConflictHandle[ID, AreaInfo]) AreaOption[ID, AreaInfo]
#### func WithAreaConflict\[ID comparable, AreaInfo any\](conflict AreaConflictHandle[ID, AreaInfo]) AreaOption[ID, AreaInfo]
<span id="WithAreaConflict"></span>
> 设置编排区域的冲突条件,冲突处理函数需要返回造成冲突的成员列表
> - 该冲突用于判断一个成员是否可以被添加到该编排区域中
> - 与 WithAreaConstraint 不同的是,冲突通常用于成员关系导致的软性约束,例如:成员的职业唯一性、成员的种族唯一性等
***
#### func WithAreaEvaluate(evaluate AreaEvaluateHandle[ID, AreaInfo]) AreaOption[ID, AreaInfo]
#### func WithAreaEvaluate\[ID comparable, AreaInfo any\](evaluate AreaEvaluateHandle[ID, AreaInfo]) AreaOption[ID, AreaInfo]
<span id="WithAreaEvaluate"></span>
> 设置编排区域的评估函数
> - 该评估函数将影响成员被编入区域的优先级
***
#### func NewArrangement(options ...Option[ID, AreaInfo]) *Arrangement[ID, AreaInfo]
#### func NewArrangement\[ID comparable, AreaInfo any\](options ...Option[ID, AreaInfo]) *Arrangement[ID, AreaInfo]
<span id="NewArrangement"></span>
> 创建一个新的编排
***
#### func WithItemFixed(matcher ItemFixedAreaHandle[AreaInfo]) ItemOption[ID, AreaInfo]
#### func WithItemFixed\[ID comparable, AreaInfo any\](matcher ItemFixedAreaHandle[AreaInfo]) ItemOption[ID, AreaInfo]
<span id="WithItemFixed"></span>
> 设置成员的固定编排区域
***
#### func WithItemPriority(priority ItemPriorityHandle[ID, AreaInfo]) ItemOption[ID, AreaInfo]
#### func WithItemPriority\[ID comparable, AreaInfo any\](priority ItemPriorityHandle[ID, AreaInfo]) ItemOption[ID, AreaInfo]
<span id="WithItemPriority"></span>
> 设置成员的优先级
***
#### func WithItemNotAllow(verify ItemNotAllowVerifyHandle[ID, AreaInfo]) ItemOption[ID, AreaInfo]
#### func WithItemNotAllow\[ID comparable, AreaInfo any\](verify ItemNotAllowVerifyHandle[ID, AreaInfo]) ItemOption[ID, AreaInfo]
<span id="WithItemNotAllow"></span>
> 设置成员不允许的编排区域
***
#### func WithRetryThreshold(threshold int) Option[ID, AreaInfo]
#### func WithRetryThreshold\[ID comparable, AreaInfo any\](threshold int) Option[ID, AreaInfo]
<span id="WithRetryThreshold"></span>
> 设置编排时的重试阈值
> - 当每一轮编排结束任有成员未被编排时,将会进行下一轮编排,直到编排次数达到该阈值
> - 默认的阈值为 10 次
***
#### func WithConstraintHandle(handle ConstraintHandle[ID, AreaInfo]) Option[ID, AreaInfo]
#### func WithConstraintHandle\[ID comparable, AreaInfo any\](handle ConstraintHandle[ID, AreaInfo]) Option[ID, AreaInfo]
<span id="WithConstraintHandle"></span>
> 设置编排时触发约束时的处理函数
> - 当约束条件触发时,将会调用该函数。如果无法在该函数中处理约束,应该继续返回 err尝试进行下一层的约束处理
@ -107,7 +107,7 @@ arrangement 包提供了一些有用的函数来处理数组的排列。
> 有意思的是,硬性约束应该永远是无解的,而当需要进行一些打破规则的操作时,则可以透过该函数传入的 editor 进行操作
***
#### func WithConflictHandle(handle ConflictHandle[ID, AreaInfo]) Option[ID, AreaInfo]
#### func WithConflictHandle\[ID comparable, AreaInfo any\](handle ConflictHandle[ID, AreaInfo]) Option[ID, AreaInfo]
<span id="WithConflictHandle"></span>
> 设置编排时触发冲突时的处理函数
> - 当冲突条件触发时,将会调用该函数。如果无法在该函数中处理冲突,应该继续返回这一批成员,尝试进行下一层的冲突处理
@ -115,6 +115,7 @@ arrangement 包提供了一些有用的函数来处理数组的排列。
> - 当所有的冲突处理函数都无法处理冲突时,将会进入下一个编排区域的尝试,如果均无法完成,将会将该成员加入到编排队列的末端,等待下一次编排
***
<span id="struct_Area"></span>
### Area `STRUCT`
编排区域
```go
@ -126,16 +127,56 @@ type Area[ID comparable, AreaInfo any] struct {
evaluate AreaEvaluateHandle[ID, AreaInfo]
}
```
<span id="struct_Area_GetAreaInfo"></span>
#### func (*Area) GetAreaInfo() AreaInfo
> 获取编排区域的信息
***
<span id="struct_Area_GetItems"></span>
#### func (*Area) GetItems() map[ID]Item[ID]
> 获取编排区域中的所有成员
***
<span id="struct_Area_IsAllow"></span>
#### func (*Area) IsAllow(item Item[ID]) (constraintErr error, conflictItems map[ID]Item[ID], allow bool)
> 检测一个成员是否可以被添加到该编排区域中
***
<span id="struct_Area_IsConflict"></span>
#### func (*Area) IsConflict(item Item[ID]) bool
> 检测一个成员是否会造成冲突
***
<span id="struct_Area_GetConflictItems"></span>
#### func (*Area) GetConflictItems(item Item[ID]) map[ID]Item[ID]
> 获取与一个成员产生冲突的所有其他成员
***
<span id="struct_Area_GetScore"></span>
#### func (*Area) GetScore(extra ...Item[ID]) float64
> 获取该编排区域的评估分数
> - 当 extra 不为空时,将会将 extra 中的内容添加到 items 中进行评估
***
<span id="struct_AreaOption"></span>
### AreaOption `STRUCT`
编排区域选项
```go
type AreaOption[ID comparable, AreaInfo any] func(area *Area[ID, AreaInfo])
```
<span id="struct_AreaConstraintHandle"></span>
### AreaConstraintHandle `STRUCT`
```go
type AreaConstraintHandle[ID comparable, AreaInfo any] func(area *Area[ID, AreaInfo], item Item[ID]) error
```
<span id="struct_Arrangement"></span>
### Arrangement `STRUCT`
用于针对多条数据进行合理编排的数据结构
- 我不知道这个数据结构的具体用途,但是我觉得这个数据结构应该是有用的
@ -153,6 +194,75 @@ type Arrangement[ID comparable, AreaInfo any] struct {
conflictHandles []ConflictHandle[ID, AreaInfo]
}
```
<span id="struct_Arrangement_AddArea"></span>
#### func (*Arrangement) AddArea(areaInfo AreaInfo, options ...AreaOption[ID, AreaInfo])
> 添加一个编排区域
***
<span id="struct_Arrangement_AddItem"></span>
#### func (*Arrangement) AddItem(item Item[ID])
> 添加一个成员
***
<span id="struct_Arrangement_Arrange"></span>
#### func (*Arrangement) Arrange() (areas []*Area[ID, AreaInfo], noSolution map[ID]Item[ID])
> 编排
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestArrangement_Arrange(t *testing.T) {
var a = arrangement.NewArrangement[int, *Team]()
a.AddArea(&Team{ID: 1}, arrangement.WithAreaConstraint[int, *Team](func(area *arrangement.Area[int, *Team], item arrangement.Item[int]) error {
if len(area.GetItems()) >= 2 {
return errors.New("too many")
}
return nil
}))
a.AddArea(&Team{ID: 2}, arrangement.WithAreaConstraint[int, *Team](func(area *arrangement.Area[int, *Team], item arrangement.Item[int]) error {
if len(area.GetItems()) >= 1 {
return errors.New("too many")
}
return nil
}))
a.AddArea(&Team{ID: 3}, arrangement.WithAreaConstraint[int, *Team](func(area *arrangement.Area[int, *Team], item arrangement.Item[int]) error {
if len(area.GetItems()) >= 2 {
return errors.New("too many")
}
return nil
}))
for i := 0; i < 10; i++ {
a.AddItem(&Player{ID: i + 1})
}
res, no := a.Arrange()
for _, area := range res {
var str = fmt.Sprintf("area %d: ", area.GetAreaInfo().ID)
for id := range area.GetItems() {
str += fmt.Sprintf("%d ", id)
}
fmt.Println(str)
}
var noStr = "no: "
for _, i := range no {
noStr += fmt.Sprintf("%d ", i.GetID())
}
fmt.Println(noStr)
}
```
</details>
***
<span id="struct_Editor"></span>
### Editor `STRUCT`
提供了大量辅助函数的编辑器
```go
@ -164,6 +274,91 @@ type Editor[ID comparable, AreaInfo any] struct {
retryCount int
}
```
<span id="struct_Editor_GetPendingCount"></span>
#### func (*Editor) GetPendingCount() int
> 获取待编排的成员数量
***
<span id="struct_Editor_RemoveAreaItem"></span>
#### func (*Editor) RemoveAreaItem(area *Area[ID, AreaInfo], item Item[ID])
> 从编排区域中移除一个成员到待编排队列中,如果该成员不存在于编排区域中,则不进行任何操作
***
<span id="struct_Editor_AddAreaItem"></span>
#### func (*Editor) AddAreaItem(area *Area[ID, AreaInfo], item Item[ID])
> 将一个成员添加到编排区域中,如果该成员已经存在于编排区域中,则不进行任何操作
***
<span id="struct_Editor_GetAreas"></span>
#### func (*Editor) GetAreas() []*Area[ID, AreaInfo]
> 获取所有的编排区域
***
<span id="struct_Editor_GetAreasWithScoreAsc"></span>
#### func (*Editor) GetAreasWithScoreAsc(extra ...Item[ID]) []*Area[ID, AreaInfo]
> 获取所有的编排区域,并按照分数升序排序
***
<span id="struct_Editor_GetAreasWithScoreDesc"></span>
#### func (*Editor) GetAreasWithScoreDesc(extra ...Item[ID]) []*Area[ID, AreaInfo]
> 获取所有的编排区域,并按照分数降序排序
***
<span id="struct_Editor_GetRetryCount"></span>
#### func (*Editor) GetRetryCount() int
> 获取重试次数
***
<span id="struct_Editor_GetThresholdProgressRate"></span>
#### func (*Editor) GetThresholdProgressRate() float64
> 获取重试次数阈值进度
***
<span id="struct_Editor_GetAllowAreas"></span>
#### func (*Editor) GetAllowAreas(item Item[ID]) []*Area[ID, AreaInfo]
> 获取允许的编排区域
***
<span id="struct_Editor_GetNoAllowAreas"></span>
#### func (*Editor) GetNoAllowAreas(item Item[ID]) []*Area[ID, AreaInfo]
> 获取不允许的编排区域
***
<span id="struct_Editor_GetBestAllowArea"></span>
#### func (*Editor) GetBestAllowArea(item Item[ID]) *Area[ID, AreaInfo]
> 获取最佳的允许的编排区域,如果不存在,则返回 nil
***
<span id="struct_Editor_GetBestNoAllowArea"></span>
#### func (*Editor) GetBestNoAllowArea(item Item[ID]) *Area[ID, AreaInfo]
> 获取最佳的不允许的编排区域,如果不存在,则返回 nil
***
<span id="struct_Editor_GetWorstAllowArea"></span>
#### func (*Editor) GetWorstAllowArea(item Item[ID]) *Area[ID, AreaInfo]
> 获取最差的允许的编排区域,如果不存在,则返回 nil
***
<span id="struct_Editor_GetWorstNoAllowArea"></span>
#### func (*Editor) GetWorstNoAllowArea(item Item[ID]) *Area[ID, AreaInfo]
> 获取最差的不允许的编排区域,如果不存在,则返回 nil
***
<span id="struct_Item"></span>
### Item `INTERFACE`
编排成员
```go
@ -172,21 +367,25 @@ type Item[ID comparable] interface {
Equal(item Item[ID]) bool
}
```
<span id="struct_ItemOption"></span>
### ItemOption `STRUCT`
编排成员选项
```go
type ItemOption[ID comparable, AreaInfo any] func(arrangement *Arrangement[ID, AreaInfo], item Item[ID])
```
<span id="struct_ItemFixedAreaHandle"></span>
### ItemFixedAreaHandle `STRUCT`
```go
type ItemFixedAreaHandle[AreaInfo any] func(areaInfo AreaInfo) bool
```
<span id="struct_Option"></span>
### Option `STRUCT`
编排选项
```go
type Option[ID comparable, AreaInfo any] func(arrangement *Arrangement[ID, AreaInfo])
```
<span id="struct_ConstraintHandle"></span>
### ConstraintHandle `STRUCT`
```go

View File

@ -31,16 +31,16 @@ buffer 提供了缓冲区相关的实用程序。
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Ring](#ring)|环形缓冲区
|`STRUCT`|[RingUnbounded](#ringunbounded)|基于环形缓冲区实现的无界缓冲区
|`STRUCT`|[Unbounded](#unbounded)|是无界缓冲区的实现
|`STRUCT`|[Ring](#struct_Ring)|环形缓冲区
|`STRUCT`|[RingUnbounded](#struct_RingUnbounded)|基于环形缓冲区实现的无界缓冲区
|`STRUCT`|[Unbounded](#struct_Unbounded)|是无界缓冲区的实现
</details>
***
## 详情信息
#### func NewRing(initSize ...int) *Ring[T]
#### func NewRing\[T any\](initSize ...int) *Ring[T]
<span id="NewRing"></span>
> 创建一个并发不安全的环形缓冲区
> - initSize: 初始容量
@ -68,12 +68,12 @@ func TestNewRing(t *testing.T) {
***
#### func NewRingUnbounded(bufferSize int) *RingUnbounded[T]
#### func NewRingUnbounded\[T any\](bufferSize int) *RingUnbounded[T]
<span id="NewRingUnbounded"></span>
> 创建一个并发安全的基于环形缓冲区实现的无界缓冲区
***
#### func NewUnbounded() *Unbounded[V]
#### func NewUnbounded\[V any\]() *Unbounded[V]
<span id="NewUnbounded"></span>
> 创建一个无界缓冲区
> - generateNil: 生成空值的函数,该函数仅需始终返回 nil 即可
@ -82,6 +82,7 @@ func TestNewRing(t *testing.T) {
> - 该缓冲区的所有方法都是线程安全的,除了用于同步的互斥锁外,不会阻塞任何东西
***
<span id="struct_Ring"></span>
### Ring `STRUCT`
环形缓冲区
```go
@ -93,8 +94,11 @@ type Ring[T any] struct {
w int
}
```
#### func (*Ring) Read() T, error
<span id="struct_Ring_Read"></span>
#### func (*Ring) Read() ( T, error)
> 读取数据
<details>
<summary>查看 / 收起基准测试</summary>
@ -119,14 +123,23 @@ func BenchmarkRing_Read(b *testing.B) {
***
<span id="struct_Ring_ReadAll"></span>
#### func (*Ring) ReadAll() []T
> 读取所有数据
***
<span id="struct_Ring_Peek"></span>
#### func (*Ring) Peek() (t T, err error)
> 查看数据
***
<span id="struct_Ring_Write"></span>
#### func (*Ring) Write(v T)
> 写入数据
<details>
<summary>查看 / 收起基准测试</summary>
@ -148,18 +161,31 @@ func BenchmarkRing_Write(b *testing.B) {
***
<span id="struct_Ring_IsEmpty"></span>
#### func (*Ring) IsEmpty() bool
> 是否为空
***
<span id="struct_Ring_Cap"></span>
#### func (*Ring) Cap() int
> 返回缓冲区容量
***
<span id="struct_Ring_Len"></span>
#### func (*Ring) Len() int
> 返回缓冲区长度
***
<span id="struct_Ring_Reset"></span>
#### func (*Ring) Reset()
> 重置缓冲区
***
<span id="struct_RingUnbounded"></span>
### RingUnbounded `STRUCT`
基于环形缓冲区实现的无界缓冲区
```go
@ -173,8 +199,11 @@ type RingUnbounded[T any] struct {
closedSignal chan struct{}
}
```
<span id="struct_RingUnbounded_Write"></span>
#### func (*RingUnbounded) Write(v T)
> 写入数据
<details>
<summary>查看 / 收起基准测试</summary>
@ -196,8 +225,11 @@ func BenchmarkRingUnbounded_Write(b *testing.B) {
***
<span id="struct_RingUnbounded_Read"></span>
#### func (*RingUnbounded) Read() chan T
> 读取数据
<details>
<summary>查看 / 收起基准测试</summary>
@ -222,11 +254,17 @@ func BenchmarkRingUnbounded_Read(b *testing.B) {
***
<span id="struct_RingUnbounded_Closed"></span>
#### func (*RingUnbounded) Closed() bool
> 判断缓冲区是否已关闭
***
<span id="struct_RingUnbounded_Close"></span>
#### func (*RingUnbounded) Close() chan struct {}
> 关闭缓冲区,关闭后将不再接收新数据,但是已有数据仍然可以读取
<details>
<summary>查看 / 收起单元测试</summary>
@ -255,6 +293,7 @@ func TestRingUnbounded_Close(t *testing.T) {
***
<span id="struct_Unbounded"></span>
### Unbounded `STRUCT`
是无界缓冲区的实现
```go
@ -265,15 +304,24 @@ type Unbounded[V any] struct {
backlog []V
}
```
<span id="struct_Unbounded_Put"></span>
#### func (*Unbounded) Put(t V)
> 将数据放入缓冲区
***
<span id="struct_Unbounded_Load"></span>
#### func (*Unbounded) Load()
> 将缓冲区中的数据发送到读取通道中,如果缓冲区中没有数据,则不会发送
> - 在每次 Get 后都应该执行该函数
***
<span id="struct_Unbounded_Get"></span>
#### func (*Unbounded) Get() chan V
> 获取读取通道
<details>
<summary>查看 / 收起单元测试</summary>
@ -296,9 +344,15 @@ func TestUnbounded_Get(t *testing.T) {
***
<span id="struct_Unbounded_Close"></span>
#### func (*Unbounded) Close()
> 关闭
***
<span id="struct_Unbounded_IsClosed"></span>
#### func (*Unbounded) IsClosed() bool
> 是否已关闭
***

File diff suppressed because it is too large Load Diff

View File

@ -4,12 +4,13 @@ import (
"slices"
)
// CloneSlice 克隆切片,该函数是 slices.Clone 的快捷方式
// CloneSlice 通过创建一个新切片并将 slice 的元素复制到新切片的方式来克隆切片
func CloneSlice[S ~[]V, V any](slice S) S {
return slices.Clone(slice)
}
// CloneMap 克隆 map
// CloneMap 通过创建一个新 map 并将 m 的元素复制到新 map 的方式来克隆 map
// - 当 m 为空时,将会返回 nil
func CloneMap[M ~map[K]V, K comparable, V any](m M) M {
if m == nil {
return nil
@ -22,7 +23,8 @@ func CloneMap[M ~map[K]V, K comparable, V any](m M) M {
return result
}
// CloneSliceN 克隆 slice 为 n 个切片进行返回
// CloneSliceN 通过创建一个新切片并将 slice 的元素复制到新切片的方式来克隆切片为 n 个切片
// - 当 slice 为空时,将会返回 nil当 n <= 0 时,将会返回零值切片
func CloneSliceN[S ~[]V, V any](slice S, n int) []S {
if slice == nil {
return nil
@ -38,7 +40,8 @@ func CloneSliceN[S ~[]V, V any](slice S, n int) []S {
return result
}
// CloneMapN 克隆 map 为 n 个 map 进行返回
// CloneMapN 通过创建一个新 map 并将 m 的元素复制到新 map 的方式来克隆 map 为 n 个 map
// - 当 m 为空时,将会返回 nil当 n <= 0 时,将会返回零值切片
func CloneMapN[M ~map[K]V, K comparable, V any](m M, n int) []M {
if m == nil {
return nil
@ -55,7 +58,9 @@ func CloneMapN[M ~map[K]V, K comparable, V any](m M, n int) []M {
return result
}
// CloneSlices 克隆多个切片
// CloneSlices 对 slices 中的每一项元素进行克隆,最终返回一个新的二维切片
// - 当 slices 为空时,将会返回 nil
// - 该函数相当于使用 CloneSlice 函数一次性对多个切片进行克隆
func CloneSlices[S ~[]V, V any](slices ...S) []S {
if slices == nil {
return nil
@ -68,7 +73,9 @@ func CloneSlices[S ~[]V, V any](slices ...S) []S {
return result
}
// CloneMaps 克隆多个 map
// CloneMaps 对 maps 中的每一项元素进行克隆,最终返回一个新的 map 切片
// - 当 maps 为空时,将会返回 nil
// - 该函数相当于使用 CloneMap 函数一次性对多个 map 进行克隆
func CloneMaps[M ~map[K]V, K comparable, V any](maps ...M) []M {
if maps == nil {
return nil

View File

@ -5,6 +5,8 @@ import (
"github.com/kercylan98/minotaur/utils/collection"
)
// 在该示例中,将 slice 克隆后将会得到一个新的 slice result而 result 和 slice 将不会有任何关联,但是如果 slice 中的元素是引用类型,那么 result 中的元素将会和 slice 中的元素指向同一个地址
// - 示例中的结果将会输出 [1 2 3]
func ExampleCloneSlice() {
var slice = []int{1, 2, 3}
var result = collection.CloneSlice(slice)
@ -13,6 +15,8 @@ func ExampleCloneSlice() {
// [1 2 3]
}
// 在该示例中,将 map 克隆后将会得到一个新的 map result而 result 和 map 将不会有任何关联,但是如果 map 中的元素是引用类型,那么 result 中的元素将会和 map 中的元素指向同一个地址
// - 示例中的结果将会输出 3
func ExampleCloneMap() {
var m = map[int]int{1: 1, 2: 2, 3: 3}
var result = collection.CloneMap(m)
@ -21,6 +25,9 @@ func ExampleCloneMap() {
// 3
}
// 通过将 slice 克隆为 2 个新的 slice将会得到一个新的 slice result而 result 和 slice 将不会有任何关联,但是如果 slice 中的元素是引用类型,那么 result 中的元素将会和 slice 中的元素指向同一个地址
// - result 的结果为 [[1 2 3] [1 2 3]]
// - 示例中的结果将会输出 2
func ExampleCloneSliceN() {
var slice = []int{1, 2, 3}
var result = collection.CloneSliceN(slice, 2)
@ -29,6 +36,9 @@ func ExampleCloneSliceN() {
// 2
}
// 通过将 map 克隆为 2 个新的 map将会得到一个新的 map result而 result 和 map 将不会有任何关联,但是如果 map 中的元素是引用类型,那么 result 中的元素将会和 map 中的元素指向同一个地址
// - result 的结果为 [map[1:1 2:2 3:3] map[1:1 2:2 3:3]] `无序的 Key-Value 对`
// - 示例中的结果将会输出 2
func ExampleCloneMapN() {
var m = map[int]int{1: 1, 2: 2, 3: 3}
var result = collection.CloneMapN(m, 2)
@ -37,6 +47,8 @@ func ExampleCloneMapN() {
// 2
}
// 通过将多个 slice 克隆为 2 个新的 slice将会得到一个新的 slice result而 result 和 slice 将不会有任何关联,但是如果 slice 中的元素是引用类型,那么 result 中的元素将会和 slice 中的元素指向同一个地址
// - result 的结果为 [[1 2 3] [1 2 3]]
func ExampleCloneSlices() {
var slice1 = []int{1, 2, 3}
var slice2 = []int{1, 2, 3}
@ -46,6 +58,8 @@ func ExampleCloneSlices() {
// 2
}
// 通过将多个 map 克隆为 2 个新的 map将会得到一个新的 map result而 result 和 map 将不会有任何关联,但是如果 map 中的元素是引用类型,那么 result 中的元素将会和 map 中的元素指向同一个地址
// - result 的结果为 [map[1:1 2:2 3:3] map[1:1 2:2 3:3]] `无序的 Key-Value 对`
func ExampleCloneMaps() {
var m1 = map[int]int{1: 1, 2: 2, 3: 3}
var m2 = map[int]int{1: 1, 2: 2, 3: 3}

View File

@ -2,5 +2,9 @@ package collection
import "github.com/kercylan98/minotaur/utils/generic"
// ComparisonHandler 用于比较 `source` 和 `target` 两个值是否相同的比较函数
// - 该函数接受两个参数,分别是源值和目标值,返回 true 的情况下即表示两者相同
type ComparisonHandler[V any] func(source, target V) bool
// OrderedValueGetter 用于获取 v 的可排序字段值的函数
type OrderedValueGetter[V any, N generic.Ordered] func(v V) N

View File

@ -1,5 +1,61 @@
package collection
// EqualSlice 检查两个切片是否相等,当 handler 返回 true 时,表示 slice1 中的某个元素和 slice2 中的某个元素相匹配
// - 当两个切片的容量不同时,不会影响最终的比较结果
func EqualSlice[S ~[]V, V any](slice1 S, slice2 S, handler ComparisonHandler[V]) bool {
if len(slice1) != len(slice2) {
return false
}
for i, v1 := range slice1 {
if !handler(v1, slice2[i]) {
return false
}
}
return true
}
// EqualComparableSlice 检查两个切片的值是否相同
// - 当两个切片的容量不同时,不会影响最终的比较结果
func EqualComparableSlice[S ~[]V, V comparable](slice1 S, slice2 S) bool {
if len(slice1) != len(slice2) {
return false
}
for i, v1 := range slice1 {
if v1 != slice2[i] {
return false
}
}
return true
}
// EqualMap 检查两个 map 是否相等,当 handler 返回 true 时,表示 map1 中的某个元素和 map2 中的某个元素相匹配
// - 当两个 map 的容量不同时,不会影响最终的比较结果
func EqualMap[M ~map[K]V, K comparable, V any](map1 M, map2 M, handler ComparisonHandler[V]) bool {
if len(map1) != len(map2) {
return false
}
for k, v1 := range map1 {
if !handler(v1, map2[k]) {
return false
}
}
return true
}
// EqualComparableMap 检查两个 map 的值是否相同
// - 当两个 map 的容量不同时,不会影响最终的比较结果
func EqualComparableMap[M ~map[K]V, K comparable, V comparable](map1 M, map2 M) bool {
if len(map1) != len(map2) {
return false
}
for k, v1 := range map1 {
if v1 != map2[k] {
return false
}
}
return true
}
// InSlice 检查 v 是否被包含在 slice 中,当 handler 返回 true 时,表示 v 和 slice 中的某个元素相匹配
func InSlice[S ~[]V, V any](slice S, v V, handler ComparisonHandler[V]) bool {
if len(slice) == 0 {

View File

@ -5,6 +5,58 @@ import (
"github.com/kercylan98/minotaur/utils/collection"
)
func ExampleEqualSlice() {
s1 := []int{1, 2, 3}
s2 := []int{1}
s3 := []int{1, 2, 3}
fmt.Println(collection.EqualSlice(s1, s2, func(source, target int) bool {
return source == target
}))
fmt.Println(collection.EqualSlice(s1, s3, func(source, target int) bool {
return source == target
}))
// Output:
// false
// true
}
func ExampleEqualComparableSlice() {
s1 := []int{1, 2, 3}
s2 := []int{1}
s3 := []int{1, 2, 3}
fmt.Println(collection.EqualComparableSlice(s1, s2))
fmt.Println(collection.EqualComparableSlice(s1, s3))
// Output:
// false
// true
}
func ExampleEqualMap() {
m1 := map[string]int{"a": 1, "b": 2}
m2 := map[string]int{"a": 1}
m3 := map[string]int{"a": 1, "b": 2}
fmt.Println(collection.EqualMap(m1, m2, func(source, target int) bool {
return source == target
}))
fmt.Println(collection.EqualMap(m1, m3, func(source, target int) bool {
return source == target
}))
// Output:
// false
// true
}
func ExampleEqualComparableMap() {
m1 := map[string]int{"a": 1, "b": 2}
m2 := map[string]int{"a": 1}
m3 := map[string]int{"a": 1, "b": 2}
fmt.Println(collection.EqualComparableMap(m1, m2))
fmt.Println(collection.EqualComparableMap(m1, m3))
// Output:
// false
// true
}
func ExampleInSlice() {
result := collection.InSlice([]int{1, 2, 3}, 2, func(source, target int) bool {
return source == target

View File

@ -9,6 +9,106 @@ var intComparisonHandler = func(source, target int) bool {
return source == target
}
func TestEqualSlice(t *testing.T) {
var cases = []struct {
name string
input []int
inputV []int
expected bool
}{
{"TestEqualSlice_NonEmptySliceEqual", []int{1, 2, 3}, []int{1, 2, 3}, true},
{"TestEqualSlice_NonEmptySliceNotEqual", []int{1, 2, 3}, []int{1, 2}, false},
{"TestEqualSlice_EmptySlice", []int{}, []int{}, true},
{"TestEqualSlice_NilSlice", nil, nil, true},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var actual = collection.EqualSlice(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, "not as expected")
}
})
}
}
func TestEqualComparableSlice(t *testing.T) {
var cases = []struct {
name string
input []int
inputV []int
expected bool
}{
{"TestEqualComparableSlice_NonEmptySliceEqual", []int{1, 2, 3}, []int{1, 2, 3}, true},
{"TestEqualComparableSlice_NonEmptySliceNotEqual", []int{1, 2, 3}, []int{1, 2}, false},
{"TestEqualComparableSlice_EmptySlice", []int{}, []int{}, true},
{"TestEqualComparableSlice_NilSlice", nil, nil, true},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var actual = collection.EqualComparableSlice(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 TestEqualMap(t *testing.T) {
var cases = []struct {
name string
input map[int]int
inputV map[int]int
expected bool
}{
{"TestEqualMap_NonEmptyMapEqual", map[int]int{1: 1, 2: 2}, map[int]int{1: 1, 2: 2}, true},
{"TestEqualMap_NonEmptyMapNotEqual", map[int]int{1: 1, 2: 2}, map[int]int{1: 1}, false},
{"TestEqualMap_EmptyMap", map[int]int{}, map[int]int{}, true},
{"TestEqualMap_NilMap", nil, nil, true},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var actual = collection.EqualMap(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, "not as expected")
}
})
}
}
func TestEqualComparableMap(t *testing.T) {
var cases = []struct {
name string
input map[int]int
inputV map[int]int
expected bool
}{
{"TestEqualComparableMap_NonEmptyMapEqual", map[int]int{1: 1, 2: 2}, map[int]int{1: 1, 2: 2}, true},
{"TestEqualComparableMap_NonEmptyMapNotEqual", map[int]int{1: 1, 2: 2}, map[int]int{1: 1}, false},
{"TestEqualComparableMap_EmptyMap", map[int]int{}, map[int]int{}, true},
{"TestEqualComparableMap_NilMap", nil, nil, true},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var actual = collection.EqualComparableMap(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 TestInSlice(t *testing.T) {
var cases = []struct {
name string

View File

@ -1,5 +1,55 @@
package collection
// ConvertSliceToBatches 将切片 s 转换为分批次的切片,当 batchSize 小于等于 0 或者 s 长度为 0 时,将会返回 nil
func ConvertSliceToBatches[S ~[]V, V any](s S, batchSize int) []S {
if len(s) == 0 || batchSize <= 0 {
return nil
}
var batches = make([]S, 0, len(s)/batchSize+1)
for i := 0; i < len(s); i += batchSize {
var end = i + batchSize
if end > len(s) {
end = len(s)
}
batches = append(batches, s[i:end])
}
return batches
}
// ConvertMapKeysToBatches 将映射的键转换为分批次的切片,当 batchSize 小于等于 0 或者 m 长度为 0 时,将会返回 nil
func ConvertMapKeysToBatches[M ~map[K]V, K comparable, V any](m M, batchSize int) [][]K {
if len(m) == 0 || batchSize <= 0 {
return nil
}
var batches = make([][]K, 0, len(m)/batchSize+1)
var keys = ConvertMapKeysToSlice(m)
for i := 0; i < len(keys); i += batchSize {
var end = i + batchSize
if end > len(keys) {
end = len(keys)
}
batches = append(batches, keys[i:end])
}
return batches
}
// ConvertMapValuesToBatches 将映射的值转换为分批次的切片,当 batchSize 小于等于 0 或者 m 长度为 0 时,将会返回 nil
func ConvertMapValuesToBatches[M ~map[K]V, K comparable, V any](m M, batchSize int) [][]V {
if len(m) == 0 || batchSize <= 0 {
return nil
}
var batches = make([][]V, 0, len(m)/batchSize+1)
var values = ConvertMapValuesToSlice(m)
for i := 0; i < len(values); i += batchSize {
var end = i + batchSize
if end > len(values) {
end = len(values)
}
batches = append(batches, values[i:end])
}
return batches
}
// ConvertSliceToAny 将切片转换为任意类型的切片
func ConvertSliceToAny[S ~[]V, V any](s S) []any {
if len(s) == 0 {

View File

@ -4,8 +4,33 @@ import (
"fmt"
"github.com/kercylan98/minotaur/utils/collection"
"reflect"
"sort"
)
func ExampleConvertSliceToBatches() {
result := collection.ConvertSliceToBatches([]int{1, 2, 3}, 2)
for _, v := range result {
fmt.Println(v)
}
// Output:
// [1 2]
// [3]
}
func ExampleConvertMapKeysToBatches() {
result := collection.ConvertMapKeysToBatches(map[int]int{1: 1, 2: 2, 3: 3}, 2)
fmt.Println(len(result))
// Output:
// 2
}
func ExampleConvertMapValuesToBatches() {
result := collection.ConvertMapValuesToBatches(map[int]int{1: 1, 2: 2, 3: 3}, 2)
fmt.Println(len(result))
// Output:
// 2
}
func ExampleConvertSliceToAny() {
result := collection.ConvertSliceToAny([]int{1, 2, 3})
fmt.Println(reflect.TypeOf(result).String(), len(result))
@ -60,6 +85,7 @@ func ExampleConvertSliceToBoolMap() {
func ExampleConvertMapKeysToSlice() {
result := collection.ConvertMapKeysToSlice(map[int]int{1: 1, 2: 2, 3: 3})
sort.Ints(result)
for i, v := range result {
fmt.Println(i, v)
}

View File

@ -6,6 +6,90 @@ import (
"testing"
)
func TestConvertSliceToBatches(t *testing.T) {
var cases = []struct {
name string
input []int
batch int
expected [][]int
}{
{name: "TestConvertSliceToBatches_NonEmpty", input: []int{1, 2, 3}, batch: 2, expected: [][]int{{1, 2}, {3}}},
{name: "TestConvertSliceToBatches_Empty", input: []int{}, batch: 2, expected: nil},
{name: "TestConvertSliceToBatches_Nil", input: nil, batch: 2, expected: nil},
{name: "TestConvertSliceToBatches_NonPositive", input: []int{1, 2, 3}, batch: 0, expected: nil},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
actual := collection.ConvertSliceToBatches(c.input, c.batch)
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 len(av) != len(ev) {
t.Errorf("expected: %v, actual: %v", c.expected, actual)
}
for j := 0; j < len(av); j++ {
aj, ej := av[j], ev[j]
if reflect.TypeOf(aj).Kind() != reflect.TypeOf(ej).Kind() {
t.Errorf("expected: %v, actual: %v", c.expected, actual)
}
if aj != ej {
t.Errorf("expected: %v, actual: %v", c.expected, actual)
}
}
}
})
}
}
func TestConvertMapKeysToBatches(t *testing.T) {
var cases = []struct {
name string
input map[int]int
batch int
expected [][]int
}{
{name: "TestConvertMapKeysToBatches_NonEmpty", input: map[int]int{1: 1, 2: 2, 3: 3}, batch: 2, expected: [][]int{{1, 2}, {3}}},
{name: "TestConvertMapKeysToBatches_Empty", input: map[int]int{}, batch: 2, expected: nil},
{name: "TestConvertMapKeysToBatches_Nil", input: nil, batch: 2, expected: nil},
{name: "TestConvertMapKeysToBatches_NonPositive", input: map[int]int{1: 1, 2: 2, 3: 3}, batch: 0, expected: nil},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
actual := collection.ConvertMapKeysToBatches(c.input, c.batch)
if len(actual) != len(c.expected) {
t.Errorf("expected: %v, actual: %v", c.expected, actual)
}
})
}
}
func TestConvertMapValuesToBatches(t *testing.T) {
var cases = []struct {
name string
input map[int]int
batch int
expected [][]int
}{
{name: "TestConvertMapValuesToBatches_NonEmpty", input: map[int]int{1: 1, 2: 2, 3: 3}, batch: 2, expected: [][]int{{1, 2}, {3}}},
{name: "TestConvertMapValuesToBatches_Empty", input: map[int]int{}, batch: 2, expected: nil},
{name: "TestConvertMapValuesToBatches_Nil", input: nil, batch: 2, expected: nil},
{name: "TestConvertMapValuesToBatches_NonPositive", input: map[int]int{1: 1, 2: 2, 3: 3}, batch: 0, expected: nil},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
actual := collection.ConvertMapValuesToBatches(c.input, c.batch)
if len(actual) != len(c.expected) {
t.Errorf("expected: %v, actual: %v", c.expected, actual)
}
})
}
}
func TestConvertSliceToAny(t *testing.T) {
var cases = []struct {
name string

View File

@ -1,2 +1,2 @@
// Package collection 用于对 input 和 map 操作的工具函数
// Package collection 定义了各种对于集合操作有用的各种函数
package collection

View File

@ -26,36 +26,37 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Matrix](#matrix)|暂无描述...
|`STRUCT`|[PagedSlice](#pagedslice)|是一个高效的动态数组,它通过分页管理内存并减少频繁的内存分配来提高性能。
|`STRUCT`|[PrioritySlice](#priorityslice)|是一个优先级切片
|`STRUCT`|[SyncSlice](#syncslice)|是基于 sync.RWMutex 实现的线程安全的 slice
|`STRUCT`|[Matrix](#struct_Matrix)|暂无描述...
|`STRUCT`|[PagedSlice](#struct_PagedSlice)|是一个高效的动态数组,它通过分页管理内存并减少频繁的内存分配来提高性能。
|`STRUCT`|[PrioritySlice](#struct_PrioritySlice)|是一个优先级切片
|`STRUCT`|[SyncSlice](#struct_SyncSlice)|是基于 sync.RWMutex 实现的线程安全的 slice
</details>
***
## 详情信息
#### func NewMatrix(dimensions ...int) *Matrix[V]
#### func NewMatrix\[V any\](dimensions ...int) *Matrix[V]
<span id="NewMatrix"></span>
> 创建一个新的 Matrix 实例。
***
#### func NewPagedSlice(pageSize int) *PagedSlice[T]
#### func NewPagedSlice\[T any\](pageSize int) *PagedSlice[T]
<span id="NewPagedSlice"></span>
> 创建一个新的 PagedSlice 实例。
***
#### func NewPrioritySlice(lengthAndCap ...int) *PrioritySlice[V]
#### func NewPrioritySlice\[V any\](lengthAndCap ...int) *PrioritySlice[V]
<span id="NewPrioritySlice"></span>
> 创建一个优先级切片
***
#### func NewSyncSlice(length int, cap int) *SyncSlice[V]
#### func NewSyncSlice\[V any\](length int, cap int) *SyncSlice[V]
<span id="NewSyncSlice"></span>
> 创建一个 SyncSlice
***
<span id="struct_Matrix"></span>
### Matrix `STRUCT`
```go
@ -64,18 +65,31 @@ type Matrix[V any] struct {
data []V
}
```
<span id="struct_Matrix_Get"></span>
#### func (*Matrix) Get(index ...int) *V
> 获取矩阵中给定索引的元素。
***
<span id="struct_Matrix_Set"></span>
#### func (*Matrix) Set(index []int, value V)
> 设置矩阵中给定索引的元素。
***
<span id="struct_Matrix_Dimensions"></span>
#### func (*Matrix) Dimensions() []int
> 返回矩阵的维度大小。
***
<span id="struct_Matrix_Clear"></span>
#### func (*Matrix) Clear()
> 清空矩阵。
***
<span id="struct_PagedSlice"></span>
### PagedSlice `STRUCT`
是一个高效的动态数组,它通过分页管理内存并减少频繁的内存分配来提高性能。
```go
@ -86,24 +100,43 @@ type PagedSlice[T any] struct {
lenLast int
}
```
<span id="struct_PagedSlice_Add"></span>
#### func (*PagedSlice) Add(value T)
> 添加一个元素到 PagedSlice 中。
***
<span id="struct_PagedSlice_Get"></span>
#### func (*PagedSlice) Get(index int) *T
> 获取 PagedSlice 中给定索引的元素。
***
<span id="struct_PagedSlice_Set"></span>
#### func (*PagedSlice) Set(index int, value T)
> 设置 PagedSlice 中给定索引的元素。
***
<span id="struct_PagedSlice_Len"></span>
#### func (*PagedSlice) Len() int
> 返回 PagedSlice 中元素的数量。
***
<span id="struct_PagedSlice_Clear"></span>
#### func (*PagedSlice) Clear()
> 清空 PagedSlice。
***
<span id="struct_PagedSlice_Range"></span>
#### func (*PagedSlice) Range(f func (index int, value T) bool)
> 迭代 PagedSlice 中的所有元素。
***
<span id="struct_PrioritySlice"></span>
### PrioritySlice `STRUCT`
是一个优先级切片
```go
@ -111,17 +144,29 @@ type PrioritySlice[V any] struct {
items []*priorityItem[V]
}
```
<span id="struct_PrioritySlice_Len"></span>
#### func (*PrioritySlice) Len() int
> 返回切片长度
***
<span id="struct_PrioritySlice_Cap"></span>
#### func (*PrioritySlice) Cap() int
> 返回切片容量
***
<span id="struct_PrioritySlice_Clear"></span>
#### func (*PrioritySlice) Clear()
> 清空切片
***
<span id="struct_PrioritySlice_Append"></span>
#### func (*PrioritySlice) Append(v V, p int)
> 添加元素
<details>
<summary>查看 / 收起单元测试</summary>
@ -142,45 +187,85 @@ func TestPrioritySlice_Append(t *testing.T) {
***
<span id="struct_PrioritySlice_Appends"></span>
#### func (*PrioritySlice) Appends(priority int, vs ...V)
> 添加元素
***
#### func (*PrioritySlice) Get(index int) V, int
<span id="struct_PrioritySlice_Get"></span>
#### func (*PrioritySlice) Get(index int) ( V, int)
> 获取元素
***
<span id="struct_PrioritySlice_GetValue"></span>
#### func (*PrioritySlice) GetValue(index int) V
> 获取元素值
***
<span id="struct_PrioritySlice_GetPriority"></span>
#### func (*PrioritySlice) GetPriority(index int) int
> 获取元素优先级
***
<span id="struct_PrioritySlice_Set"></span>
#### func (*PrioritySlice) Set(index int, value V, priority int)
> 设置元素
***
<span id="struct_PrioritySlice_SetValue"></span>
#### func (*PrioritySlice) SetValue(index int, value V)
> 设置元素值
***
<span id="struct_PrioritySlice_SetPriority"></span>
#### func (*PrioritySlice) SetPriority(index int, priority int)
> 设置元素优先级
***
<span id="struct_PrioritySlice_Action"></span>
#### func (*PrioritySlice) Action(action func (items []*priorityItem[V]) []*priorityItem[V])
> 直接操作切片,如果返回值不为 nil则替换切片
***
<span id="struct_PrioritySlice_Range"></span>
#### func (*PrioritySlice) Range(action func (index int, item *priorityItem[V]) bool)
> 遍历切片,如果返回值为 false则停止遍历
***
<span id="struct_PrioritySlice_RangeValue"></span>
#### func (*PrioritySlice) RangeValue(action func (index int, value V) bool)
> 遍历切片值,如果返回值为 false则停止遍历
***
<span id="struct_PrioritySlice_RangePriority"></span>
#### func (*PrioritySlice) RangePriority(action func (index int, priority int) bool)
> 遍历切片优先级,如果返回值为 false则停止遍历
***
<span id="struct_PrioritySlice_Slice"></span>
#### func (*PrioritySlice) Slice() []V
> 返回切片
> SyncSlice 返回切片
***
<span id="struct_PrioritySlice_String"></span>
#### func (*PrioritySlice) String() string
> 返回切片字符串
***
<span id="struct_SyncSlice"></span>
### SyncSlice `STRUCT`
是基于 sync.RWMutex 实现的线程安全的 slice
```go
@ -189,17 +274,38 @@ type SyncSlice[V any] struct {
data []V
}
```
<span id="struct_SyncSlice_Get"></span>
#### func (*SyncSlice) Get(index int) V
***
<span id="struct_SyncSlice_GetWithRange"></span>
#### func (*SyncSlice) GetWithRange(start int, end int) []V
***
<span id="struct_SyncSlice_Set"></span>
#### func (*SyncSlice) Set(index int, value V)
***
<span id="struct_SyncSlice_Append"></span>
#### func (*SyncSlice) Append(values ...V)
***
<span id="struct_SyncSlice_Release"></span>
#### func (*SyncSlice) Release()
***
<span id="struct_SyncSlice_Clear"></span>
#### func (*SyncSlice) Clear()
***
<span id="struct_SyncSlice_GetData"></span>
#### func (*SyncSlice) GetData() []V
***

188
utils/collection/loop.go Normal file
View File

@ -0,0 +1,188 @@
package collection
import (
"github.com/kercylan98/minotaur/utils/generic"
"sort"
)
// LoopSlice 迭代切片 slice 中的每一个函数,并将索引和值传递给 f 函数
// - 迭代过程将在 f 函数返回 false 时中断
func LoopSlice[S ~[]V, V any](slice S, f func(i int, val V) bool) {
for i, v := range slice {
if !f(i, v) {
break
}
}
}
// ReverseLoopSlice 逆序迭代切片 slice 中的每一个函数,并将索引和值传递给 f 函数
// - 迭代过程将在 f 函数返回 false 时中断
func ReverseLoopSlice[S ~[]V, V any](slice S, f func(i int, val V) bool) {
for i := len(slice) - 1; i >= 0; i-- {
if !f(i, slice[i]) {
break
}
}
}
// LoopMap 迭代 m 中的每一个函数,并将键和值传递给 f 函数
// - m 的迭代顺序是不确定的,因此每次迭代的顺序可能不同
// - 该函数会在 f 中传入一个从 0 开始的索引,用于表示当前迭代的次数
// - 迭代过程将在 f 函数返回 false 时中断
func LoopMap[M ~map[K]V, K comparable, V any](m M, f func(i int, key K, val V) bool) {
var i int
for k, v := range m {
if !f(i, k, v) {
break
}
i++
}
}
// LoopMapByOrderedKeyAsc 按照键的升序迭代 m 中的每一个函数,并将键和值传递给 f 函数
// - 该函数会在 f 中传入一个从 0 开始的索引,用于表示当前迭代的次数
// - 迭代过程将在 f 函数返回 false 时中断
func LoopMapByOrderedKeyAsc[M ~map[K]V, K generic.Ordered, V any](m M, f func(i int, key K, val V) bool) {
var keys []K
for k := range m {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return AscBy(keys[i], keys[j])
})
for i, k := range keys {
if !f(i, k, m[k]) {
break
}
}
}
// LoopMapByOrderedKeyDesc 按照键的降序迭代 m 中的每一个函数,并将键和值传递给 f 函数
// - 该函数会在 f 中传入一个从 0 开始的索引,用于表示当前迭代的次数
// - 迭代过程将在 f 函数返回 false 时中断
func LoopMapByOrderedKeyDesc[M ~map[K]V, K generic.Ordered, V any](m M, f func(i int, key K, val V) bool) {
var keys []K
for k := range m {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return DescBy(keys[i], keys[j])
})
for i, k := range keys {
if !f(i, k, m[k]) {
break
}
}
}
// LoopMapByOrderedValueAsc 按照值的升序迭代 m 中的每一个函数,并将键和值传递给 f 函数
// - 该函数会在 f 中传入一个从 0 开始的索引,用于表示当前迭代的次数
// - 迭代过程将在 f 函数返回 false 时中断
func LoopMapByOrderedValueAsc[M ~map[K]V, K comparable, V generic.Ordered](m M, f func(i int, key K, val V) bool) {
var keys []K
var values []V
for k, v := range m {
keys = append(keys, k)
values = append(values, v)
}
sort.Slice(values, func(i, j int) bool {
return AscBy(values[i], values[j])
})
for i, v := range values {
if !f(i, keys[i], v) {
break
}
}
}
// LoopMapByOrderedValueDesc 按照值的降序迭代 m 中的每一个函数,并将键和值传递给 f 函数
// - 该函数会在 f 中传入一个从 0 开始的索引,用于表示当前迭代的次数
// - 迭代过程将在 f 函数返回 false 时中断
func LoopMapByOrderedValueDesc[M ~map[K]V, K comparable, V generic.Ordered](m M, f func(i int, key K, val V) bool) {
var keys []K
var values []V
for k, v := range m {
keys = append(keys, k)
values = append(values, v)
}
sort.Slice(values, func(i, j int) bool {
return DescBy(values[i], values[j])
})
for i, v := range values {
if !f(i, keys[i], v) {
break
}
}
}
// LoopMapByKeyGetterAsc 按照键的升序迭代 m 中的每一个函数,并将键和值传递给 f 函数
// - 该函数会在 f 中传入一个从 0 开始的索引,用于表示当前迭代的次数
// - 迭代过程将在 f 函数返回 false 时中断
func LoopMapByKeyGetterAsc[M ~map[K]V, K comparable, V comparable, N generic.Ordered](m M, getter func(k K) N, f func(i int, key K, val V) bool) {
var keys []K
for k := range m {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return AscBy(getter(keys[i]), getter(keys[j]))
})
for i, v := range keys {
if !f(i, keys[i], m[v]) {
break
}
}
}
// LoopMapByValueGetterAsc 按照值的升序迭代 m 中的每一个函数,并将键和值传递给 f 函数
// - 该函数会在 f 中传入一个从 0 开始的索引,用于表示当前迭代的次数
// - 迭代过程将在 f 函数返回 false 时中断
func LoopMapByValueGetterAsc[M ~map[K]V, K comparable, V any, N generic.Ordered](m M, getter func(v V) N, f func(i int, key K, val V) bool) {
var keys []K
for k := range m {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return AscBy(getter(m[keys[i]]), getter(m[keys[j]]))
})
for i, v := range keys {
if !f(i, keys[i], m[v]) {
break
}
}
}
// LoopMapByKeyGetterDesc 按照键的降序迭代 m 中的每一个函数,并将键和值传递给 f 函数
// - 该函数会在 f 中传入一个从 0 开始的索引,用于表示当前迭代的次数
// - 迭代过程将在 f 函数返回 false 时中断
func LoopMapByKeyGetterDesc[M ~map[K]V, K comparable, V comparable, N generic.Ordered](m M, getter func(k K) N, f func(i int, key K, val V) bool) {
var keys []K
for k := range m {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return DescBy(getter(keys[i]), getter(keys[j]))
})
for i, v := range keys {
if !f(i, keys[i], m[v]) {
break
}
}
}
// LoopMapByValueGetterDesc 按照值的降序迭代 m 中的每一个函数,并将键和值传递给 f 函数
// - 该函数会在 f 中传入一个从 0 开始的索引,用于表示当前迭代的次数
// - 迭代过程将在 f 函数返回 false 时中断
func LoopMapByValueGetterDesc[M ~map[K]V, K comparable, V any, N generic.Ordered](m M, getter func(v V) N, f func(i int, key K, val V) bool) {
var keys []K
for k := range m {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return DescBy(getter(m[keys[i]]), getter(m[keys[j]]))
})
for i, v := range keys {
if !f(i, keys[i], m[v]) {
break
}
}
}

View File

@ -0,0 +1,159 @@
package collection_test
import (
"fmt"
"github.com/kercylan98/minotaur/utils/collection"
)
func ExampleLoopSlice() {
var result []int
collection.LoopSlice([]int{1, 2, 3, 4, 5}, func(i int, val int) bool {
result = append(result, val)
if uint(i) == 1 {
return false
}
return true
})
fmt.Println(result)
// Output: [1 2]
}
func ExampleReverseLoopSlice() {
var result []int
collection.ReverseLoopSlice([]int{1, 2, 3, 4, 5}, func(i int, val int) bool {
result = append(result, val)
if uint(i) == 1 {
return false
}
return true
})
fmt.Println(result)
// Output: [5 4 3 2]
}
func ExampleLoopMap() {
var result []int
collection.LoopMap(map[string]int{"a": 1, "b": 2, "c": 3}, func(i int, key string, val int) bool {
result = append(result, val)
return true
})
fmt.Println(collection.AllInComparableSlice(result, []int{1, 2, 3}))
// Output:
// true
}
func ExampleLoopMapByOrderedKeyAsc() {
var result []int
collection.LoopMapByOrderedKeyAsc(map[string]int{"a": 1, "b": 2, "c": 3}, func(i int, key string, val int) bool {
result = append(result, val)
return true
})
fmt.Println(collection.AllInComparableSlice(result, []int{1, 2, 3}))
// Output:
// true
}
func ExampleLoopMapByOrderedKeyDesc() {
var result []int
collection.LoopMapByOrderedKeyDesc(map[string]int{"a": 1, "b": 2, "c": 3}, func(i int, key string, val int) bool {
result = append(result, val)
return true
})
fmt.Println(collection.AllInComparableSlice(result, []int{3, 2, 1}))
// Output:
// true
}
func ExampleLoopMapByOrderedValueAsc() {
var result []int
collection.LoopMapByOrderedValueAsc(map[string]int{"a": 1, "b": 2, "c": 3}, func(i int, key string, val int) bool {
result = append(result, val)
return true
})
fmt.Println(collection.AllInComparableSlice(result, []int{1, 2, 3}))
// Output:
// true
}
func ExampleLoopMapByOrderedValueDesc() {
var result []int
collection.LoopMapByOrderedValueDesc(map[string]int{"a": 1, "b": 2, "c": 3}, func(i int, key string, val int) bool {
result = append(result, val)
return true
})
fmt.Println(collection.AllInComparableSlice(result, []int{3, 2, 1}))
// Output:
// true
}
func ExampleLoopMapByKeyGetterAsc() {
var m = map[string]int{"a": 1, "b": 2, "c": 3}
var result []int
collection.LoopMapByKeyGetterAsc(
m,
func(k string) int {
return m[k]
},
func(i int, key string, val int) bool {
result = append(result, val)
return true
},
)
fmt.Println(collection.AllInComparableSlice(result, []int{1, 2, 3}))
// Output:
// true
}
func ExampleLoopMapByKeyGetterDesc() {
var m = map[string]int{"a": 1, "b": 2, "c": 3}
var result []int
collection.LoopMapByKeyGetterDesc(
m,
func(k string) int {
return m[k]
},
func(i int, key string, val int) bool {
result = append(result, val)
return true
},
)
fmt.Println(collection.AllInComparableSlice(result, []int{3, 2, 1}))
// Output:
// true
}
func ExampleLoopMapByValueGetterAsc() {
var m = map[string]int{"a": 1, "b": 2, "c": 3}
var result []int
collection.LoopMapByValueGetterAsc(
m,
func(v int) int {
return v
},
func(i int, key string, val int) bool {
result = append(result, val)
return true
},
)
fmt.Println(collection.AllInComparableSlice(result, []int{1, 2, 3}))
// Output:
// true
}
func ExampleLoopMapByValueGetterDesc() {
var m = map[string]int{"a": 1, "b": 2, "c": 3}
var result []int
collection.LoopMapByValueGetterDesc(
m,
func(v int) int {
return v
},
func(i int, key string, val int) bool {
result = append(result, val)
return true
},
)
fmt.Println(collection.AllInComparableSlice(result, []int{3, 2, 1}))
// Output:
// true
}

View File

@ -0,0 +1,333 @@
package collection_test
import (
"github.com/kercylan98/minotaur/utils/collection"
"testing"
)
func TestLoopSlice(t *testing.T) {
var cases = []struct {
name string
in []int
out []int
breakIndex uint
}{
{"TestLoopSlice_Part", []int{1, 2, 3, 4, 5}, []int{1, 2}, 2},
{"TestLoopSlice_All", []int{1, 2, 3, 4, 5}, []int{1, 2, 3, 4, 5}, 0},
{"TestLoopSlice_Empty", []int{}, []int{}, 0},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var result []int
collection.LoopSlice(c.in, func(i int, val int) bool {
result = append(result, val)
if c.breakIndex != 0 && uint(i) == c.breakIndex-1 {
return false
}
return true
})
if !collection.EqualComparableSlice(result, c.out) {
t.Errorf("LoopSlice(%v) got %v, want %v", c.in, result, c.out)
}
})
}
}
func TestReverseLoopSlice(t *testing.T) {
var cases = []struct {
name string
in []int
out []int
breakIndex uint
}{
{"TestReverseLoopSlice_Part", []int{1, 2, 3, 4, 5}, []int{5, 4}, 2},
{"TestReverseLoopSlice_All", []int{1, 2, 3, 4, 5}, []int{5, 4, 3, 2, 1}, 0},
{"TestReverseLoopSlice_Empty", []int{}, []int{}, 0},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var result []int
collection.ReverseLoopSlice(c.in, func(i int, val int) bool {
result = append(result, val)
if c.breakIndex != 0 && uint(i) == uint(len(c.in))-c.breakIndex {
return false
}
return true
})
if !collection.EqualComparableSlice(result, c.out) {
t.Errorf("ReverseLoopSlice(%v) got %v, want %v", c.in, result, c.out)
}
})
}
}
func TestLoopMap(t *testing.T) {
var cases = []struct {
name string
in map[int]string
out map[int]string
breakIndex uint
}{
{"TestLoopMap_Part", map[int]string{1: "1", 2: "2", 3: "3"}, map[int]string{1: "1", 2: "2"}, 2},
{"TestLoopMap_All", map[int]string{1: "1", 2: "2", 3: "3"}, map[int]string{1: "1", 2: "2", 3: "3"}, 0},
{"TestLoopMap_Empty", map[int]string{}, map[int]string{}, 0},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var result = make(map[int]string)
collection.LoopMap(c.in, func(i int, key int, val string) bool {
result[key] = val
if c.breakIndex != 0 && uint(i) == c.breakIndex-1 {
return false
}
return true
})
if !collection.EqualComparableMap(result, c.out) {
t.Errorf("LoopMap(%v) got %v, want %v", c.in, result, c.out)
}
})
}
}
func TestLoopMapByOrderedKeyAsc(t *testing.T) {
var cases = []struct {
name string
in map[int]string
out []int
breakIndex uint
}{
{"TestLoopMapByOrderedKeyAsc_Part", map[int]string{1: "1", 2: "2", 3: "3"}, []int{1, 2}, 2},
{"TestLoopMapByOrderedKeyAsc_All", map[int]string{1: "1", 2: "2", 3: "3"}, []int{1, 2, 3}, 0},
{"TestLoopMapByOrderedKeyAsc_Empty", map[int]string{}, []int{}, 0},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var result []int
collection.LoopMapByOrderedKeyAsc(c.in, func(i int, key int, val string) bool {
result = append(result, key)
if c.breakIndex != 0 && uint(i) == c.breakIndex-1 {
return false
}
return true
})
if !collection.EqualComparableSlice(result, c.out) {
t.Errorf("LoopMapByOrderedKeyAsc(%v) got %v, want %v", c.in, result, c.out)
}
})
}
}
func TestLoopMapByOrderedKeyDesc(t *testing.T) {
var cases = []struct {
name string
in map[int]string
out []int
breakIndex uint
}{
{"TestLoopMapByOrderedKeyDesc_Part", map[int]string{1: "1", 2: "2", 3: "3"}, []int{3, 2}, 2},
{"TestLoopMapByOrderedKeyDesc_All", map[int]string{1: "1", 2: "2", 3: "3"}, []int{3, 2, 1}, 0},
{"TestLoopMapByOrderedKeyDesc_Empty", map[int]string{}, []int{}, 0},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var result []int
collection.LoopMapByOrderedKeyDesc(c.in, func(i int, key int, val string) bool {
result = append(result, key)
if c.breakIndex != 0 && uint(i) == c.breakIndex-1 {
return false
}
return true
})
if !collection.EqualComparableSlice(result, c.out) {
t.Errorf("LoopMapByOrderedKeyDesc(%v) got %v, want %v", c.in, result, c.out)
}
})
}
}
func TestLoopMapByOrderedValueAsc(t *testing.T) {
var cases = []struct {
name string
in map[int]string
out []string
breakIndex uint
}{
{"TestLoopMapByOrderedValueAsc_Part", map[int]string{1: "1", 2: "2", 3: "3"}, []string{"1", "2"}, 2},
{"TestLoopMapByOrderedValueAsc_All", map[int]string{1: "1", 2: "2", 3: "3"}, []string{"1", "2", "3"}, 0},
{"TestLoopMapByOrderedValueAsc_Empty", map[int]string{}, []string{}, 0},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var result []string
collection.LoopMapByOrderedValueAsc(c.in, func(i int, key int, val string) bool {
result = append(result, val)
if c.breakIndex != 0 && uint(i) == c.breakIndex-1 {
return false
}
return true
})
if !collection.EqualComparableSlice(result, c.out) {
t.Errorf("LoopMapByOrderedValueAsc(%v) got %v, want %v", c.in, result, c.out)
}
})
}
}
func TestLoopMapByOrderedValueDesc(t *testing.T) {
var cases = []struct {
name string
in map[int]string
out []string
breakIndex uint
}{
{"TestLoopMapByOrderedValueDesc_Part", map[int]string{1: "1", 2: "2", 3: "3"}, []string{"3", "2"}, 2},
{"TestLoopMapByOrderedValueDesc_All", map[int]string{1: "1", 2: "2", 3: "3"}, []string{"3", "2", "1"}, 0},
{"TestLoopMapByOrderedValueDesc_Empty", map[int]string{}, []string{}, 0},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var result []string
collection.LoopMapByOrderedValueDesc(c.in, func(i int, key int, val string) bool {
result = append(result, val)
if c.breakIndex != 0 && uint(i) == c.breakIndex-1 {
return false
}
return true
})
if !collection.EqualComparableSlice(result, c.out) {
t.Errorf("LoopMapByOrderedValueDesc(%v) got %v, want %v", c.in, result, c.out)
}
})
}
}
func TestLoopMapByKeyGetterAsc(t *testing.T) {
var cases = []struct {
name string
in map[int]string
out []int
breakIndex uint
}{
{"TestLoopMapByKeyGetterAsc_Part", map[int]string{1: "1", 2: "2", 3: "3"}, []int{1, 2}, 2},
{"TestLoopMapByKeyGetterAsc_All", map[int]string{1: "1", 2: "2", 3: "3"}, []int{1, 2, 3}, 0},
{"TestLoopMapByKeyGetterAsc_Empty", map[int]string{}, []int{}, 0},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var result []int
collection.LoopMapByKeyGetterAsc(c.in, func(key int) int {
return key
}, func(i int, key int, val string) bool {
result = append(result, key)
if c.breakIndex != 0 && uint(i) == c.breakIndex-1 {
return false
}
return true
})
if !collection.EqualComparableSlice(result, c.out) {
t.Errorf("LoopMapByKeyGetterAsc(%v) got %v, want %v", c.in, result, c.out)
}
})
}
}
func TestLoopMapByKeyGetterDesc(t *testing.T) {
var cases = []struct {
name string
in map[int]string
out []int
breakIndex uint
}{
{"TestLoopMapByKeyGetterDesc_Part", map[int]string{1: "1", 2: "2", 3: "3"}, []int{3, 2}, 2},
{"TestLoopMapByKeyGetterDesc_All", map[int]string{1: "1", 2: "2", 3: "3"}, []int{3, 2, 1}, 0},
{"TestLoopMapByKeyGetterDesc_Empty", map[int]string{}, []int{}, 0},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var result []int
collection.LoopMapByKeyGetterDesc(c.in, func(key int) int {
return key
}, func(i int, key int, val string) bool {
result = append(result, key)
if c.breakIndex != 0 && uint(i) == c.breakIndex-1 {
return false
}
return true
})
if !collection.EqualComparableSlice(result, c.out) {
t.Errorf("LoopMapByKeyGetterDesc(%v) got %v, want %v", c.in, result, c.out)
}
})
}
}
func TestLoopMapByValueGetterAsc(t *testing.T) {
var cases = []struct {
name string
in map[int]string
out []string
breakIndex uint
}{
{"TestLoopMapByValueGetterAsc_Part", map[int]string{1: "1", 2: "2", 3: "3"}, []string{"1", "2"}, 2},
{"TestLoopMapByValueGetterAsc_All", map[int]string{1: "1", 2: "2", 3: "3"}, []string{"1", "2", "3"}, 0},
{"TestLoopMapByValueGetterAsc_Empty", map[int]string{}, []string{}, 0},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var result []string
collection.LoopMapByValueGetterAsc(c.in, func(val string) string {
return val
}, func(i int, key int, val string) bool {
result = append(result, val)
if c.breakIndex != 0 && uint(i) == c.breakIndex-1 {
return false
}
return true
})
if !collection.EqualComparableSlice(result, c.out) {
t.Errorf("LoopMapByValueGetterAsc(%v) got %v, want %v", c.in, result, c.out)
}
})
}
}
func TestLoopMapByValueGetterDesc(t *testing.T) {
var cases = []struct {
name string
in map[int]string
out []string
breakIndex uint
}{
{"TestLoopMapByValueGetterDesc_Part", map[int]string{1: "1", 2: "2", 3: "3"}, []string{"3", "2"}, 2},
{"TestLoopMapByValueGetterDesc_All", map[int]string{1: "1", 2: "2", 3: "3"}, []string{"3", "2", "1"}, 0},
{"TestLoopMapByValueGetterDesc_Empty", map[int]string{}, []string{}, 0},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var result []string
collection.LoopMapByValueGetterDesc(c.in, func(val string) string {
return val
}, func(i int, key int, val string) bool {
result = append(result, val)
if c.breakIndex != 0 && uint(i) == c.breakIndex-1 {
return false
}
return true
})
if !collection.EqualComparableSlice(result, c.out) {
t.Errorf("LoopMapByValueGetterDesc(%v) got %v, want %v", c.in, result, c.out)
}
})
}
}

View File

@ -23,18 +23,19 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[SyncMap](#syncmap)|是基于 sync.RWMutex 实现的线程安全的 map
|`STRUCT`|[SyncMap](#struct_SyncMap)|是基于 sync.RWMutex 实现的线程安全的 map
</details>
***
## 详情信息
#### func NewSyncMap(source ...map[K]V) *SyncMap[K, V]
#### func NewSyncMap\[K comparable, V any\](source ...map[K]V) *SyncMap[K, V]
<span id="NewSyncMap"></span>
> 创建一个 SyncMap
***
<span id="struct_SyncMap"></span>
### SyncMap `STRUCT`
是基于 sync.RWMutex 实现的线程安全的 map
- 适用于要考虑并发读写但是并发读写的频率不高的情况
@ -45,3 +46,109 @@ type SyncMap[K comparable, V any] struct {
atom bool
}
```
<span id="struct_SyncMap_Set"></span>
#### func (*SyncMap) Set(key K, value V)
> 设置一个值
***
<span id="struct_SyncMap_Get"></span>
#### func (*SyncMap) Get(key K) V
> 获取一个值
***
<span id="struct_SyncMap_Atom"></span>
#### func (*SyncMap) Atom(handle func (m map[K]V))
> 原子操作
***
<span id="struct_SyncMap_Exist"></span>
#### func (*SyncMap) Exist(key K) bool
> 判断是否存在
***
<span id="struct_SyncMap_GetExist"></span>
#### func (*SyncMap) GetExist(key K) ( V, bool)
> 获取一个值并判断是否存在
***
<span id="struct_SyncMap_Delete"></span>
#### func (*SyncMap) Delete(key K)
> 删除一个值
***
<span id="struct_SyncMap_DeleteGet"></span>
#### func (*SyncMap) DeleteGet(key K) V
> 删除一个值并返回
***
<span id="struct_SyncMap_DeleteGetExist"></span>
#### func (*SyncMap) DeleteGetExist(key K) ( V, bool)
> 删除一个值并返回是否存在
***
<span id="struct_SyncMap_DeleteExist"></span>
#### func (*SyncMap) DeleteExist(key K) bool
> 删除一个值并返回是否存在
***
<span id="struct_SyncMap_Clear"></span>
#### func (*SyncMap) Clear()
> 清空
***
<span id="struct_SyncMap_ClearHandle"></span>
#### func (*SyncMap) ClearHandle(handle func (key K, value V))
> 清空并处理
***
<span id="struct_SyncMap_Range"></span>
#### func (*SyncMap) Range(handle func (key K, value V) bool)
> 遍历所有值,如果 handle 返回 true 则停止遍历
***
<span id="struct_SyncMap_Keys"></span>
#### func (*SyncMap) Keys() []K
> 获取所有的键
***
<span id="struct_SyncMap_Slice"></span>
#### func (*SyncMap) Slice() []V
> 获取所有的值
***
<span id="struct_SyncMap_Map"></span>
#### func (*SyncMap) Map() map[K]V
> 转换为普通 map
***
<span id="struct_SyncMap_Size"></span>
#### func (*SyncMap) Size() int
> 获取数量
***
<span id="struct_SyncMap_MarshalJSON"></span>
#### func (*SyncMap) MarshalJSON() ( []byte, error)
***
<span id="struct_SyncMap_UnmarshalJSON"></span>
#### func (*SyncMap) UnmarshalJSON(bytes []byte) error
***

View File

@ -44,7 +44,7 @@ combination 包提供了一些实用的组合函数。
|[WithValidatorHandleContinuousNot](#WithValidatorHandleContinuousNot)|校验组合成员是否不连续
|[WithValidatorHandleGroupContinuous](#WithValidatorHandleGroupContinuous)|校验组合成员是否能够按类型分组并且连续
|[WithValidatorHandleGroupContinuousN](#WithValidatorHandleGroupContinuousN)|校验组合成员是否能够按分组为 n 组类型并且连续
|[WithValidatorHandleNCarryM](#WithValidatorHandleNCarryM)| 校验组合成员是否匹配 N 携带相同的 M 的组合
|[WithValidatorHandleNCarryM](#WithValidatorHandleNCarryM)|校验组合成员是否匹配 N 携带相同的 M 的组合
|[WithValidatorHandleNCarryIndependentM](#WithValidatorHandleNCarryIndependentM)|校验组合成员是否匹配 N 携带独立的 M 的组合
@ -52,25 +52,25 @@ combination 包提供了一些实用的组合函数。
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Combination](#combination)|用于从多个匹配器内提取组合的数据结构
|`STRUCT`|[Option](#option)|组合器选项
|`INTERFACE`|[Item](#item)|暂无描述...
|`STRUCT`|[Matcher](#matcher)|用于从一组数据内提取组合的数据结构
|`STRUCT`|[MatcherOption](#matcheroption)|匹配器选项
|`STRUCT`|[Validator](#validator)|用于对组合进行验证的校验器
|`STRUCT`|[ValidatorOption](#validatoroption)|暂无描述...
|`STRUCT`|[Combination](#struct_Combination)|用于从多个匹配器内提取组合的数据结构
|`STRUCT`|[Option](#struct_Option)|组合器选项
|`INTERFACE`|[Item](#struct_Item)|暂无描述...
|`STRUCT`|[Matcher](#struct_Matcher)|用于从一组数据内提取组合的数据结构
|`STRUCT`|[MatcherOption](#struct_MatcherOption)|匹配器选项
|`STRUCT`|[Validator](#struct_Validator)|用于对组合进行验证的校验器
|`STRUCT`|[ValidatorOption](#struct_ValidatorOption)|暂无描述...
</details>
***
## 详情信息
#### func NewCombination(options ...Option[T]) *Combination[T]
#### func NewCombination\[T Item\](options ...Option[T]) *Combination[T]
<span id="NewCombination"></span>
> 创建一个新的组合器
***
#### func WithEvaluation(evaluate func (items []T) float64) Option[T]
#### func WithEvaluation\[T Item\](evaluate func (items []T) float64) Option[T]
<span id="WithEvaluation"></span>
> 设置组合评估函数
> - 用于对组合进行评估,返回一个分值的评价函数
@ -79,57 +79,57 @@ combination 包提供了一些实用的组合函数。
> - 默认的评估函数将返回一个随机数
***
#### func NewMatcher(options ...MatcherOption[T]) *Matcher[T]
#### func NewMatcher\[T Item\](options ...MatcherOption[T]) *Matcher[T]
<span id="NewMatcher"></span>
> 创建一个新的匹配器
***
#### func WithMatcherEvaluation(evaluate func (items []T) float64) MatcherOption[T]
#### func WithMatcherEvaluation\[T Item\](evaluate func (items []T) float64) MatcherOption[T]
<span id="WithMatcherEvaluation"></span>
> 设置匹配器评估函数
> - 用于对组合进行评估,返回一个分值的评价函数
> - 通过该选项将覆盖匹配器的默认(WithEvaluation)评估函数
***
#### func WithMatcherLeastLength(length int) MatcherOption[T]
#### func WithMatcherLeastLength\[T Item\](length int) MatcherOption[T]
<span id="WithMatcherLeastLength"></span>
> 通过匹配最小长度的组合创建匹配器
> - length: 组合的长度,表示需要匹配的组合最小数量
***
#### func WithMatcherLength(length int) MatcherOption[T]
#### func WithMatcherLength\[T Item\](length int) MatcherOption[T]
<span id="WithMatcherLength"></span>
> 通过匹配长度的组合创建匹配器
> - length: 组合的长度,表示需要匹配的组合数量
***
#### func WithMatcherMostLength(length int) MatcherOption[T]
#### func WithMatcherMostLength\[T Item\](length int) MatcherOption[T]
<span id="WithMatcherMostLength"></span>
> 通过匹配最大长度的组合创建匹配器
> - length: 组合的长度,表示需要匹配的组合最大数量
***
#### func WithMatcherIntervalLength(min int, max int) MatcherOption[T]
#### func WithMatcherIntervalLength\[T Item\](min int, max int) MatcherOption[T]
<span id="WithMatcherIntervalLength"></span>
> 通过匹配长度区间的组合创建匹配器
> - min: 组合的最小长度,表示需要匹配的组合最小数量
> - max: 组合的最大长度,表示需要匹配的组合最大数量
***
#### func WithMatcherContinuity(getIndex func (item T) Index) MatcherOption[T]
#### func WithMatcherContinuity\[T Item, Index generic.Number\](getIndex func (item T) Index) MatcherOption[T]
<span id="WithMatcherContinuity"></span>
> 通过匹配连续的组合创建匹配器
> - index: 用于获取组合中元素的索引值,用于判断是否连续
***
#### func WithMatcherSame(count int, getType func (item T) E) MatcherOption[T]
#### func WithMatcherSame\[T Item, E generic.Ordered\](count int, getType func (item T) E) MatcherOption[T]
<span id="WithMatcherSame"></span>
> 通过匹配相同的组合创建匹配器
> - count: 组合中相同元素的数量,当 count <= 0 时,表示相同元素的数量不限
> - getType: 用于获取组合中元素的类型,用于判断是否相同
***
#### func WithMatcherNCarryM(n int, m int, getType func (item T) E) MatcherOption[T]
#### func WithMatcherNCarryM\[T Item, E generic.Ordered\](n int, m int, getType func (item T) E) MatcherOption[T]
<span id="WithMatcherNCarryM"></span>
> 通过匹配 N 携带 M 的组合创建匹配器
> - n: 组合中元素的数量表示需要匹配的组合数量n 的类型需要全部相同
@ -137,7 +137,7 @@ combination 包提供了一些实用的组合函数。
> - getType: 用于获取组合中元素的类型,用于判断是否相同
***
#### func WithMatcherNCarryIndependentM(n int, m int, getType func (item T) E) MatcherOption[T]
#### func WithMatcherNCarryIndependentM\[T Item, E generic.Ordered\](n int, m int, getType func (item T) E) MatcherOption[T]
<span id="WithMatcherNCarryIndependentM"></span>
> 通过匹配 N 携带独立 M 的组合创建匹配器
> - n: 组合中元素的数量表示需要匹配的组合数量n 的类型需要全部相同
@ -145,95 +145,95 @@ combination 包提供了一些实用的组合函数。
> - getType: 用于获取组合中元素的类型,用于判断是否相同
***
#### func NewValidator(options ...ValidatorOption[T]) *Validator[T]
#### func NewValidator\[T Item\](options ...ValidatorOption[T]) *Validator[T]
<span id="NewValidator"></span>
> 创建一个新的校验器
***
#### func WithValidatorHandle(handle func (items []T) bool) ValidatorOption[T]
#### func WithValidatorHandle\[T Item\](handle func (items []T) bool) ValidatorOption[T]
<span id="WithValidatorHandle"></span>
> 通过特定的验证函数对组合进行验证
***
#### func WithValidatorHandleLength(length int) ValidatorOption[T]
#### func WithValidatorHandleLength\[T Item\](length int) ValidatorOption[T]
<span id="WithValidatorHandleLength"></span>
> 校验组合的长度是否符合要求
***
#### func WithValidatorHandleLengthRange(min int, max int) ValidatorOption[T]
#### func WithValidatorHandleLengthRange\[T Item\](min int, max int) ValidatorOption[T]
<span id="WithValidatorHandleLengthRange"></span>
> 校验组合的长度是否在指定的范围内
***
#### func WithValidatorHandleLengthMin(min int) ValidatorOption[T]
#### func WithValidatorHandleLengthMin\[T Item\](min int) ValidatorOption[T]
<span id="WithValidatorHandleLengthMin"></span>
> 校验组合的长度是否大于等于指定的最小值
***
#### func WithValidatorHandleLengthMax(max int) ValidatorOption[T]
#### func WithValidatorHandleLengthMax\[T Item\](max int) ValidatorOption[T]
<span id="WithValidatorHandleLengthMax"></span>
> 校验组合的长度是否小于等于指定的最大值
***
#### func WithValidatorHandleLengthNot(length int) ValidatorOption[T]
#### func WithValidatorHandleLengthNot\[T Item\](length int) ValidatorOption[T]
<span id="WithValidatorHandleLengthNot"></span>
> 校验组合的长度是否不等于指定的值
***
#### func WithValidatorHandleTypeLength(length int, getType func (item T) E) ValidatorOption[T]
#### func WithValidatorHandleTypeLength\[T Item, E generic.Ordered\](length int, getType func (item T) E) ValidatorOption[T]
<span id="WithValidatorHandleTypeLength"></span>
> 校验组合成员类型数量是否为指定的值
***
#### func WithValidatorHandleTypeLengthRange(min int, max int, getType func (item T) E) ValidatorOption[T]
#### func WithValidatorHandleTypeLengthRange\[T Item, E generic.Ordered\](min int, max int, getType func (item T) E) ValidatorOption[T]
<span id="WithValidatorHandleTypeLengthRange"></span>
> 校验组合成员类型数量是否在指定的范围内
***
#### func WithValidatorHandleTypeLengthMin(min int, getType func (item T) E) ValidatorOption[T]
#### func WithValidatorHandleTypeLengthMin\[T Item, E generic.Ordered\](min int, getType func (item T) E) ValidatorOption[T]
<span id="WithValidatorHandleTypeLengthMin"></span>
> 校验组合成员类型数量是否大于等于指定的最小值
***
#### func WithValidatorHandleTypeLengthMax(max int, getType func (item T) E) ValidatorOption[T]
#### func WithValidatorHandleTypeLengthMax\[T Item, E generic.Ordered\](max int, getType func (item T) E) ValidatorOption[T]
<span id="WithValidatorHandleTypeLengthMax"></span>
> 校验组合成员类型数量是否小于等于指定的最大值
***
#### func WithValidatorHandleTypeLengthNot(length int, getType func (item T) E) ValidatorOption[T]
#### func WithValidatorHandleTypeLengthNot\[T Item, E generic.Ordered\](length int, getType func (item T) E) ValidatorOption[T]
<span id="WithValidatorHandleTypeLengthNot"></span>
> 校验组合成员类型数量是否不等于指定的值
***
#### func WithValidatorHandleContinuous(getIndex func (item T) Index) ValidatorOption[T]
#### func WithValidatorHandleContinuous\[T Item, Index generic.Integer\](getIndex func (item T) Index) ValidatorOption[T]
<span id="WithValidatorHandleContinuous"></span>
> 校验组合成员是否连续
***
#### func WithValidatorHandleContinuousNot(getIndex func (item T) Index) ValidatorOption[T]
#### func WithValidatorHandleContinuousNot\[T Item, Index generic.Integer\](getIndex func (item T) Index) ValidatorOption[T]
<span id="WithValidatorHandleContinuousNot"></span>
> 校验组合成员是否不连续
***
#### func WithValidatorHandleGroupContinuous(getType func (item T) E, getIndex func (item T) Index) ValidatorOption[T]
#### func WithValidatorHandleGroupContinuous\[T Item, E generic.Ordered, Index generic.Integer\](getType func (item T) E, getIndex func (item T) Index) ValidatorOption[T]
<span id="WithValidatorHandleGroupContinuous"></span>
> 校验组合成员是否能够按类型分组并且连续
***
#### func WithValidatorHandleGroupContinuousN(n int, getType func (item T) E, getIndex func (item T) Index) ValidatorOption[T]
#### func WithValidatorHandleGroupContinuousN\[T Item, E generic.Ordered, Index generic.Integer\](n int, getType func (item T) E, getIndex func (item T) Index) ValidatorOption[T]
<span id="WithValidatorHandleGroupContinuousN"></span>
> 校验组合成员是否能够按分组为 n 组类型并且连续
***
#### func WithValidatorHandleNCarryM(n int, m int, getType func (item T) E) ValidatorOption[T]
#### func WithValidatorHandleNCarryM\[T Item, E generic.Ordered\](n int, m int, getType func (item T) E) ValidatorOption[T]
<span id="WithValidatorHandleNCarryM"></span>
> 校验组合成员是否匹配 N 携带相同的 M 的组合
> 校验组合成员是否匹配 N 携带相同的 M 的组合
> - n: 组合中元素的数量表示需要匹配的组合数量n 的类型需要全部相同
> - m: 组合中元素的数量表示需要匹配的组合数量m 的类型需要全部相同
> - getType: 用于获取组合中元素的类型,用于判断是否相同
***
#### func WithValidatorHandleNCarryIndependentM(n int, m int, getType func (item T) E) ValidatorOption[T]
#### func WithValidatorHandleNCarryIndependentM\[T Item, E generic.Ordered\](n int, m int, getType func (item T) E) ValidatorOption[T]
<span id="WithValidatorHandleNCarryIndependentM"></span>
> 校验组合成员是否匹配 N 携带独立的 M 的组合
> - n: 组合中元素的数量表示需要匹配的组合数量n 的类型需要全部相同
@ -241,6 +241,7 @@ combination 包提供了一些实用的组合函数。
> - getType: 用于获取组合中元素的类型,用于判断是否相同
***
<span id="struct_Combination"></span>
### Combination `STRUCT`
用于从多个匹配器内提取组合的数据结构
```go
@ -250,23 +251,41 @@ type Combination[T Item] struct {
priority []string
}
```
<span id="struct_Combination_NewMatcher"></span>
#### func (*Combination) NewMatcher(name string, options ...MatcherOption[T]) *Combination[T]
> 添加一个新的匹配器
***
<span id="struct_Combination_AddMatcher"></span>
#### func (*Combination) AddMatcher(name string, matcher *Matcher[T]) *Combination[T]
> 添加一个匹配器
***
<span id="struct_Combination_RemoveMatcher"></span>
#### func (*Combination) RemoveMatcher(name string) *Combination[T]
> 移除一个匹配器
***
<span id="struct_Combination_Combinations"></span>
#### func (*Combination) Combinations(items []T) (result [][]T)
> 从一组数据中提取所有符合匹配器规则的组合
***
<span id="struct_Combination_CombinationsToName"></span>
#### func (*Combination) CombinationsToName(items []T) (result map[string][][]T)
> 从一组数据中提取所有符合匹配器规则的组合,并返回匹配器名称
***
<span id="struct_Combination_Best"></span>
#### func (*Combination) Best(items []T) (name string, result []T)
> 从一组数据中提取符合匹配器规则的最佳组合
<details>
<summary>查看 / 收起单元测试</summary>
@ -301,19 +320,25 @@ func TestCombination_Best(t *testing.T) {
***
<span id="struct_Combination_Worst"></span>
#### func (*Combination) Worst(items []T) (name string, result []T)
> 从一组数据中提取符合匹配器规则的最差组合
***
<span id="struct_Option"></span>
### Option `STRUCT`
组合器选项
```go
type Option[T Item] func(*Combination[T])
```
<span id="struct_Item"></span>
### Item `INTERFACE`
```go
type Item interface{}
```
<span id="struct_Matcher"></span>
### Matcher `STRUCT`
用于从一组数据内提取组合的数据结构
```go
@ -322,24 +347,38 @@ type Matcher[T Item] struct {
filter []func(items []T) [][]T
}
```
<span id="struct_Matcher_AddFilter"></span>
#### func (*Matcher) AddFilter(filter func (items []T) [][]T)
> 添加一个筛选器
> - 筛选器用于对组合进行筛选,返回一个二维数组,每个数组内的元素都是一个组合
***
<span id="struct_Matcher_Combinations"></span>
#### func (*Matcher) Combinations(items []T) [][]T
> 从一组数据中提取所有符合筛选器规则的组合
***
<span id="struct_Matcher_Best"></span>
#### func (*Matcher) Best(items []T) []T
> 从一组数据中提取符筛选器规则的最佳组合
***
<span id="struct_Matcher_Worst"></span>
#### func (*Matcher) Worst(items []T) []T
> 从一组数据中提取符筛选器规则的最差组合
***
<span id="struct_MatcherOption"></span>
### MatcherOption `STRUCT`
匹配器选项
```go
type MatcherOption[T Item] func(matcher *Matcher[T])
```
<span id="struct_Validator"></span>
### Validator `STRUCT`
用于对组合进行验证的校验器
```go
@ -347,8 +386,11 @@ type Validator[T Item] struct {
vh []func(items []T) bool
}
```
<span id="struct_Validator_Validate"></span>
#### func (*Validator) Validate(items []T) bool
> 校验组合是否符合要求
<details>
<summary>查看 / 收起单元测试</summary>
@ -382,6 +424,7 @@ func TestValidator_Validate(t *testing.T) {
***
<span id="struct_ValidatorOption"></span>
### ValidatorOption `STRUCT`
```go

View File

@ -30,32 +30,32 @@ compress 提供了一些用于压缩和解压缩数据的函数。
***
## 详情信息
#### func GZipCompress(data []byte) bytes.Buffer, error
#### func GZipCompress(data []byte) (bytes.Buffer, error)
<span id="GZipCompress"></span>
> 对数据进行GZip压缩返回bytes.Buffer和错误信息
***
#### func GZipUnCompress(dataByte []byte) []byte, error
#### func GZipUnCompress(dataByte []byte) ([]byte, error)
<span id="GZipUnCompress"></span>
> 对已进行GZip压缩的数据进行解压缩返回字节数组及错误信息
***
#### func TARCompress(data []byte) bytes.Buffer, error
#### func TARCompress(data []byte) (bytes.Buffer, error)
<span id="TARCompress"></span>
> 对数据进行TAR压缩返回bytes.Buffer和错误信息
***
#### func TARUnCompress(dataByte []byte) []byte, error
#### func TARUnCompress(dataByte []byte) ([]byte, error)
<span id="TARUnCompress"></span>
> 对已进行TAR压缩的数据进行解压缩返回字节数组及错误信息
***
#### func ZIPCompress(data []byte) bytes.Buffer, error
#### func ZIPCompress(data []byte) (bytes.Buffer, error)
<span id="ZIPCompress"></span>
> 对数据进行ZIP压缩返回bytes.Buffer和错误信息
***
#### func ZIPUnCompress(dataByte []byte) []byte, error
#### func ZIPUnCompress(dataByte []byte) ([]byte, error)
<span id="ZIPUnCompress"></span>
> 对已进行ZIP压缩的数据进行解压缩返回字节数组及错误信息

View File

@ -34,52 +34,52 @@
***
## 详情信息
#### func EncryptBase64(data []byte) string
#### func EncryptBase64(data []byte) string
<span id="EncryptBase64"></span>
> 对数据进行Base64编码
***
#### func DecodedBase64(data string) []byte, error
#### func DecodedBase64(data string) ([]byte, error)
<span id="DecodedBase64"></span>
> 对数据进行Base64解码
***
#### func EncryptCRC32(str string) uint32
#### func EncryptCRC32(str string) uint32
<span id="EncryptCRC32"></span>
> 对字符串进行CRC加密并返回其结果。
***
#### func DecodedCRC32(data []byte) uint32
#### func DecodedCRC32(data []byte) uint32
<span id="DecodedCRC32"></span>
> 对字节数组进行CRC加密并返回其结果。
***
#### func EncryptMD5(str string) string
#### func EncryptMD5(str string) string
<span id="EncryptMD5"></span>
> 对字符串进行MD5加密并返回其结果。
***
#### func DecodedMD5(data []byte) string
#### func DecodedMD5(data []byte) string
<span id="DecodedMD5"></span>
> 对字节数组进行MD5加密并返回其结果。
***
#### func EncryptSHA1(str string) string
#### func EncryptSHA1(str string) string
<span id="EncryptSHA1"></span>
> 对字符串进行SHA1加密并返回其结果。
***
#### func DecodedSHA1(data []byte) string
#### func DecodedSHA1(data []byte) string
<span id="DecodedSHA1"></span>
> 对字节数组进行SHA1加密并返回其结果。
***
#### func EncryptSHA256(str string) string
#### func EncryptSHA256(str string) string
<span id="EncryptSHA256"></span>
> 对字符串进行SHA256加密并返回其结果。
***
#### func DecodedSHA256(data []byte) string
#### func DecodedSHA256(data []byte) string
<span id="DecodedSHA256"></span>
> 对字节数组进行SHA256加密并返回其结果。

View File

@ -24,25 +24,26 @@ deck 包中的内容用于针对一堆内容的管理,适用但不限于牌堆
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Deck](#deck)|甲板,用于针对一堆 Group 进行管理的数据结构
|`STRUCT`|[Group](#group)|甲板中的组,用于针对一堆内容进行管理的数据结构
|`INTERFACE`|[Item](#item)|甲板成员
|`STRUCT`|[Deck](#struct_Deck)|甲板,用于针对一堆 Group 进行管理的数据结构
|`STRUCT`|[Group](#struct_Group)|甲板中的组,用于针对一堆内容进行管理的数据结构
|`INTERFACE`|[Item](#struct_Item)|甲板成员
</details>
***
## 详情信息
#### func NewDeck() *Deck[I]
#### func NewDeck\[I Item\]() *Deck[I]
<span id="NewDeck"></span>
> 创建一个新的甲板
***
#### func NewGroup(guid int64, fillHandle func (guid int64) []I) *Group[I]
#### func NewGroup\[I Item\](guid int64, fillHandle func (guid int64) []I) *Group[I]
<span id="NewGroup"></span>
> 创建一个新的组
***
<span id="struct_Deck"></span>
### Deck `STRUCT`
甲板,用于针对一堆 Group 进行管理的数据结构
```go
@ -51,27 +52,49 @@ type Deck[I Item] struct {
sort []int64
}
```
<span id="struct_Deck_AddGroup"></span>
#### func (*Deck) AddGroup(group *Group[I])
> 将一个组添加到甲板中
***
<span id="struct_Deck_RemoveGroup"></span>
#### func (*Deck) RemoveGroup(guid int64)
> 移除甲板中的一个组
***
<span id="struct_Deck_GetCount"></span>
#### func (*Deck) GetCount() int
> 获取甲板中的组数量
***
<span id="struct_Deck_GetGroups"></span>
#### func (*Deck) GetGroups() map[int64]*Group[I]
> 获取所有组
***
<span id="struct_Deck_GetGroupsSlice"></span>
#### func (*Deck) GetGroupsSlice() []*Group[I]
> 获取所有组
***
<span id="struct_Deck_GetNext"></span>
#### func (*Deck) GetNext(guid int64) *Group[I]
> 获取特定组的下一个组
***
<span id="struct_Deck_GetPrev"></span>
#### func (*Deck) GetPrev(guid int64) *Group[I]
> 获取特定组的上一个组
***
<span id="struct_Group"></span>
### Group `STRUCT`
甲板中的组,用于针对一堆内容进行管理的数据结构
```go
@ -81,51 +104,97 @@ type Group[I Item] struct {
items []I
}
```
<span id="struct_Group_GetGuid"></span>
#### func (*Group) GetGuid() int64
> 获取组的 guid
***
<span id="struct_Group_Fill"></span>
#### func (*Group) Fill()
> 将该组的数据填充为 WithGroupFillHandle 中设置的内容
***
<span id="struct_Group_Pop"></span>
#### func (*Group) Pop() (item I)
> 从顶部获取一个内容
***
<span id="struct_Group_PopN"></span>
#### func (*Group) PopN(n int) (items []I)
> 从顶部获取指定数量的内容
***
<span id="struct_Group_PressOut"></span>
#### func (*Group) PressOut() (item I)
> 从底部压出一个内容
***
<span id="struct_Group_PressOutN"></span>
#### func (*Group) PressOutN(n int) (items []I)
> 从底部压出指定数量的内容
***
<span id="struct_Group_Push"></span>
#### func (*Group) Push(item I)
> 向顶部压入一个内容
***
<span id="struct_Group_PushN"></span>
#### func (*Group) PushN(items []I)
> 向顶部压入指定数量的内容
***
<span id="struct_Group_Insert"></span>
#### func (*Group) Insert(item I)
> 向底部插入一个内容
***
<span id="struct_Group_InsertN"></span>
#### func (*Group) InsertN(items []I)
> 向底部插入指定数量的内容
***
<span id="struct_Group_Pull"></span>
#### func (*Group) Pull(index int) (item I)
> 从特定位置拔出一个内容
***
<span id="struct_Group_Thrust"></span>
#### func (*Group) Thrust(index int, item I)
> 向特定位置插入一个内容
***
<span id="struct_Group_IsFree"></span>
#### func (*Group) IsFree() bool
> 检查组是否为空
***
<span id="struct_Group_GetCount"></span>
#### func (*Group) GetCount() int
> 获取组中剩余的内容数量
***
<span id="struct_Group_GetItem"></span>
#### func (*Group) GetItem(index int) I
> 获取组中的指定内容
***
<span id="struct_Item"></span>
### Item `INTERFACE`
甲板成员
```go

View File

@ -35,46 +35,46 @@
***
## 详情信息
#### func PathExist(path string) bool, error
#### func PathExist(path string) (bool, error)
<span id="PathExist"></span>
> 路径是否存在
***
#### func IsDir(path string) bool, error
#### func IsDir(path string) (bool, error)
<span id="IsDir"></span>
> 路径是否是文件夹
***
#### func WriterFile(filePath string, content []byte) error
#### func WriterFile(filePath string, content []byte) error
<span id="WriterFile"></span>
> 向特定文件写入内容
***
#### func ReadOnce(filePath string) []byte, error
#### func ReadOnce(filePath string) ([]byte, error)
<span id="ReadOnce"></span>
> 单次读取文件
> - 一次性对整个文件进行读取,小文件读取可以很方便的一次性将文件内容读取出来,而大文件读取会造成性能影响。
***
#### func ReadBlockHook(filePath string, bufferSize int, hook func (data []byte)) error
#### func ReadBlockHook(filePath string, bufferSize int, hook func (data []byte)) error
<span id="ReadBlockHook"></span>
> 分块读取文件
> - 将filePath路径对应的文件数据并将读到的每一部分传入hook函数中当过程中如果产生错误则会返回error。
> - 分块读取可以在读取速度和内存消耗之间有一个很好的平衡。
***
#### func ReadLine(filePath string, hook func (line string)) error
#### func ReadLine(filePath string, hook func (line string)) error
<span id="ReadLine"></span>
> 分行读取文件
> - 将filePath路径对应的文件数据并将读到的每一行传入hook函数中当过程中如果产生错误则会返回error。
***
#### func LineCount(filePath string) int
#### func LineCount(filePath string) int
<span id="LineCount"></span>
> 统计文件行数
***
#### func Paths(dir string) []string
#### func Paths(dir string) []string
<span id="Paths"></span>
> 获取指定目录下的所有文件路径
> - 包括了子目录下的文件
@ -88,7 +88,7 @@
> - 可通过 start 参数指定开始读取的位置,如果不指定则从文件开头开始读取。
***
#### func FindLineChunks(file *os.File, chunkSize int64) [][2]int64
#### func FindLineChunks(file *os.File, chunkSize int64) [][2]int64
<span id="FindLineChunks"></span>
> 查找文件按照每行划分的分块,每个分块的大小将在 chunkSize 和分割后的分块距离行首及行尾的距离中范围内
> - 使用该函数得到的分块是完整的行,不会出现行被分割的情况
@ -96,7 +96,7 @@
> - 返回值的成员是一个长度为 2 的数组,第一个元素是分块的起始位置,第二个元素是分块的结束位置
***
#### func FindLineChunksByOffset(file *os.File, offset int64, chunkSize int64) [][2]int64
#### func FindLineChunksByOffset(file *os.File, offset int64, chunkSize int64) [][2]int64
<span id="FindLineChunksByOffset"></span>
> 该函数与 FindLineChunks 类似,不同的是该函数可以指定 offset 从指定位置开始读取文件

View File

@ -28,47 +28,48 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[FSM](#fsm)|状态机
|`STRUCT`|[Option](#option)|暂无描述...
|`STRUCT`|[FSM](#struct_FSM)|状态机
|`STRUCT`|[Option](#struct_Option)|暂无描述...
</details>
***
## 详情信息
#### func NewFSM(data Data) *FSM[State, Data]
#### func NewFSM\[State comparable, Data any\](data Data) *FSM[State, Data]
<span id="NewFSM"></span>
> 创建一个新的状态机
***
#### func WithEnterBeforeEvent(fn func (state *FSM[State, Data])) Option[State, Data]
#### func WithEnterBeforeEvent\[State comparable, Data any\](fn func (state *FSM[State, Data])) Option[State, Data]
<span id="WithEnterBeforeEvent"></span>
> 设置状态进入前的回调
> - 在首次设置状态时,状态机本身的当前状态为零值状态
***
#### func WithEnterAfterEvent(fn func (state *FSM[State, Data])) Option[State, Data]
#### func WithEnterAfterEvent\[State comparable, Data any\](fn func (state *FSM[State, Data])) Option[State, Data]
<span id="WithEnterAfterEvent"></span>
> 设置状态进入后的回调
***
#### func WithUpdateEvent(fn func (state *FSM[State, Data])) Option[State, Data]
#### func WithUpdateEvent\[State comparable, Data any\](fn func (state *FSM[State, Data])) Option[State, Data]
<span id="WithUpdateEvent"></span>
> 设置状态内刷新的回调
***
#### func WithExitBeforeEvent(fn func (state *FSM[State, Data])) Option[State, Data]
#### func WithExitBeforeEvent\[State comparable, Data any\](fn func (state *FSM[State, Data])) Option[State, Data]
<span id="WithExitBeforeEvent"></span>
> 设置状态退出前的回调
> - 该阶段状态机的状态为退出前的状态,而非新的状态
***
#### func WithExitAfterEvent(fn func (state *FSM[State, Data])) Option[State, Data]
#### func WithExitAfterEvent\[State comparable, Data any\](fn func (state *FSM[State, Data])) Option[State, Data]
<span id="WithExitAfterEvent"></span>
> 设置状态退出后的回调
> - 该阶段状态机的状态为新的状态,而非退出前的状态
***
<span id="struct_FSM"></span>
### FSM `STRUCT`
状态机
```go
@ -84,6 +85,61 @@ type FSM[State comparable, Data any] struct {
exitAfterEventHandles map[State][]func(state *FSM[State, Data])
}
```
<span id="struct_FSM_Update"></span>
#### func (*FSM) Update()
> 触发当前状态
***
<span id="struct_FSM_Register"></span>
#### func (*FSM) Register(state State, options ...Option[State, Data])
> 注册状态
***
<span id="struct_FSM_Unregister"></span>
#### func (*FSM) Unregister(state State)
> 反注册状态
***
<span id="struct_FSM_HasState"></span>
#### func (*FSM) HasState(state State) bool
> 检查状态机是否存在特定状态
***
<span id="struct_FSM_Change"></span>
#### func (*FSM) Change(state State)
> 改变状态机状态到新的状态
***
<span id="struct_FSM_Current"></span>
#### func (*FSM) Current() (state State)
> 获取当前状态
***
<span id="struct_FSM_GetData"></span>
#### func (*FSM) GetData() Data
> 获取状态机数据
***
<span id="struct_FSM_IsZero"></span>
#### func (*FSM) IsZero() bool
> 检查状态机是否无状态
***
<span id="struct_FSM_PrevIsZero"></span>
#### func (*FSM) PrevIsZero() bool
> 检查状态机上一个状态是否无状态
***
<span id="struct_Option"></span>
### Option `STRUCT`
```go

View File

@ -23,20 +23,20 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Comment](#comment)|暂无描述...
|`STRUCT`|[Field](#field)|暂无描述...
|`STRUCT`|[File](#file)|暂无描述...
|`STRUCT`|[Function](#function)|暂无描述...
|`STRUCT`|[Package](#package)|暂无描述...
|`STRUCT`|[Struct](#struct)|暂无描述...
|`STRUCT`|[Type](#type)|暂无描述...
|`STRUCT`|[Comment](#struct_Comment)|暂无描述...
|`STRUCT`|[Field](#struct_Field)|暂无描述...
|`STRUCT`|[File](#struct_File)|暂无描述...
|`STRUCT`|[Function](#struct_Function)|暂无描述...
|`STRUCT`|[Package](#struct_Package)|暂无描述...
|`STRUCT`|[Struct](#struct_Struct)|暂无描述...
|`STRUCT`|[Type](#struct_Type)|暂无描述...
</details>
***
## 详情信息
#### func NewPackage(dir string) *Package, error
#### func NewPackage(dir string) (*Package, error)
<span id="NewPackage"></span>
<details>
@ -60,6 +60,7 @@ func TestNewPackage(t *testing.T) {
***
<span id="struct_Comment"></span>
### Comment `STRUCT`
```go
@ -68,6 +69,7 @@ type Comment struct {
Clear []string
}
```
<span id="struct_Field"></span>
### Field `STRUCT`
```go
@ -78,6 +80,7 @@ type Field struct {
Comments *Comment
}
```
<span id="struct_File"></span>
### File `STRUCT`
```go
@ -90,8 +93,12 @@ type File struct {
Comment *Comment
}
```
<span id="struct_File_Package"></span>
#### func (*File) Package() string
***
<span id="struct_Function"></span>
### Function `STRUCT`
```go
@ -110,8 +117,12 @@ type Function struct {
Test bool
}
```
<span id="struct_Function_Code"></span>
#### func (*Function) Code() string
***
<span id="struct_Package"></span>
### Package `STRUCT`
```go
@ -123,20 +134,42 @@ type Package struct {
Functions map[string]*Function
}
```
<span id="struct_Package_StructFunc"></span>
#### func (*Package) StructFunc(name string) []*Function
***
<span id="struct_Package_PackageFunc"></span>
#### func (*Package) PackageFunc() []*Function
***
<span id="struct_Package_Structs"></span>
#### func (*Package) Structs() []*Struct
***
<span id="struct_Package_FileComments"></span>
#### func (*Package) FileComments() *Comment
***
<span id="struct_Package_GetUnitTest"></span>
#### func (*Package) GetUnitTest(f *Function) *Function
***
<span id="struct_Package_GetExampleTest"></span>
#### func (*Package) GetExampleTest(f *Function) *Function
***
<span id="struct_Package_GetBenchmarkTest"></span>
#### func (*Package) GetBenchmarkTest(f *Function) *Function
***
<span id="struct_Struct"></span>
### Struct `STRUCT`
```go
@ -151,6 +184,7 @@ type Struct struct {
Test bool
}
```
<span id="struct_Type"></span>
### Type `STRUCT`
```go

View File

@ -5,7 +5,7 @@ import (
"strings"
)
func newComment(cg *ast.CommentGroup) *Comment {
func newComment(name string, cg *ast.CommentGroup) *Comment {
c := &Comment{}
if cg == nil {
return c
@ -14,9 +14,10 @@ func newComment(cg *ast.CommentGroup) *Comment {
c.Comments = append(c.Comments, comment.Text)
cc := strings.TrimPrefix(strings.Replace(comment.Text, "// ", "//", 1), "//")
if i == 0 {
s := strings.SplitN(cc, " ", 2)
if len(s) == 2 {
cc = s[1]
tsc := strings.TrimSpace(cc)
if strings.HasPrefix(tsc, name) {
s := strings.TrimSpace(strings.TrimPrefix(tsc, name))
cc = s
}
}
c.Clear = append(c.Clear, cc)

View File

@ -10,7 +10,7 @@ func newField(field *ast.Field) []*Field {
return []*Field{{
Anonymous: true,
Type: newType(field.Type),
Comments: newComment(field.Comment),
Comments: newComment("", field.Comment),
}}
} else {
var fs []*Field
@ -22,7 +22,7 @@ func newField(field *ast.Field) []*Field {
Anonymous: false,
Name: name.String(),
Type: newType(field.Type),
Comments: newComment(field.Comment),
Comments: newComment(name.String(), field.Comment),
})
}
return fs

View File

@ -16,7 +16,7 @@ func newFile(owner *Package, filePath string) (*File, error) {
af: af,
owner: owner,
FilePath: filePath,
Comment: newComment(af.Doc),
Comment: newComment("Package", af.Doc),
}
for _, decl := range af.Decls {
switch typ := decl.(type) {

View File

@ -12,7 +12,7 @@ func newFunction(astFunc *ast.FuncDecl) *Function {
f := &Function{
decl: astFunc,
Name: astFunc.Name.String(),
Comments: newComment(astFunc.Doc),
Comments: newComment(astFunc.Name.String(), astFunc.Doc),
}
f.IsTest = strings.HasPrefix(f.Name, "Test")
f.IsBenchmark = strings.HasPrefix(f.Name, "Benchmark")
@ -56,6 +56,9 @@ type Function struct {
}
func (f *Function) Code() string {
if f.Test {
f.decl.Doc = nil
}
var bb bytes.Buffer
if err := format.Node(&bb, token.NewFileSet(), f.decl); err != nil {
panic(err)

View File

@ -14,7 +14,8 @@ func newName(expr ast.Expr) string {
//case *ast.FuncType:
//case *ast.InterfaceType:
//case *ast.MapType:
//case *ast.ChanType:
case *ast.ChanType:
str.WriteString(newName(e.Value))
case *ast.Ident:
str.WriteString(e.Name)
case *ast.Ellipsis:
@ -28,13 +29,17 @@ func newName(expr ast.Expr) string {
case *ast.IndexExpr:
str.WriteString(newName(e.X))
case *ast.IndexListExpr:
str.WriteString(newName(e.X))
case *ast.SliceExpr:
case *ast.TypeAssertExpr:
case *ast.CallExpr:
str.WriteString(newName(e.X))
//case *ast.TypeAssertExpr:
//case *ast.CallExpr:
case *ast.StarExpr:
str.WriteString(newName(e.X))
case *ast.UnaryExpr:
str.WriteString(newName(e.X))
case *ast.BinaryExpr:
str.WriteString(newName(e.X))
}
return str.String()
}

View File

@ -91,7 +91,7 @@ func (p *Package) Structs() []*Struct {
}
func (p *Package) FileComments() *Comment {
var comment = newComment(nil)
var comment = newComment("", nil)
for _, file := range p.Files {
for _, c := range file.Comment.Comments {
comment.Comments = append(comment.Comments, c)

View File

@ -8,7 +8,7 @@ func newStruct(astGen *ast.GenDecl) *Struct {
astTypeSpec := astGen.Specs[0].(*ast.TypeSpec)
s := &Struct{
Name: astTypeSpec.Name.String(),
Comments: newComment(astGen.Doc),
Comments: newComment(astTypeSpec.Name.String(), astGen.Doc),
}
s.Internal = s.Name[0] >= 97 && s.Name[0] <= 122
if astTypeSpec.TypeParams != nil {

View File

@ -23,17 +23,18 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Builder](#builder)|暂无描述...
|`STRUCT`|[Builder](#struct_Builder)|暂无描述...
</details>
***
## 详情信息
#### func New(pkgDirPath string, output string) *Builder, error
#### func New(pkgDirPath string, output string) (*Builder, error)
<span id="New"></span>
***
<span id="struct_Builder"></span>
### Builder `STRUCT`
```go
@ -43,7 +44,10 @@ type Builder struct {
o string
}
```
<span id="struct_Builder_Generate"></span>
#### func (*Builder) Generate() error
<details>
<summary>查看 / 收起单元测试</summary>
@ -51,11 +55,11 @@ type Builder struct {
```go
func TestBuilder_Generate(t *testing.T) {
filepath.Walk("/Users/kercylan/Coding.localized/Go/minotaur", func(path string, info fs.FileInfo, err error) error {
filepath.Walk("D:/sources/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"), ".") {
if strings.Contains(strings.TrimPrefix(path, "D:/sources/minotaur"), ".") {
return nil
}
b, err := New(path, filepath.Join(path, "README.md"))

View File

@ -115,7 +115,7 @@ func (b *Builder) genMenus() {
structGen()
b.tableRow(
super.If(structInfo.Interface, "`INTERFACE`", "`STRUCT`"),
fmt.Sprintf("[%s](#%s)", structInfo.Name, strings.ToLower(structInfo.Name)),
fmt.Sprintf("[%s](#struct_%s)", structInfo.Name, structInfo.Name),
collection.FindFirstOrDefaultInSlice(structInfo.Comments.Clear, "暂无描述..."),
)
}
@ -154,8 +154,21 @@ func (b *Builder) genStructs() {
continue
}
titleBuild()
b.title(4, strings.TrimSpace(fmt.Sprintf("func %s%s %s",
b.title(4, strings.TrimSpace(fmt.Sprintf("func %s%s%s %s",
function.Name,
func() string {
var sb strings.Builder
if len(function.Generic) > 0 {
sb.WriteString("\\[")
var genericStr = make([]string, 0)
for _, field := range function.Generic {
genericStr = append(genericStr, fmt.Sprintf("%s %s", field.Name, field.Type.Sign))
}
sb.WriteString(strings.Join(genericStr, ", "))
sb.WriteString("\\]")
}
return sb.String()
}(),
func() string {
f := funcHandler(function.Params)
if !strings.HasPrefix(f, "(") {
@ -163,7 +176,13 @@ func (b *Builder) genStructs() {
}
return f
}(),
funcHandler(function.Results),
func() string {
f := strings.TrimSpace(funcHandler(function.Results))
if len(function.Results) >= 2 && !strings.HasPrefix(f, "(") {
f = "(" + f + ")"
}
return f
}(),
)))
b.newLine(fmt.Sprintf(`<span id="%s"></span>`, function.Name))
b.quote()
@ -172,15 +191,34 @@ func (b *Builder) genStructs() {
}
b.newLine()
if example := b.p.GetExampleTest(function); example != nil {
b.newLine("示例代码:", "```go\n", example.Code(), "```\n")
b.newLine("**示例代码:**").newLine()
if len(example.Comments.Clear) > 0 {
for _, s := range example.Comments.Clear {
b.newLine(fmt.Sprintf("%s", s))
}
b.newLine().newLine()
}
b.newLine("```go\n", example.Code(), "```\n")
}
if unitTest := b.p.GetUnitTest(function); unitTest != nil {
b.detailsStart("查看 / 收起单元测试")
if len(unitTest.Comments.Clear) > 0 {
for _, s := range unitTest.Comments.Clear {
b.newLine(fmt.Sprintf("%s", s))
}
b.newLine().newLine()
}
b.newLine("```go\n", unitTest.Code(), "```\n")
b.detailsEnd()
}
if benchmarkTest := b.p.GetBenchmarkTest(function); benchmarkTest != nil {
b.detailsStart("查看 / 收起基准测试")
if len(benchmarkTest.Comments.Clear) > 0 {
for _, s := range benchmarkTest.Comments.Clear {
b.newLine(fmt.Sprintf("%s", s))
}
b.newLine().newLine()
}
b.newLine("```go\n", benchmarkTest.Code(), "```\n")
b.detailsEnd()
}
@ -193,6 +231,7 @@ func (b *Builder) genStructs() {
continue
}
titleBuild()
b.newLine(fmt.Sprintf(`<span id="struct_%s"></span>`, 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")
@ -249,6 +288,7 @@ func (b *Builder) genStructs() {
if function.Internal || function.Test {
continue
}
b.newLine(fmt.Sprintf(`<span id="struct_%s_%s"></span>`, structInfo.Name, function.Name)).newLine()
b.title(4, strings.TrimSpace(fmt.Sprintf("func (%s%s) %s%s %s",
super.If(function.Struct.Type.IsPointer, "*", ""),
structInfo.Name,
@ -260,25 +300,48 @@ func (b *Builder) genStructs() {
}
return f
}(),
funcHandler(function.Results),
func() string {
f := funcHandler(function.Results)
if len(function.Results) >= 2 && !strings.HasPrefix(strings.TrimSpace(f), "(") {
f = "(" + f + ")"
}
return f
}(),
)))
b.quote()
for _, comment := range function.Comments.Clear {
b.quote(comment)
}
if function.Name == "Write" {
fmt.Println()
}
b.newLine()
if example := b.p.GetExampleTest(function); example != nil {
b.newLine("示例代码:", "```go\n", example.Code(), "```\n")
b.newLine("**示例代码:**").newLine()
if len(example.Comments.Clear) > 0 {
for _, s := range example.Comments.Clear {
b.newLine(fmt.Sprintf("%s", s))
}
b.newLine().newLine()
}
b.newLine("```go\n", example.Code(), "```\n")
}
if unitTest := b.p.GetUnitTest(function); unitTest != nil {
b.detailsStart("查看 / 收起单元测试")
if len(unitTest.Comments.Clear) > 0 {
for _, s := range unitTest.Comments.Clear {
b.newLine(fmt.Sprintf("%s", s))
}
b.newLine().newLine()
}
b.newLine("```go\n", unitTest.Code(), "```\n")
b.detailsEnd()
}
if benchmarkTest := b.p.GetBenchmarkTest(function); benchmarkTest != nil {
b.detailsStart("查看 / 收起基准测试")
if len(benchmarkTest.Comments.Clear) > 0 {
for _, s := range benchmarkTest.Comments.Clear {
b.newLine(fmt.Sprintf("%s", s))
}
b.newLine().newLine()
}
b.newLine("```go\n", benchmarkTest.Code(), "```\n")
b.detailsEnd()
}

View File

@ -16,11 +16,11 @@ func TestBuilder_Generate(t *testing.T) {
// panic(err)
//}
//return
filepath.Walk("/Users/kercylan/Coding.localized/Go/minotaur", func(path string, info fs.FileInfo, err error) error {
filepath.Walk("D:/sources/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"), ".") {
if strings.Contains(strings.TrimPrefix(path, "D:/sources/minotaur"), ".") {
return nil
}
b, err := New(

View File

@ -27,42 +27,43 @@ generic 目的在于提供一组基于泛型的用于处理通用功能的函数
|类型|名称|描述
|:--|:--|:--
|`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)|基本类型
|`INTERFACE`|[IdR](#struct_IdR)|暂无描述...
|`INTERFACE`|[IDR](#struct_IDR)|暂无描述...
|`INTERFACE`|[IdW](#struct_IdW)|暂无描述...
|`INTERFACE`|[IDW](#struct_IDW)|暂无描述...
|`INTERFACE`|[IdR2W](#struct_IdR2W)|暂无描述...
|`INTERFACE`|[IDR2W](#struct_IDR2W)|暂无描述...
|`INTERFACE`|[Ordered](#struct_Ordered)|可排序类型
|`INTERFACE`|[Number](#struct_Number)|数字类型
|`INTERFACE`|[SignedNumber](#struct_SignedNumber)|有符号数字类型
|`INTERFACE`|[Integer](#struct_Integer)|整数类型
|`INTERFACE`|[Signed](#struct_Signed)|有符号整数类型
|`INTERFACE`|[Unsigned](#struct_Unsigned)|无符号整数类型
|`INTERFACE`|[UnsignedNumber](#struct_UnsignedNumber)|无符号数字类型
|`INTERFACE`|[Float](#struct_Float)|浮点类型
|`INTERFACE`|[Basic](#struct_Basic)|基本类型
</details>
***
## 详情信息
#### func IsNil(v V) bool
#### func IsNil\[V any\](v V) bool
<span id="IsNil"></span>
> 检查指定的值是否为 nil
***
#### func IsAllNil(v ...V) bool
#### func IsAllNil\[V any\](v ...V) bool
<span id="IsAllNil"></span>
> 检查指定的值是否全部为 nil
***
#### func IsHasNil(v ...V) bool
#### func IsHasNil\[V any\](v ...V) bool
<span id="IsHasNil"></span>
> 检查指定的值是否存在 nil
***
<span id="struct_IdR"></span>
### IdR `INTERFACE`
```go
@ -70,6 +71,7 @@ type IdR[ID comparable] interface {
GetId() ID
}
```
<span id="struct_IDR"></span>
### IDR `INTERFACE`
```go
@ -77,6 +79,7 @@ type IDR[ID comparable] interface {
GetID() ID
}
```
<span id="struct_IdW"></span>
### IdW `INTERFACE`
```go
@ -84,6 +87,7 @@ type IdW[ID comparable] interface {
SetId(id ID)
}
```
<span id="struct_IDW"></span>
### IDW `INTERFACE`
```go
@ -91,6 +95,7 @@ type IDW[ID comparable] interface {
SetID(id ID)
}
```
<span id="struct_IdR2W"></span>
### IdR2W `INTERFACE`
```go
@ -99,6 +104,7 @@ type IdR2W[ID comparable] interface {
IdW[ID]
}
```
<span id="struct_IDR2W"></span>
### IDR2W `INTERFACE`
```go
@ -107,6 +113,7 @@ type IDR2W[ID comparable] interface {
IDW[ID]
}
```
<span id="struct_Ordered"></span>
### Ordered `INTERFACE`
可排序类型
```go
@ -114,6 +121,7 @@ type Ordered interface {
Integer | Float | ~string
}
```
<span id="struct_Number"></span>
### Number `INTERFACE`
数字类型
```go
@ -121,6 +129,7 @@ type Number interface {
Integer | Float
}
```
<span id="struct_SignedNumber"></span>
### SignedNumber `INTERFACE`
有符号数字类型
```go
@ -128,6 +137,7 @@ type SignedNumber interface {
Signed | Float
}
```
<span id="struct_Integer"></span>
### Integer `INTERFACE`
整数类型
```go
@ -135,6 +145,7 @@ type Integer interface {
Signed | Unsigned
}
```
<span id="struct_Signed"></span>
### Signed `INTERFACE`
有符号整数类型
```go
@ -142,6 +153,7 @@ type Signed interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}
```
<span id="struct_Unsigned"></span>
### Unsigned `INTERFACE`
无符号整数类型
```go
@ -149,6 +161,7 @@ type Unsigned interface {
UnsignedNumber | ~uintptr
}
```
<span id="struct_UnsignedNumber"></span>
### UnsignedNumber `INTERFACE`
无符号数字类型
```go
@ -156,6 +169,7 @@ type UnsignedNumber interface {
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}
```
<span id="struct_Float"></span>
### Float `INTERFACE`
浮点类型
```go
@ -163,6 +177,7 @@ type Float interface {
~float32 | ~float64
}
```
<span id="struct_Basic"></span>
### Basic `INTERFACE`
基本类型
```go

View File

@ -117,26 +117,27 @@ geometry 旨在提供一组用于处理几何形状和计算几何属性的函
|类型|名称|描述
|:--|:--|:--
|`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 搜索支持
|`STRUCT`|[Circle](#struct_Circle)|圆形
|`STRUCT`|[FloorPlan](#struct_FloorPlan)|平面图
|`STRUCT`|[Direction](#struct_Direction)|方向
|`STRUCT`|[LineSegment](#struct_LineSegment)|通过两个点表示一根线段
|`STRUCT`|[LineSegmentCap](#struct_LineSegmentCap)|可以包含一份额外数据的线段
|`STRUCT`|[Point](#struct_Point)|表示了一个由 x、y 坐标组成的点
|`STRUCT`|[PointCap](#struct_PointCap)|表示了一个由 x、y 坐标组成的点,这个点具有一个数据容量
|`STRUCT`|[Shape](#struct_Shape)|通过多个点表示了一个形状
|`STRUCT`|[ShapeSearchOption](#struct_ShapeSearchOption)|图形搜索可选项,用于 Shape.ShapeSearch 搜索支持
</details>
***
## 详情信息
#### func NewCircle(radius V, points int) Circle[V]
#### func NewCircle\[V generic.SignedNumber\](radius V, points int) Circle[V]
<span id="NewCircle"></span>
> 通过传入圆的半径和需要的点数量,生成一个圆
示例代码:
**示例代码:**
```go
func ExampleNewCircle() {
@ -146,143 +147,143 @@ func ExampleNewCircle() {
```
***
#### func CalcCircleCentroidDistance(circle1 Circle[V], circle2 Circle[V]) V
#### func CalcCircleCentroidDistance\[V generic.SignedNumber\](circle1 Circle[V], circle2 Circle[V]) V
<span id="CalcCircleCentroidDistance"></span>
> 计算两个圆质心距离
***
#### func GetOppositionDirection(direction Direction) Direction
#### func GetOppositionDirection(direction Direction) Direction
<span id="GetOppositionDirection"></span>
> 获取特定方向的对立方向
***
#### func GetDirectionNextWithCoordinate(direction Direction, x V, y V) (nx V, ny V)
#### func GetDirectionNextWithCoordinate\[V generic.SignedNumber\](direction Direction, x V, y V) (nx V, ny V)
<span id="GetDirectionNextWithCoordinate"></span>
> 获取特定方向上的下一个坐标
***
#### func GetDirectionNextWithPoint(direction Direction, point Point[V]) Point[V]
#### func GetDirectionNextWithPoint\[V generic.SignedNumber\](direction Direction, point Point[V]) Point[V]
<span id="GetDirectionNextWithPoint"></span>
> 获取特定方向上的下一个坐标
***
#### func GetDirectionNextWithPos(direction Direction, width V, pos V) V
#### func GetDirectionNextWithPos\[V generic.SignedNumber\](direction Direction, width V, pos V) V
<span id="GetDirectionNextWithPos"></span>
> 获取位置在特定宽度和特定方向上的下一个位置
> - 需要注意的是,在左右方向时,当下一个位置不在矩形区域内时,将会返回上一行的末位置或下一行的首位置
***
#### func CalcDirection(x1 V, y1 V, x2 V, y2 V) Direction
#### func CalcDirection\[V generic.SignedNumber\](x1 V, y1 V, x2 V, y2 V) Direction
<span id="CalcDirection"></span>
> 计算点2位于点1的方向
***
#### func CalcDistanceWithCoordinate(x1 V, y1 V, x2 V, y2 V) V
#### func CalcDistanceWithCoordinate\[V generic.SignedNumber\](x1 V, y1 V, x2 V, y2 V) V
<span id="CalcDistanceWithCoordinate"></span>
> 计算两点之间的距离
***
#### func CalcDistanceWithPoint(point1 Point[V], point2 Point[V]) V
#### func CalcDistanceWithPoint\[V generic.SignedNumber\](point1 Point[V], point2 Point[V]) V
<span id="CalcDistanceWithPoint"></span>
> 计算两点之间的距离
***
#### func CalcDistanceSquared(x1 V, y1 V, x2 V, y2 V) V
#### func CalcDistanceSquared\[V generic.SignedNumber\](x1 V, y1 V, x2 V, y2 V) V
<span id="CalcDistanceSquared"></span>
> 计算两点之间的平方距离
> - 这个函数的主要用途是在需要计算两点之间距离的情况下,但不需要得到实际的距离值,而只需要比较距离大小。因为平方根运算相对较为耗时,所以在只需要比较大小的情况下,通常会使用平方距离。
***
#### func CalcAngle(x1 V, y1 V, x2 V, y2 V) V
#### func CalcAngle\[V generic.SignedNumber\](x1 V, y1 V, x2 V, y2 V) V
<span id="CalcAngle"></span>
> 计算点2位于点1之间的角度
***
#### func CalcNewCoordinate(x V, y V, angle V, distance V) (newX V, newY V)
#### func CalcNewCoordinate\[V generic.SignedNumber\](x V, y V, angle V, distance V) (newX V, newY V)
<span id="CalcNewCoordinate"></span>
> 根据给定的x、y坐标、角度和距离计算新的坐标
***
#### func CalcRadianWithAngle(angle V) V
#### func CalcRadianWithAngle\[V generic.SignedNumber\](angle V) V
<span id="CalcRadianWithAngle"></span>
> 根据角度 angle 计算弧度
***
#### func CalcAngleDifference(angleA V, angleB V) V
#### func CalcAngleDifference\[V generic.SignedNumber\](angleA V, angleB V) V
<span id="CalcAngleDifference"></span>
> 计算两个角度之间的最小角度差
***
#### func CalcRayIsIntersect(x V, y V, angle V, shape Shape[V]) bool
#### func CalcRayIsIntersect\[V generic.SignedNumber\](x V, y V, angle V, shape Shape[V]) bool
<span id="CalcRayIsIntersect"></span>
> 根据给定的位置和角度生成射线,检测射线是否与多边形发生碰撞
***
#### func NewLineSegment(start Point[V], end Point[V]) LineSegment[V]
#### func NewLineSegment\[V generic.SignedNumber\](start Point[V], end Point[V]) LineSegment[V]
<span id="NewLineSegment"></span>
> 创建一根线段
***
#### func NewLineSegmentCap(start Point[V], end Point[V], data Data) LineSegmentCap[V, Data]
#### func NewLineSegmentCap\[V generic.SignedNumber, Data any\](start Point[V], end Point[V], data Data) LineSegmentCap[V, Data]
<span id="NewLineSegmentCap"></span>
> 创建一根包含数据的线段
***
#### func NewLineSegmentCapWithLine(line LineSegment[V], data Data) LineSegmentCap[V, Data]
#### func NewLineSegmentCapWithLine\[V generic.SignedNumber, Data any\](line LineSegment[V], data Data) LineSegmentCap[V, Data]
<span id="NewLineSegmentCapWithLine"></span>
> 通过已有线段创建一根包含数据的线段
***
#### func ConvertLineSegmentGeneric(line LineSegment[V]) LineSegment[TO]
#### func ConvertLineSegmentGeneric\[V generic.SignedNumber, TO generic.SignedNumber\](line LineSegment[V]) LineSegment[TO]
<span id="ConvertLineSegmentGeneric"></span>
> 转换线段的泛型类型为特定类型
***
#### func PointOnLineSegmentWithCoordinate(x1 V, y1 V, x2 V, y2 V, x V, y V) bool
#### func PointOnLineSegmentWithCoordinate\[V generic.SignedNumber\](x1 V, y1 V, x2 V, y2 V, x V, y V) bool
<span id="PointOnLineSegmentWithCoordinate"></span>
> 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
***
#### func PointOnLineSegmentWithPos(width V, pos1 V, pos2 V, pos V) bool
#### func PointOnLineSegmentWithPos\[V generic.SignedNumber\](width V, pos1 V, pos2 V, pos V) bool
<span id="PointOnLineSegmentWithPos"></span>
> 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
***
#### func PointOnLineSegmentWithPoint(point1 Point[V], point2 Point[V], point Point[V]) bool
#### func PointOnLineSegmentWithPoint\[V generic.SignedNumber\](point1 Point[V], point2 Point[V], point Point[V]) bool
<span id="PointOnLineSegmentWithPoint"></span>
> 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
***
#### func PointOnLineSegmentWithCoordinateInBounds(x1 V, y1 V, x2 V, y2 V, x V, y V) bool
#### func PointOnLineSegmentWithCoordinateInBounds\[V generic.SignedNumber\](x1 V, y1 V, x2 V, y2 V, x V, y V) bool
<span id="PointOnLineSegmentWithCoordinateInBounds"></span>
> 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
> - 与 PointOnLineSegmentWithCoordinate 不同的是, PointOnLineSegmentWithCoordinateInBounds 中会判断线段及点的位置是否正确
***
#### func PointOnLineSegmentWithPosInBounds(width V, pos1 V, pos2 V, pos V) bool
#### func PointOnLineSegmentWithPosInBounds\[V generic.SignedNumber\](width V, pos1 V, pos2 V, pos V) bool
<span id="PointOnLineSegmentWithPosInBounds"></span>
> 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
> - 与 PointOnLineSegmentWithPos 不同的是, PointOnLineSegmentWithPosInBounds 中会判断线段及点的位置是否正确
***
#### func PointOnLineSegmentWithPointInBounds(point1 Point[V], point2 Point[V], point Point[V]) bool
#### func PointOnLineSegmentWithPointInBounds\[V generic.SignedNumber\](point1 Point[V], point2 Point[V], point Point[V]) bool
<span id="PointOnLineSegmentWithPointInBounds"></span>
> 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上
> - 与 PointOnLineSegmentWithPoint 不同的是, PointOnLineSegmentWithPointInBounds 中会判断线段及点的位置是否正确
***
#### func CalcLineSegmentIsCollinear(line1 LineSegment[V], line2 LineSegment[V], tolerance V) bool
#### func CalcLineSegmentIsCollinear\[V generic.SignedNumber\](line1 LineSegment[V], line2 LineSegment[V], tolerance V) bool
<span id="CalcLineSegmentIsCollinear"></span>
> 检查两条线段在一个误差内是否共线
> - 共线是指两条线段在同一直线上,即它们的延长线可以重合
***
#### func CalcLineSegmentIsOverlap(line1 LineSegment[V], line2 LineSegment[V]) (line LineSegment[V], overlap bool)
#### func CalcLineSegmentIsOverlap\[V generic.SignedNumber\](line1 LineSegment[V], line2 LineSegment[V]) (line LineSegment[V], overlap bool)
<span id="CalcLineSegmentIsOverlap"></span>
> 通过对点进行排序来检查两条共线线段是否重叠,返回重叠线段
***
#### func CalcLineSegmentIsIntersect(line1 LineSegment[V], line2 LineSegment[V]) bool
#### func CalcLineSegmentIsIntersect\[V generic.SignedNumber\](line1 LineSegment[V], line2 LineSegment[V]) bool
<span id="CalcLineSegmentIsIntersect"></span>
> 计算两条线段是否相交
@ -305,17 +306,17 @@ func TestCalcLineSegmentIsIntersect(t *testing.T) {
***
#### func CalcLineSegmentSlope(line LineSegment[V]) V
#### func CalcLineSegmentSlope\[V generic.SignedNumber\](line LineSegment[V]) V
<span id="CalcLineSegmentSlope"></span>
> 计算线段的斜率
***
#### func CalcLineSegmentIntercept(line LineSegment[V]) V
#### func CalcLineSegmentIntercept\[V generic.SignedNumber\](line LineSegment[V]) V
<span id="CalcLineSegmentIntercept"></span>
> 计算线段的截距
***
#### func NewPoint(x V, y V) Point[V]
#### func NewPoint\[V generic.SignedNumber\](x V, y V) Point[V]
<span id="NewPoint"></span>
> 创建一个由 x、y 坐标组成的点
@ -337,150 +338,151 @@ func TestNewPoint(t *testing.T) {
***
#### func NewPointCap(x V, y V) PointCap[V, D]
#### func NewPointCap\[V generic.SignedNumber, D any\](x V, y V) PointCap[V, D]
<span id="NewPointCap"></span>
> 创建一个由 x、y 坐标组成的点,这个点具有一个数据容量
***
#### func NewPointCapWithData(x V, y V, data D) PointCap[V, D]
#### func NewPointCapWithData\[V generic.SignedNumber, D any\](x V, y V, data D) PointCap[V, D]
<span id="NewPointCapWithData"></span>
> 通过设置数据的方式创建一个由 x、y 坐标组成的点,这个点具有一个数据容量
***
#### func NewPointCapWithPoint(point Point[V], data D) PointCap[V, D]
#### func NewPointCapWithPoint\[V generic.SignedNumber, D any\](point Point[V], data D) PointCap[V, D]
<span id="NewPointCapWithPoint"></span>
> 通过设置数据的方式创建一个由已有坐标组成的点,这个点具有一个数据容量
***
#### func CoordinateToPoint(x V, y V) Point[V]
#### func CoordinateToPoint\[V generic.SignedNumber\](x V, y V) Point[V]
<span id="CoordinateToPoint"></span>
> 将坐标转换为x、y的坐标数组
***
#### func CoordinateToPos(width V, x V, y V) V
#### func CoordinateToPos\[V generic.SignedNumber\](width V, x V, y V) V
<span id="CoordinateToPos"></span>
> 将坐标转换为二维数组的顺序位置坐标
> - 需要确保x的取值范围必须小于width或者将会得到不正确的值
***
#### func PointToCoordinate(position Point[V]) (x V, y V)
#### func PointToCoordinate\[V generic.SignedNumber\](position Point[V]) (x V, y V)
<span id="PointToCoordinate"></span>
> 将坐标数组转换为x和y坐标
***
#### func PointToPos(width V, xy Point[V]) V
#### func PointToPos\[V generic.SignedNumber\](width V, xy Point[V]) V
<span id="PointToPos"></span>
> 将坐标转换为二维数组的顺序位置
> - 需要确保x的取值范围必须小于width或者将会得到不正确的值
***
#### func PosToCoordinate(width V, pos V) (x V, y V)
#### func PosToCoordinate\[V generic.SignedNumber\](width V, pos V) (x V, y V)
<span id="PosToCoordinate"></span>
> 通过宽度将一个二维数组的顺序位置转换为xy坐标
***
#### func PosToPoint(width V, pos V) Point[V]
#### func PosToPoint\[V generic.SignedNumber\](width V, pos V) Point[V]
<span id="PosToPoint"></span>
> 通过宽度将一个二维数组的顺序位置转换为x、y的坐标数组
***
#### func PosToCoordinateX(width V, pos V) V
#### func PosToCoordinateX\[V generic.SignedNumber\](width V, pos V) V
<span id="PosToCoordinateX"></span>
> 通过宽度将一个二维数组的顺序位置转换为X坐标
***
#### func PosToCoordinateY(width V, pos V) V
#### func PosToCoordinateY\[V generic.SignedNumber\](width V, pos V) V
<span id="PosToCoordinateY"></span>
> 通过宽度将一个二维数组的顺序位置转换为Y坐标
***
#### func PointCopy(point Point[V]) Point[V]
#### func PointCopy\[V generic.SignedNumber\](point Point[V]) Point[V]
<span id="PointCopy"></span>
> 复制一个坐标数组
***
#### func PointToPosWithMulti(width V, points ...Point[V]) []V
#### func PointToPosWithMulti\[V generic.SignedNumber\](width V, points ...Point[V]) []V
<span id="PointToPosWithMulti"></span>
> 将一组坐标转换为二维数组的顺序位置
> - 需要确保x的取值范围必须小于width或者将会得到不正确的值
***
#### func PosToPointWithMulti(width V, positions ...V) []Point[V]
#### func PosToPointWithMulti\[V generic.SignedNumber\](width V, positions ...V) []Point[V]
<span id="PosToPointWithMulti"></span>
> 将一组二维数组的顺序位置转换为一组数组坐标
***
#### func PosSameRow(width V, pos1 V, pos2 V) bool
#### func PosSameRow\[V generic.SignedNumber\](width V, pos1 V, pos2 V) bool
<span id="PosSameRow"></span>
> 返回两个顺序位置在同一宽度是否位于同一行
***
#### func DoublePointToCoordinate(point1 Point[V], point2 Point[V]) (x1 V, y1 V, x2 V, y2 V)
#### func DoublePointToCoordinate\[V generic.SignedNumber\](point1 Point[V], point2 Point[V]) (x1 V, y1 V, x2 V, y2 V)
<span id="DoublePointToCoordinate"></span>
> 将两个位置转换为 x1, y1, x2, y2 的坐标进行返回
***
#### func CalcProjectionPoint(line LineSegment[V], point Point[V]) Point[V]
#### func CalcProjectionPoint\[V generic.SignedNumber\](line LineSegment[V], point Point[V]) Point[V]
<span id="CalcProjectionPoint"></span>
> 计算一个点到一条线段的最近点(即投影点)的。这个函数接收一个点和一条线段作为输入,线段由两个端点组成。
> - 该函数的主要用于需要计算一个点到一条线段的最近点的情况下
***
#### func GetAdjacentTranslatePos(matrix []T, width P, pos P) (result []P)
#### func GetAdjacentTranslatePos\[T any, P generic.SignedNumber\](matrix []T, width P, pos P) (result []P)
<span id="GetAdjacentTranslatePos"></span>
> 获取一个连续位置的矩阵中,特定位置相邻的最多四个平移方向(上下左右)的位置
***
#### func GetAdjacentTranslateCoordinateXY(matrix [][]T, x P, y P) (result []Point[P])
#### func GetAdjacentTranslateCoordinateXY\[T any, P generic.SignedNumber\](matrix [][]T, x P, y P) (result []Point[P])
<span id="GetAdjacentTranslateCoordinateXY"></span>
> 获取一个基于 x、y 的二维矩阵中,特定位置相邻的最多四个平移方向(上下左右)的位置
***
#### func GetAdjacentTranslateCoordinateYX(matrix [][]T, x P, y P) (result []Point[P])
#### func GetAdjacentTranslateCoordinateYX\[T any, P generic.SignedNumber\](matrix [][]T, x P, y P) (result []Point[P])
<span id="GetAdjacentTranslateCoordinateYX"></span>
> 获取一个基于 y、x 的二维矩阵中,特定位置相邻的最多四个平移方向(上下左右)的位置
***
#### func GetAdjacentDiagonalsPos(matrix []T, width P, pos P) (result []P)
#### func GetAdjacentDiagonalsPos\[T any, P generic.SignedNumber\](matrix []T, width P, pos P) (result []P)
<span id="GetAdjacentDiagonalsPos"></span>
> 获取一个连续位置的矩阵中,特定位置相邻的对角线最多四个方向的位置
***
#### func GetAdjacentDiagonalsCoordinateXY(matrix [][]T, x P, y P) (result []Point[P])
#### func GetAdjacentDiagonalsCoordinateXY\[T any, P generic.SignedNumber\](matrix [][]T, x P, y P) (result []Point[P])
<span id="GetAdjacentDiagonalsCoordinateXY"></span>
> 获取一个基于 x、y 的二维矩阵中,特定位置相邻的对角线最多四个方向的位置
***
#### func GetAdjacentDiagonalsCoordinateYX(matrix [][]T, x P, y P) (result []Point[P])
#### func GetAdjacentDiagonalsCoordinateYX\[T any, P generic.SignedNumber\](matrix [][]T, x P, y P) (result []Point[P])
<span id="GetAdjacentDiagonalsCoordinateYX"></span>
> 获取一个基于 tx 的二维矩阵中,特定位置相邻的对角线最多四个方向的位置
***
#### func GetAdjacentPos(matrix []T, width P, pos P) (result []P)
#### func GetAdjacentPos\[T any, P generic.SignedNumber\](matrix []T, width P, pos P) (result []P)
<span id="GetAdjacentPos"></span>
> 获取一个连续位置的矩阵中,特定位置相邻的最多八个方向的位置
***
#### func GetAdjacentCoordinateXY(matrix [][]T, x P, y P) (result []Point[P])
#### func GetAdjacentCoordinateXY\[T any, P generic.SignedNumber\](matrix [][]T, x P, y P) (result []Point[P])
<span id="GetAdjacentCoordinateXY"></span>
> 获取一个基于 x、y 的二维矩阵中,特定位置相邻的最多八个方向的位置
***
#### func GetAdjacentCoordinateYX(matrix [][]T, x P, y P) (result []Point[P])
#### func GetAdjacentCoordinateYX\[T any, P generic.SignedNumber\](matrix [][]T, x P, y P) (result []Point[P])
<span id="GetAdjacentCoordinateYX"></span>
> 获取一个基于 yx 的二维矩阵中,特定位置相邻的最多八个方向的位置
***
#### func CoordinateMatrixToPosMatrix(matrix [][]V) (width int, posMatrix []V)
#### func CoordinateMatrixToPosMatrix\[V any\](matrix [][]V) (width int, posMatrix []V)
<span id="CoordinateMatrixToPosMatrix"></span>
> 将二维矩阵转换为顺序的二维矩阵
***
#### func GetShapeCoverageAreaWithPoint(points ...Point[V]) (left V, right V, top V, bottom V)
#### func GetShapeCoverageAreaWithPoint\[V generic.SignedNumber\](points ...Point[V]) (left V, right V, top V, bottom V)
<span id="GetShapeCoverageAreaWithPoint"></span>
> 通过传入的一组坐标 points 计算一个图形覆盖的矩形范围
示例代码:
**示例代码:**
```go
func ExampleGetShapeCoverageAreaWithPoint() {
@ -521,11 +523,12 @@ func TestGetShapeCoverageAreaWithPoint(t *testing.T) {
***
#### func GetShapeCoverageAreaWithPos(width V, positions ...V) (left V, right V, top V, bottom V)
#### func GetShapeCoverageAreaWithPos\[V generic.SignedNumber\](width V, positions ...V) (left V, right V, top V, bottom V)
<span id="GetShapeCoverageAreaWithPos"></span>
> 通过传入的一组坐标 positions 计算一个图形覆盖的矩形范围
示例代码:
**示例代码:**
```go
func ExampleGetShapeCoverageAreaWithPos() {
@ -558,12 +561,13 @@ func TestGetShapeCoverageAreaWithPos(t *testing.T) {
***
#### func CoverageAreaBoundless(l V, r V, t V, b V) (left V, right V, top V, bottom V)
#### func CoverageAreaBoundless\[V generic.SignedNumber\](l V, r V, t V, b V) (left V, right V, top V, bottom V)
<span id="CoverageAreaBoundless"></span>
> 将一个图形覆盖矩形范围设置为无边的
> - 无边化表示会将多余的部分进行裁剪,例如图形左边从 2 开始的时候,那么左边将会被裁剪到从 0 开始
示例代码:
**示例代码:**
```go
func ExampleCoverageAreaBoundless() {
@ -596,7 +600,7 @@ func TestCoverageAreaBoundless(t *testing.T) {
***
#### func GenerateShapeOnRectangle(points ...Point[V]) (result []PointCap[V, bool])
#### func GenerateShapeOnRectangle\[V generic.SignedNumber\](points ...Point[V]) (result []PointCap[V, bool])
<span id="GenerateShapeOnRectangle"></span>
> 生成一组二维坐标的形状
> - 这个形状将被在一个刚好能容纳形状的矩形中表示
@ -638,44 +642,44 @@ func TestGenerateShapeOnRectangle(t *testing.T) {
***
#### func GenerateShapeOnRectangleWithCoordinate(points ...Point[V]) (result [][]bool)
#### func GenerateShapeOnRectangleWithCoordinate\[V generic.SignedNumber\](points ...Point[V]) (result [][]bool)
<span id="GenerateShapeOnRectangleWithCoordinate"></span>
> 生成一组二维坐标的形状
> - 这个形状将被在一个刚好能容纳形状的矩形中表示
> - 为 true 的位置表示了形状的每一个点
***
#### func GetExpressibleRectangleBySize(width V, height V, minWidth V, minHeight V) (result []Point[V])
#### func GetExpressibleRectangleBySize\[V generic.SignedNumber\](width V, height V, minWidth V, minHeight V) (result []Point[V])
<span id="GetExpressibleRectangleBySize"></span>
> 获取一个宽高可表达的所有特定尺寸以上的矩形形状
> - 返回值表示了每一个矩形右下角的x,y位置左上角始终为0, 0
> - 矩形尺寸由大到小
***
#### func GetExpressibleRectangle(width V, height V) (result []Point[V])
#### func GetExpressibleRectangle\[V generic.SignedNumber\](width V, height V) (result []Point[V])
<span id="GetExpressibleRectangle"></span>
> 获取一个宽高可表达的所有矩形形状
> - 返回值表示了每一个矩形右下角的x,y位置左上角始终为0, 0
> - 矩形尺寸由大到小
***
#### func GetRectangleFullPointsByXY(startX V, startY V, endX V, endY V) (result []Point[V])
#### func GetRectangleFullPointsByXY\[V generic.SignedNumber\](startX V, startY V, endX V, endY V) (result []Point[V])
<span id="GetRectangleFullPointsByXY"></span>
> 通过开始结束坐标获取一个矩形包含的所有点
> - 例如 1,1 到 2,2 的矩形结果为 1,1 2,1 1,2 2,2
***
#### func GetRectangleFullPoints(width V, height V) (result []Point[V])
#### func GetRectangleFullPoints\[V generic.SignedNumber\](width V, height V) (result []Point[V])
<span id="GetRectangleFullPoints"></span>
> 获取一个矩形填充满后包含的所有点
***
#### func GetRectangleFullPos(width V, height V) (result []V)
#### func GetRectangleFullPos\[V generic.SignedNumber\](width V, height V) (result []V)
<span id="GetRectangleFullPos"></span>
> 获取一个矩形填充满后包含的所有位置
***
#### func CalcRectangleCentroid(shape Shape[V]) Point[V]
#### func CalcRectangleCentroid\[V generic.SignedNumber\](shape Shape[V]) Point[V]
<span id="CalcRectangleCentroid"></span>
> 计算矩形质心
> - 非多边形质心计算,仅为顶点的平均值 - 该区域中多边形因子的适当质心
@ -691,11 +695,12 @@ func TestGenerateShapeOnRectangle(t *testing.T) {
> 设置 Shape.String 是没有边界的
***
#### func NewShape(points ...Point[V]) Shape[V]
#### func NewShape\[V generic.SignedNumber\](points ...Point[V]) Shape[V]
<span id="NewShape"></span>
> 通过多个点生成一个形状进行返回
示例代码:
**示例代码:**
```go
func ExampleNewShape() {
@ -733,12 +738,13 @@ func TestNewShape(t *testing.T) {
***
#### func NewShapeWithString(rows []string, point rune) (shape Shape[V])
#### func NewShapeWithString\[V generic.SignedNumber\](rows []string, point rune) (shape Shape[V])
<span id="NewShapeWithString"></span>
> 通过字符串将指定 rune 转换为点位置生成形状进行返回
> - 每个点的顺序从上到下,从左到右
示例代码:
**示例代码:**
```go
func ExampleNewShapeWithString() {
@ -775,95 +781,96 @@ func TestNewShapeWithString(t *testing.T) {
***
#### func CalcBoundingRadius(shape Shape[V]) V
#### func CalcBoundingRadius\[V generic.SignedNumber\](shape Shape[V]) V
<span id="CalcBoundingRadius"></span>
> 计算多边形转换为圆的半径
***
#### func CalcBoundingRadiusWithCentroid(shape Shape[V], centroid Point[V]) V
#### func CalcBoundingRadiusWithCentroid\[V generic.SignedNumber\](shape Shape[V], centroid Point[V]) V
<span id="CalcBoundingRadiusWithCentroid"></span>
> 计算多边形在特定质心下圆的半径
***
#### func CalcTriangleTwiceArea(a Point[V], b Point[V], c Point[V]) V
#### func CalcTriangleTwiceArea\[V generic.SignedNumber\](a Point[V], b Point[V], c Point[V]) V
<span id="CalcTriangleTwiceArea"></span>
> 计算由 a、b、c 三个点组成的三角形的面积的两倍
***
#### func IsPointOnEdge(edges []LineSegment[V], point Point[V]) bool
#### func IsPointOnEdge\[V generic.SignedNumber\](edges []LineSegment[V], point Point[V]) bool
<span id="IsPointOnEdge"></span>
> 检查点是否在 edges 的任意一条边上
***
#### func ProjectionPointToShape(point Point[V], shape Shape[V]) Point[V], V
#### func ProjectionPointToShape\[V generic.SignedNumber\](point Point[V], shape Shape[V]) (Point[V], V)
<span id="ProjectionPointToShape"></span>
> 将一个点投影到一个多边形上,找到离该点最近的投影点,并返回投影点和距离
***
#### func WithShapeSearchRectangleLowerLimit(minWidth int, minHeight int) ShapeSearchOption
#### func WithShapeSearchRectangleLowerLimit(minWidth int, minHeight int) ShapeSearchOption
<span id="WithShapeSearchRectangleLowerLimit"></span>
> 通过矩形宽高下限的方式搜索
***
#### func WithShapeSearchRectangleUpperLimit(maxWidth int, maxHeight int) ShapeSearchOption
#### func WithShapeSearchRectangleUpperLimit(maxWidth int, maxHeight int) ShapeSearchOption
<span id="WithShapeSearchRectangleUpperLimit"></span>
> 通过矩形宽高上限的方式搜索
***
#### func WithShapeSearchRightAngle() ShapeSearchOption
#### func WithShapeSearchRightAngle() ShapeSearchOption
<span id="WithShapeSearchRightAngle"></span>
> 通过直角的方式进行搜索
***
#### func WithShapeSearchOppositionDirection(direction Direction) ShapeSearchOption
#### func WithShapeSearchOppositionDirection(direction Direction) ShapeSearchOption
<span id="WithShapeSearchOppositionDirection"></span>
> 通过限制对立方向的方式搜索
> - 对立方向例如上不能与下共存
***
#### func WithShapeSearchDirectionCount(count int) ShapeSearchOption
#### func WithShapeSearchDirectionCount(count int) ShapeSearchOption
<span id="WithShapeSearchDirectionCount"></span>
> 通过限制方向数量的方式搜索
***
#### func WithShapeSearchDirectionCountLowerLimit(direction Direction, count int) ShapeSearchOption
#### func WithShapeSearchDirectionCountLowerLimit(direction Direction, count int) ShapeSearchOption
<span id="WithShapeSearchDirectionCountLowerLimit"></span>
> 通过限制特定方向数量下限的方式搜索
***
#### func WithShapeSearchDirectionCountUpperLimit(direction Direction, count int) ShapeSearchOption
#### func WithShapeSearchDirectionCountUpperLimit(direction Direction, count int) ShapeSearchOption
<span id="WithShapeSearchDirectionCountUpperLimit"></span>
> 通过限制特定方向数量上限的方式搜索
***
#### func WithShapeSearchDeduplication() ShapeSearchOption
#### func WithShapeSearchDeduplication() ShapeSearchOption
<span id="WithShapeSearchDeduplication"></span>
> 通过去重的方式进行搜索
> - 去重方式中每个点仅会被使用一次
***
#### func WithShapeSearchPointCountLowerLimit(lowerLimit int) ShapeSearchOption
#### func WithShapeSearchPointCountLowerLimit(lowerLimit int) ShapeSearchOption
<span id="WithShapeSearchPointCountLowerLimit"></span>
> 通过限制图形构成的最小点数进行搜索
> - 当搜索到的图形的点数量低于 lowerLimit 时,将被忽略
***
#### func WithShapeSearchPointCountUpperLimit(upperLimit int) ShapeSearchOption
#### func WithShapeSearchPointCountUpperLimit(upperLimit int) ShapeSearchOption
<span id="WithShapeSearchPointCountUpperLimit"></span>
> 通过限制图形构成的最大点数进行搜索
> - 当搜索到的图形的点数量大于 upperLimit 时,将被忽略
***
#### func WithShapeSearchAsc() ShapeSearchOption
#### func WithShapeSearchAsc() ShapeSearchOption
<span id="WithShapeSearchAsc"></span>
> 通过升序的方式进行搜索
***
#### func WithShapeSearchDesc() ShapeSearchOption
#### func WithShapeSearchDesc() ShapeSearchOption
<span id="WithShapeSearchDesc"></span>
> 通过降序的方式进行搜索
***
<span id="struct_Circle"></span>
### Circle `STRUCT`
圆形
```go
@ -871,63 +878,109 @@ type Circle[V generic.SignedNumber] struct {
Shape[V]
}
```
<span id="struct_Circle_Radius"></span>
#### func (Circle) Radius() V
> 获取圆形半径
***
<span id="struct_Circle_Centroid"></span>
#### func (Circle) Centroid() Point[V]
> 获取圆形质心位置
***
<span id="struct_Circle_Overlap"></span>
#### func (Circle) Overlap(circle Circle[V]) bool
> 与另一个圆是否发生重叠
***
<span id="struct_Circle_Area"></span>
#### func (Circle) Area() V
> 获取圆形面积
***
<span id="struct_Circle_Length"></span>
#### func (Circle) Length() V
> 获取圆的周长
***
<span id="struct_Circle_CentroidDistance"></span>
#### func (Circle) CentroidDistance(circle Circle[V]) V
> 计算与另一个圆的质心距离
***
<span id="struct_FloorPlan"></span>
### FloorPlan `STRUCT`
平面图
```go
type FloorPlan []string
```
<span id="struct_FloorPlan_IsFree"></span>
#### func (FloorPlan) IsFree(point Point[int]) bool
> 检查位置是否为空格
***
<span id="struct_FloorPlan_IsInBounds"></span>
#### func (FloorPlan) IsInBounds(point Point[int]) bool
> 检查位置是否在边界内
***
<span id="struct_FloorPlan_Put"></span>
#### func (FloorPlan) Put(point Point[int], c rune)
> 设置平面图特定位置的字符
***
<span id="struct_FloorPlan_String"></span>
#### func (FloorPlan) String() string
> 获取平面图结果
***
<span id="struct_Direction"></span>
### Direction `STRUCT`
方向
```go
type Direction uint8
```
<span id="struct_LineSegment"></span>
### LineSegment `STRUCT`
通过两个点表示一根线段
```go
type LineSegment[V generic.SignedNumber] [2]Point[V]
```
<span id="struct_LineSegment_GetPoints"></span>
#### func (LineSegment) GetPoints() [2]Point[V]
> 获取该线段的两个点
***
<span id="struct_LineSegment_GetStart"></span>
#### func (LineSegment) GetStart() Point[V]
> 获取该线段的开始位置
***
<span id="struct_LineSegment_GetEnd"></span>
#### func (LineSegment) GetEnd() Point[V]
> 获取该线段的结束位置
***
<span id="struct_LineSegment_GetLength"></span>
#### func (LineSegment) GetLength() V
> 获取该线段的长度
***
<span id="struct_LineSegmentCap"></span>
### LineSegmentCap `STRUCT`
可以包含一份额外数据的线段
```go
@ -936,59 +989,114 @@ type LineSegmentCap[V generic.SignedNumber, Data any] struct {
Data Data
}
```
<span id="struct_LineSegmentCap_GetData"></span>
#### func (*LineSegmentCap) GetData() Data
***
<span id="struct_Point"></span>
### Point `STRUCT`
表示了一个由 x、y 坐标组成的点
```go
type Point[V generic.SignedNumber] [2]V
```
<span id="struct_Point_GetX"></span>
#### func (Point) GetX() V
> 返回该点的 x 坐标
***
<span id="struct_Point_GetY"></span>
#### func (Point) GetY() V
> 返回该点的 y 坐标
***
<span id="struct_Point_GetXY"></span>
#### func (Point) GetXY() (x V, y V)
> 返回该点的 x、y 坐标
***
<span id="struct_Point_GetPos"></span>
#### func (Point) GetPos(width V) V
> 返回该点位于特定宽度的二维数组的顺序位置
***
<span id="struct_Point_GetOffset"></span>
#### func (Point) GetOffset(x V, y V) Point[V]
> 获取偏移后的新坐标
***
<span id="struct_Point_Negative"></span>
#### func (Point) Negative() bool
> 返回该点是否是一个负数坐标
***
<span id="struct_Point_OutOf"></span>
#### func (Point) OutOf(minWidth V, minHeight V, maxWidth V, maxHeight V) bool
> 返回该点在特定宽高下是否越界f
***
<span id="struct_Point_Equal"></span>
#### func (Point) Equal(point Point[V]) bool
> 返回两个点是否相等
***
<span id="struct_Point_Copy"></span>
#### func (Point) Copy() Point[V]
> 复制一个点位置
***
<span id="struct_Point_Add"></span>
#### func (Point) Add(point Point[V]) Point[V]
> 得到加上 point 后的点
***
<span id="struct_Point_Sub"></span>
#### func (Point) Sub(point Point[V]) Point[V]
> 得到减去 point 后的点
***
<span id="struct_Point_Mul"></span>
#### func (Point) Mul(point Point[V]) Point[V]
> 得到乘以 point 后的点
***
<span id="struct_Point_Div"></span>
#### func (Point) Div(point Point[V]) Point[V]
> 得到除以 point 后的点
***
<span id="struct_Point_Abs"></span>
#### func (Point) Abs() Point[V]
> 返回位置的绝对值
***
<span id="struct_Point_Max"></span>
#### func (Point) Max(point Point[V]) Point[V]
> 返回两个位置中每个维度的最大值组成的新的位置
***
<span id="struct_Point_Min"></span>
#### func (Point) Min(point Point[V]) Point[V]
> 返回两个位置中每个维度的最小值组成的新的位置
***
<span id="struct_PointCap"></span>
### PointCap `STRUCT`
表示了一个由 x、y 坐标组成的点,这个点具有一个数据容量
```go
@ -997,14 +1105,25 @@ type PointCap[V generic.SignedNumber, D any] struct {
Data D
}
```
<span id="struct_PointCap_GetData"></span>
#### func (PointCap) GetData() D
> 获取数据
***
<span id="struct_Shape"></span>
### Shape `STRUCT`
通过多个点表示了一个形状
```go
type Shape[V generic.SignedNumber] []Point[V]
```
<span id="struct_Shape_Points"></span>
#### func (Shape) Points() []Point[V]
> 获取这个形状的所有点
示例代码:
**示例代码:**
```go
func ExampleShape_Points() {
@ -1039,9 +1158,13 @@ func TestShape_Points(t *testing.T) {
***
<span id="struct_Shape_PointCount"></span>
#### func (Shape) PointCount() int
> 获取这个形状的点数量
示例代码:
**示例代码:**
```go
func ExampleShape_PointCount() {
@ -1071,16 +1194,26 @@ func TestShape_PointCount(t *testing.T) {
***
<span id="struct_Shape_Contains"></span>
#### func (Shape) Contains(point Point[V]) bool
> 返回该形状中是否包含点
***
<span id="struct_Shape_ToCircle"></span>
#### func (Shape) ToCircle() Circle[V]
> 将形状转换为圆形进行处理
> - 当形状非圆形时将会产生意外情况
***
<span id="struct_Shape_String"></span>
#### func (Shape) String() string
> 将该形状转换为可视化的字符串进行返回
示例代码:
**示例代码:**
```go
func ExampleShape_String() {
@ -1111,6 +1244,8 @@ func TestShape_String(t *testing.T) {
***
<span id="struct_Shape_ShapeSearch"></span>
#### func (Shape) ShapeSearch(options ...ShapeSearchOption) (result []Shape[V])
> 获取该形状中包含的所有图形组合及其位置
> - 需要注意的是,即便图形最终表示为相同的,但是只要位置组合顺序不同,那么也将被认定为一种图形组合
@ -1118,7 +1253,9 @@ func TestShape_String(t *testing.T) {
> - 返回的坐标为原始形状的坐标
>
> 可通过可选项对搜索结果进行过滤
示例代码:
**示例代码:**
```go
func ExampleShape_ShapeSearch() {
@ -1132,13 +1269,20 @@ func ExampleShape_ShapeSearch() {
```
***
<span id="struct_Shape_Edges"></span>
#### func (Shape) Edges() (edges []LineSegment[V])
> 获取该形状每一条边
> - 该形状需要最少由3个点组成否则将不会返回任意一边
***
<span id="struct_Shape_IsPointOnEdge"></span>
#### func (Shape) IsPointOnEdge(point Point[V]) bool
> 检查点是否在该形状的一条边上
***
<span id="struct_ShapeSearchOption"></span>
### ShapeSearchOption `STRUCT`
图形搜索可选项,用于 Shape.ShapeSearch 搜索支持
```go

View File

@ -28,14 +28,14 @@ astar 提供用于实现 A* 算法的函数和数据结构。A* 算法是一种
|类型|名称|描述
|:--|:--|:--
|`INTERFACE`|[Graph](#graph)|适用于 A* 算法的图数据结构接口定义,表示导航网格,其中包含了节点和连接节点的边。
|`INTERFACE`|[Graph](#struct_Graph)|适用于 A* 算法的图数据结构接口定义,表示导航网格,其中包含了节点和连接节点的边。
</details>
***
## 详情信息
#### func Find(graph Graph[Node], start Node, end Node, cost func (a Node) V, heuristic func (a Node) V) []Node
#### func Find\[Node comparable, V generic.SignedNumber\](graph Graph[Node], start Node, end Node, cost func (a Node) V, heuristic func (a Node) V) []Node
<span id="Find"></span>
> 使用 A* 算法在导航网格上查找从起点到终点的最短路径,并返回路径上的节点序列。
>
@ -58,7 +58,8 @@ astar 提供用于实现 A* 算法的函数和数据结构。A* 算法是一种
> - 函数内部使用了堆数据结构来管理待处理的节点。
> - 函数返回一个节点序列,表示从起点到终点的最短路径。如果找不到路径,则返回空序列。
示例代码:
**示例代码:**
```go
func ExampleFind() {
@ -77,6 +78,7 @@ func ExampleFind() {
```
***
<span id="struct_Graph"></span>
### Graph `INTERFACE`
适用于 A* 算法的图数据结构接口定义,表示导航网格,其中包含了节点和连接节点的边。
```go
@ -84,5 +86,8 @@ type Graph[Node comparable] interface {
Neighbours(node Node) []Node
}
```
<span id="struct_Graph_Neighbours"></span>
#### func (Graph) Neighbours(point geometry.Point[int]) []geometry.Point[int]
***

View File

@ -27,15 +27,15 @@ dp (DistributionPattern) 提供用于在二维数组中根据不同的特征标
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[DistributionPattern](#distributionpattern)|分布图
|`STRUCT`|[Link](#link)|暂无描述...
|`STRUCT`|[DistributionPattern](#struct_DistributionPattern)|分布图
|`STRUCT`|[Link](#struct_Link)|暂无描述...
</details>
***
## 详情信息
#### func NewDistributionPattern(sameKindVerifyHandle func (itemA Item) bool) *DistributionPattern[Item]
#### func NewDistributionPattern\[Item any\](sameKindVerifyHandle func (itemA Item) bool) *DistributionPattern[Item]
<span id="NewDistributionPattern"></span>
> 构建一个分布图实例
@ -69,6 +69,7 @@ func TestNewDistributionPattern(t *testing.T) {
***
<span id="struct_DistributionPattern"></span>
### DistributionPattern `STRUCT`
分布图
```go
@ -80,30 +81,49 @@ type DistributionPattern[Item any] struct {
usePos bool
}
```
<span id="struct_DistributionPattern_GetLinks"></span>
#### func (*DistributionPattern) GetLinks(pos int) (result []Link[Item])
> 获取关联的成员
> - 其中包含传入的 pos 成员
***
<span id="struct_DistributionPattern_HasLink"></span>
#### func (*DistributionPattern) HasLink(pos int) bool
> 检查一个位置是否包含除它本身外的其他关联成员
***
<span id="struct_DistributionPattern_LoadMatrix"></span>
#### func (*DistributionPattern) LoadMatrix(matrix [][]Item)
> 通过二维矩阵加载分布图
> - 通过该函数加载的分布图使用的矩阵是复制后的矩阵,因此无法直接通过刷新(Refresh)来更新分布关系
> - 需要通过直接刷新的方式请使用 LoadMatrixWithPos
***
<span id="struct_DistributionPattern_LoadMatrixWithPos"></span>
#### func (*DistributionPattern) LoadMatrixWithPos(width int, matrix []Item)
> 通过二维矩阵加载分布图
***
<span id="struct_DistributionPattern_Refresh"></span>
#### func (*DistributionPattern) Refresh(pos int)
> 刷新特定位置的分布关系
> - 由于 LoadMatrix 的矩阵是复制后的矩阵,所以任何外部的改动都不会影响到分布图的变化,在这种情况下,刷新将没有任何意义
> - 需要通过直接刷新的方式请使用 LoadMatrixWithPos 加载矩阵,或者通过 RefreshWithItem 函数进行刷新
***
<span id="struct_DistributionPattern_RefreshWithItem"></span>
#### func (*DistributionPattern) RefreshWithItem(pos int, item Item)
> 通过特定的成员刷新特定位置的分布关系
> - 如果矩阵通过 LoadMatrixWithPos 加载,将会重定向至 Refresh
***
<span id="struct_Link"></span>
### Link `STRUCT`
```go

View File

@ -23,20 +23,21 @@ matrix 提供了一个简单的二维数组的实现
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Matrix](#matrix)|二维矩阵
|`STRUCT`|[Matrix](#struct_Matrix)|二维矩阵
</details>
***
## 详情信息
#### func NewMatrix(width int, height int) *Matrix[T]
#### func NewMatrix\[T any\](width int, height int) *Matrix[T]
<span id="NewMatrix"></span>
> 生成特定宽高的二维矩阵
> - 虽然提供了通过x、y坐标的操作函数但是建议无论如何使用pos进行处理
> - 该矩阵为XY而非YX
***
<span id="struct_Matrix"></span>
### Matrix `STRUCT`
二维矩阵
```go
@ -46,52 +47,100 @@ type Matrix[T any] struct {
m []T
}
```
<span id="struct_Matrix_GetWidth"></span>
#### func (*Matrix) GetWidth() int
> 获取二维矩阵宽度
***
<span id="struct_Matrix_GetHeight"></span>
#### func (*Matrix) GetHeight() int
> 获取二维矩阵高度
***
<span id="struct_Matrix_GetWidth2Height"></span>
#### func (*Matrix) GetWidth2Height() (width int, height int)
> 获取二维矩阵的宽度和高度
***
<span id="struct_Matrix_GetMatrix"></span>
#### func (*Matrix) GetMatrix() [][]T
> 获取二维矩阵
> - 通常建议使用 GetMatrixWithPos 进行处理这样将拥有更高的效率
***
<span id="struct_Matrix_GetMatrixWithPos"></span>
#### func (*Matrix) GetMatrixWithPos() []T
> 获取顺序的矩阵
***
<span id="struct_Matrix_Get"></span>
#### func (*Matrix) Get(x int, y int) (value T)
> 获取特定坐标的内容
***
<span id="struct_Matrix_GetExist"></span>
#### func (*Matrix) GetExist(x int, y int) (value T, exist bool)
> 获取特定坐标的内容,如果不存在则返回 false
***
<span id="struct_Matrix_GetWithPos"></span>
#### func (*Matrix) GetWithPos(pos int) (value T)
> 获取特定坐标的内容
***
<span id="struct_Matrix_Set"></span>
#### func (*Matrix) Set(x int, y int, data T)
> 设置特定坐标的内容
***
<span id="struct_Matrix_SetWithPos"></span>
#### func (*Matrix) SetWithPos(pos int, data T)
> 设置特定坐标的内容
***
<span id="struct_Matrix_Swap"></span>
#### func (*Matrix) Swap(x1 int, y1 int, x2 int, y2 int)
> 交换两个位置的内容
***
<span id="struct_Matrix_SwapWithPos"></span>
#### func (*Matrix) SwapWithPos(pos1 int, pos2 int)
> 交换两个位置的内容
***
<span id="struct_Matrix_TrySwap"></span>
#### func (*Matrix) TrySwap(x1 int, y1 int, x2 int, y2 int, expressionHandle func (matrix *Matrix[T]) bool)
> 尝试交换两个位置的内容,交换后不满足表达式时进行撤销
***
<span id="struct_Matrix_TrySwapWithPos"></span>
#### func (*Matrix) TrySwapWithPos(pos1 int, pos2 int, expressionHandle func (matrix *Matrix[T]) bool)
> 尝试交换两个位置的内容,交换后不满足表达式时进行撤销
***
<span id="struct_Matrix_FillFull"></span>
#### func (*Matrix) FillFull(generateHandle func (x int) T)
> 根据提供的生成器填充整个矩阵
***
<span id="struct_Matrix_FillFullWithPos"></span>
#### func (*Matrix) FillFullWithPos(generateHandle func (pos int) T)
> 根据提供的生成器填充整个矩阵
***

View File

@ -26,14 +26,14 @@ navmesh 提供了用于导航网格处理的函数和数据结构。导航网格
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[NavMesh](#navmesh)|暂无描述...
|`STRUCT`|[NavMesh](#struct_NavMesh)|暂无描述...
</details>
***
## 详情信息
#### func NewNavMesh(shapes []geometry.Shape[V], meshShrinkAmount V) *NavMesh[V]
#### func NewNavMesh\[V generic.SignedNumber\](shapes []geometry.Shape[V], meshShrinkAmount V) *NavMesh[V]
<span id="NewNavMesh"></span>
> 创建一个新的导航网格,并返回一个指向该导航网格的指针。
>
@ -54,6 +54,7 @@ navmesh 提供了用于导航网格处理的函数和数据结构。导航网格
> - 确保 NavMesh 计算精度的情况下V 建议使用 float64 类型
***
<span id="struct_NavMesh"></span>
### NavMesh `STRUCT`
```go
@ -62,9 +63,14 @@ type NavMesh[V generic.SignedNumber] struct {
meshShrinkAmount V
}
```
<span id="struct_NavMesh_Neighbours"></span>
#### func (*NavMesh) Neighbours(node *shape[V]) []*shape[V]
> 实现 astar.Graph 的接口,用于向 A* 算法提供相邻图形
***
<span id="struct_NavMesh_Find"></span>
#### func (*NavMesh) Find(point geometry.Point[V], maxDistance V) (distance V, findPoint geometry.Point[V], findShape geometry.Shape[V])
> 用于在 NavMesh 中查找离给定点最近的形状,并返回距离、找到的点和找到的形状。
>
@ -82,7 +88,10 @@ type NavMesh[V generic.SignedNumber] struct {
> - 如果给定点不在任何形状内部或者形状的边上,将计算给定点到每个形状的距离,并找到最近的形状和对应的点。
> - 距离的计算采用几何学中的投影点到形状的距离。
> - 函数返回离给定点最近的形状的距离、找到的点和找到的形状。
***
<span id="struct_NavMesh_FindPath"></span>
#### func (*NavMesh) FindPath(start geometry.Point[V], end geometry.Point[V]) (result []geometry.Point[V])
> 函数用于在 NavMesh 中查找从起点到终点的路径,并返回路径上的点序列。
>
@ -98,7 +107,9 @@ type NavMesh[V generic.SignedNumber] struct {
> - 如果起点或终点不在任何形状内部,且 NavMesh 的 meshShrinkAmount 大于0则会考虑缩小的形状。
> - 使用 A* 算法在 NavMesh 上搜索从起点形状到终点形状的最短路径。
> - 使用漏斗算法对路径进行优化,以得到最终的路径点序列。
示例代码:
**示例代码:**
```go
func ExampleNavMesh_FindPath() {

View File

@ -23,18 +23,19 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[ObjectPool](#objectpool)|基于 sync.Pool 实现的线程安全的对象池
|`STRUCT`|[ObjectPool](#struct_ObjectPool)|基于 sync.Pool 实现的线程安全的对象池
</details>
***
## 详情信息
#### func NewObjectPool(generator func () *T, releaser func (data *T)) *ObjectPool[*T]
#### func NewObjectPool\[T any\](generator func () *T, releaser func (data *T)) *ObjectPool[*T]
<span id="NewObjectPool"></span>
> 创建一个 ObjectPool
示例代码:
**示例代码:**
```go
func ExampleNewObjectPool() {
@ -89,6 +90,7 @@ func TestNewObjectPool(t *testing.T) {
***
<span id="struct_ObjectPool"></span>
### ObjectPool `STRUCT`
基于 sync.Pool 实现的线程安全的对象池
- 一些高频临时生成使用的对象可以通过 ObjectPool 进行管理,例如属性计算等
@ -98,8 +100,11 @@ type ObjectPool[T any] struct {
releaser func(data T)
}
```
<span id="struct_ObjectPool_Get"></span>
#### func (*ObjectPool) Get() T
> 获取一个对象
<details>
<summary>查看 / 收起单元测试</summary>
@ -135,8 +140,11 @@ func TestObjectPool_Get(t *testing.T) {
***
<span id="struct_ObjectPool_Release"></span>
#### func (*ObjectPool) Release(data T)
> 将使用完成的对象放回缓冲区
<details>
<summary>查看 / 收起单元测试</summary>

File diff suppressed because it is too large Load Diff

View File

@ -17,73 +17,135 @@ var (
type Int big.Int
// NewInt 创建一个 Int
func NewInt[T generic.Number](x T) *Int {
return (*Int)(big.NewInt(int64(x)))
}
// NewIntByString 通过字符串创建一个 Int
// - 如果字符串不是一个合法的数字,则返回 0
func NewIntByString(i string) *Int {
v, suc := new(big.Int).SetString(i, 10)
if !suc {
return IntZero.Copy()
// NewInt 创建一个 Int 对象,该对象的值为 x
func NewInt[T generic.Basic](x T) *Int {
var xa any = x
switch x := xa.(type) {
case int:
return (*Int)(big.NewInt(int64(x)))
case int8:
return (*Int)(big.NewInt(int64(x)))
case int16:
return (*Int)(big.NewInt(int64(x)))
case int32:
return (*Int)(big.NewInt(int64(x)))
case int64:
return (*Int)(big.NewInt(x))
case uint:
return (*Int)(big.NewInt(int64(x)))
case uint8:
return (*Int)(big.NewInt(int64(x)))
case uint16:
return (*Int)(big.NewInt(int64(x)))
case uint32:
return (*Int)(big.NewInt(int64(x)))
case uint64:
return (*Int)(big.NewInt(int64(x)))
case string:
si, suc := new(big.Int).SetString(x, 10)
if !suc {
return (*Int)(big.NewInt(0))
}
return (*Int)(si)
case bool:
if x {
return (*Int)(big.NewInt(1))
}
return (*Int)(big.NewInt(0))
case float32:
return (*Int)(big.NewInt(int64(x)))
case float64:
return (*Int)(big.NewInt(int64(x)))
}
return (*Int)(v)
return (*Int)(big.NewInt(0))
}
// applyIntOperation 应用一个 Int 操作
func applyIntOperation[T generic.Number](v *Int, i T, op func(*big.Int, *big.Int) *big.Int) *Int {
return (*Int)(op(v.ToBigint(), NewInt(i).ToBigint()))
}
// Copy 拷贝当前 Int 对象
func (slf *Int) Copy() *Int {
return (*Int)(new(big.Int).Set(slf.ToBigint()))
}
// Set 设置当前 Int 对象的值为 i
func (slf *Int) Set(i *Int) *Int {
return (*Int)(slf.ToBigint().Set(i.ToBigint()))
}
// SetString 设置当前 Int 对象的值为 i
func (slf *Int) SetString(i string) *Int {
return (*Int)(slf.ToBigint().Set((*big.Int)(NewInt(i))))
}
// SetInt 设置当前 Int 对象的值为 i
func (slf *Int) SetInt(i int) *Int {
return (*Int)(slf.ToBigint().Set((*big.Int)(NewInt(i))))
}
// SetInt8 设置当前 Int 对象的值为 i
func (slf *Int) SetInt8(i int8) *Int {
return (*Int)(slf.ToBigint().Set((*big.Int)(NewInt(i))))
}
// SetInt16 设置当前 Int 对象的值为 i
func (slf *Int) SetInt16(i int16) *Int {
return (*Int)(slf.ToBigint().Set((*big.Int)(NewInt(i))))
}
// SetInt32 设置当前 Int 对象的值为 i
func (slf *Int) SetInt32(i int32) *Int {
return (*Int)(slf.ToBigint().Set((*big.Int)(NewInt(i))))
}
// SetInt64 设置当前 Int 对象的值为 i
func (slf *Int) SetInt64(i int64) *Int {
return (*Int)(slf.ToBigint().Set((*big.Int)(NewInt(i))))
}
// SetUint 设置当前 Int 对象的值为 i
func (slf *Int) SetUint(i uint) *Int {
return (*Int)(slf.ToBigint().Set((*big.Int)(NewInt(i))))
}
// SetUint8 设置当前 Int 对象的值为 i
func (slf *Int) SetUint8(i uint8) *Int {
return (*Int)(slf.ToBigint().Set((*big.Int)(NewInt(i))))
}
// SetUint16 设置当前 Int 对象的值为 i
func (slf *Int) SetUint16(i uint16) *Int {
return (*Int)(slf.ToBigint().Set((*big.Int)(NewInt(i))))
}
// SetUint32 设置当前 Int 对象的值为 i
func (slf *Int) SetUint32(i uint32) *Int {
return (*Int)(slf.ToBigint().Set((*big.Int)(NewInt(i))))
}
// SetUint64 设置当前 Int 对象的值为 i
func (slf *Int) SetUint64(i uint64) *Int {
return (*Int)(slf.ToBigint().Set((*big.Int)(NewInt(i))))
}
// SetFloat32 设置当前 Int 对象的值为 i 向下取整后的值
func (slf *Int) SetFloat32(i float32) *Int {
return (*Int)(slf.ToBigint().Set((*big.Int)(NewInt(i))))
}
// SetFloat64 设置当前 Int 对象的值为 i 向下取整后的值
func (slf *Int) SetFloat64(i float64) *Int {
return (*Int)(slf.ToBigint().Set((*big.Int)(NewInt(i))))
}
// SetBool 设置当前 Int 对象的值为 i当 i 为 true 时,值为 1当 i 为 false 时,值为 0
func (slf *Int) SetBool(i bool) *Int {
return (*Int)(slf.ToBigint().Set((*big.Int)(NewInt(i))))
}
// IsZero 判断当前 Int 对象的值是否为 0
func (slf *Int) IsZero() bool {
if slf == nil || slf.EqualTo(IntZero) {
return true
@ -91,7 +153,11 @@ func (slf *Int) IsZero() bool {
return false
}
// ToBigint 转换为 *big.Int
func (slf *Int) ToBigint() *big.Int {
if slf == nil {
return big.NewInt(0)
}
return (*big.Int)(slf)
}
@ -100,39 +166,45 @@ func (slf *Int) Cmp(i *Int) int {
return slf.ToBigint().Cmp(i.ToBigint())
}
// GreaterThan 大于
// GreaterThan 检查 slf 是否大于 i
func (slf *Int) GreaterThan(i *Int) bool {
return slf.Cmp(i) > 0
}
// GreaterThanOrEqualTo 大于或等于
// GreaterThanOrEqualTo 检查 slf 是否大于或等于 i
func (slf *Int) GreaterThanOrEqualTo(i *Int) bool {
return slf.Cmp(i) >= 0
}
// LessThan 小于
// LessThan 检查 slf 是否小于 i
func (slf *Int) LessThan(i *Int) bool {
return slf.Cmp(i) < 0
}
// LessThanOrEqualTo 小于或等于
// LessThanOrEqualTo 检查 slf 是否小于或等于 i
func (slf *Int) LessThanOrEqualTo(i *Int) bool {
return slf.Cmp(i) <= 0
}
// EqualTo 等于
// EqualTo 检查 slf 是否等于 i
func (slf *Int) EqualTo(i *Int) bool {
return slf.Cmp(i) == 0
}
// Int64 转换为 int64 类型进行返回
func (slf *Int) Int64() int64 {
return slf.ToBigint().Int64()
}
// String 转换为 string 类型进行返回
func (slf *Int) String() string {
if slf == nil {
return "0"
}
return slf.ToBigint().String()
}
// Add 使用 i 对 slf 进行加法运算slf 的值会变为运算后的值。返回 slf
func (slf *Int) Add(i *Int) *Int {
x := slf.ToBigint()
return (*Int)(x.Add(x, i.ToBigint()))

View File

@ -0,0 +1,42 @@
package huge_test
import (
"fmt"
"github.com/kercylan98/minotaur/utils/huge"
)
// 该案例展示了 NewInt 对各种基本类型的支持及用法
func ExampleNewInt() {
fmt.Println(huge.NewInt("12345678900000000"))
fmt.Println(huge.NewInt(1234567890))
fmt.Println(huge.NewInt(true))
fmt.Println(huge.NewInt(123.123))
fmt.Println(huge.NewInt(byte(1)))
// Output:
// 12345678900000000
// 1234567890
// 1
// 123
// 1
}
func ExampleInt_Copy() {
var a = huge.NewInt(1234567890)
var b = a.Copy().SetInt64(9876543210)
fmt.Println(a)
fmt.Println(b)
// Output:
// 1234567890
// 9876543210
}
func ExampleInt_Set() {
var a = huge.NewInt(1234567890)
var b = huge.NewInt(9876543210)
fmt.Println(a)
a.Set(b)
fmt.Println(a)
// Output:
// 1234567890
// 9876543210
}

525
utils/huge/int_test.go Normal file
View File

@ -0,0 +1,525 @@
package huge_test
import (
"github.com/kercylan98/minotaur/utils/huge"
"math/big"
"testing"
)
func TestNewInt(t *testing.T) {
var cases = []struct {
name string
nil bool
in int64
mul int64
want string
}{
{name: "TestNewIntNegative", in: -1, want: "-1"},
{name: "TestNewIntZero", in: 0, want: "0"},
{name: "TestNewIntPositive", in: 1, want: "1"},
{name: "TestNewIntMax", in: 9223372036854775807, want: "9223372036854775807"},
{name: "TestNewIntMin", in: -9223372036854775808, want: "-9223372036854775808"},
{name: "TestNewIntMulNegative", in: -9223372036854775808, mul: 10000000, want: "-92233720368547758080000000"},
{name: "TestNewIntMulPositive", in: 9223372036854775807, mul: 10000000, want: "92233720368547758070000000"},
{name: "TestNewIntNil", nil: true, want: "0"},
{name: "TestNewIntNilMul", nil: true, mul: 10000000, want: "0"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var got *huge.Int
switch {
case c.nil:
if c.mul > 0 {
got = huge.NewInt(0).MulInt64(c.mul)
}
case c.mul == 0:
got = huge.NewInt(c.in)
default:
got = huge.NewInt(c.in).MulInt64(c.mul)
}
if s := got.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, got.String())
} else {
t.Log(s)
}
})
}
// other
t.Run("TestNewIntFromString", func(t *testing.T) {
if got := huge.NewInt("1234567890123456789012345678901234567890"); got.String() != "1234567890123456789012345678901234567890" {
t.Fatalf("want: %s, got: %s", "1234567890123456789012345678901234567890", got.String())
}
})
t.Run("TestNewIntFromInt", func(t *testing.T) {
if got := huge.NewInt(1234567890); got.String() != "1234567890" {
t.Fatalf("want: %s, got: %s", "1234567890", got.String())
}
})
t.Run("TestNewIntFromBool", func(t *testing.T) {
if got := huge.NewInt(true); got.String() != "1" {
t.Fatalf("want: %s, got: %s", "1", got.String())
}
})
t.Run("TestNewIntFromFloat", func(t *testing.T) {
if got := huge.NewInt(1234567890.1234567890); got.String() != "1234567890" {
t.Fatalf("want: %s, got: %s", "1234567890", got.String())
}
})
}
func TestInt_Copy(t *testing.T) {
var cases = []struct {
name string
in int64
want string
}{
{name: "TestIntCopyNegative", in: -1, want: "-1"},
{name: "TestIntCopyZero", in: 0, want: "0"},
{name: "TestIntCopyPositive", in: 1, want: "1"},
{name: "TestIntCopyMax", in: 9223372036854775807, want: "9223372036854775807"},
{name: "TestIntCopyMin", in: -9223372036854775808, want: "-9223372036854775808"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in = huge.NewInt(c.in)
var got = in.Copy()
if in.Int64() != c.in {
t.Fatalf("want: %d, got: %d", c.in, in.Int64())
}
if s := got.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, got.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_Set(t *testing.T) {
var cases = []struct {
name string
in int64
want string
}{
{name: "TestIntSetNegative", in: -1, want: "-1"},
{name: "TestIntSetZero", in: 0, want: "0"},
{name: "TestIntSetPositive", in: 1, want: "1"},
{name: "TestIntSetMax", in: 9223372036854775807, want: "9223372036854775807"},
{name: "TestIntSetMin", in: -9223372036854775808, want: "-9223372036854775808"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in *huge.Int
in = in.Set(huge.NewInt(c.in))
if s := in.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, in.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_SetString(t *testing.T) {
var cases = []struct {
name string
in string
want string
}{
{name: "TestIntSetStringNegative", in: "-1", want: "-1"},
{name: "TestIntSetStringZero", in: "0", want: "0"},
{name: "TestIntSetStringPositive", in: "1", want: "1"},
{name: "TestIntSetStringMax", in: "9223372036854775807", want: "9223372036854775807"},
{name: "TestIntSetStringMin", in: "-9223372036854775808", want: "-9223372036854775808"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in *huge.Int
in = in.SetString(c.in)
if s := in.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, in.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_SetInt(t *testing.T) {
var cases = []struct {
name string
in int64
want string
}{
{name: "TestIntSetIntNegative", in: -1, want: "-1"},
{name: "TestIntSetIntZero", in: 0, want: "0"},
{name: "TestIntSetIntPositive", in: 1, want: "1"},
{name: "TestIntSetIntMax", in: 9223372036854775807, want: "9223372036854775807"},
{name: "TestIntSetIntMin", in: -9223372036854775808, want: "-9223372036854775808"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in *huge.Int
in = in.SetInt64(c.in)
if s := in.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, in.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_SetInt8(t *testing.T) {
var cases = []struct {
name string
in int8
want string
}{
{name: "TestIntSetInt8Negative", in: -1, want: "-1"},
{name: "TestIntSetInt8Zero", in: 0, want: "0"},
{name: "TestIntSetInt8Positive", in: 1, want: "1"},
{name: "TestIntSetInt8Max", in: 127, want: "127"},
{name: "TestIntSetInt8Min", in: -128, want: "-128"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in *huge.Int
in = in.SetInt8(c.in)
if s := in.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, in.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_SetInt16(t *testing.T) {
var cases = []struct {
name string
in int16
want string
}{
{name: "TestIntSetInt16Negative", in: -1, want: "-1"},
{name: "TestIntSetInt16Zero", in: 0, want: "0"},
{name: "TestIntSetInt16Positive", in: 1, want: "1"},
{name: "TestIntSetInt16Max", in: 32767, want: "32767"},
{name: "TestIntSetInt16Min", in: -32768, want: "-32768"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in *huge.Int
in = in.SetInt16(c.in)
if s := in.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, in.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_SetInt32(t *testing.T) {
var cases = []struct {
name string
in int32
want string
}{
{name: "TestIntSetInt32Negative", in: -1, want: "-1"},
{name: "TestIntSetInt32Zero", in: 0, want: "0"},
{name: "TestIntSetInt32Positive", in: 1, want: "1"},
{name: "TestIntSetInt32Max", in: 2147483647, want: "2147483647"},
{name: "TestIntSetInt32Min", in: -2147483648, want: "-2147483648"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in *huge.Int
in = in.SetInt32(c.in)
if s := in.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, in.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_SetInt64(t *testing.T) {
var cases = []struct {
name string
in int64
want string
}{
{name: "TestIntSetInt64Negative", in: -1, want: "-1"},
{name: "TestIntSetInt64Zero", in: 0, want: "0"},
{name: "TestIntSetInt64Positive", in: 1, want: "1"},
{name: "TestIntSetInt64Max", in: 9223372036854775807, want: "9223372036854775807"},
{name: "TestIntSetInt64Min", in: -9223372036854775808, want: "-9223372036854775808"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in *huge.Int
in = in.SetInt64(c.in)
if s := in.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, in.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_SetUint(t *testing.T) {
var cases = []struct {
name string
in uint64
want string
}{
{name: "TestIntSetUintNegative", in: 0, want: "0"},
{name: "TestIntSetUintZero", in: 0, want: "0"},
{name: "TestIntSetUintPositive", in: 1, want: "1"},
{name: "TestIntSetUintMax", in: 18446744073709551615, want: "18446744073709551615"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in *huge.Int
in = in.SetUint64(c.in)
if s := in.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, in.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_SetUint8(t *testing.T) {
var cases = []struct {
name string
in uint8
want string
}{
{name: "TestIntSetUint8Negative", in: 0, want: "0"},
{name: "TestIntSetUint8Zero", in: 0, want: "0"},
{name: "TestIntSetUint8Positive", in: 1, want: "1"},
{name: "TestIntSetUint8Max", in: 255, want: "255"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in *huge.Int
in = in.SetUint8(c.in)
if s := in.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, in.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_SetUint16(t *testing.T) {
var cases = []struct {
name string
in uint16
want string
}{
{name: "TestIntSetUint16Negative", in: 0, want: "0"},
{name: "TestIntSetUint16Zero", in: 0, want: "0"},
{name: "TestIntSetUint16Positive", in: 1, want: "1"},
{name: "TestIntSetUint16Max", in: 65535, want: "65535"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in *huge.Int
in = in.SetUint16(c.in)
if s := in.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, in.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_SetUint32(t *testing.T) {
var cases = []struct {
name string
in uint32
want string
}{
{name: "TestIntSetUint32Negative", in: 0, want: "0"},
{name: "TestIntSetUint32Zero", in: 0, want: "0"},
{name: "TestIntSetUint32Positive", in: 1, want: "1"},
{name: "TestIntSetUint32Max", in: 4294967295, want: "4294967295"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in *huge.Int
in = in.SetUint32(c.in)
if s := in.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, in.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_SetUint64(t *testing.T) {
var cases = []struct {
name string
in uint64
want string
}{
{name: "TestIntSetUint64Negative", in: 0, want: "0"},
{name: "TestIntSetUint64Zero", in: 0, want: "0"},
{name: "TestIntSetUint64Positive", in: 1, want: "1"},
{name: "TestIntSetUint64Max", in: 18446744073709551615, want: "18446744073709551615"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in *huge.Int
in = in.SetUint64(c.in)
if s := in.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, in.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_SetFloat32(t *testing.T) {
var cases = []struct {
name string
in float32
want string
}{
{name: "TestIntSetFloat32Negative", in: -1.1, want: "-1"},
{name: "TestIntSetFloat32Zero", in: 0, want: "0"},
{name: "TestIntSetFloat32Positive", in: 1.1, want: "1"},
{name: "TestIntSetFloat32Max", in: 9223372036854775807, want: "9223372036854775807"},
{name: "TestIntSetFloat32Min", in: -9223372036854775808, want: "-9223372036854775808"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in *huge.Int
in = in.SetFloat32(c.in)
if s := in.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, in.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_SetFloat64(t *testing.T) {
var cases = []struct {
name string
in float64
want string
}{
{name: "TestIntSetFloat64Negative", in: -1.1, want: "-1"},
{name: "TestIntSetFloat64Zero", in: 0, want: "0"},
{name: "TestIntSetFloat64Positive", in: 1.1, want: "1"},
{name: "TestIntSetFloat64Max", in: 9223372036854775807, want: "9223372036854775807"},
{name: "TestIntSetFloat64Min", in: -9223372036854775808, want: "-9223372036854775808"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in *huge.Int
in = in.SetFloat64(c.in)
if s := in.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, in.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_SetBool(t *testing.T) {
var cases = []struct {
name string
in bool
want string
}{
{name: "TestIntSetBoolFalse", in: false, want: "0"},
{name: "TestIntSetBoolTrue", in: true, want: "1"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var in *huge.Int
in = in.SetBool(c.in)
if s := in.String(); s != c.want {
t.Fatalf("want: %s, got: %s", c.want, in.String())
} else {
t.Log(s)
}
})
}
}
func TestInt_IsZero(t *testing.T) {
var cases = []struct {
name string
in int64
want bool
}{
{name: "TestIntIsZeroNegative", in: -1, want: false},
{name: "TestIntIsZeroZero", in: 0, want: true},
{name: "TestIntIsZeroPositive", in: 1, want: false},
{name: "TestIntIsZeroMax", in: 9223372036854775807, want: false},
{name: "TestIntIsZeroMin", in: -9223372036854775808, want: false},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if got := huge.NewInt(c.in).IsZero(); got != c.want {
t.Fatalf("want: %t, got: %t", c.want, got)
}
})
}
}
func TestInt_ToBigint(t *testing.T) {
var cases = []struct {
name string
in int64
want *big.Int
}{
{name: "TestIntToBigintNegative", in: -1, want: big.NewInt(-1)},
{name: "TestIntToBigintZero", in: 0, want: big.NewInt(0)},
{name: "TestIntToBigintPositive", in: 1, want: big.NewInt(1)},
{name: "TestIntToBigintMax", in: 9223372036854775807, want: big.NewInt(9223372036854775807)},
{name: "TestIntToBigintMin", in: -9223372036854775808, want: big.NewInt(-9223372036854775808)},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if got := huge.NewInt(c.in).ToBigint(); got.Cmp(c.want) != 0 {
t.Fatalf("want: %s, got: %s", c.want.String(), got.String())
}
})
}
}

View File

@ -25,20 +25,21 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[BinarySearch](#binarysearch)|暂无描述...
|`STRUCT`|[BinarySearchRankChangeEventHandle](#binarysearchrankchangeeventhandle)|暂无描述...
|`STRUCT`|[BinarySearchOption](#binarysearchoption)|暂无描述...
|`STRUCT`|[BinarySearch](#struct_BinarySearch)|暂无描述...
|`STRUCT`|[BinarySearchRankChangeEventHandle](#struct_BinarySearchRankChangeEventHandle)|暂无描述...
|`STRUCT`|[BinarySearchOption](#struct_BinarySearchOption)|暂无描述...
</details>
***
## 详情信息
#### func NewBinarySearch(options ...BinarySearchOption[CompetitorID, Score]) *BinarySearch[CompetitorID, Score]
#### func NewBinarySearch\[CompetitorID comparable, Score generic.Ordered\](options ...BinarySearchOption[CompetitorID, Score]) *BinarySearch[CompetitorID, Score]
<span id="NewBinarySearch"></span>
> 创建一个基于内存的二分查找排行榜
示例代码:
**示例代码:**
```go
func ExampleNewBinarySearch() {
@ -49,18 +50,19 @@ func ExampleNewBinarySearch() {
```
***
#### func WithBinarySearchCount(rankCount int) BinarySearchOption[CompetitorID, Score]
#### func WithBinarySearchCount\[CompetitorID comparable, Score generic.Ordered\](rankCount int) BinarySearchOption[CompetitorID, Score]
<span id="WithBinarySearchCount"></span>
> 通过限制排行榜竞争者数量来创建排行榜
> - 默认情况下允许100位竞争者
***
#### func WithBinarySearchASC() BinarySearchOption[CompetitorID, Score]
#### func WithBinarySearchASC\[CompetitorID comparable, Score generic.Ordered\]() BinarySearchOption[CompetitorID, Score]
<span id="WithBinarySearchASC"></span>
> 通过升序的方式创建排行榜
> - 默认情况下为降序
***
<span id="struct_BinarySearch"></span>
### BinarySearch `STRUCT`
```go
@ -74,11 +76,168 @@ type BinarySearch[CompetitorID comparable, Score generic.Ordered] struct {
rankClearBeforeEventHandles []BinarySearchRankClearBeforeEventHandle[CompetitorID, Score]
}
```
<span id="struct_BinarySearch_Competitor"></span>
#### func (*BinarySearch) Competitor(competitorId CompetitorID, score Score)
> 声明排行榜竞争者
> - 如果竞争者存在的情况下,会更新已有成绩,否则新增竞争者
**示例代码:**
```go
func ExampleBinarySearch_Competitor() {
bs := leaderboard2.NewBinarySearch[string, int](leaderboard2.WithBinarySearchCount[string, int](10))
scores := []int{6131, 132, 5133, 134, 135, 136, 137, 138, 139, 140, 222, 333, 444, 555, 666}
for i := 1; i <= 15; i++ {
bs.Competitor(fmt.Sprintf("competitor_%2d", i), scores[i-1])
}
for rank, competitor := range bs.GetAllCompetitor() {
fmt.Println(rank, competitor)
}
}
```
***
<span id="struct_BinarySearch_RemoveCompetitor"></span>
#### func (*BinarySearch) RemoveCompetitor(competitorId CompetitorID)
> 删除特定竞争者
**示例代码:**
```go
func ExampleBinarySearch_RemoveCompetitor() {
bs := leaderboard2.NewBinarySearch[string, int](leaderboard2.WithBinarySearchCount[string, int](10))
scores := []int{6131, 132, 5133, 134, 135, 136, 137, 138, 139, 140, 222, 333, 444, 555, 666}
for i := 1; i <= 15; i++ {
bs.Competitor(fmt.Sprintf("competitor_%2d", i), scores[i-1])
}
bs.RemoveCompetitor("competitor_ 1")
for rank, competitor := range bs.GetAllCompetitor() {
fmt.Println(rank, competitor)
}
}
```
***
<span id="struct_BinarySearch_Size"></span>
#### func (*BinarySearch) Size() int
> 获取竞争者数量
***
<span id="struct_BinarySearch_GetRankDefault"></span>
#### func (*BinarySearch) GetRankDefault(competitorId CompetitorID, defaultValue int) int
> 获取竞争者排名,如果竞争者不存在则返回默认值
> - 排名从 0 开始
***
<span id="struct_BinarySearch_GetRank"></span>
#### func (*BinarySearch) GetRank(competitorId CompetitorID) ( int, error)
> 获取竞争者排名
> - 排名从 0 开始
**示例代码:**
```go
func ExampleBinarySearch_GetRank() {
bs := leaderboard2.NewBinarySearch[string, int](leaderboard2.WithBinarySearchCount[string, int](10))
scores := []int{6131, 132, 5133, 134, 135, 136, 137, 138, 139, 140, 222, 333, 444, 555, 666}
for i := 1; i <= 15; i++ {
bs.Competitor(fmt.Sprintf("competitor_%2d", i), scores[i-1])
}
fmt.Println(bs.GetRank("competitor_ 1"))
}
```
***
<span id="struct_BinarySearch_GetCompetitor"></span>
#### func (*BinarySearch) GetCompetitor(rank int) (competitorId CompetitorID, err error)
> 获取特定排名的竞争者
***
<span id="struct_BinarySearch_GetCompetitorWithRange"></span>
#### func (*BinarySearch) GetCompetitorWithRange(start int, end int) ( []CompetitorID, error)
> 获取第start名到第end名竞争者
***
<span id="struct_BinarySearch_GetScore"></span>
#### func (*BinarySearch) GetScore(competitorId CompetitorID) (score Score, err error)
> 获取竞争者成绩
***
<span id="struct_BinarySearch_GetScoreDefault"></span>
#### func (*BinarySearch) GetScoreDefault(competitorId CompetitorID, defaultValue Score) Score
> 获取竞争者成绩,不存在时返回默认值
***
<span id="struct_BinarySearch_GetAllCompetitor"></span>
#### func (*BinarySearch) GetAllCompetitor() []CompetitorID
> 获取所有竞争者ID
> - 结果为名次有序的
***
<span id="struct_BinarySearch_Clear"></span>
#### func (*BinarySearch) Clear()
> 清空排行榜
***
<span id="struct_BinarySearch_Cmp"></span>
#### func (*BinarySearch) Cmp(s1 Score, s2 Score) int
***
<span id="struct_BinarySearch_UnmarshalJSON"></span>
#### func (*BinarySearch) UnmarshalJSON(bytes []byte) error
***
<span id="struct_BinarySearch_MarshalJSON"></span>
#### func (*BinarySearch) MarshalJSON() ( []byte, error)
***
<span id="struct_BinarySearch_RegRankChangeEvent"></span>
#### func (*BinarySearch) RegRankChangeEvent(handle BinarySearchRankChangeEventHandle[CompetitorID, Score])
***
<span id="struct_BinarySearch_OnRankChangeEvent"></span>
#### func (*BinarySearch) OnRankChangeEvent(competitorId CompetitorID, oldRank int, newRank int, oldScore Score, newScore Score)
***
<span id="struct_BinarySearch_RegRankClearBeforeEvent"></span>
#### func (*BinarySearch) RegRankClearBeforeEvent(handle BinarySearchRankClearBeforeEventHandle[CompetitorID, Score])
***
<span id="struct_BinarySearch_OnRankClearBeforeEvent"></span>
#### func (*BinarySearch) OnRankClearBeforeEvent()
***
<span id="struct_BinarySearchRankChangeEventHandle"></span>
### BinarySearchRankChangeEventHandle `STRUCT`
```go
type BinarySearchRankChangeEventHandle[CompetitorID comparable, Score generic.Ordered] func(leaderboard *BinarySearch[CompetitorID, Score], competitorId CompetitorID, oldRank int, oldScore Score)
```
<span id="struct_BinarySearchOption"></span>
### BinarySearchOption `STRUCT`
```go

View File

@ -77,10 +77,10 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Field](#field)|暂无描述...
|`STRUCT`|[Logger](#logger)|暂无描述...
|`STRUCT`|[MultiHandler](#multihandler)|暂无描述...
|`STRUCT`|[Option](#option)|暂无描述...
|`STRUCT`|[Field](#struct_Field)|暂无描述...
|`STRUCT`|[Logger](#struct_Logger)|暂无描述...
|`STRUCT`|[MultiHandler](#struct_MultiHandler)|暂无描述...
|`STRUCT`|[Option](#struct_Option)|暂无描述...
</details>
@ -96,7 +96,7 @@
<span id="Println"></span>
***
#### func Default() *Logger
#### func Default() *Logger
<span id="Default"></span>
> 获取默认的日志记录器
@ -150,193 +150,193 @@
> - 然后记录器调用 os.Exit(1),即使 FatalLevel 的日志记录被禁用
***
#### func Skip(vs ...any) slog.Attr
#### func Skip(vs ...any) slog.Attr
<span id="Skip"></span>
> 构造一个无操作字段,这在处理其他 Field 构造函数中的无效输入时通常很有用
> - 该函数还支持将其他字段快捷的转换为 Skip 字段
***
#### func Duration(key string, val time.Duration) slog.Attr
#### func Duration(key string, val time.Duration) slog.Attr
<span id="Duration"></span>
> 使用给定的键和值构造一个字段。编码器控制持续时间的序列化方式
***
#### func DurationP(key string, val *time.Duration) slog.Attr
#### func DurationP(key string, val *time.Duration) slog.Attr
<span id="DurationP"></span>
> 构造一个带有 time.Duration 的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Bool(key string, val bool) slog.Attr
#### func Bool(key string, val bool) slog.Attr
<span id="Bool"></span>
> 构造一个带有布尔值的字段
***
#### func BoolP(key string, val *bool) slog.Attr
#### func BoolP(key string, val *bool) slog.Attr
<span id="BoolP"></span>
> 构造一个带有布尔值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func String(key string, val string) slog.Attr
#### func String(key string, val string) slog.Attr
<span id="String"></span>
> 构造一个带有字符串值的字段
***
#### func StringP(key string, val *string) slog.Attr
#### func StringP(key string, val *string) slog.Attr
<span id="StringP"></span>
> 构造一个带有字符串值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Int(key string, val I) slog.Attr
#### func Int\[I generic.Integer\](key string, val I) slog.Attr
<span id="Int"></span>
> 构造一个带有整数值的字段
***
#### func IntP(key string, val *I) slog.Attr
#### func IntP\[I generic.Integer\](key string, val *I) slog.Attr
<span id="IntP"></span>
> 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Int8(key string, val I) slog.Attr
#### func Int8\[I generic.Integer\](key string, val I) slog.Attr
<span id="Int8"></span>
> 构造一个带有整数值的字段
***
#### func Int8P(key string, val *I) slog.Attr
#### func Int8P\[I generic.Integer\](key string, val *I) slog.Attr
<span id="Int8P"></span>
> 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Int16(key string, val I) slog.Attr
#### func Int16\[I generic.Integer\](key string, val I) slog.Attr
<span id="Int16"></span>
> 构造一个带有整数值的字段
***
#### func Int16P(key string, val *I) slog.Attr
#### func Int16P\[I generic.Integer\](key string, val *I) slog.Attr
<span id="Int16P"></span>
> 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Int32(key string, val I) slog.Attr
#### func Int32\[I generic.Integer\](key string, val I) slog.Attr
<span id="Int32"></span>
> 构造一个带有整数值的字段
***
#### func Int32P(key string, val *I) slog.Attr
#### func Int32P\[I generic.Integer\](key string, val *I) slog.Attr
<span id="Int32P"></span>
> 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Int64(key string, val I) slog.Attr
#### func Int64\[I generic.Integer\](key string, val I) slog.Attr
<span id="Int64"></span>
> 构造一个带有整数值的字段
***
#### func Int64P(key string, val *I) slog.Attr
#### func Int64P\[I generic.Integer\](key string, val *I) slog.Attr
<span id="Int64P"></span>
> 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Uint(key string, val I) slog.Attr
#### func Uint\[I generic.Integer\](key string, val I) slog.Attr
<span id="Uint"></span>
> 构造一个带有整数值的字段
***
#### func UintP(key string, val *I) slog.Attr
#### func UintP\[I generic.Integer\](key string, val *I) slog.Attr
<span id="UintP"></span>
> 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Uint8(key string, val I) slog.Attr
#### func Uint8\[I generic.Integer\](key string, val I) slog.Attr
<span id="Uint8"></span>
> 构造一个带有整数值的字段
***
#### func Uint8P(key string, val *I) slog.Attr
#### func Uint8P\[I generic.Integer\](key string, val *I) slog.Attr
<span id="Uint8P"></span>
> 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Uint16(key string, val I) slog.Attr
#### func Uint16\[I generic.Integer\](key string, val I) slog.Attr
<span id="Uint16"></span>
> 构造一个带有整数值的字段
***
#### func Uint16P(key string, val *I) slog.Attr
#### func Uint16P\[I generic.Integer\](key string, val *I) slog.Attr
<span id="Uint16P"></span>
> 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Uint32(key string, val I) slog.Attr
#### func Uint32\[I generic.Integer\](key string, val I) slog.Attr
<span id="Uint32"></span>
> 构造一个带有整数值的字段
***
#### func Uint32P(key string, val *I) slog.Attr
#### func Uint32P\[I generic.Integer\](key string, val *I) slog.Attr
<span id="Uint32P"></span>
> 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Uint64(key string, val I) slog.Attr
#### func Uint64\[I generic.Integer\](key string, val I) slog.Attr
<span id="Uint64"></span>
> 构造一个带有整数值的字段
***
#### func Uint64P(key string, val *I) slog.Attr
#### func Uint64P\[I generic.Integer\](key string, val *I) slog.Attr
<span id="Uint64P"></span>
> 构造一个带有整数值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Float(key string, val F) slog.Attr
#### func Float\[F generic.Float\](key string, val F) slog.Attr
<span id="Float"></span>
> 构造一个带有浮点值的字段
***
#### func FloatP(key string, val *F) slog.Attr
#### func FloatP\[F generic.Float\](key string, val *F) slog.Attr
<span id="FloatP"></span>
> 构造一个带有浮点值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Float32(key string, val F) slog.Attr
#### func Float32\[F generic.Float\](key string, val F) slog.Attr
<span id="Float32"></span>
> 构造一个带有浮点值的字段
***
#### func Float32P(key string, val *F) slog.Attr
#### func Float32P\[F generic.Float\](key string, val *F) slog.Attr
<span id="Float32P"></span>
> 构造一个带有浮点值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Float64(key string, val F) slog.Attr
#### func Float64\[F generic.Float\](key string, val F) slog.Attr
<span id="Float64"></span>
> 构造一个带有浮点值的字段
***
#### func Float64P(key string, val *F) slog.Attr
#### func Float64P\[F generic.Float\](key string, val *F) slog.Attr
<span id="Float64P"></span>
> 构造一个带有浮点值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Time(key string, val time.Time) slog.Attr
#### func Time(key string, val time.Time) slog.Attr
<span id="Time"></span>
> 构造一个带有时间值的字段
***
#### func TimeP(key string, val *time.Time) slog.Attr
#### func TimeP(key string, val *time.Time) slog.Attr
<span id="TimeP"></span>
> 构造一个带有时间值的字段。返回的 Field 将在适当的时候安全且显式地表示 "null"
***
#### func Any(key string, val any) slog.Attr
#### func Any(key string, val any) slog.Attr
<span id="Any"></span>
> 构造一个带有任意值的字段
***
#### func Group(key string, args ...any) slog.Attr
#### func Group(key string, args ...any) slog.Attr
<span id="Group"></span>
> 返回分组字段
***
#### func Stack(key string) slog.Attr
#### func Stack(key string) slog.Attr
<span id="Stack"></span>
> 返回堆栈字段
@ -368,36 +368,38 @@ func TestStack(t *testing.T) {
***
#### func Err(err error) slog.Attr
#### func Err(err error) slog.Attr
<span id="Err"></span>
> 构造一个带有错误值的字段
***
#### func NewHandler(w io.Writer, opts *Options) slog.Handler
#### func NewHandler(w io.Writer, opts *Options) slog.Handler
<span id="NewHandler"></span>
> 创建一个更偏向于人类可读的处理程序,该处理程序也是默认的处理程序
***
#### func NewLogger(handlers ...slog.Handler) *Logger
#### func NewLogger(handlers ...slog.Handler) *Logger
<span id="NewLogger"></span>
> 创建一个新的日志记录器
***
#### func NewMultiHandler(handlers ...slog.Handler) slog.Handler
#### func NewMultiHandler(handlers ...slog.Handler) slog.Handler
<span id="NewMultiHandler"></span>
> 创建一个新的多处理程序
***
#### func NewOptions() *Options
#### func NewOptions() *Options
<span id="NewOptions"></span>
> 创建一个新的日志选项
***
<span id="struct_Field"></span>
### Field `STRUCT`
```go
type Field slog.Attr
```
<span id="struct_Logger"></span>
### Logger `STRUCT`
```go
@ -405,6 +407,7 @@ type Logger struct {
*slog.Logger
}
```
<span id="struct_MultiHandler"></span>
### MultiHandler `STRUCT`
```go
@ -412,14 +415,27 @@ type MultiHandler struct {
handlers []slog.Handler
}
```
<span id="struct_MultiHandler_Enabled"></span>
#### func (MultiHandler) Enabled(ctx context.Context, level slog.Level) bool
***
<span id="struct_MultiHandler_Handle"></span>
#### func (MultiHandler) Handle(ctx context.Context, record slog.Record) (err error)
***
<span id="struct_MultiHandler_WithAttrs"></span>
#### func (MultiHandler) WithAttrs(attrs []slog.Attr) slog.Handler
***
<span id="struct_MultiHandler_WithGroup"></span>
#### func (MultiHandler) WithGroup(name string) slog.Handler
***
<span id="struct_Option"></span>
### Option `STRUCT`
```go

View File

@ -32,26 +32,26 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Analyzer](#analyzer)|分析器
|`INTERFACE`|[Flusher](#flusher)|用于刷新缓冲区的接口
|`STRUCT`|[FileFlusher](#fileflusher)|暂无描述...
|`STRUCT`|[Option](#option)|选项
|`STRUCT`|[Result](#result)|暂无描述...
|`STRUCT`|[R](#r)|记录器所记录的一条数据
|`STRUCT`|[Report](#report)|分析报告
|`STRUCT`|[Analyzer](#struct_Analyzer)|分析器
|`INTERFACE`|[Flusher](#struct_Flusher)|用于刷新缓冲区的接口
|`STRUCT`|[FileFlusher](#struct_FileFlusher)|暂无描述...
|`STRUCT`|[Option](#struct_Option)|选项
|`STRUCT`|[Result](#struct_Result)|暂无描述...
|`STRUCT`|[R](#struct_R)|记录器所记录的一条数据
|`STRUCT`|[Report](#struct_Report)|分析报告
</details>
***
## 详情信息
#### func NewFileFlusher(filePath string, layout ...string) *FileFlusher
#### func NewFileFlusher(filePath string, layout ...string) *FileFlusher
<span id="NewFileFlusher"></span>
> 创建一个文件刷新器
> - layout 为日志文件名的时间戳格式 (默认为 time.DateOnly)
***
#### func WithFlushInterval(interval time.Duration) Option
#### func WithFlushInterval(interval time.Duration) Option
<span id="WithFlushInterval"></span>
> 设置日志文件刷新间隔
> - 默认为 3s当日志文件刷新间隔 <= 0 时,将会在每次写入日志时刷新日志文件
@ -83,19 +83,19 @@
> 关闭运营日志记录器
***
#### func Analyze(filePath string, handle func (analyzer *Analyzer, record R)) *Report
#### func Analyze(filePath string, handle func (analyzer *Analyzer, record R)) *Report
<span id="Analyze"></span>
> 分析特定文件的记录,当发生错误时,会发生 panic
> - handle 为并行执行的,需要自行处理并发安全
> - 适用于外部进程对于日志文件的读取,但是需要注意的是,此时日志文件可能正在被写入,所以可能会读取到错误的数据
***
#### func AnalyzeMulti(filePaths []string, handle func (analyzer *Analyzer, record R)) *Report
#### func AnalyzeMulti(filePaths []string, handle func (analyzer *Analyzer, record R)) *Report
<span id="AnalyzeMulti"></span>
> 与 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)
<span id="IncrementAnalyze"></span>
> 增量分析,返回一个函数,每次调用该函数都会分析文件中新增的内容
@ -153,6 +153,7 @@ func TestIncrementAnalyze(t *testing.T) {
***
<span id="struct_Analyzer"></span>
### Analyzer `STRUCT`
分析器
```go
@ -165,32 +166,58 @@ type Analyzer struct {
m sync.Mutex
}
```
<span id="struct_Analyzer_Sub"></span>
#### func (*Analyzer) Sub(key string) *Analyzer
> 获取子分析器
***
<span id="struct_Analyzer_SetFormat"></span>
#### func (*Analyzer) SetFormat(key string, format func (v any) any)
> 设置格式化函数
***
<span id="struct_Analyzer_SetValueIfGreaterThan"></span>
#### func (*Analyzer) SetValueIfGreaterThan(key string, value float64)
> 设置指定 key 的值,当新值大于旧值时
> - 当已有值不为 float64 时,将会被忽略
***
<span id="struct_Analyzer_SetValueIfLessThan"></span>
#### func (*Analyzer) SetValueIfLessThan(key string, value float64)
> 设置指定 key 的值,当新值小于旧值时
> - 当已有值不为 float64 时,将会被忽略
***
<span id="struct_Analyzer_SetValueIf"></span>
#### func (*Analyzer) SetValueIf(key string, expression bool, value float64)
> 当表达式满足的时候将设置指定 key 的值为 value
***
<span id="struct_Analyzer_SetValueStringIf"></span>
#### func (*Analyzer) SetValueStringIf(key string, expression bool, value string)
> 当表达式满足的时候将设置指定 key 的值为 value
***
<span id="struct_Analyzer_SetValue"></span>
#### func (*Analyzer) SetValue(key string, value float64)
> 设置指定 key 的值
***
<span id="struct_Analyzer_SetValueString"></span>
#### func (*Analyzer) SetValueString(key string, value string)
> 设置指定 key 的值
***
<span id="struct_Analyzer_Increase"></span>
#### func (*Analyzer) Increase(key string, record R, recordKey string)
> 在指定 key 现有值的基础上增加 recordKey 的值
> - 当分析器已经记录过相同 key 的值时,会根据已有的值类型进行不同处理
@ -198,22 +225,39 @@ type Analyzer struct {
> 处理方式:
> - 当已有值类型为 string 时,将会使用新的值的 string 类型进行覆盖
> - 当已有值类型为 float64 时,当新的值类型不为 float64 时,将会被忽略
***
<span id="struct_Analyzer_IncreaseValue"></span>
#### func (*Analyzer) IncreaseValue(key string, value float64)
> 在指定 key 现有值的基础上增加 value
***
<span id="struct_Analyzer_IncreaseNonRepeat"></span>
#### func (*Analyzer) IncreaseNonRepeat(key string, record R, recordKey string, dimension ...string)
> 在指定 key 现有值的基础上增加 recordKey 的值,但是当去重维度 dimension 相同时,不会增加
***
<span id="struct_Analyzer_IncreaseValueNonRepeat"></span>
#### func (*Analyzer) IncreaseValueNonRepeat(key string, record R, value float64, dimension ...string)
> 在指定 key 现有值的基础上增加 value但是当去重维度 dimension 相同时,不会增加
***
<span id="struct_Analyzer_GetValue"></span>
#### func (*Analyzer) GetValue(key string) float64
> 获取当前记录的值
***
<span id="struct_Analyzer_GetValueString"></span>
#### func (*Analyzer) GetValueString(key string) string
> 获取当前记录的值
***
<span id="struct_Flusher"></span>
### Flusher `INTERFACE`
用于刷新缓冲区的接口
```go
@ -222,6 +266,7 @@ type Flusher interface {
Info() string
}
```
<span id="struct_FileFlusher"></span>
### FileFlusher `STRUCT`
```go
@ -233,53 +278,90 @@ type FileFlusher struct {
layoutLen int
}
```
<span id="struct_FileFlusher_Flush"></span>
#### func (*FileFlusher) Flush(records []string)
***
<span id="struct_FileFlusher_Info"></span>
#### func (*FileFlusher) Info() string
***
<span id="struct_Option"></span>
### Option `STRUCT`
选项
```go
type Option func(logger *logger)
```
<span id="struct_Result"></span>
### Result `STRUCT`
```go
type Result gjson.Result
```
<span id="struct_R"></span>
### R `STRUCT`
记录器所记录的一条数据
```go
type R string
```
<span id="struct_R_GetTime"></span>
#### func (R) GetTime(layout string) time.Time
> 获取该记录的时间
***
<span id="struct_R_Get"></span>
#### func (R) Get(key string) Result
> 获取指定 key 的值
> - 当 key 为嵌套 key 时,使用 . 进行分割例如a.b.c
> - 更多用法参考https://github.com/tidwall/gjson
***
<span id="struct_R_Exist"></span>
#### func (R) Exist(key string) bool
> 判断指定 key 是否存在
***
<span id="struct_R_GetString"></span>
#### func (R) GetString(key string) string
> 该函数为 Get(key).String() 的简写
***
<span id="struct_R_GetInt64"></span>
#### func (R) GetInt64(key string) int64
> 该函数为 Get(key).Int() 的简写
***
<span id="struct_R_GetInt"></span>
#### func (R) GetInt(key string) int
> 该函数为 Get(key).Int() 的简写,但是返回值为 int 类型
***
<span id="struct_R_GetFloat64"></span>
#### func (R) GetFloat64(key string) float64
> 该函数为 Get(key).Float() 的简写
***
<span id="struct_R_GetBool"></span>
#### func (R) GetBool(key string) bool
> 该函数为 Get(key).Bool() 的简写
***
<span id="struct_R_String"></span>
#### func (R) String() string
***
<span id="struct_Report"></span>
### Report `STRUCT`
分析报告
```go
@ -291,26 +373,50 @@ type Report struct {
Subs []*Report
}
```
<span id="struct_Report_Avg"></span>
#### func (*Report) Avg(key string) float64
> 计算平均值
***
<span id="struct_Report_Count"></span>
#### func (*Report) Count(key string) int64
> 获取特定 key 的计数次数
***
<span id="struct_Report_Sum"></span>
#### func (*Report) Sum(keys ...string) float64
> 获取特定 key 的总和
***
<span id="struct_Report_Sub"></span>
#### func (*Report) Sub(name string) *Report
> 获取特定名称的子报告
***
<span id="struct_Report_ReserveSubByPrefix"></span>
#### func (*Report) ReserveSubByPrefix(prefix string) *Report
> 仅保留特定前缀的子报告
***
<span id="struct_Report_ReserveSub"></span>
#### func (*Report) ReserveSub(names ...string) *Report
> 仅保留特定名称子报告
***
<span id="struct_Report_FilterSub"></span>
#### func (*Report) FilterSub(names ...string) *Report
> 将特定名称的子报告过滤掉
***
<span id="struct_Report_String"></span>
#### func (*Report) String() string
***

View File

@ -45,23 +45,24 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[CompareExpression](#compareexpression)|比较表达式
|`STRUCT`|[CompareExpression](#struct_CompareExpression)|比较表达式
</details>
***
## 详情信息
#### func Compare(a V, expression CompareExpression, b V) bool
#### func Compare\[V generic.Ordered\](a V, expression CompareExpression, b V) bool
<span id="Compare"></span>
> 根据特定表达式比较两个值
***
#### func IsContinuity(values S) bool
#### func IsContinuity\[S ~[]V, V generic.Integer\](values S) bool
<span id="IsContinuity"></span>
> 检查一组值是否连续
示例代码:
**示例代码:**
```go
func ExampleIsContinuity() {
@ -72,82 +73,83 @@ func ExampleIsContinuity() {
```
***
#### func IsContinuityWithSort(values S) bool
#### func IsContinuityWithSort\[S ~[]V, V generic.Integer\](values S) bool
<span id="IsContinuityWithSort"></span>
> 检查一组值排序后是否连续
***
#### func GetDefaultTolerance() float64
#### func GetDefaultTolerance() float64
<span id="GetDefaultTolerance"></span>
> 获取默认误差范围
***
#### func Pow(a int, n int) int
#### func Pow(a int, n int) int
<span id="Pow"></span>
> 整数幂运算
***
#### func PowInt64(a int64, n int64) int64
#### func PowInt64(a int64, n int64) int64
<span id="PowInt64"></span>
> 整数幂运算
***
#### func Min(a V, b V) V
#### func Min\[V generic.Number\](a V, b V) V
<span id="Min"></span>
> 返回两个数之中较小的值
***
#### func Max(a V, b V) V
#### func Max\[V generic.Number\](a V, b V) V
<span id="Max"></span>
> 返回两个数之中较大的值
***
#### func MinMax(a V, b V) (min V, max V)
#### func MinMax\[V generic.Number\](a V, b V) (min V, max V)
<span id="MinMax"></span>
> 将两个数按照较小的和较大的顺序进行返回
***
#### func MaxMin(a V, b V) (max V, min V)
#### func MaxMin\[V generic.Number\](a V, b V) (max V, min V)
<span id="MaxMin"></span>
> 将两个数按照较大的和较小的顺序进行返回
***
#### func Clamp(value V, min V, max V) V
#### func Clamp\[V generic.Number\](value V, min V, max V) V
<span id="Clamp"></span>
> 将给定值限制在最小值和最大值之间
***
#### func Tolerance(value1 V, value2 V, tolerance V) bool
#### func Tolerance\[V generic.Number\](value1 V, value2 V, tolerance V) bool
<span id="Tolerance"></span>
> 检查两个值是否在一个误差范围内
***
#### func Merge(refer V, a V, b V) V
#### func Merge\[V generic.SignedNumber\](refer V, a V, b V) V
<span id="Merge"></span>
> 通过一个参考值合并两个数字
***
#### func UnMerge(refer V, num V) (a V, b V)
#### func UnMerge\[V generic.SignedNumber\](refer V, num V) (a V, b V)
<span id="UnMerge"></span>
> 通过一个参考值取消合并的两个数字
***
#### func MergeToInt64(v1 V, v2 V) int64
#### func MergeToInt64\[V generic.SignedNumber\](v1 V, v2 V) int64
<span id="MergeToInt64"></span>
> 将两个数字合并为一个 int64 数字
***
#### func UnMergeInt64(n int64) V, V
#### func UnMergeInt64\[V generic.SignedNumber\](n int64) (V, V)
<span id="UnMergeInt64"></span>
> 将一个 int64 数字拆分为两个数字
***
#### func ToContinuous(nums S) map[V]V
#### func ToContinuous\[S ~[]V, V generic.Integer\](nums S) map[V]V
<span id="ToContinuous"></span>
> 将一组非连续的数字转换为从1开始的连续数字
> - 返回值是一个 mapkey 是从 1 开始的连续数字value 是原始数字
示例代码:
**示例代码:**
```go
func ExampleToContinuous() {
@ -160,34 +162,34 @@ func ExampleToContinuous() {
```
***
#### func CountDigits(num V) int
#### func CountDigits\[V generic.Number\](num V) int
<span id="CountDigits"></span>
> 接收一个整数 num 作为输入,并返回该数字的位数
***
#### func GetDigitValue(num int64, digit int) int64
#### func GetDigitValue(num int64, digit int) int64
<span id="GetDigitValue"></span>
> 接收一个整数 num 和一个表示目标位数的整数 digit 作为输入,并返
> 回数字 num 在指定位数上的数值。我们使用 math.Abs() 函数获取 num 的绝对值,并通
> 过除以10的操作将 num 移动到目标位数上。然后,通过取余运算得到位数上的数值
***
#### func JoinNumbers(num1 V, n ...V) V
#### func JoinNumbers\[V generic.Number\](num1 V, n ...V) V
<span id="JoinNumbers"></span>
> 将一组数字连接起来
***
#### func IsOdd(n V) bool
#### func IsOdd\[V generic.Integer\](n V) bool
<span id="IsOdd"></span>
> 返回 n 是否为奇数
***
#### func IsEven(n V) bool
#### func IsEven\[V generic.Integer\](n V) bool
<span id="IsEven"></span>
> 返回 n 是否为偶数
***
#### func MakeLastDigitsZero(num T, digits int) T
#### func MakeLastDigitsZero\[T generic.Number\](num T, digits int) T
<span id="MakeLastDigitsZero"></span>
> 返回一个新的数,其中 num 的最后 digits 位数被设为零。
> - 函数首先创建一个 10 的 digits 次方的遮罩,然后通过整除和乘以这个遮罩来使 num 的最后 digits 位归零。
@ -213,6 +215,7 @@ func TestMakeLastDigitsZero(t *testing.T) {
***
<span id="struct_CompareExpression"></span>
### CompareExpression `STRUCT`
比较表达式
```go

View File

@ -26,7 +26,7 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Option](#option)|暂无描述...
|`STRUCT`|[Option](#struct_Option)|暂无描述...
</details>
@ -38,7 +38,7 @@
> 运行持久化缓存程序
***
#### func BindPersistCacheProgram(name string, handler OutputParamHandlerFunc, option ...*Option) func ()
#### func BindPersistCacheProgram\[OutputParamHandlerFunc any\](name string, handler OutputParamHandlerFunc, option ...*Option) func ()
<span id="BindPersistCacheProgram"></span>
> 绑定持久化缓存程序
> - name 持久化缓存程序名称
@ -52,7 +52,7 @@
> - 所有持久化程序绑定完成后,应该主动调用 Run 函数运行
***
#### func BindAction(name string, handler Func) Func
#### func BindAction\[Func any\](name string, handler Func) Func
<span id="BindAction"></span>
> 绑定需要缓存的操作函数
> - name 缓存操作名称
@ -92,10 +92,11 @@ func TestBindAction(t *testing.T) {
***
#### func NewOption() *Option
#### func NewOption() *Option
<span id="NewOption"></span>
***
<span id="struct_Option"></span>
### Option `STRUCT`
```go
@ -106,10 +107,13 @@ type Option struct {
delay time.Duration
}
```
<span id="struct_Option_WithPeriodicity"></span>
#### func (*Option) WithPeriodicity(ticker *timer.Ticker, firstDelay time.Duration, interval time.Duration, delay time.Duration) *Option
> 设置持久化周期
> - ticker 定时器,通常建议使用服务器的定时器,这样可以降低多线程的程序复杂性
> - firstDelay 首次持久化延迟,当首次持久化为 0 时,将会在下一个持久化周期开始时持久化
> - interval 持久化间隔
> - delay 每条数据持久化间隔适当的设置该值可以使持久化期间尽量降低对用户体验的影响如果为0将会一次性持久化所有数据
***

View File

@ -27,21 +27,22 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[TwoDimensional](#twodimensional)|用于2D对象移动的数据结构
|`INTERFACE`|[TwoDimensionalEntity](#twodimensionalentity)|2D移动对象接口定义
|`STRUCT`|[Position2DChangeEventHandle](#position2dchangeeventhandle)|暂无描述...
|`STRUCT`|[TwoDimensionalOption](#twodimensionaloption)|暂无描述...
|`STRUCT`|[TwoDimensional](#struct_TwoDimensional)|用于2D对象移动的数据结构
|`INTERFACE`|[TwoDimensionalEntity](#struct_TwoDimensionalEntity)|2D移动对象接口定义
|`STRUCT`|[Position2DChangeEventHandle](#struct_Position2DChangeEventHandle)|暂无描述...
|`STRUCT`|[TwoDimensionalOption](#struct_TwoDimensionalOption)|暂无描述...
</details>
***
## 详情信息
#### func NewTwoDimensional(options ...TwoDimensionalOption[EID, PosType]) *TwoDimensional[EID, PosType]
#### func NewTwoDimensional\[EID generic.Basic, PosType generic.SignedNumber\](options ...TwoDimensionalOption[EID, PosType]) *TwoDimensional[EID, PosType]
<span id="NewTwoDimensional"></span>
> 创建一个用于2D对象移动的实例(TwoDimensional)
示例代码:
**示例代码:**
```go
func ExampleNewTwoDimensional() {
@ -74,27 +75,28 @@ func TestNewTwoDimensional(t *testing.T) {
***
#### func WithTwoDimensionalTimeUnit(duration time.Duration) TwoDimensionalOption[EID, PosType]
#### func WithTwoDimensionalTimeUnit\[EID generic.Basic, PosType generic.SignedNumber\](duration time.Duration) TwoDimensionalOption[EID, PosType]
<span id="WithTwoDimensionalTimeUnit"></span>
> 通过特定时间单位创建
> - 默认单位为1毫秒最小单位也为1毫秒
***
#### func WithTwoDimensionalIdleWaitTime(duration time.Duration) TwoDimensionalOption[EID, PosType]
#### func WithTwoDimensionalIdleWaitTime\[EID generic.Basic, PosType generic.SignedNumber\](duration time.Duration) TwoDimensionalOption[EID, PosType]
<span id="WithTwoDimensionalIdleWaitTime"></span>
> 通过特定的空闲等待时间创建
> - 默认情况下在没有新的移动计划时将限制 100毫秒 + 移动间隔事件(默认100毫秒)
***
#### func WithTwoDimensionalInterval(duration time.Duration) TwoDimensionalOption[EID, PosType]
#### func WithTwoDimensionalInterval\[EID generic.Basic, PosType generic.SignedNumber\](duration time.Duration) TwoDimensionalOption[EID, PosType]
<span id="WithTwoDimensionalInterval"></span>
> 通过特定的移动间隔时间创建
***
#### func NewEntity(guid int64, speed float64) *MoveEntity
#### func NewEntity(guid int64, speed float64) *MoveEntity
<span id="NewEntity"></span>
***
<span id="struct_TwoDimensional"></span>
### TwoDimensional `STRUCT`
用于2D对象移动的数据结构
- 通过对象调用 MoveTo 方法后将开始执行该对象的移动
@ -113,6 +115,152 @@ type TwoDimensional[EID generic.Basic, PosType generic.SignedNumber] struct {
position2DStopMoveEventHandles []Position2DStopMoveEventHandle[EID, PosType]
}
```
<span id="struct_TwoDimensional_MoveTo"></span>
#### func (*TwoDimensional) MoveTo(entity TwoDimensionalEntity[EID, PosType], x PosType, y PosType)
> 设置对象移动到特定位置
**示例代码:**
```go
func ExampleTwoDimensional_MoveTo() {
m := moving2.NewTwoDimensional(moving2.WithTwoDimensionalTimeUnit[int64, float64](time.Second))
defer func() {
m.Release()
}()
var wait sync.WaitGroup
m.RegPosition2DDestinationEvent(func(moving *moving2.TwoDimensional[int64, float64], entity moving2.TwoDimensionalEntity[int64, float64]) {
fmt.Println("done")
wait.Done()
})
wait.Add(1)
entity := NewEntity(1, 100)
m.MoveTo(entity, 50, 30)
wait.Wait()
}
```
***
<span id="struct_TwoDimensional_StopMove"></span>
#### func (*TwoDimensional) StopMove(id EID)
> 停止特定对象的移动
**示例代码:**
```go
func ExampleTwoDimensional_StopMove() {
m := moving2.NewTwoDimensional(moving2.WithTwoDimensionalTimeUnit[int64, float64](time.Second))
defer func() {
m.Release()
}()
var wait sync.WaitGroup
m.RegPosition2DChangeEvent(func(moving *moving2.TwoDimensional[int64, float64], entity moving2.TwoDimensionalEntity[int64, float64], oldX, oldY float64) {
fmt.Println("move")
})
m.RegPosition2DStopMoveEvent(func(moving *moving2.TwoDimensional[int64, float64], entity moving2.TwoDimensionalEntity[int64, float64]) {
fmt.Println("stop")
wait.Done()
})
m.RegPosition2DDestinationEvent(func(moving *moving2.TwoDimensional[int64, float64], entity moving2.TwoDimensionalEntity[int64, float64]) {
fmt.Println("done")
wait.Done()
})
wait.Add(1)
entity := NewEntity(1, 100)
m.MoveTo(entity, 50, 300)
m.StopMove(1)
wait.Wait()
}
```
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestTwoDimensional_StopMove(t *testing.T) {
var wait sync.WaitGroup
m := moving2.NewTwoDimensional(moving2.WithTwoDimensionalTimeUnit[int64, float64](time.Second))
defer func() {
m.Release()
}()
m.RegPosition2DChangeEvent(func(moving *moving2.TwoDimensional[int64, float64], entity moving2.TwoDimensionalEntity[int64, float64], oldX, oldY float64) {
x, y := entity.GetPosition().GetXY()
fmt.Println(fmt.Sprintf("%d : %d | %f, %f > %f, %f", entity.GetTwoDimensionalEntityID(), time.Now().UnixMilli(), oldX, oldY, x, y))
})
m.RegPosition2DDestinationEvent(func(moving *moving2.TwoDimensional[int64, float64], entity moving2.TwoDimensionalEntity[int64, float64]) {
fmt.Println(fmt.Sprintf("%d : %d | destination", entity.GetTwoDimensionalEntityID(), time.Now().UnixMilli()))
wait.Done()
})
m.RegPosition2DStopMoveEvent(func(moving *moving2.TwoDimensional[int64, float64], entity moving2.TwoDimensionalEntity[int64, float64]) {
fmt.Println(fmt.Sprintf("%d : %d | stop", entity.GetTwoDimensionalEntityID(), time.Now().UnixMilli()))
wait.Done()
})
for i := 0; i < 10; i++ {
wait.Add(1)
entity := NewEntity(int64(i)+1, float64(10+i))
m.MoveTo(entity, 50, 30)
}
time.Sleep(time.Second * 1)
for i := 0; i < 10; i++ {
m.StopMove(int64(i) + 1)
}
wait.Wait()
}
```
</details>
***
<span id="struct_TwoDimensional_RegPosition2DChangeEvent"></span>
#### func (*TwoDimensional) RegPosition2DChangeEvent(handle Position2DChangeEventHandle[EID, PosType])
> 在对象位置改变时将执行注册的事件处理函数
***
<span id="struct_TwoDimensional_OnPosition2DChangeEvent"></span>
#### func (*TwoDimensional) OnPosition2DChangeEvent(entity TwoDimensionalEntity[EID, PosType], oldX PosType, oldY PosType)
***
<span id="struct_TwoDimensional_RegPosition2DDestinationEvent"></span>
#### func (*TwoDimensional) RegPosition2DDestinationEvent(handle Position2DDestinationEventHandle[EID, PosType])
> 在对象到达终点时将执行被注册的事件处理函数
***
<span id="struct_TwoDimensional_OnPosition2DDestinationEvent"></span>
#### func (*TwoDimensional) OnPosition2DDestinationEvent(entity TwoDimensionalEntity[EID, PosType])
***
<span id="struct_TwoDimensional_RegPosition2DStopMoveEvent"></span>
#### func (*TwoDimensional) RegPosition2DStopMoveEvent(handle Position2DStopMoveEventHandle[EID, PosType])
> 在对象停止移动时将执行被注册的事件处理函数
***
<span id="struct_TwoDimensional_OnPosition2DStopMoveEvent"></span>
#### func (*TwoDimensional) OnPosition2DStopMoveEvent(entity TwoDimensionalEntity[EID, PosType])
***
<span id="struct_TwoDimensional_Release"></span>
#### func (*TwoDimensional) Release()
> 释放对象移动对象所占用的资源
***
<span id="struct_TwoDimensionalEntity"></span>
### TwoDimensionalEntity `INTERFACE`
2D移动对象接口定义
```go
@ -123,11 +271,13 @@ type TwoDimensionalEntity[EID generic.Basic, PosType generic.SignedNumber] inter
SetPosition(geometry.Point[PosType])
}
```
<span id="struct_Position2DChangeEventHandle"></span>
### Position2DChangeEventHandle `STRUCT`
```go
type Position2DChangeEventHandle[EID generic.Basic, PosType generic.SignedNumber] func(moving *TwoDimensional[EID, PosType], entity TwoDimensionalEntity[EID, PosType], oldX PosType)
```
<span id="struct_TwoDimensionalOption"></span>
### TwoDimensionalOption `STRUCT`
```go

View File

@ -27,14 +27,14 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Time](#time)|带有偏移量的时间
|`STRUCT`|[Time](#struct_Time)|带有偏移量的时间
</details>
***
## 详情信息
#### func NewTime(offset time.Duration) *Time
#### func NewTime(offset time.Duration) *Time
<span id="NewTime"></span>
> 新建一个包含偏移的时间
@ -44,21 +44,22 @@
> 设置全局偏移时间
***
#### func GetGlobal() *Time
#### func GetGlobal() *Time
<span id="GetGlobal"></span>
> 获取全局偏移时间
***
#### func Now() time.Time
#### func Now() time.Time
<span id="Now"></span>
> 获取当前时间偏移后的时间
***
#### func Since(t time.Time) time.Duration
#### func Since(t time.Time) time.Duration
<span id="Since"></span>
> 获取当前时间偏移后的时间自从 t 以来经过的时间
***
<span id="struct_Time"></span>
### Time `STRUCT`
带有偏移量的时间
```go
@ -66,12 +67,21 @@ type Time struct {
offset time.Duration
}
```
<span id="struct_Time_SetOffset"></span>
#### func (*Time) SetOffset(offset time.Duration)
> 设置时间偏移
***
<span id="struct_Time_Now"></span>
#### func (*Time) Now() time.Time
> 获取当前时间偏移后的时间
***
<span id="struct_Time_Since"></span>
#### func (*Time) Since(t time.Time) time.Duration
> 获取当前时间偏移后的时间自从 t 以来经过的时间
***

View File

@ -20,6 +20,7 @@
|[DiceN](#DiceN)|掷骰子
|[NetIP](#NetIP)|返回一个随机的IP地址
|[Port](#Port)|返回一个随机的端口号
|[UsablePort](#UsablePort)|随机返回一个可用的端口号,如果没有可用端口号则返回 -1
|[IPv4](#IPv4)|返回一个随机产生的IPv4地址。
|[IPv4Port](#IPv4Port)|返回一个随机产生的IPv4地址和端口。
|[Int64](#Int64)|返回一个介于min和max之间的int64类型的随机数。
@ -52,74 +53,79 @@
***
## 详情信息
#### func Dice() int
#### func Dice() int
<span id="Dice"></span>
> 掷骰子
> - 常规掷骰子将返回 1-6 的随机数
***
#### func DiceN(n int) int
#### func DiceN(n int) int
<span id="DiceN"></span>
> 掷骰子
> - 与 Dice 不同的是,将返回 1-N 的随机数
***
#### func NetIP() net.IP
#### func NetIP() net.IP
<span id="NetIP"></span>
> 返回一个随机的IP地址
***
#### func Port() int
#### func Port() int
<span id="Port"></span>
> 返回一个随机的端口号
***
#### func IPv4() string
#### func UsablePort() int
<span id="UsablePort"></span>
> 随机返回一个可用的端口号,如果没有可用端口号则返回 -1
***
#### func IPv4() string
<span id="IPv4"></span>
> 返回一个随机产生的IPv4地址。
***
#### func IPv4Port() string
#### func IPv4Port() string
<span id="IPv4Port"></span>
> 返回一个随机产生的IPv4地址和端口。
***
#### func Int64(min int64, max int64) int64
#### func Int64(min int64, max int64) int64
<span id="Int64"></span>
> 返回一个介于min和max之间的int64类型的随机数。
***
#### func Int(min int, max int) int
#### func Int(min int, max int) int
<span id="Int"></span>
> 返回一个介于min和max之间的的int类型的随机数。
***
#### func Duration(min int64, max int64) time.Duration
#### func Duration(min int64, max int64) time.Duration
<span id="Duration"></span>
> 返回一个介于min和max之间的的Duration类型的随机数。
***
#### func Float64() float64
#### func Float64() float64
<span id="Float64"></span>
> 返回一个0~1的浮点数
***
#### func Float32() float32
#### func Float32() float32
<span id="Float32"></span>
> 返回一个0~1的浮点数
***
#### func IntN(n int) int
#### func IntN(n int) int
<span id="IntN"></span>
> 返回一个0~n的整数
***
#### func Bool() bool
#### func Bool() bool
<span id="Bool"></span>
> 返回一个随机的布尔值
***
#### func ProbabilitySlice(getProbabilityHandle func (data T) float64, data ...T) (hit T, miss bool)
#### func ProbabilitySlice\[T any\](getProbabilityHandle func (data T) float64, data ...T) (hit T, miss bool)
<span id="ProbabilitySlice"></span>
> 按概率随机从切片中产生一个数据并返回命中的对象及是否未命中
> - 当总概率小于 1 将会发生未命中的情况
@ -147,19 +153,19 @@ func TestProbabilitySlice(t *testing.T) {
***
#### func ProbabilitySliceIndex(getProbabilityHandle func (data T) float64, data ...T) (hit T, index int, miss bool)
#### func ProbabilitySliceIndex\[T any\](getProbabilityHandle func (data T) float64, data ...T) (hit T, index int, miss bool)
<span id="ProbabilitySliceIndex"></span>
> 按概率随机从切片中产生一个数据并返回命中的对象及对象索引以及是否未命中
> - 当总概率小于 1 将会发生未命中的情况
***
#### func Probability(p int, full ...int) bool
#### func Probability(p int, full ...int) bool
<span id="Probability"></span>
> 输入一个概率,返回是否命中
> - 当 full 不为空时,将以 full 为基数p 为分子,计算命中概率
***
#### func ProbabilityChooseOne(ps ...int) int
#### func ProbabilityChooseOne(ps ...int) int
<span id="ProbabilityChooseOne"></span>
> 输入一组概率,返回命中的索引
@ -168,54 +174,54 @@ func TestProbabilitySlice(t *testing.T) {
<span id="RefreshSeed"></span>
***
#### func ChineseName() string
#### func ChineseName() string
<span id="ChineseName"></span>
> 返回一个随机组成的中文姓名。
***
#### func EnglishName() string
#### func EnglishName() string
<span id="EnglishName"></span>
> 返回一个随机组成的英文姓名。
***
#### func Name() string
#### func Name() string
<span id="Name"></span>
> 返回一个随机组成的中文或英文姓名
> - 以1/2的概率决定生产的是中文还是英文姓名。
***
#### func NumberString(min int, max int) string
#### func NumberString(min int, max int) string
<span id="NumberString"></span>
> 返回一个介于min和max之间的string类型的随机数。
***
#### func NumberStringRepair(min int, max int) string
#### func NumberStringRepair(min int, max int) string
<span id="NumberStringRepair"></span>
> 返回一个介于min和max之间的string类型的随机数
> - 通过Int64生成一个随机数当结果的字符串长度小于max的字符串长度的情况下使用0在开头补齐。
***
#### func HostName() string
#### func HostName() string
<span id="HostName"></span>
> 返回一个随机产生的hostname。
***
#### func WeightSlice(getWeightHandle func (data T) int64, data ...T) T
#### func WeightSlice\[T any\](getWeightHandle func (data T) int64, data ...T) T
<span id="WeightSlice"></span>
> 按权重随机从切片中产生一个数据并返回
***
#### func WeightSliceIndex(getWeightHandle func (data T) int64, data ...T) (item T, index int)
#### func WeightSliceIndex\[T any\](getWeightHandle func (data T) int64, data ...T) (item T, index int)
<span id="WeightSliceIndex"></span>
> 按权重随机从切片中产生一个数据并返回数据和对应索引
***
#### func WeightMap(getWeightHandle func (data T) int64, data map[K]T) T
#### func WeightMap\[K comparable, T any\](getWeightHandle func (data T) int64, data map[K]T) T
<span id="WeightMap"></span>
> 按权重随机从map中产生一个数据并返回
***
#### func WeightMapKey(getWeightHandle func (data T) int64, data map[K]T) (item T, key K)
#### func WeightMapKey\[K comparable, T any\](getWeightHandle func (data T) int64, data map[K]T) (item T, key K)
<span id="WeightMapKey"></span>
> 按权重随机从map中产生一个数据并返回数据和对应 key

View File

@ -15,6 +15,20 @@ func Port() int {
return Int(1, 65535)
}
// UsablePort 随机返回一个可用的端口号,如果没有可用端口号则返回 -1
func UsablePort() int {
addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0")
if err != nil {
return -1
}
cli, err := net.ListenTCP("tcp", addr)
if err != nil {
return -1
}
defer func() { _ = cli.Close() }()
return cli.Addr().(*net.TCPAddr).Port
}
// IPv4 返回一个随机产生的IPv4地址。
func IPv4() string {
return fmt.Sprintf("%d.%d.%d.%d", Int(1, 255), Int(0, 255), Int(0, 255), Int(0, 255))

View File

@ -32,27 +32,27 @@
***
## 详情信息
#### func WrapperFunc(f any, wrapper func (call func ( []reflect.Value) []reflect.Value) func (args []reflect.Value) []reflect.Value) (wf Func, err error)
#### func WrapperFunc\[Func any\](f any, wrapper func (call func ( []reflect.Value) []reflect.Value) func (args []reflect.Value) []reflect.Value) (wf Func, err error)
<span id="WrapperFunc"></span>
> 包装函数
***
#### func WrapperFuncBefore2After(f Func, before func (), after func ()) (wf Func, err error)
#### func WrapperFuncBefore2After\[Func any\](f Func, before func (), after func ()) (wf Func, err error)
<span id="WrapperFuncBefore2After"></span>
> 包装函数,前置函数执行前,后置函数执行后
***
#### func WrapperFuncBefore(f Func, before func ()) (wf Func, err error)
#### func WrapperFuncBefore\[Func any\](f Func, before func ()) (wf Func, err error)
<span id="WrapperFuncBefore"></span>
> 包装函数,前置函数执行前
***
#### func WrapperFuncAfter(f Func, after func ()) (wf Func, err error)
#### func WrapperFuncAfter\[Func any\](f Func, after func ()) (wf Func, err error)
<span id="WrapperFuncAfter"></span>
> 包装函数,后置函数执行后
***
#### func GetPtrUnExportFiled(s reflect.Value, filedIndex int) reflect.Value
#### func GetPtrUnExportFiled(s reflect.Value, filedIndex int) reflect.Value
<span id="GetPtrUnExportFiled"></span>
> 获取指针类型的未导出字段
@ -62,12 +62,12 @@
> 设置指针类型的未导出字段
***
#### func Copy(s reflect.Value) reflect.Value
#### func Copy(s reflect.Value) reflect.Value
<span id="Copy"></span>
> 拷贝
***
#### func GetPointer(src T) reflect.Value
#### func GetPointer\[T any\](src T) reflect.Value
<span id="GetPointer"></span>
> 获取指针

View File

@ -29,27 +29,27 @@
***
## 详情信息
#### func GetWorkingDir() string
#### func GetWorkingDir() string
<span id="GetWorkingDir"></span>
> 获取工作目录绝对路径
***
#### func GetTempDir() string
#### func GetTempDir() string
<span id="GetTempDir"></span>
> 获取系统临时目录
***
#### func GetExecutablePathByBuild() string
#### func GetExecutablePathByBuild() string
<span id="GetExecutablePathByBuild"></span>
> 获取当前执行文件绝对路径go build
***
#### func GetExecutablePathByCaller() string
#### func GetExecutablePathByCaller() string
<span id="GetExecutablePathByCaller"></span>
> 获取当前执行文件绝对路径go run
***
#### func CurrentRunningFuncName(skip ...int) string
#### func CurrentRunningFuncName(skip ...int) string
<span id="CurrentRunningFuncName"></span>
> 获取正在运行的函数名

View File

@ -38,7 +38,7 @@
|类型|名称|描述
|:--|:--|:--
|`STRUCT`|[Once](#once)|用于数据取值去重的结构体
|`STRUCT`|[Once](#struct_Once)|用于数据取值去重的结构体
</details>
@ -55,7 +55,7 @@
> 解除注销特定命名空间的唯一标识符
***
#### func Get() int64
#### func Get() int64
<span id="Get"></span>
> 获取全局唯一标识符
@ -65,7 +65,7 @@
> 重置全局唯一标识符
***
#### func GetWith(name any) int64
#### func GetWith(name any) int64
<span id="GetWith"></span>
> 获取特定命名空间的唯一标识符
@ -75,17 +75,17 @@
> 重置特定命名空间的唯一标识符
***
#### func NewOnce() *Once[V]
#### func NewOnce\[V any\]() *Once[V]
<span id="NewOnce"></span>
> 创建一个用于数据取值去重的结构实例
***
#### func SonyflakeIDE() int64, error
#### func SonyflakeIDE() (int64, error)
<span id="SonyflakeIDE"></span>
> 获取一个雪花id
***
#### func SonyflakeID() int64
#### func SonyflakeID() int64
<span id="SonyflakeID"></span>
> 获取一个雪花id
@ -95,36 +95,37 @@
> 配置雪花id生成策略
***
#### func AutoIncrementUint32() uint32
#### func AutoIncrementUint32() uint32
<span id="AutoIncrementUint32"></span>
> 获取一个自增的 uint32 值
***
#### func AutoIncrementUint64() uint64
#### func AutoIncrementUint64() uint64
<span id="AutoIncrementUint64"></span>
> 获取一个自增的 uint64 值
***
#### func AutoIncrementInt32() int32
#### func AutoIncrementInt32() int32
<span id="AutoIncrementInt32"></span>
> 获取一个自增的 int32 值
***
#### func AutoIncrementInt64() int64
#### func AutoIncrementInt64() int64
<span id="AutoIncrementInt64"></span>
> 获取一个自增的 int64 值
***
#### func AutoIncrementInt() int
#### func AutoIncrementInt() int
<span id="AutoIncrementInt"></span>
> 获取一个自增的 int 值
***
#### func AutoIncrementString() string
#### func AutoIncrementString() string
<span id="AutoIncrementString"></span>
> 获取一个自增的字符串
***
<span id="struct_Once"></span>
### Once `STRUCT`
用于数据取值去重的结构体
```go
@ -132,9 +133,15 @@ type Once[V any] struct {
r map[any]struct{}
}
```
<span id="struct_Once_Get"></span>
#### func (*Once) Get(key any, value V, defaultValue V) V
> 获取一个值,当该值已经被获取过的时候,返回 defaultValue否则返回 value
***
<span id="struct_Once_Reset"></span>
#### func (*Once) Reset(key ...any)
> 当 key 数量大于 0 时,将会重置对应 key 的记录,否则重置所有记录
***

View File

@ -25,7 +25,7 @@
***
## 详情信息
#### func Topological(slice S, queryIndexHandler func (item V) Index, queryDependsHandler func (item V) []Index) S, error
#### func Topological\[S ~[]V, Index comparable, V any\](slice S, queryIndexHandler func (item V) Index, queryDependsHandler func (item V) []Index) (S, error)
<span id="Topological"></span>
> 拓扑排序是一种对有向图进行排序的算法,它可以用来解决一些依赖关系的问题,比如计算字段的依赖关系。拓扑排序会将存在依赖关系的元素进行排序,使得依赖关系的元素总是排在被依赖的元素之前。
> - slice: 需要排序的切片
@ -34,7 +34,8 @@
>
> 该函数在存在循环依赖的情况下将会返回 ErrCircularDependencyDetected 错误
示例代码:
**示例代码:**
```go
func ExampleTopological() {

Some files were not shown because too many files have changed in this diff Show More