Skip to content

Commit

Permalink
Merge pull request #7 from catatsuy/refactor/split_cli
Browse files Browse the repository at this point in the history
split package for cli
  • Loading branch information
catatsuy authored Sep 1, 2018
2 parents 4fa0fd3 + 094f8ab commit a9d750c
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 119 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ GLIDE = glide

all: bin/notify_slack bin/output

bin/notify_slack: cmd/notify_slack/main.go slack/*.go throttle/*.go config/*.go
bin/notify_slack: cmd/notify_slack/main.go slack/*.go throttle/*.go config/*.go cli/*.go
go build -o bin/notify_slack cmd/notify_slack/main.go

bin/output: cmd/output/main.go
Expand Down
167 changes: 167 additions & 0 deletions cli/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package cli

import (
"context"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"os/signal"
"syscall"
"time"

"github.com/catatsuy/notify_slack/config"
"github.com/catatsuy/notify_slack/slack"
"github.com/catatsuy/notify_slack/throttle"
"github.com/pkg/errors"
)

const (
ExitCodeOK = 0
ExitCodeParseFlagError = 1
ExitCodeFail = 1
)

type CLI struct {
outStream, errStream io.Writer
inputStream io.Reader

sClient *slack.Client
conf *config.Config
}

func NewCLI(outStream, errStream io.Writer, inputStream io.Reader) *CLI {
return &CLI{outStream: outStream, errStream: errStream, inputStream: inputStream}
}

func (c *CLI) Run(args []string) int {
var (
tomlFile string
duration time.Duration
)

c.conf = config.NewConfig()

flags := flag.NewFlagSet("notify_slack", flag.ContinueOnError)
flags.SetOutput(c.errStream)

flags.StringVar(&c.conf.Channel, "channel", "", "specify channel")
flags.StringVar(&c.conf.SlackURL, "slack-url", "", "slack url")
flags.StringVar(&c.conf.Token, "token", "", "token")
flags.StringVar(&c.conf.Username, "username", "", "specify username")
flags.StringVar(&c.conf.IconEmoji, "icon-emoji", "", "specify icon emoji")

flags.DurationVar(&duration, "interval", time.Second, "interval")
flags.StringVar(&tomlFile, "c", "", "config file name")

err := flags.Parse(args[1:])
if err != nil {
return ExitCodeParseFlagError
}

argv := flags.Args()
filename := ""
if len(argv) == 1 {
filename = argv[0]
}

tomlFile = config.LoadTOMLFilename(tomlFile)

if tomlFile != "" {
c.conf.LoadTOML(tomlFile)
}

if c.conf.SlackURL == "" {
fmt.Fprintln(c.errStream, "provide Slack URL")
return ExitCodeFail
}

c.sClient, err = slack.NewClient(c.conf.SlackURL, nil)
if err != nil {
fmt.Fprintln(c.errStream, err)
return ExitCodeFail
}

if filename != "" {
if c.conf.Token == "" {
fmt.Fprintln(c.errStream, "provide Slack token")
return ExitCodeFail
}

err := c.uploadSnippet(context.Background(), filename)
if err != nil {
fmt.Fprintln(c.errStream, err)
return ExitCodeFail
}

return ExitCodeOK
}

copyStdin := io.TeeReader(c.inputStream, c.outStream)

ex := throttle.NewExec(copyStdin)

exitC := make(chan os.Signal, 0)
signal.Notify(exitC, syscall.SIGTERM, syscall.SIGINT)

param := &slack.PostTextParam{
Channel: c.conf.Channel,
Username: c.conf.Username,
IconEmoji: c.conf.IconEmoji,
}

flushCallback := func(_ context.Context, output string) error {
param.Text = output
return c.sClient.PostText(context.Background(), param)
}

done := make(chan struct{}, 0)

doneCallback := func(ctx context.Context, output string) error {
defer func() {
done <- struct{}{}
}()

return flushCallback(ctx, output)
}

interval := time.Tick(duration)
ctx, cancel := context.WithCancel(context.Background())

ex.Start(ctx, interval, flushCallback, doneCallback)

select {
case <-exitC:
case <-ex.Wait():
}
cancel()

<-done

return ExitCodeOK
}

func (c *CLI) uploadSnippet(ctx context.Context, filename string) error {
_, err := os.Stat(filename)
if err != nil {
return errors.Wrapf(err, "%s does not exist", filename)
}

content, err := ioutil.ReadFile(filename)
if err != nil {
return err
}

param := &slack.PostFileParam{
Channel: c.conf.Channel,
Filename: filename,
Content: string(content),
}
err = c.sClient.PostFile(ctx, c.conf.Token, param)
if err != nil {
return err
}

return nil
}
121 changes: 3 additions & 118 deletions cmd/notify_slack/main.go
Original file line number Diff line number Diff line change
@@ -1,127 +1,12 @@
package main

import (
"context"
"flag"
"io"
"io/ioutil"
"log"
"os"
"os/signal"
"syscall"
"time"

"github.com/catatsuy/notify_slack/config"
"github.com/catatsuy/notify_slack/slack"
"github.com/catatsuy/notify_slack/throttle"
"github.com/catatsuy/notify_slack/cli"
)

func main() {
var (
tomlFile string
duration time.Duration
)

conf := config.NewConfig()

flag.StringVar(&conf.Channel, "channel", "", "specify channel")
flag.StringVar(&conf.SlackURL, "slack-url", "", "slack url")
flag.StringVar(&conf.Token, "token", "", "token")
flag.StringVar(&conf.Username, "username", "", "specify username")
flag.StringVar(&conf.IconEmoji, "icon-emoji", "", "specify icon emoji")

flag.DurationVar(&duration, "interval", time.Second, "interval")
flag.StringVar(&tomlFile, "c", "", "config file name")

flag.Parse()

args := flag.Args()
filename := ""
if len(args) == 1 {
filename = args[0]
}

tomlFile = config.LoadTOMLFilename(tomlFile)

if tomlFile != "" {
conf.LoadTOML(tomlFile)
}

if conf.SlackURL == "" {
log.Fatal("provide Slack URL")
}

sClient, err := slack.NewClient(conf.SlackURL, nil)
if err != nil {
log.Fatal(err)
}

if filename != "" {
if conf.Token == "" {
log.Fatal("provide Slack token")
}

_, err = os.Stat(filename)
if err != nil {
log.Fatalf("%s does not exist", filename)
}

content, err := ioutil.ReadFile(filename)
if err != nil {
log.Fatal(err)
}

param := &slack.PostFileParam{
Channel: conf.Channel,
Filename: filename,
Content: string(content),
}
err = sClient.PostFile(context.Background(), conf.Token, param)
if err != nil {
log.Fatal(err)
}

return
}

copyStdin := io.TeeReader(os.Stdin, os.Stdout)

ex := throttle.NewExec(copyStdin)

c := make(chan os.Signal, 0)
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)

param := &slack.PostTextParam{
Channel: conf.Channel,
Username: conf.Username,
IconEmoji: conf.IconEmoji,
}

flushCallback := func(_ context.Context, output string) error {
param.Text = output
return sClient.PostText(context.Background(), param)
}

done := make(chan struct{}, 0)

doneCallback := func(ctx context.Context, output string) error {
defer func() {
done <- struct{}{}
}()

return flushCallback(ctx, output)
}

interval := time.Tick(duration)
ctx, cancel := context.WithCancel(context.Background())

ex.Start(ctx, interval, flushCallback, doneCallback)

select {
case <-c:
case <-ex.Wait():
}
cancel()

<-done
c := cli.NewCLI(os.Stdout, os.Stderr, os.Stdin)
os.Exit(c.Run(os.Args))
}

0 comments on commit a9d750c

Please sign in to comment.