golangのお勉強　chromeの作業中お気に入りタブ的なこと


チュートリアルは一通りやったけど実戦はあまり。。

## 個別処理を調べつつ実装

### golangでのCUIコマンドのベースとして参照

Golangで実装するCUIコマンド - Qiita
http://qiita.com/curious-eyes/items/5c109043d0a0c01bb54a

Golang の defer 文と panic/recover 機構について - CUBE SUGAR CONTAINER
http://blog.amedama.jp/entry/2015/10/11/123535

deferって終了処理があるのね。クラス的。

panic() したときにも呼び出されるらしい。
> panic/recover の機構が他言語での例外機構のように振る舞うのであれば、error との使い分けをどうすれば良いのだろうか。
...
外部に公開する API はエラーを伝える手段として panic() を使ってはいけない
多値の返り値 w/ error インターフェースを使うこと
panic() はパッケージをまたいで伝搬させることがないようにする
panic/recover 機構はスタックが深くなるような呼び出しをする際に有用
使用することで可読性を向上させることができる
つまり、外部にエラーを伝える手段としては error インターフェースを使うのが正式なやり方で、panic/recover はあくまで内部的に用いるべきもの、ということらしい。


ポインタ参照、、引数を受け取った時にflagパッケージで引数のポインタに。。
みたいなことを踏まえて記述かな。。よくわかってない。
ここではflagパッケージで引数をスクリプト側で設定（flag.Parse）してるのかな。。

```
failure = flag.Bool("failure", false, "失敗させます")
if *failer{...}
```

### exec.Commandで外部コマンドを実行

Golangで外部コマンドを実行する方法まとめ - Qiita
http://qiita.com/tanksuzuki/items/9205ff70c57c4c03b5e5

引数の扱いとかパイプとかやりたいことはひとまず大体できるのかな。


### スライスのdiffをとる

go - How to find the difference between two slices of strings in Golang? - Stack Overflow
http://stackoverflow.com/questions/19374219/how-to-find-the-difference-between-two-slices-of-strings-in-golang

若干変更バージョン

```
func difference(slice1 []string, slice2 []string, bothFlag int) []string {
	var diff []string

	// Loop two times, first to find slice1 strings not in slice2,
	// second loop to find slice2 strings not in slice1
	max := 1
	if bothFlag == 1 {
		max = 2
	}
	for i := 0; i < max; i++ {
		for _, s1 := range slice1 {
			found := false
			for _, s2 := range slice2 {
				if s1 == s2 {
					found = true
					break
				}
			}
			// String not found. We add it to return slice
			if !found {
				diff = append(diff, s1)
			}
		}
		// Swap the slices, only if it was the first loop
		if i == 0 {
			slice1, slice2 = slice2, slice1
		}
	}

	return diff
}
```

### スライスをループで上書き

arrays - Change values while iterating in golang - Stack Overflow
http://stackoverflow.com/questions/15945030/change-values-while-iterating-in-golang


```
for i, attr := range n.Attr {
    if attr.Key == "href" {
        n.Attr[i].Val = "something"
    }
}
```

### godoc

ドキュメント - golang.jp
http://golang.jp/category/%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88/page/10

若干フォーマットがよくわからん。
シンプルに見えるけどちゃんと書いたほうがいいのかしら。。


### 逆引きリファレンス的なもの

逆引きGolang (配列)
http://ashitani.jp/golangtips/tips_slice.html#slice_Define

便利そう

## できたもの

使い勝手悪いな。。
pecoとかで個別で選択して選べるインターフェイスも。。
たぶん、つくらない。

```
package main

import (
	"flag"
	"fmt"
	"time"
	"os"
	"os/exec"
	"log"
	"strings"
	"regexp"
	"io"
	"github.com/comail/colog"
)

const VERSION = `0.0.1`

type ErrMessage struct {
	What string
}

func (e ErrMessage) Error() string {
	return fmt.Sprintf("%v: %v", time.Now(), e.What)
}

var (
	version = flag.Bool("V", false, "display version information")
	failure = flag.Bool("failure", false, "失敗させます")
)

/*
スライスの差分を取得
 */
func difference(slice1 []string, slice2 []string, bothFlag int) []string {
	var diff []string

	// Loop two times, first to find slice1 strings not in slice2,
	// second loop to find slice2 strings not in slice1
	max := 1
	if bothFlag == 1 {
		max = 2
	}
	for i := 0; i < max; i++ {
		for _, s1 := range slice1 {
			found := false
			for _, s2 := range slice2 {
				if s1 == s2 {
					found = true
					break
				}
			}
			// String not found. We add it to return slice
			if !found {
				diff = append(diff, s1)
			}
		}
		// Swap the slices, only if it was the first loop
		if i == 0 {
			slice1, slice2 = slice2, slice1
		}
	}

	return diff
}

/*
お気に入りを開く
 */
func openFavorite(str string) (err error) {
	defer func() {
		// panicがおきたらRollback
		if e := recover(); e != nil {
			fmt.Fprintf(os.Stderr, "Rollback!!\n")
			// エラーメッセージフォーマット変換
			x, _ := e.(string)
			err = ErrMessage{x}
		}
	}()

	if *failure {
		// Error発生
		panic("DoSomething error...")
	}

	// お気に入りの定義
	urlList := []string{
		"https://twitter.com/",
	}
	log.Println("======================== urlList ============================")
	log.Println(urlList)

	// ブラウザのタブを取得
	out, err := exec.Command("chrome-cli", "list", "links").Output()
	urlTabs := strings.Split(string(out), "\n")
	re := regexp.MustCompile(`^\[[0-9]+?\] `)
	for i, _ := range urlTabs {
		urlTabs[i] = re.ReplaceAllString(urlTabs[i], "")
		//urlTabs[i] = strings.Replace(urlTabs[i], "/[.*]/", "-", 1) // 最後の引数は回数
	}
	log.Println("======================== urlTabs ============================")
	log.Println(urlTabs)

	// 差分を取得して開く
	diffList := difference(urlList, urlTabs, 0)
	log.Println("======================== diff ============================")
	log.Println(diffList)
	for i, _ := range diffList {
		cmd := exec.Command("chrome-cli", "open", diffList[i])
		stdin, _ := cmd.StdinPipe()
		io.WriteString(stdin, "hoge")
		stdin.Close()
		out, _ := cmd.Output()
		fmt.Printf("結果: %s", out)
	}
	return nil
}

/*
メイン
 */
func main() {
	colog.SetDefaultLevel(colog.LDebug)
	colog.SetMinLevel(colog.LTrace)
	colog.SetFormatter(&colog.StdFormatter{
		Colors: true,
		Flag:   log.Ldate | log.Ltime | log.Lshortfile,
	})
	colog.Register()

	flag.Parse()
	if *version {
		fmt.Printf("openFavorite %s\n", VERSION)
		os.Exit(0)
	}

	str := "do something & commit!!"
	err := openFavorite(str)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err)
		os.Exit(1)
	}

	str = "and finish!!"
	fmt.Fprintf(os.Stdout, "%s\n", str)
}

```