support serving and receiving requests for status via sockets

This commit is contained in:
Kevin Schoon 2021-07-29 13:01:25 -04:00
parent cae290eacd
commit a897198dae
5 changed files with 105 additions and 10 deletions

View File

@ -59,6 +59,21 @@ Example:
## Integrations ## Integrations
By default pomo will setup a Unix socket and serve it's status there.
```bash
echo | socat stdio UNIX-CONNECT:/home/kevin/.pomo/pomo.sock | jq .
{
"state": 1,
"remaining": 1492000000000,
"count": 0,
"n_pomodoros": 4
}
```
Alternately by setting the `publish` flag to `true` it will publish it's status
to an existing socket.
### Status Bars ### Status Bars
The Pomo CLI can output the current state of a running task session via the `pomo status` The Pomo CLI can output the current state of a running task session via the `pomo status`
@ -76,6 +91,43 @@ interval = 1
exec = pomo status exec = pomo status
``` ```
#### [luastatus](https://github.com/shdown/luastatus)
Configured this bar by setting `publish` to `true`.
```lua
widget = {
plugin = "unixsock",
opts = {
path = "pomo.sock",
timeout = 2,
},
cb = function(t)
local full_text
local foreground = ""
local background = ""
if t.what == "line" then
if string.match(t.line, "R") then
-- green
foreground = "#ffffff"
background = "#307335"
end
if string.match(t.line, "B") or string.match(t.line, "P") or string.match(t.line, "C") then
-- red
foreground = "#ffffff"
background = "ff8080"
end
return { full_text = t.line, background = background, foreground = foreground }
elseif t.what == "timeout" then
return { full_text = "-" }
elseif t.what == "hello" then
return { full_text = "-" }
end
end,
}
```
## Roadmap ## Roadmap

View File

@ -212,13 +212,13 @@ func _status(config *pomo.Config) func(*cli.Cmd) {
cmd.Action = func() { cmd.Action = func() {
client, err := pomo.NewClient(config.SocketPath) client, err := pomo.NewClient(config.SocketPath)
if err != nil { if err != nil {
pomo.OutputStatus(pomo.Status{}) fmt.Println(pomo.FormatStatus(pomo.Status{}))
return return
} }
defer client.Close() defer client.Close()
status, err := client.Status() status, err := client.Status()
maybe(err) maybe(err)
pomo.OutputStatus(*status) fmt.Println(pomo.FormatStatus(*status))
} }
} }
} }

View File

@ -21,6 +21,12 @@ type Config struct {
DBPath string `json:"dbPath"` DBPath string `json:"dbPath"`
SocketPath string `json:"socketPath"` SocketPath string `json:"socketPath"`
IconPath string `json:"iconPath"` IconPath string `json:"iconPath"`
// Publish pushes updates to the configured
// SocketPath rather than listening for requests
Publish bool `json:"publish"`
// PublishJson pushes socket updates as a JSON
// encoded status message instead of string formatted
PublishJson bool `json:"publishJson"`
} }
type ColorMap struct { type ColorMap struct {

View File

@ -6,14 +6,18 @@ import (
"fmt" "fmt"
"net" "net"
"os" "os"
"time"
) )
// Server listens on a Unix domain socket // Server listens on a Unix domain socket
// for Pomo status requests // for Pomo status requests
type Server struct { type Server struct {
listener net.Listener listener net.Listener
runner *TaskRunner runner *TaskRunner
running bool running bool
publish bool
publishJson bool
socketPath string
} }
func (s *Server) listen() { func (s *Server) listen() {
@ -31,17 +35,50 @@ func (s *Server) listen() {
} }
} }
func (s *Server) push() {
ticker := time.NewTicker(1 * time.Second)
for s.running {
conn, err := net.Dial("unix", s.socketPath)
if err != nil {
<-ticker.C
continue
}
status := s.runner.Status()
if s.publishJson {
raw, _ := json.Marshal(status)
json.NewEncoder(conn).Encode(raw)
} else {
conn.Write([]byte(FormatStatus(*status) + "\n"))
}
conn.Close()
<-ticker.C
}
}
func (s *Server) Start() { func (s *Server) Start() {
s.running = true s.running = true
go s.listen() if s.publish {
go s.push()
} else {
go s.listen()
}
} }
func (s *Server) Stop() { func (s *Server) Stop() {
s.running = false s.running = false
s.listener.Close() if s.listener != nil {
s.listener.Close()
}
} }
func NewServer(runner *TaskRunner, config *Config) (*Server, error) { func NewServer(runner *TaskRunner, config *Config) (*Server, error) {
if config.Publish {
return &Server{
runner: runner,
publish: true,
publishJson: config.PublishJson,
socketPath: config.SocketPath}, nil
}
//check if socket file exists //check if socket file exists
if _, err := os.Stat(config.SocketPath); err == nil { if _, err := os.Stat(config.SocketPath); err == nil {
_, err := net.Dial("unix", config.SocketPath) _, err := net.Dial("unix", config.SocketPath)

View File

@ -68,14 +68,14 @@ func SummerizeTasks(config *Config, tasks []*Task) {
} }
} }
func OutputStatus(status Status) { func FormatStatus(status Status) string {
state := "?" state := "?"
if status.State >= RUNNING { if status.State >= RUNNING {
state = string(status.State.String()[0]) state = string(status.State.String()[0])
} }
if status.State == RUNNING { if status.State == RUNNING {
fmt.Printf("%s [%d/%d] %s", state, status.Count, status.NPomodoros, status.Remaining) return fmt.Sprintf("%s [%d/%d] %s", state, status.Count, status.NPomodoros, status.Remaining)
} else { } else {
fmt.Printf("%s [%d/%d] -", state, status.Count, status.NPomodoros) return fmt.Sprintf("%s [%d/%d] -", state, status.Count, status.NPomodoros)
} }
} }