diff --git a/internal/server/session.go b/internal/server/session.go index 55c265a..f7b807f 100644 --- a/internal/server/session.go +++ b/internal/server/session.go @@ -85,7 +85,7 @@ func (sn *session) read() { sn.outbox <- wire.ErrorResponse(0, "unable to parse request: %v", err) break } - sn.outbox <- wire.NewResponse(req.Seq, wire.OK{}) + sn.outbox <- wire.Response{req.Seq, wire.OK{}} case websocket.BinaryMessage: sn.outbox <- wire.ErrorResponse(0, "unable to parse binary frames") } diff --git a/internal/ui/ui.go b/internal/ui/ui.go index edbbbee..7bed2d0 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -1,6 +1,8 @@ package ui import ( + "fmt" + "github.com/gdamore/tcell/v2" "github.com/jordanorelli/astro-domu/internal/exit" "github.com/jordanorelli/astro-domu/internal/wire" @@ -15,18 +17,41 @@ type UI struct { } func (ui *UI) Run() { + ui.setupTerminal() + defer ui.clearTerminal() + + if err := ui.connect(); err != nil { + return + } + + ui.mode = &boxWalker{width: 10, height: 6} + ui.Info("running ui") + if ui.handleUserInput() { + ui.Info("user requested close") + ui.Info("closing client") + ui.client.Close() + ui.Info("client closed") + ui.Info("finalizing screen") + } + ui.Info("run loop done, shutting down") +} + +func (ui *UI) connect() error { ui.client = &wire.Client{ Log: ui.Child("client"), Host: "127.0.0.1", Port: 12805, } - notifications, err := ui.client.Dial() + c, err := ui.client.Dial() if err != nil { - exit.WithMessage(1, "unable to dial server: %v", err) + return fmt.Errorf("unable to dial server: %v", err) } - go ui.handleNotifications(notifications) + go ui.handleNotifications(c) + return nil +} +func (ui *UI) setupTerminal() { screen, err := tcell.NewScreen() if err != nil { exit.WithMessage(1, "unable to create a screen: %v", err) @@ -43,21 +68,11 @@ func (ui *UI) Run() { ui.Debug("screen height: %d", height) ui.Debug("screen colors: %v", screen.Colors()) ui.Debug("screen has mouse: %v", screen.HasMouse()) +} - ui.mode = &boxWalker{ - width: 10, - height: 6, - } - ui.Info("running ui") - if ui.run() { - ui.Info("user requested close") - ui.Info("closing client") - ui.client.Close() - ui.Info("client closed") - ui.Info("finalizing screen") - } - ui.Info("run loop done, shutting down") - screen.Fini() +func (ui *UI) clearTerminal() { + ui.screen.Clear() + ui.screen.Fini() } func (ui *UI) handleNotifications(c <-chan wire.Response) { @@ -68,6 +83,7 @@ func (ui *UI) handleNotifications(c <-chan wire.Response) { ui.Info("clearing and finalizing screen from notifications goroutine") ui.screen.Clear() ui.screen.Fini() + ui.Info("screen finalized") } // writeString writes a string in the given style from left to right beginning @@ -87,7 +103,7 @@ func (ui *UI) writeString(x, y int, s string, style tcell.Style) { } } -func (ui *UI) run() bool { +func (ui *UI) handleUserInput() bool { ui.screen.Clear() ui.mode.draw(ui) @@ -98,6 +114,7 @@ func (ui *UI) run() bool { // someone else shut us down, so return false return false } + ui.Info("screen sees event: %v", e) switch v := e.(type) { case *tcell.EventKey: diff --git a/internal/wire/error.go b/internal/wire/error.go index 6ce0865..272e670 100644 --- a/internal/wire/error.go +++ b/internal/wire/error.go @@ -1,6 +1,7 @@ package wire import ( + "encoding/json" "fmt" ) @@ -12,6 +13,10 @@ func (e Error) Error() string { return e.val.Error() } func (e Error) NetTag() string { return "error" } func (e Error) Unwrap() error { return e.val } +func (e Error) MarshalJSON() ([]byte, error) { + return json.Marshal(e.Error()) +} + func Errorf(t string, args ...interface{}) Error { return Error{val: fmt.Errorf(t, args...)} } diff --git a/internal/wire/ok.go b/internal/wire/ok.go index f416341..e76b591 100644 --- a/internal/wire/ok.go +++ b/internal/wire/ok.go @@ -4,6 +4,8 @@ type OK struct{} func (OK) NetTag() string { return "ok" } +func (OK) MarshalJSON() ([]byte, error) { return []byte(`"ok"`), nil } + func init() { Register(func() Value { return new(OK) }) } diff --git a/internal/wire/response.go b/internal/wire/response.go index 5cbc28e..535e447 100644 --- a/internal/wire/response.go +++ b/internal/wire/response.go @@ -1,19 +1,45 @@ package wire +import ( + "encoding/json" + "fmt" +) + type Response struct { - Re int `json:"re,omitempty"` - Type string `json:"type"` - Body interface{} `json:"body"` + Re int + Body Value +} + +func (r Response) MarshalJSON() ([]byte, error) { + return json.Marshal([3]interface{}{r.Re, r.Body.NetTag(), r.Body}) } -func NewResponse(re int, v Value) Response { - return Response{ - Re: re, - Type: v.NetTag(), - Body: v, +func (r *Response) UnmarshalJSON(b []byte) error { + var parts [3]json.RawMessage + if err := json.Unmarshal(b, &parts); err != nil { + return err + } + if err := json.Unmarshal(parts[0], &r.Re); err != nil { + return err + } + + var tag string + if err := json.Unmarshal(parts[1], &tag); err != nil { + return err + } + + f, ok := registry[tag] + if !ok { + return fmt.Errorf("unknown tag: %q", tag) + } + v := f() + if err := json.Unmarshal(parts[2], v); err != nil { + return err } + r.Body = v + return nil } func ErrorResponse(re int, t string, args ...interface{}) Response { - return NewResponse(re, Errorf(t, args...)) + return Response{re, Errorf(t, args...)} } diff --git a/internal/wire/self.go b/internal/wire/self.go index c3e3533..e9583aa 100644 --- a/internal/wire/self.go +++ b/internal/wire/self.go @@ -7,3 +7,7 @@ type Self_Move struct { } func (Self_Move) NetTag() string { return "self/move" } + +func init() { + Register(func() Value { return new(Self_Move) }) +}