diff --git a/server/conn.go b/server/conn.go index e7bdb178..f26b9a08 100644 --- a/server/conn.go +++ b/server/conn.go @@ -156,7 +156,7 @@ func (c *conn) initWriteQueue() { if atomic.LoadInt32(&c.state) == ConnStatusClosed { break // 连接已关闭,退出写入循环,交给下一次循环处理关闭 } - c.server.events.onConnectionAsyncWriteError(c, p, err) + c.server.events.OnConnectionAsyncWriteError(c, p, err) } } } diff --git a/server/controller.go b/server/controller.go index b3a32bff..6f212573 100644 --- a/server/controller.go +++ b/server/controller.go @@ -11,13 +11,15 @@ type Controller interface { // GetServer 获取服务器 GetServer() Server // RegisterConnection 注册连接 - RegisterConnection(conn net.Conn, writer ConnWriter) + RegisterConnection(conn net.Conn, writer ConnWriter, callback func(conn Conn)) // EliminateConnection 消除连接 EliminateConnection(conn net.Conn, err error) // ReactPacket 反应连接数据包 ReactPacket(conn net.Conn, packet Packet) // GetAnts 获取服务器异步池 GetAnts() *ants.Pool + // OnConnectionAsyncWriteError 注册连接异步写入数据错误事件 + OnConnectionAsyncWriteError(conn Conn, packet Packet, err error) } type controller struct { @@ -39,10 +41,13 @@ func (s *controller) GetAnts() *ants.Pool { return s.server.ants } -func (s *controller) RegisterConnection(conn net.Conn, writer ConnWriter) { +func (s *controller) RegisterConnection(conn net.Conn, writer ConnWriter, callback func(conn Conn)) { s.server.PublishSyncMessage(s.getSysQueue(), func(ctx context.Context) { c := newConn(s.server, conn, writer) s.server.connections[conn] = c + if callback != nil { + callback(c) + } s.events.onConnectionOpened(c) }) } diff --git a/server/events.go b/server/events.go index c0707ec4..9326fa23 100644 --- a/server/events.go +++ b/server/events.go @@ -133,7 +133,7 @@ func (s *events) RegisterConnectionAsyncWriteErrorEvent(handler ConnectionAsyncW s.connectionAsyncWriteErrorEventHandlers.AppendByOptionalPriority(handler, priority...) } -func (s *events) onConnectionAsyncWriteError(conn Conn, packet Packet, err error) { +func (s *events) OnConnectionAsyncWriteError(conn Conn, packet Packet, err error) { s.PublishSyncMessage(conn.GetQueue(), func(ctx context.Context) { s.connectionAsyncWriteErrorEventHandlers.RangeValue(func(index int, value ConnectionAsyncWriteErrorEventHandler) bool { value(s, conn, packet, err) diff --git a/server/network/gnet.go b/server/network/gnet.go new file mode 100644 index 00000000..bf661b2e --- /dev/null +++ b/server/network/gnet.go @@ -0,0 +1,97 @@ +package network + +import ( + "context" + "fmt" + "github.com/kercylan98/minotaur/server" + "github.com/kercylan98/minotaur/toolkit/collection" + "github.com/panjf2000/gnet/v2" + "time" +) + +var ( + schemaWebSocket = "ws" + schemaTcp = "tcp" + schemaTcp4 = "tcp4" + schemaTcp6 = "tcp6" + schemaUdp = "udp" + schemaUdp4 = "udp4" + schemaUdp6 = "udp6" + schemaUnix = "unix" +) + +type gNetHandler interface { + OnInit(core *gNetCore) + gnet.EventHandler + + GetEngine() *gnet.Engine +} + +func newGNetCore(handler gNetHandler, schema, addr string, pattern ...string) server.Network { + ws := &gNetCore{ + handler: handler, + addr: addr, + schema: schema, + pattern: collection.FindFirstOrDefaultInSlice(pattern, "/"), + } + return ws +} + +type gNetCore struct { + ctx context.Context + controller server.Controller + handler gNetHandler + addr string + schema string + pattern string +} + +func (w *gNetCore) OnSetup(ctx context.Context, controller server.Controller) (err error) { + w.ctx = ctx + w.controller = controller + return +} + +func (w *gNetCore) OnRun() (err error) { + var addr string + switch w.schema { + case schemaTcp, schemaWebSocket: + addr = fmt.Sprintf("tcp://%s", w.addr) + case schemaTcp4: + addr = fmt.Sprintf("tcp4://%s", w.addr) + case schemaTcp6: + addr = fmt.Sprintf("tcp6://%s", w.addr) + case schemaUdp: + addr = fmt.Sprintf("udp://%s", w.addr) + case schemaUdp4: + addr = fmt.Sprintf("udp4://%s", w.addr) + case schemaUdp6: + addr = fmt.Sprintf("udp6://%s", w.addr) + case schemaUnix: + addr = fmt.Sprintf("unix://%s", w.addr) + default: + return fmt.Errorf("unsupported schema: %s", w.schema) + } + err = gnet.Run(w.handler, addr) + return +} + +func (w *gNetCore) OnShutdown() error { + if w.handler.GetEngine() != nil { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + return w.handler.GetEngine().Stop(ctx) + } + return nil +} + +func (w *gNetCore) Schema() string { + return w.schema +} + +func (w *gNetCore) Address() string { + if w.pattern == "/" { + return w.addr + } + return fmt.Sprintf("%s:%s", w.addr, w.pattern) +} diff --git a/server/network/gnet_handler.go b/server/network/gnet_handler.go new file mode 100644 index 00000000..179169ec --- /dev/null +++ b/server/network/gnet_handler.go @@ -0,0 +1,64 @@ +package network + +import ( + "github.com/kercylan98/minotaur/server" + "github.com/panjf2000/gnet/v2" + "time" +) + +type gNetGenericHandler struct { + engine *gnet.Engine + *gNetCore +} + +func (t *gNetGenericHandler) OnInit(core *gNetCore) { + t.gNetCore = core +} + +func (t *gNetGenericHandler) OnBoot(eng gnet.Engine) (action gnet.Action) { + t.engine = &eng + return +} + +func (t *gNetGenericHandler) OnShutdown(eng gnet.Engine) { + +} + +func (t *gNetGenericHandler) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) { + t.controller.RegisterConnection(c, + func(packet server.Packet) error { + return c.AsyncWrite(packet.GetBytes(), func(c gnet.Conn, err error) error { + t.controller.OnConnectionAsyncWriteError(c.Context().(server.Conn), packet, err) + return nil + }) + }, func(conn server.Conn) { + c.SetContext(conn) + }) + return +} + +func (t *gNetGenericHandler) OnClose(c gnet.Conn, err error) (action gnet.Action) { + t.controller.EliminateConnection(c, err) + return +} + +func (t *gNetGenericHandler) OnTraffic(c gnet.Conn) (action gnet.Action) { + buf, err := c.Next(-1) + if err != nil { + return gnet.Close + } + + var clone = make([]byte, len(buf)) + copy(clone, buf) + + t.controller.ReactPacket(c, server.NewPacket(clone)) + return +} + +func (t *gNetGenericHandler) OnTick() (delay time.Duration, action gnet.Action) { + return +} + +func (t *gNetGenericHandler) GetEngine() *gnet.Engine { + return t.engine +} diff --git a/server/network/http.go b/server/network/http.go index 88745242..c963b149 100644 --- a/server/network/http.go +++ b/server/network/http.go @@ -2,29 +2,13 @@ package network import ( "context" + "github.com/kercylan98/minotaur/server" "github.com/pkg/errors" "net" "net/http" "time" ) -func Http(addr string) server.Network { - return HttpWithHandler(addr, &HttpServe{ServeMux: http.NewServeMux()}) -} - -func HttpWithHandler[H http.Handler](addr string, handler H) server.Network { - c := &httpCore[H]{ - addr: addr, - handler: handler, - srv: &http.Server{ - Addr: addr, - Handler: handler, - DisableGeneralOptionsHandler: false, - }, - } - return c -} - type httpCore[H http.Handler] struct { addr string handler H diff --git a/server/network/networks.go b/server/network/networks.go new file mode 100644 index 00000000..e292e674 --- /dev/null +++ b/server/network/networks.go @@ -0,0 +1,74 @@ +package network + +import ( + "github.com/kercylan98/minotaur/server" + "net/http" +) + +// Http 创建一个基于 http.ServeMux 的 HTTP 的网络 +func Http(addr string) server.Network { + return HttpWithHandler(addr, &HttpServe{ServeMux: http.NewServeMux()}) +} + +// HttpWithHandler 创建一个基于 http.Handler 的 HTTP 的网络 +func HttpWithHandler[H http.Handler](addr string, handler H) server.Network { + c := &httpCore[H]{ + addr: addr, + handler: handler, + srv: &http.Server{ + Addr: addr, + Handler: handler, + DisableGeneralOptionsHandler: false, + }, + } + return c +} + +// WebSocket 创建一个基于 TCP 的 WebSocket 网络 +// - addr 期望为类似于 127.0.0.1:1234 或 :1234 的地址 +// - pattern 期望为 WebSocket 的路径,如果为空则默认为 / +func WebSocket(addr string, pattern ...string) server.Network { + return newGNetCore(new(websocketHandler), schemaWebSocket, addr, pattern...) +} + +// Tcp 创建一个 TCP 网络 +// - addr 期望为类似于 127.0.0.1:1234 或 :1234 的地址 +func Tcp(addr string) server.Network { + return newGNetCore(new(gNetGenericHandler), schemaTcp, addr) +} + +// Tcp4 创建一个 IPv4 TCP 网络 +// - addr 期望为类似于 127.0.0.1:1234 或 :1234 的地址 +func Tcp4(addr string) server.Network { + return newGNetCore(new(gNetGenericHandler), schemaTcp4, addr) +} + +// Tcp6 创建一个 IPv6 TCP 网络 +// - addr 期望为类似于 [::1]:1234 的地址 +func Tcp6(addr string) server.Network { + return newGNetCore(new(gNetGenericHandler), schemaTcp6, addr) +} + +// Udp 创建一个 UDP 网络 +// - addr 期望为类似于 127.0.0.1:1234 或 :1234 的地址 +func Udp(addr string) server.Network { + return newGNetCore(new(gNetGenericHandler), schemaUdp, addr) +} + +// Udp4 创建一个 IPv4 UDP 网络 +// - addr 期望为类似于 127.0.0.1:1234 或 :1234 的地址 +func Udp4(addr string) server.Network { + return newGNetCore(new(gNetGenericHandler), schemaUdp4, addr) +} + +// Udp6 创建一个 IPv6 UDP 网络 +// - addr 期望为类似于 [::1]:1234 的地址 +func Udp6(addr string) server.Network { + return newGNetCore(new(gNetGenericHandler), schemaUdp6, addr) +} + +// Unix 创建一个 Unix Domain Socket 网络 +// - addr 期望为类似于 /tmp/xxx.sock 的文件地址 +func Unix(addr string) server.Network { + return newGNetCore(new(gNetGenericHandler), schemaUnix, addr) +} diff --git a/server/network/websocket.go b/server/network/websocket.go index 3a045bc4..02649791 100644 --- a/server/network/websocket.go +++ b/server/network/websocket.go @@ -1,58 +1,91 @@ package network import ( - "context" - "fmt" + "errors" + "github.com/gobwas/ws" + "github.com/gobwas/ws/wsutil" "github.com/kercylan98/minotaur/server" - "github.com/kercylan98/minotaur/toolkit/collection" "github.com/panjf2000/gnet/v2" "time" ) -func WebSocket(addr string, pattern ...string) server.Network { - ws := &websocketCore{ - addr: addr, - pattern: collection.FindFirstOrDefaultInSlice(pattern, "/"), - } - return ws +type websocketHandler struct { + engine *gnet.Engine + upgrader ws.Upgrader + *gNetCore } -type websocketCore struct { - ctx context.Context - controller server.Controller - handler *websocketHandler - addr string - pattern string +func (w *websocketHandler) OnInit(core *gNetCore) { + w.gNetCore = core } -func (w *websocketCore) OnSetup(ctx context.Context, controller server.Controller) (err error) { - w.ctx = ctx - w.handler = newWebsocketHandler(w) - w.controller = controller +func (w *websocketHandler) OnBoot(eng gnet.Engine) (action gnet.Action) { + w.engine = &eng + w.initUpgrader() return } -func (w *websocketCore) OnRun() (err error) { - err = gnet.Run(w.handler, fmt.Sprintf("tcp://%s", w.addr)) +func (w *websocketHandler) OnShutdown(eng gnet.Engine) { + +} + +func (w *websocketHandler) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) { + wrapper := newWebsocketWrapper(c) + c.SetContext(wrapper) return } -func (w *websocketCore) OnShutdown() error { - if w.handler.engine != nil { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - return w.handler.engine.Stop(ctx) +func (w *websocketHandler) OnClose(c gnet.Conn, err error) (action gnet.Action) { + w.controller.EliminateConnection(c, err) + return +} + +func (w *websocketHandler) OnTraffic(c gnet.Conn) (action gnet.Action) { + wrapper := c.Context().(*websocketWrapper) + + if err := wrapper.readToBuffer(); err != nil { + return gnet.Close + } + + if err := wrapper.upgrade(w.upgrader, func() { + // 协议升级成功后视为连接建立 + w.controller.RegisterConnection(c, func(packet server.Packet) error { + return wsutil.WriteServerMessage(c, packet.GetContext().(ws.OpCode), packet.GetBytes()) + }, nil) + }); err != nil { + return gnet.Close + } + wrapper.active = time.Now() + + // decode + messages, err := wrapper.decode() + if err != nil { + return gnet.Close + } + + for _, message := range messages { + packet := server.NewPacket(message.Payload) + packet.SetContext(message.OpCode) + w.controller.ReactPacket(c, packet) } - return nil + return } -func (w *websocketCore) Schema() string { - return "ws" +func (w *websocketHandler) OnTick() (delay time.Duration, action gnet.Action) { + return } -func (w *websocketCore) Address() string { - if w.pattern == "/" { - return w.addr +func (w *websocketHandler) initUpgrader() { + w.upgrader = ws.Upgrader{ + OnRequest: func(uri []byte) (err error) { + if string(uri) != w.pattern { + err = errors.New("bad request") + } + return + }, } - return fmt.Sprintf("%s:%s", w.addr, w.pattern) +} + +func (w *websocketHandler) GetEngine() *gnet.Engine { + return w.engine } diff --git a/server/network/websocket_handler.go b/server/network/websocket_handler.go deleted file mode 100644 index 6b537382..00000000 --- a/server/network/websocket_handler.go +++ /dev/null @@ -1,89 +0,0 @@ -package network - -import ( - "errors" - "github.com/gobwas/ws" - "github.com/gobwas/ws/wsutil" - "github.com/kercylan98/minotaur/server" - "github.com/panjf2000/gnet/v2" - "time" -) - -func newWebsocketHandler(core *websocketCore) *websocketHandler { - return &websocketHandler{ - websocketCore: core, - } -} - -type websocketHandler struct { - engine *gnet.Engine - upgrader ws.Upgrader - *websocketCore -} - -func (w *websocketHandler) OnBoot(eng gnet.Engine) (action gnet.Action) { - w.engine = &eng - w.initUpgrader() - return -} - -func (w *websocketHandler) OnShutdown(eng gnet.Engine) { - -} - -func (w *websocketHandler) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) { - wrapper := newWebsocketWrapper(c) - c.SetContext(wrapper) - return -} - -func (w *websocketHandler) OnClose(c gnet.Conn, err error) (action gnet.Action) { - w.controller.EliminateConnection(c, err) - return -} - -func (w *websocketHandler) OnTraffic(c gnet.Conn) (action gnet.Action) { - wrapper := c.Context().(*websocketWrapper) - - if err := wrapper.readToBuffer(); err != nil { - return gnet.Close - } - - if err := wrapper.upgrade(w.upgrader, func() { - // 协议升级成功后视为连接建立 - w.controller.RegisterConnection(c, func(packet server.Packet) error { - return wsutil.WriteServerMessage(c, packet.GetContext().(ws.OpCode), packet.GetBytes()) - }) - }); err != nil { - return gnet.Close - } - wrapper.active = time.Now() - - // decode - messages, err := wrapper.decode() - if err != nil { - return gnet.Close - } - - for _, message := range messages { - packet := server.NewPacket(message.Payload) - packet.SetContext(message.OpCode) - w.controller.ReactPacket(c, packet) - } - return -} - -func (w *websocketHandler) OnTick() (delay time.Duration, action gnet.Action) { - return -} - -func (w *websocketHandler) initUpgrader() { - w.upgrader = ws.Upgrader{ - OnRequest: func(uri []byte) (err error) { - if string(uri) != w.pattern { - err = errors.New("bad request") - } - return - }, - } -} diff --git a/server/server.go b/server/server.go index b3a36201..beb2db8f 100644 --- a/server/server.go +++ b/server/server.go @@ -3,6 +3,9 @@ package server import ( "context" "fmt" + "github.com/kercylan98/minotaur/toolkit/collection" + "github.com/kercylan98/minotaur/toolkit/log" + "github.com/kercylan98/minotaur/toolkit/network" "github.com/kercylan98/minotaur/toolkit/nexus" "github.com/kercylan98/minotaur/toolkit/nexus/brokers" messageEvents "github.com/kercylan98/minotaur/toolkit/nexus/events" diff --git a/toolkit/chrono/moment.go b/toolkit/chrono/moment.go index c7ddff1f..8de9ad58 100644 --- a/toolkit/chrono/moment.go +++ b/toolkit/chrono/moment.go @@ -1,7 +1,7 @@ package chrono import ( - "github.com/kercylan98/minotaur/utils/generic" + "github.com/kercylan98/minotaur/toolkit/constraints" "math" "time" ) @@ -316,7 +316,7 @@ func GetMonthDays(t time.Time) int { } // ToDuration 将一个数值转换为 time.Duration 类型,当 unit 为空时,默认为纳秒单位 -func ToDuration[V generic.Number](v V, unit ...time.Duration) time.Duration { +func ToDuration[V constraints.Number](v V, unit ...time.Duration) time.Duration { var u = Nanosecond if len(unit) > 0 { u = unit[0] @@ -325,26 +325,26 @@ func ToDuration[V generic.Number](v V, unit ...time.Duration) time.Duration { } // ToDurationSecond 将一个数值转换为秒的 time.Duration 类型 -func ToDurationSecond[V generic.Number](v V) time.Duration { +func ToDurationSecond[V constraints.Number](v V) time.Duration { return ToDuration(v, Second) } // ToDurationMinute 将一个数值转换为分钟的 time.Duration 类型 -func ToDurationMinute[V generic.Number](v V) time.Duration { +func ToDurationMinute[V constraints.Number](v V) time.Duration { return ToDuration(v, Minute) } // ToDurationHour 将一个数值转换为小时的 time.Duration 类型 -func ToDurationHour[V generic.Number](v V) time.Duration { +func ToDurationHour[V constraints.Number](v V) time.Duration { return ToDuration(v, Hour) } // ToDurationDay 将一个数值转换为天的 time.Duration 类型 -func ToDurationDay[V generic.Number](v V) time.Duration { +func ToDurationDay[V constraints.Number](v V) time.Duration { return ToDuration(v, Day) } // ToDurationWeek 将一个数值转换为周的 time.Duration 类型 -func ToDurationWeek[V generic.Number](v V) time.Duration { +func ToDurationWeek[V constraints.Number](v V) time.Duration { return ToDuration(v, Week) } diff --git a/toolkit/chrono/state_line.go b/toolkit/chrono/state_line.go index 20b569fa..94c7145b 100644 --- a/toolkit/chrono/state_line.go +++ b/toolkit/chrono/state_line.go @@ -2,14 +2,13 @@ package chrono import ( "fmt" -"github.com/kercylan98/minotaur/toolkit/collection" -"github.com/kercylan98/minotaur/utils/generic" -"strings" -"time" + "github.com/kercylan98/minotaur/toolkit/collection" + "strings" + "time" ) // NewStateLine 创建一个从左向右由早到晚的状态时间线 -func NewStateLine[State generic.Basic](zero State) *StateLine[State] { +func NewStateLine[State comparable](zero State) *StateLine[State] { return &StateLine[State]{ states: []State{zero}, points: []time.Time{{}}, @@ -19,7 +18,7 @@ func NewStateLine[State generic.Basic](zero State) *StateLine[State] { // StateLine 表示一个状态时间线,它记录了一系列时间点及其对应的状态和触发器。 // 在时间线中,每个时间点都与一个状态和一组触发器相关联,可以通过时间点查找状态,并触发与之相关联的触发器。 -type StateLine[State generic.Basic] struct { +type StateLine[State comparable] struct { states []State // 每个时间点对应的状态 points []time.Time // 每个时间点 trigger [][]func() // 每个时间点对应的触发器 diff --git a/toolkit/collection/topological.go b/toolkit/collection/topological.go index cd91a83b..192345b8 100644 --- a/toolkit/collection/topological.go +++ b/toolkit/collection/topological.go @@ -1,6 +1,8 @@ package collection -import "github.com/kercylan98/minotaur/internal/utils/sorts" +import "errors" + +var ErrCircularDependencyDetected = errors.New("circular dependency detected") type topologicalSortNode[V any] struct { value V @@ -54,7 +56,7 @@ func TopologicalSort[S ~[]V, Index comparable, V any](slice S, queryIndexHandler } if len(sorted) != len(slice) { - return nil, sorts.ErrCircularDependencyDetected + return nil, ErrCircularDependencyDetected } return sorted, nil diff --git a/toolkit/convert/int.go b/toolkit/convert/int.go index 3e4ed319..04998b6f 100644 --- a/toolkit/convert/int.go +++ b/toolkit/convert/int.go @@ -66,8 +66,3 @@ func Uint64ToString(i uint64) string { func IntToBoolean[I constraints.Int](i I) bool { return i != 0 } - -// IntToRome 将数字转换为罗马数字 -func IntToRome[I constraints.Int](num I) string { - return romeThousands[num/1000] + romeHundreds[num%1000/100] + romeTens[num%100/10] + romeOnes[num%10] -} diff --git a/toolkit/loadbalancer/consistent_hash.go b/toolkit/loadbalancer/consistent_hash.go index e6d5895d..504c1559 100644 --- a/toolkit/loadbalancer/consistent_hash.go +++ b/toolkit/loadbalancer/consistent_hash.go @@ -1,7 +1,7 @@ package loadbalancer import ( - "github.com/kercylan98/minotaur/utils/super" + "github.com/kercylan98/minotaur/toolkit/convert" "hash/fnv" "sort" "sync" @@ -29,7 +29,7 @@ func (c *ConsistentHash) Add(node string) { defer c.mutex.Unlock() for i := 0; i < c.replicas; i++ { - hash := c.hash(node + super.IntToString(i)) + hash := c.hash(node + convert.IntToString(i)) c.keys = append(c.keys, hash) c.hashMap[hash] = node } @@ -42,7 +42,7 @@ func (c *ConsistentHash) Remove(node string) { defer c.mutex.Unlock() for i := 0; i < c.replicas; i++ { - hash := c.hash(node + super.IntToString(i)) + hash := c.hash(node + convert.IntToString(i)) delete(c.hashMap, hash) // 从 keys 中移除节点的哈希值 for j, k := range c.keys { diff --git a/toolkit/log/handler.go b/toolkit/log/handler.go index 7ab0fb27..60ebee42 100644 --- a/toolkit/log/handler.go +++ b/toolkit/log/handler.go @@ -4,7 +4,6 @@ import ( "context" "encoding" "fmt" - "github.com/kercylan98/minotaur/internal/utils/str" "github.com/kercylan98/minotaur/toolkit/charproc" "io" "log/slog" @@ -141,7 +140,7 @@ func processCaller(buffer *strings.Builder, record slog.Record, opt *Options) { runtime.CallersFrames(pcs[:runtime.Callers(opt.callerSkip, pcs)]) fs := runtime.CallersFrames(pcs) f, _ := fs.Next() - if f.File == str.None { + if f.File == charproc.None { return } @@ -225,7 +224,7 @@ func processAttrsString(s string, quote bool) string { } func processAttrsKey(buffer *strings.Builder, opt *Options, key, groups string, replaceColor ...*Color) { - if key == str.None { + if key == charproc.None { return } color := opt.keyColor[AttrTypeField] @@ -239,7 +238,7 @@ func processAttrsKey(buffer *strings.Builder, opt *Options, key, groups string, } delimiterText := opt.delimiterText[AttrTypeField] - if delimiterText != str.None { + if delimiterText != charproc.None { delimiterColor := opt.delimiterColor[AttrTypeField] if len(replaceColor) > 1 { delimiterColor = replaceColor[1] @@ -294,7 +293,7 @@ func processAttrsValue(buffer *strings.Builder, opt *Options, v slog.Value, quot } default: } - if text == str.None { + if text == charproc.None { return } @@ -312,7 +311,7 @@ func processAttrType(buffer *strings.Builder, opt *Options, attrType AttrType, v delimiterColor := opt.delimiterColor[attrType] valueColor := opt.valueColor[attrType] - if prefixText != str.None { + if prefixText != charproc.None { // 前缀 if opt.disabledColor || prefixColor == nil { buffer.WriteString(prefixText) @@ -321,7 +320,7 @@ func processAttrType(buffer *strings.Builder, opt *Options, attrType AttrType, v } // 分隔符 - if delimiterText != str.None { + if delimiterText != charproc.None { if opt.disabledColor || delimiterColor == nil { buffer.WriteString(delimiterText) } else { diff --git a/toolkit/network/ip.go b/toolkit/network/ip.go index f5d43145..aecfcb81 100644 --- a/toolkit/network/ip.go +++ b/toolkit/network/ip.go @@ -14,3 +14,17 @@ func IP() (ip net.IP, err error) { ip = localAddr.IP return } + +// IPv4 返回本机出站 IPv4 地址 +func IPv4() (ip net.IP, err error) { + return IP() +} + +// IPv6 返回本机出站 IPv6 地址 +func IPv6() (ip net.IP, err error) { + ip, err = IP() + if err == nil { + ip = ip.To16() + } + return +}