forked from x/icebergs
225 lines
7.2 KiB
Go
225 lines
7.2 KiB
Go
package ice
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"reflect"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
kit "shylinux.com/x/toolkits"
|
|
"shylinux.com/x/toolkits/logs"
|
|
"shylinux.com/x/toolkits/task"
|
|
)
|
|
|
|
func (m *Message) TryCatch(msg *Message, catch bool, cb ...func(msg *Message)) {
|
|
defer func() {
|
|
switch e := recover(); e {
|
|
case io.EOF, nil:
|
|
default:
|
|
fileline := m.FormatStack(2, 1)
|
|
m.Log(LOG_WARN, "catch: %s %s", e, fileline).Log("chain", msg.FormatChain())
|
|
m.Log(LOG_WARN, "catch: %s %s", e, fileline).Log("stack", m.FormatStack(2, 100))
|
|
m.Log(LOG_WARN, "catch: %s %s", e, fileline).Result(ErrWarn, e, SP, m.FormatStack(2, 5))
|
|
if len(cb) > 1 {
|
|
m.TryCatch(msg, catch, cb[1:]...)
|
|
} else if !catch {
|
|
m.Assert(e)
|
|
}
|
|
}
|
|
}()
|
|
kit.If(len(cb) > 0, func() { cb[0](msg) })
|
|
}
|
|
func (m *Message) Assert(expr Any) bool {
|
|
switch e := expr.(type) {
|
|
case nil:
|
|
return true
|
|
case bool:
|
|
if e == true {
|
|
return true
|
|
}
|
|
case error:
|
|
default:
|
|
expr = errors.New(kit.Format("error: %v", e))
|
|
}
|
|
m.Result(ErrWarn, expr, logs.FileLine(2))
|
|
panic(expr)
|
|
}
|
|
func (m *Message) Sleep(d Any, arg ...Any) *Message {
|
|
defer kit.If(len(arg) > 0, func() { m.Cmdy(arg...) })
|
|
time.Sleep(kit.Duration(d))
|
|
return m
|
|
}
|
|
func (m *Message) Sleep300ms(arg ...Any) *Message { return m.Sleep("300ms", arg...) }
|
|
func (m *Message) Sleep30ms(arg ...Any) *Message { return m.Sleep("30ms", arg...) }
|
|
func (m *Message) Sleep3s(arg ...Any) *Message { return m.Sleep("3s", arg...) }
|
|
func (m *Message) GoSleep(t string, arg ...Any) { m.Go(func() { m.Sleep(t).Cmd(arg...) }) }
|
|
func (m *Message) Go(cb func(), arg ...Any) {
|
|
kit.If(len(arg) == 0, func() { arg = append(arg, logs.FileLine(cb)) })
|
|
task.Put(arg[0], func(task *task.Task) { m.TryCatch(m, true, func(m *Message) { cb() }) })
|
|
}
|
|
func (m *Message) Wait(cb ...Handler) (wait func(), done Handler) {
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(1)
|
|
t := time.AfterFunc(kit.Duration("30s"), func() { wg.Done() })
|
|
return func() { wg.Wait() }, func(msg *Message, arg ...string) {
|
|
defer wg.Done()
|
|
defer t.Stop()
|
|
kit.If(len(cb) > 0 && cb[0] != nil, func() { cb[0](msg, arg...) }, func() { m.Copy(msg) })
|
|
}
|
|
}
|
|
|
|
func (m *Message) Cmd(arg ...Any) *Message { return m._command(arg...) }
|
|
func (m *Message) Cmds(arg ...Any) *Message { return m.Cmd(append(arg, OptionFields(""))...) }
|
|
func (m *Message) Cmdv(arg ...Any) string {
|
|
args := kit.Simple(arg...)
|
|
field := kit.Slice(args, -1)[0]
|
|
return m._command(kit.Slice(args, 0, -1), OptionFields(field)).Append(field)
|
|
}
|
|
func (m *Message) Cmdx(arg ...Any) string {
|
|
res := kit.Select("", m._command(arg...).meta[MSG_RESULT], 0)
|
|
return kit.Select("", res, res != ErrWarn)
|
|
}
|
|
func (m *Message) Cmdy(arg ...Any) *Message { return m.Copy(m._command(arg...)) }
|
|
func (m *Message) CmdHand(cmd *Command, key string, arg ...string) *Message {
|
|
if m._cmd, m._key = cmd, key; cmd == nil {
|
|
return m
|
|
}
|
|
if m._target = cmd.FileLines(); key == SELECT {
|
|
m.Log(LOG_CMDS, "%s.%s %d %v %v", m.Target().Name, key, len(arg), arg, m.Optionv(MSG_FIELDS), logs.FileLineMeta(m._fileline()))
|
|
} else {
|
|
m.Log(LOG_CMDS, "%s.%s %d %v", m.Target().Name, key, len(arg), arg, logs.FileLineMeta(m._fileline()))
|
|
}
|
|
if cmd.Hand != nil {
|
|
cmd.Hand(m, arg...)
|
|
} else if cmd.Actions != nil && cmd.Actions[SELECT] != nil {
|
|
cmd.Actions[SELECT].Hand(m, arg...)
|
|
}
|
|
return m
|
|
}
|
|
func (m *Message) ActionHand(cmd *Command, key, sub string, arg ...string) *Message {
|
|
if action, ok := cmd.Actions[sub]; !m.Warn(!ok, ErrNotFound, sub, cmd.FileLines()) {
|
|
return m.Target()._action(m, cmd, key, sub, action, arg...)
|
|
}
|
|
return m
|
|
}
|
|
func (m *Message) _command(arg ...Any) *Message {
|
|
args, opts, cbs, _source := []Any{}, Map{}, kit.Value(nil), logs.FileLine(3)
|
|
for _, v := range arg {
|
|
switch val := v.(type) {
|
|
case nil:
|
|
case string:
|
|
args = append(args, v)
|
|
case Maps:
|
|
kit.For(val, func(k, v string) { opts[k] = v })
|
|
case Map:
|
|
kit.For(kit.KeyValue(nil, "", val), func(k string, v Any) { opts[k] = v })
|
|
case Option:
|
|
opts[val.Name] = val.Value
|
|
case logs.Meta:
|
|
kit.If(val.Key == "fileline", func() { _source = val.Value })
|
|
case func(int, Maps, []string):
|
|
defer func() { m.Table(val) }()
|
|
case func(Maps):
|
|
defer func() { m.Table(val) }()
|
|
default:
|
|
if reflect.TypeOf(val).Kind() == reflect.Func {
|
|
cbs = val
|
|
} else {
|
|
args = append(args, v)
|
|
}
|
|
}
|
|
}
|
|
list := kit.Simple(args...)
|
|
kit.If(len(list) == 0, func() { list = m.meta[MSG_DETAIL] })
|
|
if len(list) == 0 {
|
|
return m
|
|
}
|
|
ok := false
|
|
run := func(msg *Message, ctx *Context, cmd *Command, key string, arg ...string) {
|
|
ok = true
|
|
msg._source = _source
|
|
key = kit.Slice(strings.Split(key, PT), -1)[0]
|
|
kit.If(cbs, func() { msg.OptionCB(key, cbs) })
|
|
kit.For(opts, func(k string, v Any) { msg.Option(k, v) })
|
|
m = ctx._command(msg, cmd, key, arg...)
|
|
}
|
|
if list[0] == "" {
|
|
run(m.Spawn(), m.target, m._cmd, m._key, list[1:]...)
|
|
} else if cmd, ok := m.target.Commands[strings.TrimPrefix(list[0], m.target.Prefix()+PT)]; ok {
|
|
run(m.Spawn(), m.target, cmd, list[0], list[1:]...)
|
|
} else if cmd, ok := m.source.Commands[strings.TrimPrefix(list[0], m.source.Prefix()+PT)]; ok {
|
|
run(m.Spawn(m.source), m.source, cmd, list[0], list[1:]...)
|
|
} else {
|
|
m.Search(list[0], func(p *Context, s *Context, key string, cmd *Command) { run(m.Spawn(s), s, cmd, key, list[1:]...) })
|
|
}
|
|
m.Warn(!ok, ErrNotFound, kit.Format(list))
|
|
return m
|
|
}
|
|
func (c *Context) _command(m *Message, cmd *Command, key string, arg ...string) *Message {
|
|
if m._cmd, m._key, m._sub = cmd, key, SELECT; cmd == nil {
|
|
return m
|
|
}
|
|
if m.meta[MSG_DETAIL] = kit.Simple(m.PrefixKey(), arg); cmd.Actions != nil {
|
|
if len(arg) > 1 && arg[0] == ACTION {
|
|
if h, ok := cmd.Actions[arg[1]]; ok {
|
|
return c._action(m, cmd, key, arg[1], h, arg[2:]...)
|
|
}
|
|
} else if len(arg) > 0 {
|
|
if h, ok := cmd.Actions[arg[0]]; ok {
|
|
return c._action(m, cmd, key, arg[0], h, arg[1:]...)
|
|
}
|
|
}
|
|
}
|
|
if len(arg) > 0 && arg[0] == ACTION && arg[1] == INPUTS {
|
|
return m
|
|
}
|
|
return m.CmdHand(cmd, key, arg...)
|
|
}
|
|
func (c *Context) _action(m *Message, cmd *Command, key string, sub string, h *Action, arg ...string) *Message {
|
|
if h.Hand == nil {
|
|
return m.Cmdy(kit.Split(kit.Select(sub, h.Name)), arg)
|
|
}
|
|
if m._cmd, m._key, m._sub = cmd, key, sub; len(h.List) > 0 && sub != SEARCH {
|
|
order := false
|
|
for i, v := range h.List {
|
|
name := kit.Format(kit.Value(v, NAME))
|
|
if i == 0 {
|
|
if len(arg) > 0 && arg[0] == name {
|
|
kit.For(arg, func(k, v string) { m.Option(k, v) })
|
|
} else {
|
|
order = true
|
|
}
|
|
}
|
|
kit.If(order && i < len(arg), func() { m.Option(name, arg[i]) })
|
|
if m.Warn(m.OptionDefault(name, kit.Format(kit.Value(v, VALUE))) == "" && kit.Value(v, "need") == "must", ErrNotValid, name) {
|
|
return m
|
|
}
|
|
}
|
|
}
|
|
m._target = kit.Select(logs.FileLine(h.Hand), cmd.FileLines(), cmd.RawHand != nil)
|
|
m.Log(LOG_CMDS, "%s.%s %s %d %v", c.Name, key, sub, len(arg), arg, logs.FileLineMeta(m._fileline()))
|
|
h.Hand(m, arg...)
|
|
return m
|
|
}
|
|
func (c *Command) FileLines() string {
|
|
return kit.Join(kit.Slice(kit.Split(c.FileLine(), PS), -3), PS)
|
|
}
|
|
func (c *Command) FileLine() string {
|
|
if c == nil {
|
|
return ""
|
|
} else if c.RawHand != nil {
|
|
switch h := c.RawHand.(type) {
|
|
case string:
|
|
return h
|
|
default:
|
|
return logs.FileLines(c.RawHand)
|
|
}
|
|
} else if c.Hand != nil {
|
|
return logs.FileLines(c.Hand)
|
|
} else {
|
|
return ""
|
|
}
|
|
}
|