diff --git a/kadai1/marltake/README.md b/kadai1/marltake/README.md new file mode 100644 index 0000000..42b7875 --- /dev/null +++ b/kadai1/marltake/README.md @@ -0,0 +1,10 @@ +# image format converter + +## usage + +`convert [-t [][,]] ` + +## description + + Search image files that's SRC_IMAGE_FORMAT type in TARGET_DIRECTORY recursively and convert to DEST_IMAGE_FORMAT type. + Default SRC_IMAGE_FORMAT is jpeg and default DEST_IMAGE_FORMAT is png. \ No newline at end of file diff --git a/kadai1/marltake/convert/convert.go b/kadai1/marltake/convert/convert.go new file mode 100644 index 0000000..aff8194 --- /dev/null +++ b/kadai1/marltake/convert/convert.go @@ -0,0 +1,81 @@ +package convert + +import ( + "image" + "image/gif" + "image/jpeg" + "image/png" + "os" + "path/filepath" + "strings" +) + +func ConfigConvert(src string, dest string) func(string, os.FileInfo, error) error { + srcExt := "." + src + lenSrcExt := len(srcExt) + destExt := "." + dest + // TODO declare decode for src and encode for dest here + return func(path string, info os.FileInfo, err error) error { + println(path) + if strings.ToLower(filepath.Ext(path)) == srcExt { + destPath := path[:len(path)-lenSrcExt] + destExt + // TODO error handling + if _, err := os.Stat(destPath); os.IsNotExist(err) { + file, _ := os.Open(path) + defer file.Close() + var img image.Image + switch src { + case "jpg": + img, _ = jpeg.Decode(file) + case "png": + img, _ = png.Decode(file) + case "gif": + img, _ = gif.Decode(file) + } + destfile, _ := os.Create(destPath) + defer destfile.Close() + switch dest { + case "jpg": + jpeg.Encode(destfile, img, nil) + case "png": + png.Encode(destfile, img) + case "gif": + gif.Encode(destfile, img, nil) + } + } else { + println("skip not to over write.", path) + } + } + return nil + } +} + +// ParseTarget parse and validate target image type option +func ParseTarget(target string) (src string, dest string, ok bool) { + targets := strings.Split(target, ",") + allowedExt := map[string]bool{ + "jpg": true, + "png": true, + "gif": true, + } + src, dest, ok = "", "", false + if len(targets) == 0 { + src, dest = "jpg", "png" + } else if len(targets) == 1 { + src, dest = targets[0], "png" + } else if len(targets) == 2 { + src, dest = targets[0], targets[1] + } else { + return + } + if src == "" { + src = "jpg" + } + if dest == "" { + dest = "png" + } + if ok = src != dest && allowedExt[src] && allowedExt[dest]; !ok { + src, dest = "", "" + } + return +} diff --git a/kadai1/marltake/convert/convert_test.go b/kadai1/marltake/convert/convert_test.go new file mode 100644 index 0000000..663e903 --- /dev/null +++ b/kadai1/marltake/convert/convert_test.go @@ -0,0 +1,44 @@ +package convert_test + +import ( + "marltake/convert" + "testing" + + "github.com/google/go-cmp/cmp" +) + +type result struct { + Src string + Dest string + Ok bool +} + +func TestParseTarget(t *testing.T) { + cases := []struct { + name string + input string + expected result + }{ + {name: "default", input: "", expected: result{"jpg", "png", true}}, + {name: "default_dest", input: "jpg", expected: result{"jpg", "png", true}}, + {name: "default_src", input: ",png", expected: result{"jpg", "png", true}}, + {name: "jpg to png", input: "jpg,png", expected: result{"jpg", "png", true}}, + {name: "jpg to gif", input: "jpg,gif", expected: result{"jpg", "gif", true}}, + {name: "png to jpg", input: "png,jpg", expected: result{"png", "jpg", true}}, + {name: "png to gif", input: "png,gif", expected: result{"png", "gif", true}}, + {name: "gif to jpg", input: "gif,jpg", expected: result{"gif", "jpg", true}}, + {name: "gif to png", input: "gif,png", expected: result{"gif", "png", true}}, + {name: "invalid dest", input: ",mp3", expected: result{"", "", false}}, + } + for _, c := range cases { + c := c + t.Run(c.name, func(t *testing.T) { + t.Parallel() + if src, dest, ok := convert.ParseTarget(c.input); !cmp.Equal(c.expected, result{src, dest, ok}) { + t.Errorf( + "want ParseTarget(%q) = %v, got %v", + c.input, c.expected, result{src, dest, ok}) + } + }) + } +} diff --git a/kadai1/marltake/doc/kadai1.md b/kadai1/marltake/doc/kadai1.md new file mode 100644 index 0000000..e72fb2f --- /dev/null +++ b/kadai1/marltake/doc/kadai1.md @@ -0,0 +1,14 @@ +# 課題1回答 +## 画像変換コマンド仕様 +* 指定されたディレクトリ以下の画像ファイルを再帰的に検索して処理する +* デフォルトはJPGファイルをPNGファイルに変換 +* オプションで変更前後の形式を指定可能 + * 前後のフォーマットが同じオプションは指定不可 + * 返還後のファイル名が存在する場合は、処理をskip + * -i 問い合わせる、-f 強制上書きとかできるかな +## 実装条件 +* mainパッケージと分離する +* 自作パッケージと標準パッケージと準標準パッケージのみ使う + * 準標準パッケージ:golang.org/x以下のパッケージ +* ユーザ定義型を作ってみる +* GoDocを生成してみる diff --git a/kadai1/marltake/doc/kadai2.md b/kadai1/marltake/doc/kadai2.md new file mode 100644 index 0000000..8c9375c --- /dev/null +++ b/kadai1/marltake/doc/kadai2.md @@ -0,0 +1,15 @@ +# 課題2 + +## io.Readerとio.Writerについて調べる +* 標準パッケージでどのように使われているか +* io.Readerとio.Writerがあることで、どういう利点があるのか具体例を挙げて考えてみる + + File, メモリ上の配列、ネットワークインターフェースなど複数種類のデバイスを、データ読み取り可能なもの(Reader)、データ書き込み可能なもの(Writer)と抽象化して、デバイスの特性をケアすることなく、データ処理部分を記述すことが可能になる。 + buffio.NewReader(io.Reader)では単純なReaderをwrapして、複数種類の読み出し方法を提供している。 + +## 1回目の宿題のテストを作る +* テストのしやすさを考えてリファクタリングしてみる +* テストのカバレッジを取ってみる slide 287 + * `go test -coverprofile=profile ./...` +* テーブル駆動テストを行う + サブテスト slide 284 +* テストヘルパーを作ってみる diff --git a/kadai1/marltake/go.mod b/kadai1/marltake/go.mod new file mode 100644 index 0000000..2d21aa4 --- /dev/null +++ b/kadai1/marltake/go.mod @@ -0,0 +1,5 @@ +module marltake + +go 1.12 + +require github.com/google/go-cmp v0.3.1 diff --git a/kadai1/marltake/main.go b/kadai1/marltake/main.go new file mode 100644 index 0000000..2447169 --- /dev/null +++ b/kadai1/marltake/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "flag" + "fmt" + "log" + "path/filepath" + + "marltake/convert" +) + +func main() { + var target = flag.String("t", "jpg,png", "source and destination picture type") + flag.Parse() + src, dest, ok := convert.ParseTarget(*target) + if !ok { + log.Fatal(fmt.Errorf("Invalid target option %s", *target)) + } + err := filepath.Walk(flag.Arg(0), convert.ConfigConvert(src, dest)) + if err != nil { + log.Fatal(err) + } +}