Okay, I think that's how error handling should work

This commit is contained in:
Christian Rocha 2020-01-18 10:13:44 -05:00
parent 9909356eb7
commit d976902768
No known key found for this signature in database
GPG Key ID: D6CC7A16E5878018
2 changed files with 57 additions and 25 deletions

View File

@ -3,6 +3,7 @@ package main
// A simple program that counts down from 5 and then exits. // A simple program that counts down from 5 and then exits.
import ( import (
"errors"
"fmt" "fmt"
"log" "log"
"tea" "tea"
@ -10,8 +11,9 @@ import (
"tea/input" "tea/input"
) )
type model struct { type Model struct {
input input.Model Input input.Model
Error error
} }
type tickMsg struct{} type tickMsg struct{}
@ -20,19 +22,21 @@ func main() {
tea.UseSysLog("tea") tea.UseSysLog("tea")
p := tea.NewProgram( p := tea.NewProgram(
model{ Model{
input: input.DefaultModel(), Input: input.DefaultModel(),
Error: nil,
}, },
update, update,
view, view,
[]tea.Sub{ []tea.Sub{
// Just hand off the subscription to the input component // We just hand off the subscription to the input component, giving
func(m tea.Model) tea.Msg { // it the model it expects.
if m, ok := m.(model); ok { func(model tea.Model) tea.Msg {
return input.Blink(m.input) m, ok := model.(Model)
if !ok {
return tea.NewErrMsg("could not perform assertion on model")
} }
// TODO: return error return input.Blink(m.Input)
return nil
}, },
}, },
) )
@ -42,9 +46,16 @@ func main() {
} }
} }
func update(msg tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) { func update(msg tea.Msg, model tea.Model) (tea.Model, tea.Cmd) {
var cmd tea.Cmd var cmd tea.Cmd
m, _ := mdl.(model) m, ok := model.(Model)
if !ok {
// When we encounter errors in Update we simply add the error to the
// model so we can handle it in the view. We could also return a command
// that does something else with the error, like logs it via IO.
m.Error = errors.New("could not perform assertion on model")
return m, nil
}
switch msg := msg.(type) { switch msg := msg.(type) {
case tea.KeyMsg: case tea.KeyMsg:
@ -54,22 +65,27 @@ func update(msg tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) {
case "esc": case "esc":
return m, tea.Quit return m, tea.Quit
} }
// We handle errors just like any other message
case tea.ErrMsg:
m.Error = msg
return m, nil
} }
m.input, cmd = input.Update(msg, m.input) m.Input, cmd = input.Update(msg, m.Input)
return m, cmd return m, cmd
} }
func view(m tea.Model) string { func view(model tea.Model) string {
if m, ok := m.(model); ok { m, ok := model.(Model)
help := "(esc to exit)" if !ok {
return "Oh no: could not perform assertion on model."
} else if m.Error != nil {
return fmt.Sprintf("Uh oh: %s", m.Error)
}
return fmt.Sprintf( return fmt.Sprintf(
"Whats your favorite Pokémon?\n\n%s\n\n%s", "Whats your favorite Pokémon?\n\n%s\n\n%s",
input.View(m.input), input.View(m.Input),
help, "(esc to quit)",
) )
}
// TODO: return error
return ""
} }

18
tea.go
View File

@ -1,6 +1,7 @@
package tea package tea
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -44,6 +45,22 @@ type Program struct {
rw io.ReadWriter rw io.ReadWriter
} }
// ErrMsg is just a regular message containing an error. We handle it in Update
// just like a regular message by case switching. Of course, the developer
// could also define her own errors as well.
type ErrMsg struct {
error
}
func (e ErrMsg) String() string {
return e.Error()
}
// NewErrMsg is a convenience function for creating a generic ErrMsg
func NewErrMsg(s string) ErrMsg {
return ErrMsg{errors.New(s)}
}
// Quit is a command that tells the program to exit // Quit is a command that tells the program to exit
func Quit() Msg { func Quit() Msg {
return quitMsg{} return quitMsg{}
@ -63,7 +80,6 @@ func NewProgram(model Model, update Update, view View, subs []Sub) *Program {
} }
// Start initializes the program // Start initializes the program
// TODO: error channel
func (p *Program) Start() error { func (p *Program) Start() error {
var ( var (
model = p.model model = p.model