An attempt at a new low level keybase interface that prevents each command from re-spawning a new keybase instance on low memory systems.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

112 lines
2.2 KiB

package keybase
import (
"bufio"
"context"
"fmt"
"sync"
"git.hugfreevikings.wtf/keybase/keybase/pkg/ctxreader"
)
type ChatAPI struct {
sync.Mutex
toStdin chan []byte
fromStdout chan []byte
fromListen chan []byte
errors chan error
}
func NewChatAPI(ctx context.Context, opts *Options) (api *ChatAPI, err error) {
// set up the channels
api.toStdin = make(chan []byte, opts.ChannelBufferSize)
api.fromStdout = make(chan []byte, opts.ChannelBufferSize)
api.fromListen = make(chan []byte, opts.ChannelBufferSize)
api.errors = make(chan error, opts.ChannelBufferSize)
// get the basics
kbCmd := opts.locateKeybase()
chatApiArgs := opts.buildArgs("chat", "api")
chatApiListenArgs := opts.buildArgs("chat", "api-listen")
// build the commands
chatApi, err := newApiCmd(ctx, kbCmd, chatApiArgs...)
if err != nil {
return nil, err
}
chatListen, err := newApiCmd(ctx, kbCmd, chatApiListenArgs...)
if err != nil {
return nil, err
}
// create the goroutines
// listen reader
go func() {
cr := ctxreader.NewContextReader(ctx, chatListen.Stdout())
scanner := bufio.NewScanner(cr)
for scanner.Scan() {
api.fromListen <- scanner.Bytes()
}
}()
// writing to stdin
go func() {
for {
select {
case msg := <-api.toStdin:
_, err := chatApi.Stdin().Write(msg)
if err != nil {
api.errors <- err
}
case <-ctx.Done():
return
}
}
}()
// reading from stdout
go func() {
cr := ctxreader.NewContextReader(ctx, chatApi.Stdout())
scanner := bufio.NewScanner(cr)
for scanner.Scan() {
api.fromStdout <- scanner.Bytes()
}
}()
// reading from stderr
go func() {
cr := ctxreader.NewContextReader(ctx, chatApi.Stderr())
scanner := bufio.NewScanner(cr)
for scanner.Scan() {
api.errors <- fmt.Errorf("%v", scanner.Text())
}
}()
// then start the cmds
err = chatApi.Start()
if err != nil {
return nil, err
}
err = chatListen.Start()
if err != nil {
return nil, err
}
return
}
func (c *ChatAPI) Listen() chan []byte {
return c.fromListen
}
func (c *ChatAPI) SendRaw(msg []byte) ([]byte, error) {
c.Lock()
defer c.Unlock()
c.toStdin <- msg
select {
case resp := <-c.fromStdout:
return resp, nil
case err := <-c.errors:
return nil, err
}
}