优化cli命令行的问题
This commit is contained in:
parent
83afa05fa3
commit
972eb004d2
20
cmd/main.go
20
cmd/main.go
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"embed"
|
||||
"flag"
|
||||
"git.nspix.com/golang/kos/entry/cli"
|
||||
"git.nspix.com/golang/kos/entry/http"
|
||||
httpkg "net/http"
|
||||
|
||||
|
@ -16,11 +17,29 @@ var webDir embed.FS
|
|||
type subServer struct {
|
||||
}
|
||||
|
||||
type users struct {
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
func (s *subServer) Start(ctx context.Context) (err error) {
|
||||
kos.Http().Root("/web", httpkg.FS(webDir))
|
||||
kos.Http().Handle(httpkg.MethodGet, "/hello", func(ctx *http.Context) (err error) {
|
||||
return ctx.Success("Hello World")
|
||||
})
|
||||
kos.Command().Handle("/test", "test command", func(ctx *cli.Context) (err error) {
|
||||
return ctx.Success([][]string{
|
||||
[]string{"NAME", "AGE"},
|
||||
[]string{"SSS", "aaa"},
|
||||
})
|
||||
})
|
||||
kos.Command().Handle("/users", "test command", func(ctx *cli.Context) (err error) {
|
||||
return ctx.Success([]*users{
|
||||
{Name: "Zhan", Age: 10, Tags: []string{"a", "b"}},
|
||||
{Name: "Lisi", Age: 15, Tags: []string{"c", "d"}},
|
||||
})
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -33,7 +52,6 @@ func main() {
|
|||
svr := kos.Init(
|
||||
kos.WithName("git.nspix.com/golang/test", "0.0.1"),
|
||||
kos.WithServer(&subServer{}),
|
||||
kos.WithDirectHttp(),
|
||||
)
|
||||
svr.Run()
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ func (client *Client) completer(str string) (ss []string) {
|
|||
)
|
||||
ss = make([]string, 0)
|
||||
seq = client.getSequence()
|
||||
if err = writeFrame(client.conn, newFrame(PacketTypeCompleter, FlagComplete, seq, []byte(str))); err != nil {
|
||||
if err = writeFrame(client.conn, newFrame(PacketTypeCompleter, FlagComplete, seq, client.Timeout, []byte(str))); err != nil {
|
||||
return
|
||||
}
|
||||
select {
|
||||
|
@ -166,10 +166,10 @@ func (client *Client) Execute(s string) (err error) {
|
|||
}()
|
||||
go client.ioLoop(client.conn)
|
||||
seq = client.getSequence()
|
||||
if err = writeFrame(client.conn, newFrame(PacketTypeCommand, FlagComplete, seq, []byte(s))); err != nil {
|
||||
if err = writeFrame(client.conn, newFrame(PacketTypeCommand, FlagComplete, seq, client.Timeout, []byte(s))); err != nil {
|
||||
return err
|
||||
}
|
||||
client.waitResponse(seq, time.Second*30)
|
||||
client.waitResponse(seq, client.Timeout)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -185,7 +185,7 @@ func (client *Client) Shell() (err error) {
|
|||
defer func() {
|
||||
_ = client.Close()
|
||||
}()
|
||||
if err = writeFrame(client.conn, newFrame(PacketTypeHandshake, FlagComplete, client.getSequence(), nil)); err != nil {
|
||||
if err = writeFrame(client.conn, newFrame(PacketTypeHandshake, FlagComplete, client.getSequence(), client.Timeout, nil)); err != nil {
|
||||
return
|
||||
}
|
||||
go client.ioLoop(client.conn)
|
||||
|
@ -216,7 +216,7 @@ func (client *Client) Shell() (err error) {
|
|||
continue
|
||||
}
|
||||
seq = client.getSequence()
|
||||
if err = writeFrame(client.conn, newFrame(PacketTypeCommand, FlagComplete, seq, []byte(line))); err != nil {
|
||||
if err = writeFrame(client.conn, newFrame(PacketTypeCommand, FlagComplete, seq, client.Timeout, []byte(line))); err != nil {
|
||||
break
|
||||
}
|
||||
client.liner.AppendHistory(line)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
@ -9,6 +10,7 @@ import (
|
|||
type Context struct {
|
||||
Id int64
|
||||
seq uint16
|
||||
ctx context.Context
|
||||
wc io.WriteCloser
|
||||
params map[string]string
|
||||
args []string
|
||||
|
@ -18,6 +20,7 @@ func (ctx *Context) reset(id int64, wc io.WriteCloser) {
|
|||
ctx.Id = id
|
||||
ctx.wc = wc
|
||||
ctx.seq = 0
|
||||
ctx.ctx = context.Background()
|
||||
ctx.args = make([]string, 0)
|
||||
ctx.params = make(map[string]string)
|
||||
}
|
||||
|
@ -34,6 +37,14 @@ func (ctx *Context) Bind(v any) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (ctx *Context) setContext(c context.Context) {
|
||||
ctx.ctx = c
|
||||
}
|
||||
|
||||
func (ctx *Context) Context() context.Context {
|
||||
return ctx.Context()
|
||||
}
|
||||
|
||||
func (ctx *Context) Argument(index int) string {
|
||||
if index >= len(ctx.args) || index < 0 {
|
||||
return ""
|
||||
|
@ -93,12 +104,12 @@ __END:
|
|||
chunkSize := math.MaxInt16 - 1
|
||||
n := len(buf) / chunkSize
|
||||
for i := 0; i < n; i++ {
|
||||
if err = writeFrame(ctx.wc, newFrame(res.Type, FlagPortion, ctx.seq, buf[offset:chunkSize+offset])); err != nil {
|
||||
if err = writeFrame(ctx.wc, newFrame(res.Type, FlagPortion, ctx.seq, 0, buf[offset:chunkSize+offset])); err != nil {
|
||||
return
|
||||
}
|
||||
offset += chunkSize
|
||||
}
|
||||
err = writeFrame(ctx.wc, newFrame(res.Type, FlagComplete, ctx.seq, buf[offset:]))
|
||||
err = writeFrame(ctx.wc, newFrame(res.Type, FlagComplete, ctx.seq, 0, buf[offset:]))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ type (
|
|||
Seq uint16 `json:"seq"`
|
||||
Data []byte `json:"data"`
|
||||
Error string `json:"error"`
|
||||
Timeout int64 `json:"timeout"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
)
|
||||
|
@ -55,6 +56,9 @@ func readFrame(r io.Reader) (frame *Frame, err error) {
|
|||
if err = binary.Read(r, binary.LittleEndian, &frame.Seq); err != nil {
|
||||
return
|
||||
}
|
||||
if err = binary.Read(r, binary.LittleEndian, &frame.Timeout); err != nil {
|
||||
return
|
||||
}
|
||||
if err = binary.Read(r, binary.LittleEndian, &frame.Timestamp); err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -116,6 +120,9 @@ func writeFrame(w io.Writer, frame *Frame) (err error) {
|
|||
if err = binary.Write(w, binary.LittleEndian, frame.Seq); err != nil {
|
||||
return
|
||||
}
|
||||
if err = binary.Write(w, binary.LittleEndian, frame.Timeout); err != nil {
|
||||
return
|
||||
}
|
||||
if err = binary.Write(w, binary.LittleEndian, frame.Timestamp); err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -142,13 +149,14 @@ func writeFrame(w io.Writer, frame *Frame) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func newFrame(t, f byte, seq uint16, data []byte) *Frame {
|
||||
func newFrame(t, f byte, seq uint16, timeout time.Duration, data []byte) *Frame {
|
||||
return &Frame{
|
||||
Feature: Feature,
|
||||
Type: t,
|
||||
Flag: f,
|
||||
Seq: seq,
|
||||
Data: data,
|
||||
Timeout: int64(timeout),
|
||||
Timestamp: time.Now().Unix(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,6 +97,9 @@ func (r *Router) Handle(path string, command Command) {
|
|||
name = path
|
||||
path = ""
|
||||
}
|
||||
if name == "-" {
|
||||
name = "app"
|
||||
}
|
||||
children := r.getChildren(name)
|
||||
if children == nil {
|
||||
children = newRouter(name)
|
||||
|
|
|
@ -170,7 +170,7 @@ func serializeArray(val []any) (buf []byte, err error) {
|
|||
if rv.Kind() == reflect.Array || rv.Kind() == reflect.Slice {
|
||||
row := make([]any, 0, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
if isNormalKind(rv.Index(i).Elem().Kind()) || rv.Index(i).Interface() == nil {
|
||||
if isNormalKind(rv.Index(i).Kind()) || rv.Index(i).Interface() == nil {
|
||||
row = append(row, rv.Index(i).Interface())
|
||||
} else {
|
||||
goto __END
|
||||
|
|
|
@ -44,9 +44,8 @@ func (svr *Server) releaseContext(ctx *Context) {
|
|||
ctxPool.Put(ctx)
|
||||
}
|
||||
|
||||
func (svr *Server) handle(ctx *Context, frame *Frame) {
|
||||
func (svr *Server) handle(ctx *Context, frame *Frame) (err error) {
|
||||
var (
|
||||
err error
|
||||
params map[string]string
|
||||
tokens []string
|
||||
args []string
|
||||
|
@ -54,6 +53,15 @@ func (svr *Server) handle(ctx *Context, frame *Frame) {
|
|||
)
|
||||
cmd := string(frame.Data)
|
||||
tokens = strings.Fields(cmd)
|
||||
if frame.Timeout > 0 {
|
||||
childCtx, cancelFunc := context.WithTimeout(svr.ctx, time.Duration(frame.Timeout))
|
||||
ctx.setContext(childCtx)
|
||||
defer func() {
|
||||
cancelFunc()
|
||||
}()
|
||||
} else {
|
||||
ctx.setContext(svr.ctx)
|
||||
}
|
||||
if r, args, err = svr.router.Lookup(tokens); err != nil {
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
err = ctx.Error(errNotFound, fmt.Sprintf("Command %s not found", cmd))
|
||||
|
@ -75,6 +83,7 @@ func (svr *Server) handle(ctx *Context, frame *Frame) {
|
|||
ctx.setParam(params)
|
||||
err = r.command.Handle(ctx)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (svr *Server) nextSequence() int64 {
|
||||
|
@ -130,7 +139,9 @@ func (svr *Server) process(conn net.Conn) {
|
|||
break
|
||||
}
|
||||
case PacketTypeCommand:
|
||||
svr.handle(ctx, frame)
|
||||
if err = svr.handle(ctx, frame); err != nil {
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
20
options.go
20
options.go
|
@ -31,6 +31,12 @@ type (
|
|||
}
|
||||
|
||||
Option func(o *Options)
|
||||
|
||||
HandleOptions struct {
|
||||
description string
|
||||
}
|
||||
|
||||
HandleOption func(o *HandleOptions)
|
||||
)
|
||||
|
||||
func (o *Options) ShortName() string {
|
||||
|
@ -45,6 +51,12 @@ func (o *Options) ShortName() string {
|
|||
return o.shortName
|
||||
}
|
||||
|
||||
func WithHandleDescription(s string) HandleOption {
|
||||
return func(o *HandleOptions) {
|
||||
o.description = s
|
||||
}
|
||||
}
|
||||
|
||||
func WithName(name string, version string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = name
|
||||
|
@ -100,3 +112,11 @@ func NewOptions(cbs ...Option) *Options {
|
|||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
func newHandleOptions(cbs ...HandleOption) *HandleOptions {
|
||||
opts := &HandleOptions{}
|
||||
for _, cb := range cbs {
|
||||
cb(opts)
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
|
|
@ -76,14 +76,15 @@ func (app *application) Command() *cli.Server {
|
|||
return app.command
|
||||
}
|
||||
|
||||
func (app *application) Handle(path string, cb HandleFunc) {
|
||||
func (app *application) Handle(path string, cb HandleFunc, cbs ...HandleOption) {
|
||||
opts := newHandleOptions(cbs...)
|
||||
if app.http != nil {
|
||||
app.http.Handle(http.MethodPost, path, func(ctx *http.Context) (err error) {
|
||||
return cb(ctx)
|
||||
})
|
||||
}
|
||||
if app.command != nil {
|
||||
app.command.Handle(path, "", func(ctx *cli.Context) (err error) {
|
||||
app.command.Handle(path, opts.description, func(ctx *cli.Context) (err error) {
|
||||
return cb(ctx)
|
||||
})
|
||||
}
|
||||
|
@ -252,10 +253,10 @@ func (app *application) preStart() (err error) {
|
|||
Uptime: time.Now().Sub(app.uptime).String(),
|
||||
Gateway: app.gateway.State(),
|
||||
})
|
||||
})
|
||||
}, WithHandleDescription("Display application state"))
|
||||
app.Handle("/-/healthy", func(ctx Context) (err error) {
|
||||
return ctx.Success(app.Healthy())
|
||||
})
|
||||
}, WithHandleDescription("Display application healthy"))
|
||||
}
|
||||
app.plugins.Range(func(key, value any) bool {
|
||||
if plugin, ok := value.(Plugin); ok {
|
||||
|
|
Loading…
Reference in New Issue