Skip to content

Commit 7e45726

Browse files
author
Yusaku Hatanaka
authored
add remain command (#40)
feat: add remain command
2 parents 0b34821 + 27ad5ef commit 7e45726

File tree

7 files changed

+221
-14
lines changed

7 files changed

+221
-14
lines changed

cmd/remain.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Package cmd has startCmd defined
2+
package cmd
3+
4+
import (
5+
"fmt"
6+
7+
"github.com/spf13/cobra"
8+
9+
"github.com/hatappi/gomodoro/config"
10+
"github.com/hatappi/gomodoro/net/unix"
11+
)
12+
13+
// remainCmd represents the remain command
14+
var remainCmd = &cobra.Command{
15+
Use: "remain",
16+
Short: "get remain time",
17+
RunE: func(cmd *cobra.Command, args []string) error {
18+
config, err := config.GetConfig()
19+
if err != nil {
20+
return err
21+
}
22+
23+
c, err := unix.NewClient(config.UnixDomainScoketPath)
24+
if err != nil {
25+
return err
26+
}
27+
28+
r, err := c.Get()
29+
if err != nil {
30+
return err
31+
}
32+
33+
fmt.Printf("%s", r.GetRemain())
34+
35+
return nil
36+
},
37+
}
38+
39+
func init() {
40+
rootCmd.AddCommand(remainCmd)
41+
}

cmd/root.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ var cfgFile string
1818

1919
// rootCmd represents the base command when called without any subcommands
2020
var rootCmd = &cobra.Command{
21-
Use: "gomodoro",
21+
Use: "gomodoro",
22+
SilenceUsage: true,
23+
SilenceErrors: true,
2224
}
2325

2426
// Execute adds all child commands to the root command and sets flags appropriately.
@@ -41,6 +43,18 @@ func init() {
4143
fmt.Println(err)
4244
os.Exit(1)
4345
}
46+
47+
p, err := homedir.Expand("~/.gomodoro/gomodoro.sock")
48+
if err != nil {
49+
fmt.Println(err)
50+
os.Exit(1)
51+
}
52+
rootCmd.PersistentFlags().String("unix-domain-socket-path", p, "unix domain socket path")
53+
err = viper.BindPFlag("unix_domain_socket_path", rootCmd.PersistentFlags().Lookup("unix-domain-socket-path"))
54+
if err != nil {
55+
fmt.Println(err)
56+
os.Exit(1)
57+
}
4458
}
4559

4660
// initConfig reads in config file and ENV variables if set.
@@ -63,9 +77,7 @@ func initConfig() {
6377
viper.AutomaticEnv()
6478

6579
// If a config file is found, read it in.
66-
if err := viper.ReadInConfig(); err == nil {
67-
fmt.Println("Using config file:", viper.ConfigFileUsed())
68-
}
80+
_ = viper.ReadInConfig()
6981
}
7082

7183
func initLog() {

cmd/start.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/hatappi/gomodoro/config"
1111
"github.com/hatappi/gomodoro/errors"
12+
"github.com/hatappi/gomodoro/net/unix"
1213
"github.com/hatappi/gomodoro/pomodoro"
1314
"github.com/hatappi/gomodoro/screen"
1415
"github.com/hatappi/gomodoro/toggl"
@@ -27,7 +28,7 @@ please specify argument or config yaml.
2728
if err != nil {
2829
return err
2930
}
30-
31+
// pomodoro
3132
s, err := screen.NewScreen()
3233
if err != nil {
3334
return err
@@ -47,13 +48,17 @@ please specify argument or config yaml.
4748
opts = append(opts, pomodoro.WithRecordToggl(togglClient))
4849
}
4950

50-
p := pomodoro.NewPomodoro(
51-
s,
52-
config.TaskFile,
53-
opts...,
54-
)
51+
p := pomodoro.NewPomodoro(s, config.TaskFile, opts...)
5552
defer p.Finish()
5653

54+
// unix domain socket server
55+
server, err := unix.NewServer(config.UnixDomainScoketPath, p.GetTimer())
56+
if err != nil {
57+
return err
58+
}
59+
defer server.Close()
60+
go server.Serve()
61+
5762
err = p.Start()
5863
if err != nil {
5964
if xerrors.Is(err, errors.ErrCancel) {

config/config.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ import (
88

99
// Config config for gomodoro
1010
type Config struct {
11-
Pomodoro PomodoroConfig `mapstructure:"pomodoro"`
12-
Toggl TogglConfig `mapstructure:"toggl"`
13-
LogFile string `mapstructure:"log_file"`
14-
TaskFile string `mapstructure:"task_file"`
11+
Pomodoro PomodoroConfig `mapstructure:"pomodoro"`
12+
Toggl TogglConfig `mapstructure:"toggl"`
13+
LogFile string `mapstructure:"log_file"`
14+
TaskFile string `mapstructure:"task_file"`
15+
UnixDomainScoketPath string `mapstructure:"unix_domain_socket_path"`
1516
}
1617

1718
// PomodoroConfig config for pomodoro

net/unix/client.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package unix
2+
3+
import (
4+
"bufio"
5+
"encoding/json"
6+
"io/ioutil"
7+
"net"
8+
9+
"github.com/hatappi/gomodoro/logger"
10+
)
11+
12+
// Client represents unix server client
13+
type Client interface {
14+
Get() (*Response, error)
15+
16+
Close()
17+
}
18+
19+
type clientImpl struct {
20+
conn net.Conn
21+
}
22+
23+
// NewClient initialize Client
24+
func NewClient(socketPath string) (Client, error) {
25+
conn, err := net.Dial("unix", socketPath)
26+
if err != nil {
27+
return nil, err
28+
}
29+
30+
return &clientImpl{
31+
conn: conn,
32+
}, nil
33+
}
34+
35+
func (c *clientImpl) Get() (*Response, error) {
36+
b, err := ioutil.ReadAll(bufio.NewReader(c.conn))
37+
if err != nil {
38+
logger.Errorf("error is %+v", err)
39+
}
40+
41+
r := &Response{}
42+
err = json.Unmarshal(b, r)
43+
if err != nil {
44+
return nil, err
45+
}
46+
47+
return r, nil
48+
}
49+
50+
func (c *clientImpl) Close() {
51+
_ = c.conn.Close()
52+
}

net/unix/server.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Package unix communicate internal access
2+
package unix
3+
4+
import (
5+
"encoding/json"
6+
"fmt"
7+
"net"
8+
9+
"github.com/hatappi/gomodoro/logger"
10+
"github.com/hatappi/gomodoro/timer"
11+
)
12+
13+
// Response represents unix server response
14+
type Response struct {
15+
RemainSec int
16+
}
17+
18+
// GetRemain get remain string
19+
func (r *Response) GetRemain() string {
20+
if r.RemainSec == 0 {
21+
return "00:00"
22+
}
23+
min := r.RemainSec / 60
24+
sec := r.RemainSec % 60
25+
return fmt.Sprintf("%02d:%02d", min, sec)
26+
}
27+
28+
// Server represents server
29+
type Server interface {
30+
Serve()
31+
Close()
32+
}
33+
34+
type serverImpl struct {
35+
listener net.Listener
36+
timer timer.Timer
37+
}
38+
39+
// NewServer initialize Server
40+
func NewServer(socketPath string, timer timer.Timer) (Server, error) {
41+
listener, err := net.Listen("unix", socketPath)
42+
if err != nil {
43+
return nil, err
44+
}
45+
46+
return &serverImpl{
47+
listener: listener,
48+
timer: timer,
49+
}, nil
50+
}
51+
52+
// Serve start unix domain socket server
53+
func (c *serverImpl) Serve() {
54+
for {
55+
conn, err := c.listener.Accept()
56+
logger.Infof("accept")
57+
if err != nil {
58+
logger.Warnf("listener failed to accept. err: %+s", err)
59+
return
60+
}
61+
62+
go func() {
63+
defer func() {
64+
_ = conn.Close()
65+
}()
66+
67+
rs := c.timer.GetRemainSec()
68+
69+
r := &Response{
70+
RemainSec: rs,
71+
}
72+
73+
b, err := json.Marshal(r)
74+
if err != nil {
75+
logger.Errorf("faield to marshal Response: %+v", err)
76+
return
77+
}
78+
79+
_, err = conn.Write(b)
80+
if err != nil {
81+
logger.Errorf("faield to write Response: %+v", err)
82+
}
83+
}()
84+
}
85+
}
86+
87+
func (c *serverImpl) Close() {
88+
_ = c.listener.Close()
89+
}

pomodoro/pomodoro.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ const (
2323
type Pomodoro interface {
2424
Start() error
2525
Stop()
26+
27+
GetTimer() timer.Timer
28+
2629
Finish()
2730
}
2831

@@ -121,6 +124,10 @@ func (p *pomodoroImpl) Start() error {
121124
}
122125
}
123126

127+
func (p *pomodoroImpl) GetTimer() timer.Timer {
128+
return p.timer
129+
}
130+
124131
func (p *pomodoroImpl) Stop() {
125132
}
126133

0 commit comments

Comments
 (0)