Reduce boilerplate in worker/UI

This commit is contained in:
Drew DeVault 2018-02-01 18:42:03 -05:00
parent ee73c41950
commit d24e4712a4
4 changed files with 100 additions and 74 deletions

View file

@ -12,11 +12,10 @@ import (
type AccountTab struct { type AccountTab struct {
Config *config.AccountConfig Config *config.AccountConfig
Worker worker.Worker Worker *types.Worker
Parent *UIState Parent *UIState
logger *log.Logger logger *log.Logger
counter int counter int
callbacks map[types.WorkerMessage]func(msg types.WorkerMessage)
} }
func NewAccountTab(conf *config.AccountConfig, func NewAccountTab(conf *config.AccountConfig,
@ -26,15 +25,14 @@ func NewAccountTab(conf *config.AccountConfig,
if err != nil { if err != nil {
return nil, err return nil, err
} }
go work.Run() go work.Backend.Run()
acc := &AccountTab{ acc := &AccountTab{
Config: conf, Config: conf,
Worker: work, Worker: work,
logger: logger, logger: logger,
callbacks: make(map[types.WorkerMessage]func(msg types.WorkerMessage)),
} }
acc.postAction(types.Configure{Config: conf}, nil) acc.Worker.PostAction(types.Configure{Config: conf}, nil)
acc.postAction(types.Connect{}, func(msg types.WorkerMessage) { acc.Worker.PostAction(types.Connect{}, func(msg types.WorkerMessage) {
if _, ok := msg.(types.Ack); ok { if _, ok := msg.(types.Ack); ok {
acc.logger.Println("Connected.") acc.logger.Println("Connected.")
} else { } else {
@ -68,36 +66,22 @@ func (acc *AccountTab) Render(at Geometry) {
} }
func (acc *AccountTab) GetChannel() chan types.WorkerMessage { func (acc *AccountTab) GetChannel() chan types.WorkerMessage {
return acc.Worker.GetMessages() return acc.Worker.Messages
}
func (acc *AccountTab) postAction(msg types.WorkerMessage,
cb func(msg types.WorkerMessage)) {
acc.logger.Printf("-> %T\n", msg)
acc.Worker.PostAction(msg)
if cb != nil {
acc.callbacks[msg] = cb
delete(acc.callbacks, msg)
}
} }
func (acc *AccountTab) HandleMessage(msg types.WorkerMessage) { func (acc *AccountTab) HandleMessage(msg types.WorkerMessage) {
acc.logger.Printf("<- %T\n", msg) msg = acc.Worker.ProcessMessage(msg)
if cb, ok := acc.callbacks[msg.InResponseTo()]; ok {
cb(msg)
}
switch msg.(type) { switch msg.(type) {
case types.Ack: case types.Ack:
// no-op // no-op
case types.ApproveCertificate: case types.ApproveCertificate:
// TODO: Ask the user // TODO: Ask the user
acc.logger.Println("Approving certificate") acc.logger.Println("Approving certificate")
acc.postAction(types.Ack{ acc.Worker.PostAction(types.Ack{
Message: types.RespondTo(msg), Message: types.RespondTo(msg),
}, nil) }, nil)
default: default:
acc.postAction(types.Unsupported{ acc.Worker.PostAction(types.Unsupported{
Message: types.RespondTo(msg), Message: types.RespondTo(msg),
}, nil) }, nil)
} }

View file

@ -4,7 +4,6 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"fmt" "fmt"
"log"
"net/url" "net/url"
"strings" "strings"
@ -23,9 +22,6 @@ type imapClient struct {
} }
type IMAPWorker struct { type IMAPWorker struct {
messages chan types.WorkerMessage
actions chan types.WorkerMessage
config struct { config struct {
scheme string scheme string
insecure bool insecure bool
@ -33,33 +29,18 @@ type IMAPWorker struct {
user *url.Userinfo user *url.Userinfo
} }
worker *types.Worker
client *imapClient client *imapClient
updates chan client.Update updates chan client.Update
logger *log.Logger
} }
func NewIMAPWorker(logger *log.Logger) *IMAPWorker { func NewIMAPWorker(worker *types.Worker) *IMAPWorker {
return &IMAPWorker{ return &IMAPWorker{
messages: make(chan types.WorkerMessage, 50), worker: worker,
actions: make(chan types.WorkerMessage, 50),
updates: make(chan client.Update, 50), updates: make(chan client.Update, 50),
logger: logger,
} }
} }
func (w *IMAPWorker) GetMessages() chan types.WorkerMessage {
return w.messages
}
func (w *IMAPWorker) PostAction(msg types.WorkerMessage) {
w.actions <- msg
}
func (w *IMAPWorker) postMessage(msg types.WorkerMessage) {
w.logger.Printf("=> %T\n", msg)
w.messages <- msg
}
func (w *IMAPWorker) verifyPeerCert(msg types.WorkerMessage) func( func (w *IMAPWorker) verifyPeerCert(msg types.WorkerMessage) func(
rawCerts [][]byte, _ [][]*x509.Certificate) error { rawCerts [][]byte, _ [][]*x509.Certificate) error {
@ -77,9 +58,9 @@ func (w *IMAPWorker) verifyPeerCert(msg types.WorkerMessage) func(
Message: types.RespondTo(msg), Message: types.RespondTo(msg),
CertPool: pool, CertPool: pool,
} }
w.postMessage(request) w.worker.PostMessage(request, nil)
response := <-w.actions response := <-w.worker.Actions
if response.InResponseTo() != request { if response.InResponseTo() != request {
return fmt.Errorf("Expected UI to answer cert request") return fmt.Errorf("Expected UI to answer cert request")
} }
@ -176,24 +157,24 @@ func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error {
func (w *IMAPWorker) Run() { func (w *IMAPWorker) Run() {
for { for {
select { select {
case msg := <-w.actions: case msg := <-w.worker.Actions:
w.logger.Printf("<= %T\n", msg) msg = w.worker.ProcessAction(msg)
if err := w.handleMessage(msg); err == errUnsupported { if err := w.handleMessage(msg); err == errUnsupported {
w.postMessage(types.Unsupported{ w.worker.PostMessage(types.Unsupported{
Message: types.RespondTo(msg), Message: types.RespondTo(msg),
}) }, nil)
} else if err != nil { } else if err != nil {
w.postMessage(types.Error{ w.worker.PostMessage(types.Error{
Message: types.RespondTo(msg), Message: types.RespondTo(msg),
Error: err, Error: err,
}) }, nil)
} else { } else {
w.postMessage(types.Ack{ w.worker.PostMessage(types.Ack{
Message: types.RespondTo(msg), Message: types.RespondTo(msg),
}) }, nil)
} }
case update := <-w.updates: case update := <-w.updates:
w.logger.Printf("[= %T", update) w.worker.Logger.Printf("(= %T", update)
} }
} }
} }

59
worker/types/worker.go Normal file
View file

@ -0,0 +1,59 @@
package types
import (
"log"
)
type Backend interface {
Run()
}
type Worker struct {
Actions chan WorkerMessage
Backend Backend
Callbacks map[WorkerMessage]func(msg WorkerMessage)
Messages chan WorkerMessage
Logger *log.Logger
}
func (worker *Worker) PostAction(msg WorkerMessage,
cb func(msg WorkerMessage)) {
worker.Logger.Printf("=> %T\n", msg)
worker.Actions <- msg
if cb != nil {
worker.Callbacks[msg] = cb
}
}
func (worker *Worker) PostMessage(msg WorkerMessage,
cb func(msg WorkerMessage)) {
worker.Logger.Printf("-> %T\n", msg)
worker.Messages <- msg
if cb != nil {
worker.Callbacks[msg] = cb
}
}
func (worker *Worker) ProcessMessage(msg WorkerMessage) WorkerMessage {
worker.Logger.Printf("<= %T\n", msg)
if cb, ok := worker.Callbacks[msg.InResponseTo()]; ok {
cb(msg)
delete(worker.Callbacks, msg)
}
return msg
}
func (worker *Worker) ProcessAction(msg WorkerMessage) WorkerMessage {
worker.Logger.Printf("<- %T\n", msg)
if cb, ok := worker.Callbacks[msg.InResponseTo()]; ok {
cb(msg)
delete(worker.Callbacks, msg)
}
return msg
}

View file

@ -9,22 +9,24 @@ import (
"net/url" "net/url"
) )
type Worker interface {
GetMessages() chan types.WorkerMessage
PostAction(types.WorkerMessage)
Run()
}
// Guesses the appropriate worker type based on the given source string // Guesses the appropriate worker type based on the given source string
func NewWorker(source string, logger *log.Logger) (Worker, error) { func NewWorker(source string, logger *log.Logger) (*types.Worker, error) {
u, err := url.Parse(source) u, err := url.Parse(source)
if err != nil { if err != nil {
return nil, err return nil, err
} }
worker := &types.Worker{
Actions: make(chan types.WorkerMessage, 50),
Callbacks: make(map[types.WorkerMessage]func(msg types.WorkerMessage)),
Messages: make(chan types.WorkerMessage, 50),
Logger: logger,
}
switch u.Scheme { switch u.Scheme {
case "imap": case "imap":
case "imaps": case "imaps":
return imap.NewIMAPWorker(logger), nil worker.Backend = imap.NewIMAPWorker(worker)
} default:
return nil, fmt.Errorf("Unknown backend %s", u.Scheme) return nil, fmt.Errorf("Unknown backend %s", u.Scheme)
} }
return worker, nil
}