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"))
|
runner, err := NewTaskRunner(task, db, NewXnotifier(*path+"/icon.png"))
|
||||||
maybe(err)
|
maybe(err)
|
||||||
|
server, err := NewServer(*path+"/pomo.sock", runner)
|
||||||
|
maybe(err)
|
||||||
|
server.Start()
|
||||||
|
defer server.Stop()
|
||||||
runner.Start()
|
runner.Start()
|
||||||
startUI(runner)
|
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() {
|
func main() {
|
||||||
app := cli.App("pomo", "Pomodoro CLI")
|
app := cli.App("pomo", "Pomodoro CLI")
|
||||||
app.Spec = "[OPTIONS]"
|
app.Spec = "[OPTIONS]"
|
||||||
|
@ -105,5 +126,6 @@ func main() {
|
||||||
app.Command("init", "initialize the sqlite database", initialize(path))
|
app.Command("init", "initialize the sqlite database", initialize(path))
|
||||||
app.Command("list l", "list historical tasks", list(path))
|
app.Command("list l", "list historical tasks", list(path))
|
||||||
app.Command("delete d", "delete a stored task", _delete(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)
|
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() {
|
func (t *TaskRunner) Pause() {
|
||||||
t.pause <- true
|
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))
|
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
|
// Notifier sends a system notification
|
||||||
type Notifier interface {
|
type Notifier interface {
|
||||||
Notify(string, string) error
|
Notify(string, string) error
|
||||||
|
|
24
ui.go
24
ui.go
|
@ -5,9 +5,9 @@ import (
|
||||||
"github.com/gizak/termui"
|
"github.com/gizak/termui"
|
||||||
)
|
)
|
||||||
|
|
||||||
func status(wheel *Wheel, runner *TaskRunner) termui.GridBufferer {
|
func render(wheel *Wheel, status *Status) termui.GridBufferer {
|
||||||
var text string
|
var text string
|
||||||
switch runner.state {
|
switch status.State {
|
||||||
case RUNNING:
|
case RUNNING:
|
||||||
text = fmt.Sprintf(
|
text = fmt.Sprintf(
|
||||||
`[%d/%d] Pomodoros completed
|
`[%d/%d] Pomodoros completed
|
||||||
|
@ -17,10 +17,10 @@ func status(wheel *Wheel, runner *TaskRunner) termui.GridBufferer {
|
||||||
|
|
||||||
[q] - quit [p] - pause
|
[q] - quit [p] - pause
|
||||||
`,
|
`,
|
||||||
runner.count,
|
status.Count,
|
||||||
runner.nPomodoros,
|
status.NPomodoros,
|
||||||
wheel,
|
wheel,
|
||||||
runner.TimeRemaining(),
|
status.Remaining,
|
||||||
)
|
)
|
||||||
case BREAKING:
|
case BREAKING:
|
||||||
text = `It is time to take a break!
|
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 := termui.NewPar(text)
|
||||||
par.Height = 8
|
par.Height = 8
|
||||||
par.BorderLabel = fmt.Sprintf("Pomo - %s", runner.state)
|
par.BorderLabel = fmt.Sprintf("Pomo - %s", status.State)
|
||||||
par.BorderLabelFg = termui.ColorWhite
|
par.BorderLabelFg = termui.ColorWhite
|
||||||
par.BorderFg = termui.ColorRed
|
par.BorderFg = termui.ColorRed
|
||||||
if runner.state == RUNNING {
|
if status.State == RUNNING {
|
||||||
par.BorderFg = termui.ColorGreen
|
par.BorderFg = termui.ColorGreen
|
||||||
}
|
}
|
||||||
return par
|
return par
|
||||||
|
@ -94,24 +94,24 @@ func startUI(runner *TaskRunner) {
|
||||||
|
|
||||||
defer termui.Close()
|
defer termui.Close()
|
||||||
|
|
||||||
termui.Render(centered(status(&wheel, runner)))
|
termui.Render(centered(render(&wheel, runner.Status())))
|
||||||
|
|
||||||
termui.Handle("/timer/1s", func(termui.Event) {
|
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.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) {
|
termui.Handle("/sys/kbd/<enter>", func(termui.Event) {
|
||||||
runner.Toggle()
|
runner.Toggle()
|
||||||
termui.Render(centered(status(&wheel, runner)))
|
termui.Render(centered(render(&wheel, runner.Status())))
|
||||||
})
|
})
|
||||||
|
|
||||||
termui.Handle("/sys/kbd/p", func(termui.Event) {
|
termui.Handle("/sys/kbd/p", func(termui.Event) {
|
||||||
runner.Pause()
|
runner.Pause()
|
||||||
termui.Render(centered(status(&wheel, runner)))
|
termui.Render(centered(render(&wheel, runner.Status())))
|
||||||
})
|
})
|
||||||
|
|
||||||
termui.Handle("/sys/kbd/q", func(termui.Event) {
|
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")
|
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