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