From a897198dae1df4827712961562e786332eca9854 Mon Sep 17 00:00:00 2001 From: Kevin Schoon Date: Thu, 29 Jul 2021 13:01:25 -0400 Subject: [PATCH] support serving and receiving requests for status via sockets --- README.md | 52 ++++++++++++++++++++++++++++++++++++++++++ pkg/cmd/cmd.go | 4 ++-- pkg/internal/config.go | 6 +++++ pkg/internal/server.go | 47 ++++++++++++++++++++++++++++++++++---- pkg/internal/util.go | 6 ++--- 5 files changed, 105 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index dac20d9..28fe9e5 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,21 @@ Example: ## 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 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 ``` +#### [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 diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 0f6825c..2c8fdc9 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -212,13 +212,13 @@ func _status(config *pomo.Config) func(*cli.Cmd) { cmd.Action = func() { client, err := pomo.NewClient(config.SocketPath) if err != nil { - pomo.OutputStatus(pomo.Status{}) + fmt.Println(pomo.FormatStatus(pomo.Status{})) return } defer client.Close() status, err := client.Status() maybe(err) - pomo.OutputStatus(*status) + fmt.Println(pomo.FormatStatus(*status)) } } } diff --git a/pkg/internal/config.go b/pkg/internal/config.go index e5090f1..04d3a44 100644 --- a/pkg/internal/config.go +++ b/pkg/internal/config.go @@ -21,6 +21,12 @@ type Config struct { DBPath string `json:"dbPath"` SocketPath string `json:"socketPath"` 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 { diff --git a/pkg/internal/server.go b/pkg/internal/server.go index 38357c7..03e7443 100644 --- a/pkg/internal/server.go +++ b/pkg/internal/server.go @@ -6,14 +6,18 @@ import ( "fmt" "net" "os" + "time" ) // Server listens on a Unix domain socket // for Pomo status requests type Server struct { - listener net.Listener - runner *TaskRunner - running bool + listener net.Listener + runner *TaskRunner + running bool + publish bool + publishJson bool + socketPath string } 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() { s.running = true - go s.listen() + if s.publish { + go s.push() + } else { + go s.listen() + } } func (s *Server) Stop() { s.running = false - s.listener.Close() + if s.listener != nil { + s.listener.Close() + } } 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 if _, err := os.Stat(config.SocketPath); err == nil { _, err := net.Dial("unix", config.SocketPath) diff --git a/pkg/internal/util.go b/pkg/internal/util.go index 70b4433..f8b0c5c 100644 --- a/pkg/internal/util.go +++ b/pkg/internal/util.go @@ -68,14 +68,14 @@ func SummerizeTasks(config *Config, tasks []*Task) { } } -func OutputStatus(status Status) { +func FormatStatus(status Status) string { 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) + return fmt.Sprintf("%s [%d/%d] %s", state, status.Count, status.NPomodoros, status.Remaining) } else { - fmt.Printf("%s [%d/%d] -", state, status.Count, status.NPomodoros) + return fmt.Sprintf("%s [%d/%d] -", state, status.Count, status.NPomodoros) } }