add socket server for scriptable status output
This commit is contained in:
parent
6c541894ab
commit
2fc414efdd
22
main.go
22
main.go
|
@ -32,6 +32,10 @@ func start(path *string) func(*cli.Cmd) {
|
|||
}
|
||||
runner, err := NewTaskRunner(task, db, NewXnotifier(*path+"/icon.png"))
|
||||
maybe(err)
|
||||
server, err := NewServer(*path+"/pomo.sock", runner)
|
||||
maybe(err)
|
||||
server.Start()
|
||||
defer server.Stop()
|
||||
runner.Start()
|
||||
startUI(runner)
|
||||
}
|
||||
|
@ -94,6 +98,23 @@ func _delete(path *string) func(*cli.Cmd) {
|
|||
}
|
||||
}
|
||||
|
||||
func _status(path *string) func(*cli.Cmd) {
|
||||
return func(cmd *cli.Cmd) {
|
||||
cmd.Spec = "[OPTIONS]"
|
||||
cmd.Action = func() {
|
||||
client, err := NewClient(*path + "/pomo.sock")
|
||||
if err != nil {
|
||||
outputStatus(Status{})
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
status, err := client.Status()
|
||||
maybe(err)
|
||||
outputStatus(*status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := cli.App("pomo", "Pomodoro CLI")
|
||||
app.Spec = "[OPTIONS]"
|
||||
|
@ -105,5 +126,6 @@ func main() {
|
|||
app.Command("init", "initialize the sqlite database", initialize(path))
|
||||
app.Command("list l", "list historical tasks", list(path))
|
||||
app.Command("delete d", "delete a stored task", _delete(path))
|
||||
app.Command("status st", "output the current status", _status(path))
|
||||
app.Run(os.Args)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Server listens on a Unix domain socket
|
||||
// for Pomo status requests
|
||||
type Server struct {
|
||||
listener net.Listener
|
||||
runner *TaskRunner
|
||||
running bool
|
||||
}
|
||||
|
||||
func (s *Server) listen() {
|
||||
for s.running {
|
||||
conn, err := s.listener.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
buf := make([]byte, 512)
|
||||
// Ignore any content
|
||||
conn.Read(buf)
|
||||
raw, _ := json.Marshal(s.runner.Status())
|
||||
conn.Write(raw)
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Start() {
|
||||
s.running = true
|
||||
go s.listen()
|
||||
}
|
||||
|
||||
func (s *Server) Stop() {
|
||||
s.running = false
|
||||
s.listener.Close()
|
||||
}
|
||||
|
||||
func NewServer(path string, runner *TaskRunner) (*Server, error) {
|
||||
listener, err := net.Listen("unix", path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Server{listener: listener, runner: runner}, nil
|
||||
}
|
||||
|
||||
// Client makes requests to a listening
|
||||
// pomo server to check the status of
|
||||
// any currently running task session.
|
||||
type Client struct {
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
func (c Client) read(statusCh chan *Status) {
|
||||
buf := make([]byte, 512)
|
||||
n, _ := c.conn.Read(buf)
|
||||
status := &Status{}
|
||||
json.Unmarshal(buf[0:n], status)
|
||||
statusCh <- status
|
||||
}
|
||||
|
||||
func (c Client) Status() (*Status, error) {
|
||||
statusCh := make(chan *Status)
|
||||
c.conn.Write([]byte("status"))
|
||||
go c.read(statusCh)
|
||||
return <-statusCh, nil
|
||||
}
|
||||
|
||||
func (c Client) Close() error { return c.conn.Close() }
|
||||
|
||||
func NewClient(path string) (*Client, error) {
|
||||
conn, err := net.Dial("unix", path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Client{conn: conn}, nil
|
||||
}
|
9
task.go
9
task.go
|
@ -118,3 +118,12 @@ func (t *TaskRunner) Toggle() {
|
|||
func (t *TaskRunner) Pause() {
|
||||
t.pause <- true
|
||||
}
|
||||
|
||||
func (t *TaskRunner) Status() *Status {
|
||||
return &Status{
|
||||
State: t.state,
|
||||
Count: t.count,
|
||||
NPomodoros: t.nPomodoros,
|
||||
Remaining: t.TimeRemaining(),
|
||||
}
|
||||
}
|
||||
|
|
9
types.go
9
types.go
|
@ -163,6 +163,15 @@ func (p Pomodoro) Duration() time.Duration {
|
|||
return (p.End.Sub(p.Start))
|
||||
}
|
||||
|
||||
// Status is used to communicate the state
|
||||
// of a running Pomodoro session
|
||||
type Status struct {
|
||||
State State `json:"state"`
|
||||
Remaining time.Duration `json:"remaining"`
|
||||
Count int `json:"count"`
|
||||
NPomodoros int `json:"n_pomodoros"`
|
||||
}
|
||||
|
||||
// Notifier sends a system notification
|
||||
type Notifier interface {
|
||||
Notify(string, string) error
|
||||
|
|
24
ui.go
24
ui.go
|
@ -5,9 +5,9 @@ import (
|
|||
"github.com/gizak/termui"
|
||||
)
|
||||
|
||||
func status(wheel *Wheel, runner *TaskRunner) termui.GridBufferer {
|
||||
func render(wheel *Wheel, status *Status) termui.GridBufferer {
|
||||
var text string
|
||||
switch runner.state {
|
||||
switch status.State {
|
||||
case RUNNING:
|
||||
text = fmt.Sprintf(
|
||||
`[%d/%d] Pomodoros completed
|
||||
|
@ -17,10 +17,10 @@ func status(wheel *Wheel, runner *TaskRunner) termui.GridBufferer {
|
|||
|
||||
[q] - quit [p] - pause
|
||||
`,
|
||||
runner.count,
|
||||
runner.nPomodoros,
|
||||
status.Count,
|
||||
status.NPomodoros,
|
||||
wheel,
|
||||
runner.TimeRemaining(),
|
||||
status.Remaining,
|
||||
)
|
||||
case BREAKING:
|
||||
text = `It is time to take a break!
|
||||
|
@ -49,10 +49,10 @@ func status(wheel *Wheel, runner *TaskRunner) termui.GridBufferer {
|
|||
}
|
||||
par := termui.NewPar(text)
|
||||
par.Height = 8
|
||||
par.BorderLabel = fmt.Sprintf("Pomo - %s", runner.state)
|
||||
par.BorderLabel = fmt.Sprintf("Pomo - %s", status.State)
|
||||
par.BorderLabelFg = termui.ColorWhite
|
||||
par.BorderFg = termui.ColorRed
|
||||
if runner.state == RUNNING {
|
||||
if status.State == RUNNING {
|
||||
par.BorderFg = termui.ColorGreen
|
||||
}
|
||||
return par
|
||||
|
@ -94,24 +94,24 @@ func startUI(runner *TaskRunner) {
|
|||
|
||||
defer termui.Close()
|
||||
|
||||
termui.Render(centered(status(&wheel, runner)))
|
||||
termui.Render(centered(render(&wheel, runner.Status())))
|
||||
|
||||
termui.Handle("/timer/1s", func(termui.Event) {
|
||||
termui.Render(centered(status(&wheel, runner)))
|
||||
termui.Render(centered(render(&wheel, runner.Status())))
|
||||
})
|
||||
|
||||
termui.Handle("/sys/wnd/resize", func(termui.Event) {
|
||||
termui.Render(centered(status(&wheel, runner)))
|
||||
termui.Render(centered(render(&wheel, runner.Status())))
|
||||
})
|
||||
|
||||
termui.Handle("/sys/kbd/<enter>", func(termui.Event) {
|
||||
runner.Toggle()
|
||||
termui.Render(centered(status(&wheel, runner)))
|
||||
termui.Render(centered(render(&wheel, runner.Status())))
|
||||
})
|
||||
|
||||
termui.Handle("/sys/kbd/p", func(termui.Event) {
|
||||
runner.Pause()
|
||||
termui.Render(centered(status(&wheel, runner)))
|
||||
termui.Render(centered(render(&wheel, runner.Status())))
|
||||
})
|
||||
|
||||
termui.Handle("/sys/kbd/q", func(termui.Event) {
|
||||
|
|
12
util.go
12
util.go
|
@ -74,3 +74,15 @@ func summerizeTasks(config *Config, tasks []*Task) {
|
|||
fmt.Printf("\n")
|
||||
}
|
||||
}
|
||||
|
||||
func outputStatus(status Status) {
|
||||
state := "?"
|
||||
if status.State >= RUNNING {
|
||||
state = string(status.State.String()[0])
|
||||
}
|
||||
if status.State == RUNNING {
|
||||
fmt.Printf("%s [%d/%d] %s", state, status.Count, status.NPomodoros, status.Remaining)
|
||||
} else {
|
||||
fmt.Printf("%s [%d/%d] -", state, status.Count, status.NPomodoros)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue