mirror of
https://shylinux.com/x/ContextOS
synced 2025-04-25 16:58:06 +08:00
569 lines
12 KiB
Go
569 lines
12 KiB
Go
package ctx
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"time"
|
|
"toolkit"
|
|
)
|
|
|
|
func (c *Context) Register(s *Context, x Server, args ...interface{}) *Context {
|
|
name, force := s.Name, false
|
|
if len(args) > 0 {
|
|
switch arg := args[0].(type) {
|
|
case bool:
|
|
force = arg
|
|
case string:
|
|
name, s.Name = arg, arg
|
|
}
|
|
}
|
|
|
|
if c.contexts == nil {
|
|
c.contexts = make(map[string]*Context)
|
|
}
|
|
if x, ok := c.contexts[name]; ok && !force {
|
|
panic(errors.New(c.Name + "上下文中已存在模块:" + x.Name))
|
|
}
|
|
|
|
c.contexts[name] = s
|
|
s.context = c
|
|
s.Server = x
|
|
return s
|
|
}
|
|
func (c *Context) Plugin(s *Context, args []string) string {
|
|
c.Register(s, nil)
|
|
m := &Message{code: 0, time: time.Now(), source: s, target: s, Meta: map[string][]string{}}
|
|
kit.DisableLog = true
|
|
m.Option("log.disable", true)
|
|
m.Option("bio.modal", "action")
|
|
|
|
if len(args) == 0 {
|
|
m.Echo("%s: %s\n\n", s.Name, s.Help)
|
|
m.Echo("命令列表:\n")
|
|
for k, v := range s.Commands {
|
|
if !strings.HasPrefix(k, "_") {
|
|
m.Echo(" %s: %s\n %v\n\n", k, v.Name, v.Help)
|
|
}
|
|
}
|
|
m.Echo("配置列表:\n")
|
|
for k, v := range s.Configs {
|
|
if !strings.HasPrefix(k, "_") {
|
|
m.Echo("--%s(%v): %s\n", k, kit.Formats(v.Value), v.Help)
|
|
}
|
|
}
|
|
} else {
|
|
if Index.Begin(Pulse, args...); Index.Start(Pulse, args...) {
|
|
}
|
|
m.Cmd(args)
|
|
}
|
|
for _, v := range m.Meta["result"] {
|
|
fmt.Printf(v)
|
|
}
|
|
return ""
|
|
}
|
|
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)
|
|
|
|
if c.Server != nil {
|
|
c.Server.Begin(m, m.Meta["detail"]...)
|
|
}
|
|
m.root.Capi("ncontext", 1)
|
|
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.Gos(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...) {
|
|
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 (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 {
|
|
m.Assert(e)
|
|
}
|
|
}
|
|
}()
|
|
|
|
if len(hand) > 0 {
|
|
hand[0](msg)
|
|
}
|
|
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 = errors.New(strings.Join(v.Meta["result"], ""))
|
|
default:
|
|
if kit.Right(v) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
switch e.(type) {
|
|
case error:
|
|
default:
|
|
e = errors.New(kit.Format(msg))
|
|
}
|
|
|
|
kit.Log("error", "%v", e)
|
|
panic(e)
|
|
}
|
|
func (m *Message) GoLoop(msg *Message, hand ...func(msg *Message)) *Message {
|
|
m.Gos(msg, func(msg *Message) {
|
|
for {
|
|
hand[0](msg)
|
|
}
|
|
})
|
|
return m
|
|
}
|
|
func (m *Message) Gos(msg *Message, hand ...func(msg *Message)) *Message {
|
|
go func() {
|
|
msg.Option("ctx.routine", m.Capi("ngo", 1))
|
|
m.TryCatch(msg, true, hand...)
|
|
}()
|
|
return m
|
|
}
|
|
|
|
func (m *Message) Spawn(arg ...interface{}) *Message {
|
|
temp := false
|
|
c := m.target
|
|
for i := 0; i < len(arg); i++ {
|
|
switch v := arg[i].(type) {
|
|
case *Context:
|
|
c = v
|
|
case *Message:
|
|
c = v.target
|
|
case string:
|
|
temp = kit.Right(v)
|
|
case bool:
|
|
temp = v
|
|
}
|
|
}
|
|
|
|
msg := &Message{
|
|
time: time.Now(),
|
|
code: -1,
|
|
source: m.target,
|
|
target: c,
|
|
message: m,
|
|
root: m.root,
|
|
}
|
|
|
|
if temp {
|
|
return msg
|
|
}
|
|
|
|
msg.code = m.Capi("nmessage", 1)
|
|
m.messages = append(m.messages, msg)
|
|
return msg
|
|
}
|
|
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
|
|
}
|
|
}
|
|
|
|
temp := false
|
|
if len(arg) > 0 {
|
|
switch v := arg[0].(type) {
|
|
case bool:
|
|
temp, arg = v, arg[1:]
|
|
}
|
|
}
|
|
|
|
for msg := m; msg != nil; msg = msg.message {
|
|
if x, ok := msg.Sessions[key]; ok {
|
|
if spawn {
|
|
x = m.Spawn(x.target, temp)
|
|
x.callback = func(sub *Message) *Message { return sub }
|
|
}
|
|
return x
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
func (m *Message) Form(x *Command, arg []string) []string {
|
|
form, ok := m.Optionv("ctx.form").(map[string]int)
|
|
if !ok {
|
|
return arg
|
|
}
|
|
for _, form := range []map[string]int{form, 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) {
|
|
m.Add("option", arg[i], arg[i+1:])
|
|
} else {
|
|
m.Add("option", arg[i], arg[i+1:i+1+n])
|
|
}
|
|
i += n
|
|
} else {
|
|
args = append(args, arg[i])
|
|
}
|
|
}
|
|
arg = args
|
|
}
|
|
}
|
|
|
|
return arg
|
|
}
|
|
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) 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.Call(func(sub *Message) *Message {
|
|
msg := cb(sub)
|
|
wait <- m
|
|
return msg
|
|
}, arg...)
|
|
|
|
select {
|
|
case <-time.After(kit.Duration(m.Confx("call_timeout"))):
|
|
m.Log("sync", m.Format("timeout", "detail", "option"))
|
|
m.Echo("time out %v", m.Confx("call_timeout"))
|
|
case <-wait:
|
|
}
|
|
return m
|
|
}
|
|
func (m *Message) Free(cbs ...func(msg *Message) (done bool)) *Message {
|
|
if len(cbs) == 0 {
|
|
for i := len(m.freeback) - 1; i >= 0; i-- {
|
|
m.Log("free", "%d/%d", i, len(m.freeback)-1)
|
|
if !m.freeback[i](m) {
|
|
break
|
|
}
|
|
m.freeback = m.freeback[:i]
|
|
}
|
|
return m
|
|
}
|
|
|
|
m.freeback = append(m.freeback, cbs...)
|
|
return m
|
|
}
|
|
|
|
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 kit.Trans(m.Optionv("ctx.chain")) {
|
|
if msg := m.Sess(v, false); msg != nil && msg.target != nil {
|
|
if msg.target != m.target && msg.target != m.source {
|
|
context = append(context, msg.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) 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) 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) Goshy(input []string, index int, stack *kit.Stack, cb func(*Message)) bool {
|
|
m.Optionv("bio.msg", m)
|
|
if stack == nil {
|
|
stack = &kit.Stack{}
|
|
stack.Push("source", true, 0)
|
|
}
|
|
m.Optionv("bio.stack", stack)
|
|
m.Optionv("bio.input", input)
|
|
|
|
for i := index; i < len(input); i++ {
|
|
line := input[i]
|
|
m.Optioni("stack.pos", i)
|
|
|
|
// 执行语句
|
|
msg := m.Sess("yac", true, true).Cmd("parse", line+"\n")
|
|
if cb != nil {
|
|
cb(msg)
|
|
}
|
|
|
|
// 切换模块
|
|
if v := msg.Optionv("bio.ctx"); v != nil {
|
|
m.Optionv("bio.ctx", v)
|
|
}
|
|
|
|
// 跳转语句
|
|
if msg.Appends("bio.pos0") {
|
|
i = int(msg.Appendi("bio.pos0")) - 1
|
|
msg.Append("bio.pos0", "")
|
|
}
|
|
|
|
// 结束脚本
|
|
if msg.Appends("bio.end") {
|
|
m.Copy(msg, "append").Copy(msg, "result")
|
|
msg.Appends("bio.end", "")
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|