优化cli命令行的问题

This commit is contained in:
fancl 2024-01-18 17:11:44 +08:00
parent 83afa05fa3
commit 972eb004d2
10 changed files with 91 additions and 19 deletions

View File

@ -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()
}

View File

@ -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)

View File

@ -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
}

View File

@ -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(),
}
}

View File

@ -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)

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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 {

View File

@ -22,9 +22,9 @@ type (
Info() *Info
Http() *http.Server
Command() *cli.Server
Handle(method string, cb HandleFunc)
Handle(method string, cb HandleFunc, opts ...HandleOption)
}
// Info application information
Info struct {
ID string `json:"id"`