diff --git a/src/contexts/ctx/meta.go b/src/contexts/ctx/meta.go new file mode 100644 index 00000000..66353bf5 --- /dev/null +++ b/src/contexts/ctx/meta.go @@ -0,0 +1,1932 @@ +package ctx + +import ( + "fmt" + "log" + "math/rand" + "regexp" + "runtime" + "strconv" + "strings" + + "errors" + "io" + "sort" + "time" + "toolkit" +) + +type Cache struct { + Value string + Name string + Help string + Hand func(m *Message, x *Cache, arg ...string) string +} +type Config struct { + Value interface{} + Name string + Help string + Hand func(m *Message, x *Config, arg ...string) string +} +type Command struct { + Form map[string]int + Name string + Help interface{} + Auto func(m *Message, c *Context, key string, arg ...string) (ok bool) + Hand func(m *Message, c *Context, key string, arg ...string) (e error) +} +type Server interface { + Spawn(m *Message, c *Context, arg ...string) Server + Begin(m *Message, arg ...string) Server + Start(m *Message, arg ...string) bool + Close(m *Message, arg ...string) bool +} +type Context struct { + Name string + Help string + + Caches map[string]*Cache + Configs map[string]*Config + Commands map[string]*Command + + message *Message + requests []*Message + sessions []*Message + + contexts map[string]*Context + context *Context + root *Context + + exit chan bool + Server +} + +func (c *Context) Register(s *Context, x Server, args ...interface{}) { + force := false + if len(args) > 0 { + switch arg := args[0].(type) { + case bool: + force = arg + } + } + + if c.contexts == nil { + c.contexts = make(map[string]*Context) + } + if x, ok := c.contexts[s.Name]; ok && !force { + panic(errors.New(c.Name + "上下文中已存在模块:" + x.Name)) + } + + c.contexts[s.Name] = s + s.context = c + s.Server = x +} +func (c *Context) Spawn(m *Message, name string, help string) *Context { + s := &Context{Name: name, Help: help, root: c.root, context: c, message: m, + Caches: map[string]*Cache{}, + Configs: map[string]*Config{}, + Commands: map[string]*Command{}, + } + + if m.target = s; c.Server != nil { + c.Register(s, c.Server.Spawn(m, s, m.Meta["detail"]...)) + } else { + c.Register(s, nil) + } + return s +} +func (c *Context) Begin(m *Message, arg ...string) *Context { + if len(arg) > 0 { + m.Set("detail", arg) + } + + module := c.Name + if c.context != nil && c.context.Caches != nil && c.context.Caches["module"] != nil { + module = c.context.Caches["module"].Value + "." + c.Name + } + + c.Caches["module"] = &Cache{Name: "module", Value: module, Help: "模块域名"} + c.Caches["status"] = &Cache{Name: "status(begin/start/close)", Value: "begin", Help: "模块状态, begin: 初始完成, start: 正在运行, close: 运行结束"} + c.Caches["stream"] = &Cache{Name: "stream", Value: "", Help: "模块数据"} + + c.message = m + c.requests = append(c.requests, m) + m.source.sessions = append(m.source.sessions, m) + c.exit = make(chan bool, 3) + + /* + m.Log("begin", "%d context %v %v", m.Capi("ncontext", 1), m.Meta["detail"], m.Meta["option"]) + for k, x := range c.Configs { + if x.Hand != nil { + m.Log("begin", "%s config %v", k, m.Conf(k, x.Value)) + } + } + */ + + if c.Server != nil { + c.Server.Begin(m, m.Meta["detail"]...) + } + return c +} +func (c *Context) Start(m *Message, arg ...string) bool { + sync := false + if len(arg) > 0 && arg[0] == "sync" { + sync, arg = true, arg[1:] + } + if len(arg) > 0 { + m.Set("detail", arg) + } + + c.requests = append(c.requests, m) + m.source.sessions = append(m.source.sessions, m) + + if m.Hand = true; m.Cap("status") == "start" { + return true + } + + m.GoFunc(m, func(m *Message) { + m.Log(m.Cap("status", "start"), "%d server %v %v", m.Capi("nserver", 1), m.Meta["detail"], m.Meta["option"]) + + c.message = m + if c.exit <- false; c.Server == nil || c.Server.Start(m, m.Meta["detail"]...) { + c.Close(m, m.Meta["detail"]...) + c.exit <- true + } + }, func(m *Message) { + c.Close(m, m.Meta["detail"]...) + c.exit <- true + }) + + if sync { + for !<-c.exit { + } + return true + } + return <-c.exit +} +func (c *Context) Close(m *Message, arg ...string) bool { + if len(c.requests) == 0 { + return true + } + + if m.target == c { + for i := len(c.requests) - 1; i >= 0; i-- { + if msg := c.requests[i]; msg.code == m.code { + if c.Server == nil || c.Server.Close(m, arg...) { + m.Log("close", "request %d/%d", i, len(c.requests)-1) + msg.Free() + for j := i; j < len(c.requests)-1; j++ { + c.requests[j] = c.requests[j+1] + } + c.requests = c.requests[:len(c.requests)-1] + } + } + } + } + + if len(c.requests) > 0 { + return false + } + + if m.Cap("status") == "start" { + m.Log(m.Cap("status", "close"), "%d server %v", m.root.Capi("nserver", -1), arg) + for _, msg := range c.sessions { + if msg.Cap("status") != "close" { + msg.target.Close(msg, arg...) + } + } + } + + if c.context != nil { + m.Log("close", "%d context %v", m.root.Capi("ncontext", -1), arg) + delete(c.context.contexts, c.Name) + c.exit <- true + } + return true +} + +func (c *Context) Context() *Context { + return c.context +} +func (c *Context) Message() *Message { + return c.message +} +func (c *Context) Has(key ...string) bool { + switch len(key) { + case 2: + if _, ok := c.Commands[key[0]]; ok && key[1] == "command" { + return true + } + if _, ok := c.Configs[key[0]]; ok && key[1] == "config" { + return true + } + if _, ok := c.Caches[key[0]]; ok && key[1] == "cache" { + return true + } + case 1: + if _, ok := c.Commands[key[0]]; ok { + return true + } + if _, ok := c.Configs[key[0]]; ok { + return true + } + if _, ok := c.Caches[key[0]]; ok { + return true + } + } + return false +} +func (c *Context) Sub(key string) *Context { + return c.contexts[key] +} +func (c *Context) Travel(m *Message, hand func(m *Message, n int) (stop bool)) *Context { + if c == nil { + return nil + } + target := m.target + + cs := []*Context{c} + for i := 0; i < len(cs); i++ { + if m.target = cs[i]; hand(m, i) { + return cs[i] + } + + keys := []string{} + for k, _ := range cs[i].contexts { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + cs = append(cs, cs[i].contexts[k]) + } + } + + m.target = target + return target +} +func (c *Context) BackTrace(m *Message, hand func(m *Message) (stop bool)) *Context { + target := m.target + + for s := m.target; s != nil; s = s.context { + if m.target = s; hand(m) { + return s + } + } + + m.target = target + return target +} + +func (c *Context) Plugin(args []string) string { + m := &Message{code: 0, time: time.Now(), source: c, target: c, Meta: map[string][]string{}} + if len(args) == 0 { + m.Echo("%s: %s\n", c.Name, c.Help) + for k, v := range c.Commands { + m.Echo("%s: %s %v\n", k, v.Name, v.Help) + } + } else if cs, ok := c.Commands[args[0]]; ok { + h := cs.Hand + if e := h(m, c, args[0], args[1:]...); e != nil { + m.Echo("error: ").Echo("%v\n", e) + } + } else { + m.Echo("error: ").Echo("not found: %v\n", args[0]) + } + return strings.Join(m.Meta["result"], "") +} + +type DEBUG interface { + Wait(*Message, ...interface{}) interface{} + Goon(interface{}, ...interface{}) +} +type LOGGER interface { + Log(*Message, string, string, ...interface{}) +} +type Message struct { + time time.Time + code int + + source *Context + target *Context + + Meta map[string][]string + Data map[string]interface{} + + callback func(msg *Message) (sub *Message) + freedoms []func(msg *Message) (done bool) + Sessions map[string]*Message + + messages []*Message + message *Message + root *Message + + Remote chan bool + Hand bool +} + +func (m *Message) Spawn(arg ...interface{}) *Message { + temp := false + c := m.target + if len(arg) > 0 { + switch v := arg[0].(type) { + case *Context: + c = v + case *Message: + c = v.target + case string: + temp = kit.Right(v) + } + } + + msg := &Message{ + time: time.Now(), + code: m.Capi("nmessage", 1), + source: m.target, + target: c, + message: m, + root: m.root, + } + + if temp { + return msg + } + + m.messages = append(m.messages, msg) + return msg +} +func (m *Message) Time(arg ...interface{}) string { + t := m.time + + if len(arg) > 0 { + if d, e := time.ParseDuration(arg[0].(string)); e == nil { + arg = arg[1:] + t.Add(d) + } + } + + str := m.Conf("time_format") + if len(arg) > 1 { + str = fmt.Sprintf(arg[0].(string), arg[1:]...) + } else if len(arg) > 0 { + str = fmt.Sprintf("%v", arg[0]) + } + if str == "stamp" { + return kit.Format(t.Unix()) + } + return t.Format(str) +} +func (m *Message) Code() int { + return m.code +} +func (m *Message) Source() *Context { + return m.source +} +func (m *Message) Target() *Context { + return m.target +} +func (m *Message) Message() *Message { + return m.message +} +func (m *Message) Format(arg ...interface{}) string { + if len(arg) == 0 { + arg = append(arg, "time", "ship") + } + + meta := []string{} + for _, v := range arg { + switch kit.Format(v) { + case "summary": + msg := arg[1].(*Message) + ms := make([]*Message, 0, 1024) + ms = append(ms, msg.message, msg) + + for i := 0; i < len(ms); i++ { + msg := ms[i] + if m.Add("append", "index", i); msg == nil { + m.Add("append", "message", "") + m.Add("append", "time", "") + m.Add("append", "code", "") + m.Add("append", "source", "") + m.Add("append", "target", "") + m.Add("append", "details", "") + m.Add("append", "options", "") + continue + } + + if msg.message != nil { + m.Add("append", "message", msg.message.code) + } else { + m.Add("append", "message", "") + } + m.Add("append", "time", msg.time.Format("15:04:05")) + m.Add("append", "code", msg.code) + m.Add("append", "source", msg.source.Name) + m.Add("append", "target", msg.target.Name) + m.Add("append", "details", fmt.Sprintf("%v", msg.Meta["detail"])) + m.Add("append", "options", fmt.Sprintf("%v", msg.Meta["option"])) + + if i == 0 { + continue + } + + if len(ms) < 30 && len(arg) > 2 && arg[2] == "deep" { + ms = append(ms, ms[i].messages...) + } + } + m.Table() + case "time": + meta = append(meta, m.Time()) + case "code": + meta = append(meta, kit.Format(m.code)) + case "ship": + meta = append(meta, fmt.Sprintf("%s:%d(%s->%s)", m.Option("routine"), m.code, m.source.Name, m.target.Name)) + case "source": + target := m.target + m.target = m.source + meta = append(meta, m.Cap("module")) + m.target = target + case "target": + meta = append(meta, m.Cap("module")) + + case "detail": + meta = append(meta, fmt.Sprintf("%v", m.Meta["detail"])) + case "option": + meta = append(meta, fmt.Sprintf("%v", m.Meta["option"])) + case "append": + meta = append(meta, fmt.Sprintf("%v", m.Meta["append"])) + case "result": + meta = append(meta, fmt.Sprintf("%v", m.Meta["result"])) + + case "full": + case "chain": + ms := []*Message{} + if v == "full" { + ms = append(ms, m) + } else { + for msg := m; msg != nil; msg = msg.message { + ms = append(ms, msg) + } + } + + meta = append(meta, "\n") + for i := len(ms) - 1; i >= 0; i-- { + msg := ms[i] + + meta = append(meta, fmt.Sprintf("%s\n", msg.Format("time", "ship"))) + if len(msg.Meta["detail"]) > 0 { + meta = append(meta, fmt.Sprintf(" detail: %d %v\n", len(msg.Meta["detail"]), msg.Meta["detail"])) + } + if len(msg.Meta["option"]) > 0 { + meta = append(meta, fmt.Sprintf(" option: %d %v\n", len(msg.Meta["option"]), msg.Meta["option"])) + for _, k := range msg.Meta["option"] { + if v, ok := msg.Data[k]; ok { + meta = append(meta, fmt.Sprintf(" %s: %v\n", k, kit.Format(v))) + } else if v, ok := msg.Meta[k]; ok { + meta = append(meta, fmt.Sprintf(" %s: %d %v\n", k, len(v), v)) + } + } + } + if len(msg.Meta["append"]) > 0 { + meta = append(meta, fmt.Sprintf(" append: %d %v\n", len(msg.Meta["append"]), msg.Meta["append"])) + for _, k := range msg.Meta["append"] { + if v, ok := msg.Data[k]; ok { + meta = append(meta, fmt.Sprintf(" %s: %v\n", k, kit.Format(v))) + } else if v, ok := msg.Meta[k]; ok { + meta = append(meta, fmt.Sprintf(" %s: %d %v\n", k, len(v), v)) + } + } + } + if len(msg.Meta["result"]) > 0 { + meta = append(meta, fmt.Sprintf(" result: %d %v\n", len(msg.Meta["result"]), msg.Meta["result"])) + } + } + case "stack": + pc := make([]uintptr, 100) + pc = pc[:runtime.Callers(6, pc)] + frames := runtime.CallersFrames(pc) + + for { + frame, more := frames.Next() + file := strings.Split(frame.File, "/") + name := strings.Split(frame.Function, "/") + meta = append(meta, fmt.Sprintf("\n%s:%d\t%s", file[len(file)-1], frame.Line, name[len(name)-1])) + if !more { + break + } + } + + default: + meta = append(meta, kit.FileName(kit.Format(v), "time")) + } + } + return strings.Join(meta, " ") +} +func (m *Message) Tree(code int) *Message { + ms := []*Message{m} + for i := 0; i < len(ms); i++ { + if ms[i].Code() == code { + return ms[i] + } + ms = append(ms, ms[i].messages...) + } + return nil +} + +func (m *Message) Add(meta string, key string, value ...interface{}) *Message { + if m.Meta == nil { + m.Meta = make(map[string][]string) + } + if _, ok := m.Meta[meta]; !ok { + m.Meta[meta] = make([]string, 0, 3) + } + + switch meta { + case "detail", "result": + m.Meta[meta] = append(m.Meta[meta], key) + m.Meta[meta] = append(m.Meta[meta], kit.Trans(value...)...) + + case "option", "append": + if _, ok := m.Meta[key]; !ok { + m.Meta[key] = make([]string, 0, 3) + } + m.Meta[key] = append(m.Meta[key], kit.Trans(value...)...) + + for _, v := range m.Meta[meta] { + if v == key { + return m + } + } + m.Meta[meta] = append(m.Meta[meta], key) + + default: + m.Log("error", "add meta error %s %s %v", meta, key, value) + } + + return m +} +func (m *Message) Set(meta string, arg ...interface{}) *Message { + switch meta { + case "detail", "result": + if m != nil && m.Meta != nil { + delete(m.Meta, meta) + } + case "option", "append": + if len(arg) > 0 { + delete(m.Meta, kit.Format(arg[0])) + } else { + for _, k := range m.Meta[meta] { + delete(m.Data, k) + delete(m.Meta, k) + } + delete(m.Meta, meta) + } + default: + m.Log("error", "set meta error %s %s %v", meta, arg) + } + + if args := kit.Trans(arg...); len(args) > 0 { + m.Add(meta, args[0], args[1:]) + } + return m +} +func (m *Message) Put(meta string, key string, value interface{}) *Message { + switch meta { + case "option", "append": + if m.Set(meta, key); m.Data == nil { + m.Data = make(map[string]interface{}) + } + m.Data[key] = value + + default: + m.Log("error", "put data error %s %s %v", meta, key, value) + } + return m +} +func (m *Message) Get(key string, arg ...interface{}) string { + if meta, ok := m.Meta[key]; ok && len(meta) > 0 { + index := 0 + if len(arg) > 0 { + index = kit.Int(arg[0]) + } + + index = (index+2)%(len(meta)+2) - 2 + if index >= 0 && index < len(meta) { + return meta[index] + } + } + return "" +} +func (m *Message) Has(key ...string) bool { + switch len(key) { + case 1: + if _, ok := m.Data[key[0]]; ok { + return true + } + if _, ok := m.Meta[key[0]]; ok { + return true + } + } + return false +} +func (m *Message) CopyTo(msg *Message, arg ...string) *Message { + msg.Copy(m, "append").Copy(m, "result") + return m +} +func (m *Message) Copy(msg *Message, arg ...string) *Message { + if msg == nil || m == msg { + return m + } + + for i := 0; i < len(arg); i++ { + meta := arg[i] + + switch meta { + case "target": + m.target = msg.target + case "callback": + m.callback = msg.callback + case "detail", "result": + if len(msg.Meta[meta]) > 0 { + m.Add(meta, msg.Meta[meta][0], msg.Meta[meta][1:]) + } + case "option", "append": + if msg.Meta == nil { + msg.Meta = map[string][]string{} + } + if msg.Meta[meta] == nil { + break + } + if i == len(arg)-1 { + arg = append(arg, msg.Meta[meta]...) + } + + for i++; i < len(arg); i++ { + if v, ok := msg.Data[arg[i]]; ok { + m.Put(meta, arg[i], v) + } else if v, ok := msg.Meta[arg[i]]; ok { + m.Set(meta, arg[i], v) // TODO fuck Add + } + } + default: + if msg.Hand { + meta = "append" + } else { + meta = "option" + } + + if v, ok := msg.Data[arg[i]]; ok { + m.Put(meta, arg[i], v) + } + if v, ok := msg.Meta[arg[i]]; ok { + m.Add(meta, arg[i], v) + } + } + } + + return m +} +func (m *Message) CopyFuck(msg *Message, arg ...string) *Message { + if m == msg { + return m + } + + for i := 0; i < len(arg); i++ { + meta := arg[i] + + switch meta { + case "target": + m.target = msg.target + case "callback": + m.callback = msg.callback + case "detail", "result": + if len(msg.Meta[meta]) > 0 { + m.Add(meta, msg.Meta[meta][0], msg.Meta[meta][1:]) + } + case "option", "append": + if msg.Meta == nil { + msg.Meta = map[string][]string{} + } + if msg.Meta[meta] == nil { + break + } + if i == len(arg)-1 { + arg = append(arg, msg.Meta[meta]...) + } + + for i++; i < len(arg); i++ { + if v, ok := msg.Data[arg[i]]; ok { + m.Put(meta, arg[i], v) + } else if v, ok := msg.Meta[arg[i]]; ok { + m.Add(meta, arg[i], v) // TODO fuck Add + } + } + default: + if msg.Hand { + meta = "append" + } else { + meta = "option" + } + + if v, ok := msg.Data[arg[i]]; ok { + m.Put(meta, arg[i], v) + } + if v, ok := msg.Meta[arg[i]]; ok { + m.Add(meta, arg[i], v) + } + } + } + + return m +} +func (m *Message) Echo(str string, arg ...interface{}) *Message { + if len(arg) > 0 { + return m.Add("result", fmt.Sprintf(str, arg...)) + } + return m.Add("result", str) +} +func (m *Message) Auto(arg ...string) *Message { + for i := 0; i < len(arg); i += 3 { + m.Add("append", "value", arg[i]) + m.Add("append", "name", arg[i+1]) + m.Add("append", "help", arg[i+2]) + } + return m +} + +func (m *Message) Insert(meta string, index int, arg ...interface{}) string { + if m.Meta == nil { + m.Meta = make(map[string][]string) + } + m.Meta[meta] = kit.Array(m.Meta[meta], index, arg) + + if -1 < index && index < len(m.Meta[meta]) { + return m.Meta[meta][index] + } + return "" +} +func (m *Message) Detail(arg ...interface{}) string { + noset, index := true, 0 + if len(arg) > 0 { + switch v := arg[0].(type) { + case int: + noset, index, arg = false, v, arg[1:] + } + } + if noset && len(arg) > 0 { + index = -2 + } + + return m.Insert("detail", index, arg...) +} +func (m *Message) Detaili(arg ...interface{}) int { + return kit.Int(m.Detail(arg...)) +} +func (m *Message) Details(arg ...interface{}) bool { + return kit.Right(m.Detail(arg...)) +} +func (m *Message) Result(arg ...interface{}) string { + noset, index := true, 0 + if len(arg) > 0 { + switch v := arg[0].(type) { + case int: + noset, index, arg = false, v, arg[1:] + } + } + if noset && len(arg) > 0 { + index = -2 + } + + return m.Insert("result", index, arg...) +} +func (m *Message) Resulti(arg ...interface{}) int { + return kit.Int(m.Result(arg...)) +} +func (m *Message) Results(arg ...interface{}) bool { + return kit.Right(m.Result(arg...)) +} +func (m *Message) Option(key string, arg ...interface{}) string { + if len(arg) > 0 { + m.Insert(key, 0, arg...) + if _, ok := m.Meta[key]; ok { + m.Add("option", key) + } + } + + for msg := m; msg != nil; msg = msg.message { + if !msg.Has(key) { + continue + } + for _, k := range msg.Meta["option"] { + if k == key { + return msg.Get(key) + } + } + } + return "" +} +func (m *Message) Optioni(key string, arg ...interface{}) int { + return kit.Int(m.Option(key, arg...)) + +} +func (m *Message) Options(key string, arg ...interface{}) bool { + return kit.Right(m.Option(key, arg...)) +} +func (m *Message) Optionv(key string, arg ...interface{}) interface{} { + if len(arg) > 0 { + switch arg[0].(type) { + case nil: + // case []string: + // m.Option(key, v...) + // case string: + // m.Option(key, v) + default: + m.Put("option", key, arg[0]) + } + } + + for msg := m; msg != nil; msg = msg.message { + if msg.Meta == nil || !msg.Has(key) { + continue + } + for _, k := range msg.Meta["option"] { + if k == key { + if v, ok := msg.Data[key]; ok { + return v + } + return msg.Meta[key] + } + } + } + return nil +} +func (m *Message) Optionx(key string, arg ...string) interface{} { + value := m.Conf(key) + if value == "" { + value = m.Option(key) + } + + if len(arg) > 0 { + value = fmt.Sprintf(arg[0], value) + } + return value +} +func (m *Message) Magic(begin string, chain interface{}, args ...interface{}) interface{} { + auth := []string{"bench", "session", "user", "role", "componet", "command"} + key := []string{"bench", "sessid", "username", "role", "componet", "command"} + aaa := m.Sess("aaa", false) + for i, v := range auth { + if v == begin { + h := m.Option(key[i]) + if v == "user" { + h, _ = kit.Hash("username", m.Option("username")) + } + + data := aaa.Confv("auth", []string{h, "data"}) + + if kit.Format(chain) == "" { + return data + } + + if len(args) > 0 { + value := kit.Chain(data, chain, args[0]) + aaa.Conf("auth", []string{m.Option(key[i]), "data"}, value) + return value + } + + value := kit.Chain(data, chain) + if value != nil { + return value + } + + if i < len(auth)-1 { + begin = auth[i+1] + } + } + } + return nil +} +func (m *Message) Current(text string) string { + cs := []string{} + if pod := kit.Format(m.Magic("session", "current.pod")); pod != "" { + cs = append(cs, "context", "ssh", "remote", "'"+pod+"'") + } + if ctx := kit.Format(m.Magic("session", "current.ctx")); ctx != "" { + cs = append(cs, "context", ctx) + } + if cmd := kit.Format(m.Magic("session", "current.cmd")); cmd != "" { + cs = append(cs, cmd) + } + m.Log("info", "%s %s current %v", m.Option("username"), m.Option("sessid"), cs) + cs = append(cs, text) + return strings.Join(cs, " ") +} +func (m *Message) Append(key string, arg ...interface{}) string { + if len(arg) > 0 { + m.Insert(key, 0, arg...) + if _, ok := m.Meta[key]; ok { + m.Add("append", key) + } + } + + ms := []*Message{m} + for i := 0; i < len(ms); i++ { + ms = append(ms, ms[i].messages...) + if !ms[i].Has(key) { + continue + } + for _, k := range ms[i].Meta["append"] { + if k == key { + return ms[i].Get(key) + } + } + } + return "" +} +func (m *Message) Appendi(key string, arg ...interface{}) int64 { + i, _ := strconv.ParseInt(m.Append(key, arg...), 10, 64) + return i +} +func (m *Message) Appends(key string, arg ...interface{}) bool { + return kit.Right(m.Append(key, arg...)) +} +func (m *Message) Appendv(key string, arg ...interface{}) interface{} { + if len(arg) > 0 { + m.Put("append", key, arg[0]) + } + + ms := []*Message{m} + for i := 0; i < len(ms); i++ { + ms = append(ms, ms[i].messages...) + if !ms[i].Has(key) { + continue + } + for _, k := range ms[i].Meta["append"] { + if k == key { + if v, ok := ms[i].Data[key]; ok { + return v + } + return ms[i].Meta[key] + } + } + } + return nil +} +func (m *Message) Table(cbs ...interface{}) *Message { + if len(m.Meta["append"]) == 0 { + return m + } + + // 遍历函数 + if len(cbs) > 0 { + switch cb := cbs[0].(type) { + case func(map[string]string) bool: + nrow := len(m.Meta[m.Meta["append"][0]]) + line := map[string]string{} + for i := 0; i < nrow; i++ { + for _, k := range m.Meta["append"] { + line[k] = m.Meta[k][i] + } + if !cb(line) { + break + } + } + return m + case func(map[string]string): + nrow := len(m.Meta[m.Meta["append"][0]]) + for i := 0; i < nrow; i++ { + line := map[string]string{} + for _, k := range m.Meta["append"] { + line[k] = m.Meta[k][i] + } + cb(line) + } + return m + case func(int, map[string]string): + nrow := len(m.Meta[m.Meta["append"][0]]) + for i := 0; i < nrow; i++ { + line := map[string]string{} + for _, k := range m.Meta["append"] { + line[k] = m.Meta[k][i] + } + cb(i, line) + } + return m + } + } + + //计算列宽 + space := m.Confx("table_space") + depth, width := 0, map[string]int{} + for _, k := range m.Meta["append"] { + if len(m.Meta[k]) > depth { + depth = len(m.Meta[k]) + } + width[k] = kit.Width(k, len(space)) + for _, v := range m.Meta[k] { + if kit.Width(v, len(space)) > width[k] { + width[k] = kit.Width(v, len(space)) + } + } + } + + // 回调函数 + var cb func(maps map[string]string, list []string, line int) (goon bool) + if len(cbs) > 0 { + cb = cbs[0].(func(maps map[string]string, list []string, line int) (goon bool)) + } else { + row := m.Confx("table_row_sep") + col := m.Confx("table_col_sep") + compact := kit.Right(m.Confx("table_compact")) + cb = func(maps map[string]string, lists []string, line int) bool { + for i, v := range lists { + if k := m.Meta["append"][i]; compact { + v = maps[k] + } + + if m.Echo(v); i < len(lists)-1 { + m.Echo(col) + } + } + m.Echo(row) + return true + } + } + + // 输出表头 + row := map[string]string{} + wor := []string{} + for _, k := range m.Meta["append"] { + row[k], wor = k, append(wor, k+strings.Repeat(space, width[k]-kit.Width(k, len(space)))) + } + if !cb(row, wor, -1) { + return m + } + + // 输出数据 + for i := 0; i < depth; i++ { + row := map[string]string{} + wor := []string{} + for _, k := range m.Meta["append"] { + data := "" + if i < len(m.Meta[k]) { + data = m.Meta[k][i] + } + + row[k], wor = data, append(wor, data+strings.Repeat(space, width[k]-kit.Width(data, len(space)))) + } + if !cb(row, wor, i) { + break + } + } + + return m +} +func (m *Message) Sort(key string, arg ...string) *Message { + cmp := "str" + if len(arg) > 0 { + cmp = arg[0] + } + + number := map[int]int{} + table := []map[string]string{} + m.Table(func(line map[string]string, lists []string, index int) bool { + if index != -1 { + table = append(table, line) + switch cmp { + case "int": + number[index] = kit.Int(line[key]) + case "int_r": + number[index] = -kit.Int(line[key]) + case "time": + number[index] = kit.Time(line[key]) + case "time_r": + number[index] = -kit.Time(line[key]) + } + } + return true + }) + + for i := 0; i < len(table)-1; i++ { + for j := i + 1; j < len(table); j++ { + result := false + switch cmp { + case "str": + if table[i][key] > table[j][key] { + result = true + } + case "str_r": + if table[i][key] < table[j][key] { + result = true + } + default: + if number[i] > number[j] { + result = true + } + } + + if result { + table[i], table[j] = table[j], table[i] + number[i], number[j] = number[j], number[i] + } + } + } + + for _, k := range m.Meta["append"] { + delete(m.Meta, k) + } + + for _, v := range table { + for _, k := range m.Meta["append"] { + m.Add("append", k, v[k]) + } + } + return m +} +func (m *Message) Parse(arg interface{}) string { + switch str := arg.(type) { + case string: + if len(str) > 1 && str[0] == '$' { + return m.Cap(str[1:]) + } + if len(str) > 1 && str[0] == '@' { + if v := m.Option(str[1:]); v != "" { + return v + } + if v := kit.Format(m.Magic("bench", str[1:])); v != "" { + return v + } + v := m.Conf(str[1:]) + return v + } + return str + } + return "" +} +func (m *Message) ToHTML(style string) string { + cmd := strings.Join(m.Meta["detail"], " ") + result := []string{} + if len(m.Meta["append"]) > 0 { + result = append(result, fmt.Sprintf("
", v, " | ") + } + result = append(result, "
---|
", v, " | ") + } + result = append(result, "
")
+ result = append(result, fmt.Sprintf("%s", m.Find("shy", false).Conf("prompt")), cmd, "\n")
+ result = append(result, m.Meta["result"]...)
+ result = append(result, "
")
+ }
+ return strings.Join(result, "")
+}
+
+func (m *Message) Gdb(arg ...interface{}) interface{} {
+ if g := m.Sess("gdb", false); g != nil {
+ if gdb, ok := g.target.Server.(DEBUG); ok {
+ return gdb.Wait(m, arg...)
+ }
+ }
+ return nil
+}
+func (m *Message) Log(action string, str string, arg ...interface{}) *Message {
+ if m.Options("log.disable") {
+ return m
+ }
+
+ if l := m.Sess("log", false); l != nil {
+ if log, ok := l.target.Server.(LOGGER); ok {
+ if action == "error" {
+ log.Log(m, "error", "chain: %s", m.Format("chain"))
+ }
+ log.Log(m, action, str, arg...)
+ if action == "error" {
+ log.Log(m, "error", "stack: %s", m.Format("stack"))
+ }
+ return m
+ }
+ } else {
+ log.Printf(str, arg...)
+ }
+
+ if action == "error" {
+ kit.Log("error", fmt.Sprintf("chain: %s", m.Format("chain")))
+ kit.Log("error", fmt.Sprintf("%s %s %s", m.Format(), action, fmt.Sprintf(str, arg...)))
+ kit.Log("error", fmt.Sprintf("stack: %s", m.Format("stack")))
+ }
+
+ return m
+}
+func (m *Message) Show(args ...interface{}) *Message {
+ if m.Option("cli.modal") == "action" {
+ fmt.Printf(kit.Format(args...))
+ } else if kit.STDIO != nil {
+ kit.STDIO.Show(args...)
+ }
+ return m
+}
+func (m *Message) Assert(e interface{}, msg ...string) bool {
+ switch v := e.(type) {
+ case nil:
+ return true
+ case *Message:
+ if v.Result(0) != "error: " {
+ return true
+ }
+ e = v.Result(1)
+ e = errors.New(v.Result(1))
+ default:
+ if kit.Right(v) {
+ return true
+ }
+ }
+
+ switch e.(type) {
+ case error:
+ default:
+ e = errors.New(kit.Format(msg))
+ }
+
+ m.Log("error", "%v", e)
+ panic(e)
+}
+func (m *Message) TryCatch(msg *Message, safe bool, hand ...func(msg *Message)) *Message {
+ defer func() {
+ switch e := recover(); e {
+ case io.EOF:
+ case nil:
+ default:
+ m.Log("bench", "chain: %s", msg.Format("chain"))
+ m.Log("bench", "catch: %s", e)
+ m.Log("bench", "stack: %s", msg.Format("stack"))
+
+ if m.Log("error", "catch: %s", e); len(hand) > 1 {
+ m.TryCatch(msg, safe, hand[1:]...)
+ } else if !safe {
+ msg.Assert(e)
+ }
+ }
+ }()
+
+ if len(hand) > 0 {
+ hand[0](msg)
+ }
+ return m
+}
+func (m *Message) GoFunc(msg *Message, hand ...func(msg *Message)) *Message {
+ go func() {
+ msg.Option("routine", m.Capi("ngo", 1))
+ // msg.Log("info", "%v safe go begin", ngo)
+ // kit.Log("error", "%s ngo %s start", msg.Format(), ngo)
+ m.TryCatch(msg, true, hand...)
+ // kit.Log("error", "%s ngo %s end", msg.Format(), ngo)
+ // msg.Log("info", "%v safe go end", ngo)
+ }()
+ return m
+}
+func (m *Message) GoLoop(msg *Message, hand ...func(msg *Message)) *Message {
+ m.GoFunc(msg, func(msg *Message) {
+ for {
+ hand[0](msg)
+ }
+ })
+ return m
+}
+func (m *Message) Start(name string, help string, arg ...string) bool {
+ return m.Set("detail", arg).target.Spawn(m, name, help).Begin(m).Start(m)
+}
+func (m *Message) Close(arg ...string) bool {
+ return m.Target().Close(m, arg...)
+}
+func (m *Message) Wait() bool {
+ if m.target.exit != nil {
+ return <-m.target.exit
+ }
+ return true
+}
+
+func (m *Message) Find(name string, root ...bool) *Message {
+ if name == "" {
+ return m.Spawn()
+ }
+ target := m.target.root
+ if len(root) > 0 && !root[0] {
+ target = m.target
+ }
+
+ cs := target.contexts
+ for _, v := range strings.Split(name, ".") {
+ if x, ok := cs[v]; ok {
+ target, cs = x, x.contexts
+ } else if target.Name == v {
+ continue
+ } else {
+ m.Log("error", "context not find %s", name)
+ return nil
+ }
+ }
+
+ if len(root) > 1 && root[1] {
+ m.target = target
+ return m
+ }
+
+ return m.Spawn(target)
+}
+func (m *Message) Search(key string, root ...bool) []*Message {
+ reg, e := regexp.Compile(key)
+ m.Assert(e)
+
+ target := m.target
+ if target == nil {
+ return []*Message{nil}
+ }
+ if len(root) > 0 && root[0] {
+ target = m.target.root
+ }
+
+ cs := make([]*Context, 0, 3)
+ target.Travel(m, func(m *Message, i int) bool {
+ if reg.MatchString(m.target.Name) || reg.FindString(m.target.Help) != "" {
+ m.Log("search", "%d %s match [%s]", len(cs), m.target.Name, key)
+ cs = append(cs, m.target)
+ }
+ return false
+ })
+
+ ms := make([]*Message, len(cs))
+ for i := 0; i < len(cs); i++ {
+ ms[i] = m.Spawn(cs[i])
+ }
+ if len(ms) == 0 {
+ ms = append(ms, nil)
+ }
+
+ return ms
+}
+func (m *Message) Sess(key string, arg ...interface{}) *Message {
+ if key == "" {
+ return m.Spawn()
+ }
+
+ spawn := true
+ if len(arg) > 0 {
+ switch v := arg[0].(type) {
+ case bool:
+ spawn, arg = v, arg[1:]
+ }
+ }
+
+ if len(arg) > 0 {
+ if m.Sessions == nil {
+ m.Sessions = make(map[string]*Message)
+ }
+
+ switch value := arg[0].(type) {
+ case *Message:
+ m.Sessions[key] = value
+ return m.Sessions[key]
+ case *Context:
+ m.Sessions[key] = m.Spawn(value)
+ return m.Sessions[key]
+ case string:
+ root := len(arg) < 3 || kit.Right(arg[2])
+
+ method := "find"
+ if len(arg) > 1 {
+ method = kit.Format(arg[1])
+ }
+
+ switch method {
+ case "find":
+ m.Sessions[key] = m.Find(value, root)
+ case "search":
+ m.Sessions[key] = m.Search(value, root)[0]
+ }
+ return m.Sessions[key]
+ case nil:
+ delete(m.Sessions, key)
+ return nil
+ }
+ }
+
+ for msg := m; msg != nil; msg = msg.message {
+ if x, ok := msg.Sessions[key]; ok {
+ if spawn {
+ x = m.Spawn(x.target)
+ x.callback = func(sub *Message) *Message { return sub }
+ }
+ return x
+ }
+ }
+
+ return nil
+}
+func (m *Message) Match(key string, spawn bool, hand func(m *Message, s *Context, c *Context, key string) bool) *Message {
+ if m == nil {
+ return m
+ }
+
+ context := []*Context{m.target}
+ for _, v := range []string{"aaa", "ssh", "cli", "nfs"} {
+ if msg := m.Sess(v, false); msg != nil && msg.target != nil {
+ context = append(context, msg.target)
+ }
+ }
+ // if m.target.root != nil && m.target.root.Configs != nil && m.target.root.Configs["search"] != nil && m.target.root.Configs["search"].Value != nil {
+ // target := m.target
+ // for _, v := range kit.Trans(kit.Chain(m.target.root.Configs["search"].Value, "context")) {
+ // if t := m.Find(v, true, true); t != nil {
+ // kit.Log("error", "%v", t)
+ // // // context = append(context, t.target)
+ // }
+ // }
+ // m.target = target
+ // }
+
+ context = append(context, m.source)
+
+ for _, s := range context {
+ for c := s; c != nil; c = c.context {
+ if hand(m, s, c, key) {
+ return m
+ }
+ }
+ }
+ return m
+}
+func (m *Message) Call(cb func(msg *Message) (sub *Message), arg ...interface{}) *Message {
+ if m == nil {
+ return m
+ }
+ if m.callback = cb; len(arg) > 0 || len(m.Meta["detail"]) > 0 {
+ m.Log("call", m.Format("detail", "option"))
+ m.Cmd(arg...)
+ }
+ return m
+}
+func (m *Message) Back(ms ...*Message) *Message {
+ if m.callback == nil {
+ return m
+ }
+
+ if len(ms) == 0 {
+ ms = append(ms, m.Spawn(m.source).Copy(m, "append").Copy(m, "result"))
+ }
+
+ ns := []*Message{}
+
+ for _, msg := range ms {
+ if msg.Hand {
+ m.Log("back", msg.Format("ship", "result", "append"))
+ } else {
+ m.Log("back", msg.Format("ship", "detail", "option"))
+ }
+
+ if sub := m.callback(msg); sub != nil && m.message != nil && m.message != m {
+ ns = append(ns, sub)
+ }
+ }
+
+ if len(ns) > 0 {
+ m.message.Back(ns...)
+ }
+ return m
+}
+func (m *Message) Backs(msg *Message) *Message {
+ m.Back(msg)
+ return msg
+}
+func (m *Message) CallBack(sync bool, cb func(msg *Message) (sub *Message), arg ...interface{}) *Message {
+ if !sync {
+ return m.Call(cb, arg...)
+ }
+
+ wait := make(chan *Message, 10)
+ // m.GoFunc(m, func(m *Message) {
+ m.Call(func(sub *Message) *Message {
+ msg := cb(sub)
+ m.Log("sync", m.Format("done", "result", "append"))
+ wait <- m
+ return msg
+ }, arg...)
+ // })
+
+ m.Log("sync", m.Format("wait", "result", "append"))
+ select {
+ case <-time.After(kit.Duration(m.Conf("call_timeout"))):
+ m.Log("sync", m.Format("timeout", "result", "append"))
+ case <-wait:
+ }
+ return m
+}
+func (m *Message) Free(cbs ...func(msg *Message) (done bool)) *Message {
+ if len(cbs) == 0 {
+ for i := len(m.freedoms) - 1; i >= 0; i-- {
+ m.Log("free", "%d/%d", i, len(m.freedoms)-1)
+ if !m.freedoms[i](m) {
+ break
+ }
+ m.freedoms = m.freedoms[:i]
+ }
+ return m
+ }
+
+ m.freedoms = append(m.freedoms, cbs...)
+ return m
+}
+
+func (m *Message) Cmdp(t time.Duration, head []string, prefix []string, suffix [][]string) *Message {
+ if head != nil && len(head) > 0 {
+ m.Show(strings.Join(head, " "), "...\n")
+ }
+
+ for i, v := range suffix {
+ m.Show(fmt.Sprintf("%v/%v %v...\n", i+1, len(suffix), v))
+ m.CopyFuck(m.Cmd(prefix, v), "append")
+ time.Sleep(t)
+ }
+ m.Show("\n")
+ m.Table()
+ return m
+}
+func (m *Message) Cmdm(args ...interface{}) *Message {
+ m.Log("info", "current: %v", m.Magic("session", "current"))
+
+ arg := []string{}
+ if pod := kit.Format(m.Magic("session", "current.pod")); pod != "" {
+ arg = append(arg, "context", "ssh", "remote", pod)
+ }
+ if ctx := kit.Format(m.Magic("session", "current.ctx")); ctx != "" {
+ arg = append(arg, "context", ctx)
+ }
+ arg = append(arg, kit.Trans(args...)...)
+
+ // 执行命令
+ m.Spawn().Cmd(arg).CopyTo(m)
+ // m.Magic("session", "current.ctx", msg.target.Name)
+ return m
+}
+func (m *Message) Cmdy(args ...interface{}) *Message {
+ m.Cmd(args...).CopyTo(m)
+ return m
+}
+func (m *Message) Cmdx(args ...interface{}) string {
+ msg := m.Cmd(args...)
+ if msg.Result(0) == "error: " {
+ return msg.Result(1)
+ }
+ return msg.Result(0)
+}
+func (m *Message) Cmds(args ...interface{}) bool {
+ return m.Cmd(args...).Results(0)
+}
+func (m *Message) Cmd(args ...interface{}) *Message {
+ if m == nil {
+ return m
+ }
+
+ if len(args) > 0 {
+ m.Set("detail", kit.Trans(args...))
+ }
+ key, arg := m.Meta["detail"][0], m.Meta["detail"][1:]
+
+ msg := m
+ if strings.Contains(key, ":") {
+ ps := strings.Split(key, ":")
+ msg, key, arg = msg.Sess("ssh"), "_route", append([]string{"sync", ps[0], ps[1]}, arg...)
+ defer func() { m.Copy(msg, "append").Copy(msg, "result") }()
+ m.Hand = true
+
+ } else if strings.Contains(key, ".") {
+ arg := strings.Split(key, ".")
+ msg, key = msg.Sess(arg[0]), arg[1]
+ msg.Option("remote_code", "")
+ }
+ if msg == nil {
+ return msg
+ }
+
+ msg = msg.Match(key, true, func(msg *Message, s *Context, c *Context, key string) bool {
+ msg.Hand = false
+ if x, ok := c.Commands[key]; ok && x.Hand != nil {
+ msg.TryCatch(msg, true, func(msg *Message) {
+ msg.Log("cmd", "%s %s %v %v", c.Name, key, arg, msg.Meta["option"])
+
+ for _, form := range []map[string]int{map[string]int{"page.limit": 1, "page.offset": 1}, x.Form} {
+
+ if args := []string{}; form != nil {
+ for i := 0; i < len(arg); i++ {
+ if n, ok := form[arg[i]]; ok {
+ if n < 0 {
+ n += len(arg) - i
+ }
+ for j := i + 1; j <= i+n && j < len(arg); j++ {
+ if _, ok := form[arg[j]]; ok {
+ n = j - i - 1
+ }
+ }
+ if i+1+n > len(arg) {
+ msg.Add("option", arg[i], arg[i+1:])
+ } else {
+ msg.Add("option", arg[i], arg[i+1:i+1+n])
+ }
+ i += n
+ } else {
+ args = append(args, arg[i])
+ }
+ }
+ arg = args
+ }
+ }
+
+ target := msg.target
+ msg.target = s
+
+ msg.Hand = true
+ switch v := msg.Gdb("command", key, arg).(type) {
+ case string:
+ msg.Echo(v)
+ case nil:
+ if msg.Options("auto_cmd") {
+ if x.Auto != nil {
+ x.Auto(msg, c, key, arg...)
+ }
+ } else {
+ x.Hand(msg, c, key, arg...)
+ }
+ }
+ if msg.target == s {
+ msg.target = target
+ }
+ })
+ }
+ return msg.Hand
+ })
+
+ if !msg.Hand {
+ msg.Log("error", "cmd run error %s", msg.Format())
+ }
+ return msg
+}
+
+func (m *Message) Confm(key string, args ...interface{}) map[string]interface{} {
+ random := ""
+ var chain interface{}
+ if len(args) > 0 {
+ switch arg := args[0].(type) {
+ case []interface{}:
+ chain, args = arg, args[1:]
+ case []string:
+ chain, args = arg, args[1:]
+ case string:
+ switch arg {
+ case "%", "*":
+ random, args = arg, args[1:]
+ default:
+ chain, args = arg, args[1:]
+ }
+ }
+ }
+
+ var v interface{}
+ if chain == nil {
+ v = m.Confv(key)
+ } else {
+ v = m.Confv(key, chain)
+ }
+
+ table, _ := v.([]interface{})
+ value, _ := v.(map[string]interface{})
+ if len(args) == 0 {
+ return value
+ }
+
+ switch fun := args[0].(type) {
+ case func(int, string):
+ for i, v := range table {
+ fun(i, kit.Format(v))
+ }
+ case func(int, string) bool:
+ for i, v := range table {
+ if fun(i, kit.Format(v)) {
+ break
+ }
+ }
+ case func(string, string):
+ for k, v := range value {
+ fun(k, kit.Format(v))
+ }
+ case func(string, string) bool:
+ for k, v := range value {
+ if fun(k, kit.Format(v)) {
+ break
+ }
+ }
+ case func(map[string]interface{}):
+ if len(value) == 0 {
+ return nil
+ }
+ fun(value)
+ case func(string, map[string]interface{}):
+ switch random {
+ case "%":
+ n, i := rand.Intn(len(value)), 0
+ for k, v := range value {
+ if val, ok := v.(map[string]interface{}); i == n && ok {
+ fun(k, val)
+ break
+ }
+ i++
+ }
+ case "*":
+ fallthrough
+ default:
+ for k, v := range value {
+ if val, ok := v.(map[string]interface{}); ok {
+ fun(k, val)
+ }
+ }
+ }
+ case func(string, int, map[string]interface{}):
+ for k, v := range value {
+ if val, ok := v.([]interface{}); ok {
+ for i, v := range val {
+ if val, ok := v.(map[string]interface{}); ok {
+ fun(k, i, val)
+ }
+ }
+ }
+ }
+
+ case func(string, map[string]interface{}) bool:
+ for k, v := range value {
+ if val, ok := v.(map[string]interface{}); ok {
+ if fun(k, val) {
+ break
+ }
+ }
+ }
+ case func(int, map[string]interface{}):
+ for i := m.Optioni("page.begin"); i < len(table); i++ {
+ if val, ok := table[i].(map[string]interface{}); ok {
+ fun(i, val)
+ }
+ }
+ }
+ return value
+}
+func (m *Message) Confx(key string, args ...interface{}) string {
+ value := kit.Select(m.Conf(key), m.Option(key))
+ if len(args) == 0 {
+ return value
+ }
+
+ switch arg := args[0].(type) {
+ case []string:
+ if len(args) > 1 {
+ value = kit.Select(value, arg, args[1])
+ } else {
+ value = kit.Select(value, arg)
+ }
+ args = args[1:]
+ case map[string]interface{}:
+ value = kit.Select(value, kit.Format(arg[key]))
+ case string:
+ value = kit.Select(value, arg)
+ case nil:
+ default:
+ value = kit.Select(value, args[0])
+ }
+
+ format := "%s"
+ if args = args[1:]; len(args) > 0 {
+ format, args = kit.Format(args[0]), args[1:]
+ }
+ arg := []interface{}{format, value}
+ for _, v := range args {
+ arg = append(arg, v)
+ }
+
+ return kit.Format(arg...)
+}
+func (m *Message) Confs(key string, arg ...interface{}) bool {
+ return kit.Right(m.Confv(key, arg...))
+}
+func (m *Message) Confi(key string, arg ...interface{}) int {
+ return kit.Int(m.Confv(key, arg...))
+}
+func (m *Message) Confv(key string, args ...interface{}) interface{} {
+ if strings.Contains(key, ".") {
+ target := m.target
+ defer func() { m.target = target }()
+
+ ps := strings.Split(key, ".")
+ if msg := m.Sess(ps[0], false); msg != nil {
+ m.target, key = msg.target, ps[1]
+ }
+ }
+
+ var config *Config
+ m.Match(key, false, func(m *Message, s *Context, c *Context, key string) bool {
+ if x, ok := c.Configs[key]; ok {
+ config = x
+ return true
+ }
+ return false
+ })
+
+ if len(args) == 0 {
+ if config == nil {
+ return nil
+ }
+ return config.Value
+ }
+
+ if config == nil {
+ config = &Config{}
+ m.target.Configs[key] = config
+ }
+
+ switch config.Value.(type) {
+ case string:
+ config.Value = kit.Format(args...)
+ case bool:
+ config.Value = kit.Right(args...)
+ case int:
+ config.Value = kit.Int(args...)
+ case nil:
+ config.Value = args[0]
+ default:
+ for i := 0; i < len(args); i += 2 {
+ if i < len(args)-1 {
+ config.Value = kit.Chain(config.Value, args[i], args[i+1])
+ } else {
+ return kit.Chain(config.Value, args[i])
+ }
+ }
+ }
+
+ return config.Value
+}
+func (m *Message) Conf(key string, args ...interface{}) string {
+ return kit.Format(m.Confv(key, args...))
+}
+func (m *Message) Caps(key string, arg ...interface{}) bool {
+ if len(arg) > 0 {
+ return kit.Right(m.Cap(key, arg...))
+ }
+ return kit.Right(m.Cap(key))
+}
+func (m *Message) Capi(key string, arg ...interface{}) int {
+ n := kit.Int(m.Cap(key))
+ if len(arg) > 0 {
+ return kit.Int(m.Cap(key, n+kit.Int(arg...)))
+ }
+ return n
+}
+func (m *Message) Cap(key string, arg ...interface{}) string {
+ var cache *Cache
+ m.Match(key, false, func(m *Message, s *Context, c *Context, key string) bool {
+ if x, ok := c.Caches[key]; ok {
+ cache = x
+ return true
+ }
+ return false
+ })
+
+ if len(arg) == 0 {
+ if cache == nil {
+ return ""
+ }
+ if cache.Hand != nil {
+ return cache.Hand(m, cache)
+ }
+ return cache.Value
+ }
+
+ if cache == nil {
+ cache = &Cache{}
+ m.target.Caches[key] = cache
+ }
+
+ if cache.Hand != nil {
+ cache.Value = cache.Hand(m, cache, kit.Format(arg...))
+ } else {
+ cache.Value = kit.Format(arg...)
+ }
+ return cache.Value
+}