1
0
forked from x/icebergs
icebergs/type.go
2024-08-04 01:49:24 +08:00

418 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package ice
import (
"encoding/json"
"io"
"net/http"
"strings"
"sync/atomic"
"time"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/logs"
"shylinux.com/x/toolkits/task"
)
type List = []Any
type Any = interface{}
type Map = map[string]Any
type Maps = map[string]string
type Handler func(m *Message, arg ...string)
type Messages = map[string]*Message
type Contexts = map[string]*Context
type Commands = map[string]*Command
type Actions = map[string]*Action
type Configs = map[string]*Config
type Caches = map[string]*Cache
type Cache struct {
Name string
Help string
Value string
}
type Config struct {
Name string
Help string
Value Any
}
type Action struct {
Name string
Help string
Icon string
Role string
Style string
Hand Handler
List List
}
type Command struct {
Name string
Help string
Icon string
Role string
Actions Actions
Hand Handler
RawHand Any
List List
Meta Map
}
type Context struct {
Name string
Help string
Caches Caches
Configs Configs
Commands Commands
contexts Contexts
context *Context
root *Context
server Server
id int32
}
type Server interface {
Begin(m *Message, arg ...string)
Start(m *Message, arg ...string)
Close(m *Message, arg ...string)
}
func (c *Context) Cap(key string, arg ...Any) string {
kit.If(len(arg) > 0, func() { c.Caches[key].Value = kit.Format(arg[0]) })
return c.Caches[key].Value
}
func (c *Context) Cmd(m *Message, key string, arg ...string) *Message {
return c._command(m, c.Commands[key], key, arg...)
}
func (c *Context) Prefix(arg ...string) string {
return kit.Keys(c.Cap(CTX_FOLLOW), arg)
}
func (c *Context) Server() Server { return c.server }
func (c *Context) ID() int32 { return atomic.AddInt32(&c.id, 1) }
func (c *Context) Register(s *Context, x Server, cmd ...string) *Context {
kit.For(cmd, func(cmd string) {
if s, ok := Info.Index[cmd].(*Context); ok {
panic(kit.Format("%s %s registed by %v", ErrWarn, cmd, s.Name))
}
Info.Index[cmd] = s
})
kit.If(c.contexts == nil, func() { c.contexts = Contexts{} })
c.contexts[s.Name] = s
s.root = c.root
s.context = c
s.server = x
return s
}
func (c *Context) MergeCommands(Commands Commands) *Context {
for _, cmd := range Commands {
if cmd.Hand == nil && cmd.RawHand == nil {
cmd.RawHand = logs.FileLines(2)
}
}
configs := Configs{}
for k := range Commands {
configs[k] = &Config{Value: kit.Data()}
}
return c.Merge(&Context{Commands: Commands, Configs: configs})
}
func (c *Context) Merge(s *Context) *Context {
kit.If(c.Commands == nil, func() { c.Commands = Commands{} })
kit.If(c.Commands[CTX_INIT] == nil, func() { c.Commands[CTX_INIT] = &Command{Hand: func(m *Message, arg ...string) { Info.Load(m) }} })
kit.If(c.Commands[CTX_EXIT] == nil, func() { c.Commands[CTX_EXIT] = &Command{Hand: func(m *Message, arg ...string) { Info.Save(m) }} })
merge := func(pre *Command, init bool, key string, cmd *Command, cb Handler) {
if cb == nil {
return
}
last := pre.Hand
pre.Hand = func(m *Message, arg ...string) {
kit.If(init, func() { last(m, arg...) })
defer kit.If(!init, func() { last(m, arg...) })
_key, _cmd := m._key, m._cmd
defer func() { m._key, m._cmd = _key, _cmd }()
m._key, m._cmd = key, cmd
cb(m, arg...)
}
}
for key, cmd := range s.Commands {
if pre, ok := c.Commands[key]; ok && s != c {
switch key {
case CTX_INIT:
merge(pre, true, key, cmd, cmd.Hand)
continue
case CTX_EXIT:
merge(pre, false, key, cmd, cmd.Hand)
continue
}
}
c.Commands[key] = cmd
kit.If(cmd.Meta == nil, func() { cmd.Meta = kit.Dict() })
for sub, action := range cmd.Actions {
if pre, ok := c.Commands[sub]; ok && s == c {
kit.Switch(sub,
CTX_INIT, func() { merge(pre, true, key, cmd, action.Hand) },
CTX_EXIT, func() { merge(pre, false, key, cmd, action.Hand) },
)
}
if s == c {
for _, h := range Info.merges {
switch h := h.(type) {
case func(c *Context, key string, cmd *Command, sub string, action *Action) Handler:
merge(c.Commands[CTX_INIT], true, key, cmd, h(c, key, cmd, sub, action))
case func(c *Context, key string, cmd *Command, sub string, action *Action):
h(c, key, cmd, sub, action)
}
}
}
kit.If(sub == SELECT, func() { cmd.Name = kit.Select(action.Name, cmd.Name) })
kit.If(sub == SELECT, func() { cmd.Help = kit.Select(action.Help, cmd.Help) })
if help := kit.Split(action.Help, " :"); len(help) > 0 {
if kit.Value(cmd.Meta, kit.Keys(CTX_TRANS, strings.TrimPrefix(sub, "_")), help[0]); len(help) > 1 {
kit.Value(cmd.Meta, kit.Keys(CTX_TITLE, sub), help[1])
}
}
kit.Value(cmd.Meta, kit.Keys(CTX_ICONS, sub), action.Icon)
if action.Hand == nil {
continue
}
kit.If(action.List == nil, func() { action.List = SplitCmd(action.Name, nil) })
kit.If(len(action.List) > 0, func() { cmd.Meta[sub] = action.List })
}
kit.If(strings.HasPrefix(cmd.Name, LIST), func() { cmd.Name = strings.Replace(cmd.Name, LIST, key, 1) })
kit.If(cmd.List == nil, func() { cmd.List = SplitCmd(cmd.Name, cmd.Actions) })
}
kit.If(c.Configs == nil, func() { c.Configs = Configs{} })
for k, v := range s.Configs {
if c.Configs[k] == nil || c.Configs[k].Value == nil {
c.Configs[k] = v
}
}
kit.If(c.Caches == nil, func() { c.Caches = Caches{} })
return c
}
func (c *Context) Begin(m *Message, arg ...string) *Context {
kit.If(c.Caches == nil, func() { c.Caches = Caches{} })
c.Caches[CTX_FOLLOW] = &Cache{Value: c.Name}
kit.If(c.context != nil && c.context != Index, func() { c.Cap(CTX_FOLLOW, c.context.Prefix(c.Name)) })
kit.If(c.server != nil, func() { c.server.Begin(m, arg...) })
return c.Merge(c)
}
func (c *Context) Start(m *Message, arg ...string) {
m.Log(CTX_START, c.Prefix(), logs.FileLineMeta(2))
kit.If(c.server != nil, func() { c.server.Start(m, arg...) })
}
func (c *Context) Close(m *Message, arg ...string) {
m.Log(CTX_CLOSE, c.Prefix(), logs.FileLineMeta(2))
kit.If(c.server != nil, func() { c.server.Close(m, arg...) })
}
type Message struct {
time time.Time
code int
_data Map
_meta map[string][]string
lock task.Lock
root *Message
message *Message
_source string
_target string
source *Context
target *Context
_cmd *Command
_key string
_sub string
W http.ResponseWriter
R *http.Request
O io.Writer
I io.Reader
}
func (m *Message) Time(arg ...string) string {
t := m.time
if len(arg) > 0 {
if d, e := time.ParseDuration(arg[0]); e == nil {
t, arg = t.Add(d), arg[1:]
}
}
return t.Format(kit.Select(MOD_TIME, arg, 0))
}
func (m *Message) Message() *Message { return m.message }
func (m *Message) Source() *Context { return m.source }
func (m *Message) Target() *Context { return m.target }
func (m *Message) _fileline() string {
switch m.target.Name {
case MDB, AAA, GDB:
return m._source
default:
return m._target
}
}
func (m *Message) Spawn(arg ...Any) *Message {
msg := &Message{time: time.Now(), code: int(m.target.root.ID()),
_meta: map[string][]string{}, _data: Map{}, message: m, root: m.root,
_source: logs.FileLine(2), source: m.target, target: m.target, _cmd: m._cmd, _key: m._key, _sub: m._sub,
W: m.W, R: m.R, O: m.O, I: m.I,
}
for _, val := range arg {
switch val := val.(type) {
case []byte:
if m.WarnNotValid(json.Unmarshal(val, &msg._meta), string(val)) {
m.Debug(m.FormatStack(1, 100))
}
case Option:
msg.Option(val.Name, val.Value)
case Maps:
kit.For(val, func(k, v string) { msg.Option(k, v) })
case Map:
kit.For(kit.KeyValue(nil, "", val), func(k string, v Any) { msg.Option(k, v) })
case *Context:
msg.target = val
case *Command:
msg._cmd = val
case string:
msg._key = val
case http.ResponseWriter:
msg.W = val
case *http.Request:
msg.R = val
}
}
return msg
}
func (m *Message) Start(key string, arg ...string) *Message {
return m.Search(key+PT, func(p *Context, s *Context) {
m.Cmd(ROUTINE, CREATE, kit.Select(m.Prefix(), key), func() { s.Start(m.Spawn(s), arg...) })
})
}
func (m *Message) Travel(cb Any) *Message {
target, cmd, key := m.target, m._cmd, m._key
defer func() { m.target, m._cmd, m._key = target, cmd, key }()
list := []*Context{m.root.target}
for i := 0; i < len(list); i++ {
switch cb := cb.(type) {
case func(*Context, *Context):
cb(list[i].context, list[i])
case func(*Context, *Context, string, *Command):
m.target = list[i]
kit.For(kit.SortedKey(list[i].Commands), func(k string) {
m._cmd, m._key = list[i].Commands[k], k
cb(list[i].context, list[i], k, list[i].Commands[k])
})
case func(*Context, *Context, string, *Config):
m.target = list[i]
kit.For(kit.SortedKey(list[i].Configs), func(k string) { cb(list[i].context, list[i], k, list[i].Configs[k]) })
default:
m.ErrorNotImplement(cb)
}
kit.For(kit.SortedKey(list[i].contexts), func(k string) { list = append(list, list[i].contexts[k]) })
}
return m
}
func (m *Message) Search(key string, cb Any) *Message {
if key == "" {
return m
}
_target, _key, _cmd := m.target, m._key, m._cmd
defer func() { m.target, m._key, m._cmd = _target, _key, _cmd }()
p := m.target.root
if key = strings.TrimPrefix(key, "ice."); key == PT {
p, key = m.target, ""
} else if key == ".." {
p, key = m.target.context, ""
} else if key == "..." {
p, key = m.target.root, ""
} else if strings.Contains(key, PT) {
ls := strings.Split(key, PT)
for _, p = range []*Context{m.target.root, m.target, m.source} {
if p == nil {
continue
}
for _, k := range ls[:len(ls)-1] {
if p = p.contexts[k]; p == nil {
break
}
}
if p != nil {
break
}
}
if p == nil {
return m
}
key = ls[len(ls)-1]
} else if ctx, ok := Info.Index[key].(*Context); ok {
p = ctx
} else {
p = m.target
}
switch cb := cb.(type) {
case func(key string, cmd *Command):
if cmd, ok := p.Commands[key]; ok {
m.target, m._key, m._cmd = p, key, cmd
cb(key, cmd)
}
case func(p *Context, s *Context, key string, cmd *Command):
for _, p := range []*Context{p, m.target, m.source} {
for s := p; s != nil; s = s.context {
if cmd, ok := s.Commands[key]; ok {
func() {
_target, _key := m.target, m._key
defer func() { m.target, m._key = _target, _key }()
m.target, m._key = s, key
cb(s.context, s, key, cmd)
}()
return m
}
}
}
case func(p *Context, s *Context, key string, conf *Config):
for _, p := range []*Context{p, m.target, m.source} {
for s := p; s != nil; s = s.context {
if cmd, ok := s.Configs[key]; ok {
cb(s.context, s, key, cmd)
return m
}
}
}
case func(p *Context, s *Context):
cb(p.context, p)
default:
m.ErrorNotImplement(cb)
}
return m
}
func (m *Message) Design(action Any, help string, input ...Any) {
list := kit.List()
for _, input := range input {
switch input := input.(type) {
case string:
list = append(list, SplitCmd("action "+input, nil)...)
case Map:
if kit.Format(input[TYPE]) != "" && kit.Format(input[NAME]) != "" {
list = append(list, input)
continue
}
kit.For(kit.KeyValue(nil, "", input), func(k string, v Any) {
list = append(list, kit.Dict(NAME, k, TYPE, TEXT, VALUE, v))
})
default:
m.ErrorNotImplement(input)
}
}
if k := kit.Format(action); k == LIST {
m._cmd.Meta[k], m._cmd.List = list, list
if help != "" {
kit.Value(m._cmd.Meta, kit.Keys(CTX_TRANS, k), help)
}
} else if a, ok := m._cmd.Actions[k]; ok {
m._cmd.Meta[k], a.List = list, list
if help != "" {
kit.Value(m._cmd.Meta, kit.Keys(CTX_TRANS, k), help)
}
}
}
func (m *Message) Actions(key string) *Action { return m._cmd.Actions[key] }
func (m *Message) Commands(key string) *Command { return m.Target().Commands[kit.Select(m._key, key)] }