diff --git a/kadai1/micchie/README.md b/kadai1/micchie/README.md new file mode 100644 index 0000000..a72efdb --- /dev/null +++ b/kadai1/micchie/README.md @@ -0,0 +1,14 @@ +# Gopher 道場 +## 6-1: 画像変換コマンドを作ろう +### 1. 次の仕様を満たすコマンドを作って下さい +- ディレクトリを指定する +- 指定したディレクトリ以下の JPG ファイルをPNGに変換 (デフォルト) +- ディレクトリ以下は再帰的に処理する +- 変換前と変換後の画像形式を指定できる (オプション) + +### 2. 以下を満たすように開発してください +- mainパッケージと分離する +- 自作パッケージと標準パッケージと準標準パッケージのみ使う + - 準標準パッケージ: `golang.org/x` 以下のパッケージ +- ユーザ定義型を作ってみる +- GoDoc を生成してみる diff --git a/kadai1/micchie/go.mod b/kadai1/micchie/go.mod new file mode 100644 index 0000000..64563c1 --- /dev/null +++ b/kadai1/micchie/go.mod @@ -0,0 +1,5 @@ +module github.com/gopherdojo/dojo6/kadai1/micchie + +go 1.12 + +require github.com/pkg/errors v0.8.1 diff --git a/kadai1/micchie/go.sum b/kadai1/micchie/go.sum new file mode 100644 index 0000000..f29ab35 --- /dev/null +++ b/kadai1/micchie/go.sum @@ -0,0 +1,2 @@ +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/kadai1/micchie/imgconvert/imgconvert.go b/kadai1/micchie/imgconvert/imgconvert.go new file mode 100644 index 0000000..d80aeb2 --- /dev/null +++ b/kadai1/micchie/imgconvert/imgconvert.go @@ -0,0 +1,69 @@ +package imgconvert + +import ( + "image" + "image/jpeg" + "image/png" + "os" + "path/filepath" + "strings" +) + +// ImageFile is the Image file. +type ImageFile string + +// Directory is the File directory. +func (i ImageFile) Directory() (string, error) { + p, err := filepath.Abs(filepath.Clean(string(i))) + return filepath.Dir(p), err +} + +// Ext is the File extention. +func (i ImageFile) Ext() string { + return strings.ToLower(filepath.Ext(string(i))) +} + +// Name is the File name without extension. +func (i ImageFile) Name() string { + file := string(i) + return filepath.Base(file[:len(file)-len(i.Ext())]) +} + +// ConvertImage configures convert images. +type ConvertImage struct { + Before ImageFile + After ImageFile +} + +// Convert writes the Image to format. +func (cnv ConvertImage) Convert(format string) error { + bp, err := os.Open(string(cnv.Before)) + if err != nil { + return err + } + defer bp.Close() + + ap, err := os.Create(string(cnv.After)) + if err != nil { + return err + } + defer ap.Close() + + img, _, err := image.Decode(bp) + if err != nil { + return err + } + + switch format { + case "png": + err = png.Encode(ap, img) + case "jpg": + opts := &jpeg.Options{Quality: jpeg.DefaultQuality} + err = jpeg.Encode(ap, img, opts) + } + + if err != nil { + return err + } + return nil +} diff --git a/kadai1/micchie/main.go b/kadai1/micchie/main.go new file mode 100644 index 0000000..5a84604 --- /dev/null +++ b/kadai1/micchie/main.go @@ -0,0 +1,94 @@ +package main + +import ( + "flag" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/gopherdojo/dojo6/kadai1/micchie/imgconvert" + "github.com/pkg/errors" +) + +var ( + dir string + beforeFormat string + afterFormat string +) + +func init() { + flag.StringVar(&dir, "d", "", "path of images to convert [required]") + flag.StringVar(&beforeFormat, "before", "jpg", "image format before conversion") + flag.StringVar(&afterFormat, "after", "png", "image format after conversion") + flag.Parse() +} + +func main() { + if err := exec(); err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err.Error()) + os.Exit(1) + } +} + +func exec() error { + d := strings.TrimSpace(dir) + if d == "" { + return errors.New("directory is required") + } + + info, err := os.Stat(d) + if os.IsNotExist(err) { + return errors.Wrap(err, "directory does not exist") + } + + if !info.IsDir() { + return errors.New("set a directory") + } + + var bf, af string + bf, err = validationFormat(beforeFormat) + if err != nil { + return errors.Wrapf(err, "format: %v", beforeFormat) + } + + af, err = validationFormat(afterFormat) + if err != nil { + return errors.Wrapf(err, "format: %v", afterFormat) + } + + return filepath.Walk(d, func(p string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if info.IsDir() { + return nil + } + + img := &imgconvert.ConvertImage{} + img.Before = imgconvert.ImageFile(p) + + if img.Before.Ext() != fmt.Sprintf(".%v", bf) { + return nil + } + + dir, err := img.Before.Directory() + if err != nil { + return nil + } + + img.After = imgconvert.ImageFile(fmt.Sprintf("%v/%v.%v", dir, img.Before.Name(), af)) + + return img.Convert(af) + }) +} + +func validationFormat(format string) (string, error) { + f := strings.ToLower(format) + switch { + case f == "jpg" || f == "png": + return f, nil + } + return "", errors.New("format is not valid") +} diff --git a/kadai2/micchie/README.md b/kadai2/micchie/README.md new file mode 100644 index 0000000..23f2fa5 --- /dev/null +++ b/kadai2/micchie/README.md @@ -0,0 +1,29 @@ +# Gopher 道場 +## 6-2-1: io.Readerとio.Writer +io.Reader と io.Writer について調べてみよう。 + +### 1. 標準パッケージでどのように使われているか +- バッファ付入出力: + - https://golang.org/pkg/bufio/ +- 文字列フォーマット: + https://golang.org/pkg/fmt/ + - Fprint, Fprintf, Fprintln + - Fscan, Fscanf, Fscanln +- JSON エンコード・デコード: + - https://golang.org/pkg/encoding/json/ + - NewDecorder, NewEncoder +- HTTP: + - https://golang.org/pkg/net/http/ + +### 2. io.Reader と io.Writer があることでどういう利点があるのか具体例を挙げて考えてみる +- 入出力を一律 io.Reader, io.Writer のインターフェースで抽象化しているため, 「読み書きをする」ことに対してはすべて共通して扱える。 +- io.Pipe を利用することで, 複数の Read, Write を効率よくつなげて処理できるので, 大量のデータを扱う場合はメモリの節約ができるのでは。 + - https://golang.org/pkg/io/#Pipe + +## 6-2-2: テストを書いてみよう +1回目の宿題のテストを作ってみて下さい。 + +- テストのしやすさを考えてリファクタリングしてみる +- テストのカバレッジを取ってみる +- テーブル駆動テストを行う +- テストヘルパーを作ってみる diff --git a/kadai2/micchie/go.mod b/kadai2/micchie/go.mod new file mode 100644 index 0000000..11c0521 --- /dev/null +++ b/kadai2/micchie/go.mod @@ -0,0 +1,5 @@ +module github.com/gopherdojo/dojo6/kadai2/micchie + +go 1.12 + +require github.com/pkg/errors v0.8.1 diff --git a/kadai2/micchie/go.sum b/kadai2/micchie/go.sum new file mode 100644 index 0000000..3f3e4bf --- /dev/null +++ b/kadai2/micchie/go.sum @@ -0,0 +1,3 @@ +github.com/gopherdojo/dojo6 v0.0.0-20190710155631-1b40d3406c2f h1:UOg/ZpRwvokKADOjfCukjqQXYHb7HOdVSgZGABZc2BQ= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/kadai2/micchie/imgconvert/imgconvert.go b/kadai2/micchie/imgconvert/imgconvert.go new file mode 100644 index 0000000..d80aeb2 --- /dev/null +++ b/kadai2/micchie/imgconvert/imgconvert.go @@ -0,0 +1,69 @@ +package imgconvert + +import ( + "image" + "image/jpeg" + "image/png" + "os" + "path/filepath" + "strings" +) + +// ImageFile is the Image file. +type ImageFile string + +// Directory is the File directory. +func (i ImageFile) Directory() (string, error) { + p, err := filepath.Abs(filepath.Clean(string(i))) + return filepath.Dir(p), err +} + +// Ext is the File extention. +func (i ImageFile) Ext() string { + return strings.ToLower(filepath.Ext(string(i))) +} + +// Name is the File name without extension. +func (i ImageFile) Name() string { + file := string(i) + return filepath.Base(file[:len(file)-len(i.Ext())]) +} + +// ConvertImage configures convert images. +type ConvertImage struct { + Before ImageFile + After ImageFile +} + +// Convert writes the Image to format. +func (cnv ConvertImage) Convert(format string) error { + bp, err := os.Open(string(cnv.Before)) + if err != nil { + return err + } + defer bp.Close() + + ap, err := os.Create(string(cnv.After)) + if err != nil { + return err + } + defer ap.Close() + + img, _, err := image.Decode(bp) + if err != nil { + return err + } + + switch format { + case "png": + err = png.Encode(ap, img) + case "jpg": + opts := &jpeg.Options{Quality: jpeg.DefaultQuality} + err = jpeg.Encode(ap, img, opts) + } + + if err != nil { + return err + } + return nil +} diff --git a/kadai2/micchie/imgconvert/imgconvert_test.go b/kadai2/micchie/imgconvert/imgconvert_test.go new file mode 100644 index 0000000..dc4e1bf --- /dev/null +++ b/kadai2/micchie/imgconvert/imgconvert_test.go @@ -0,0 +1,74 @@ +package imgconvert + +import "testing" + +func TestImageFile_Ext(t *testing.T) { + tests := []struct { + file string + ext string + }{ + {file: "../testdata/test1.png", ext: ".png"}, + {file: "../testdata/test2.png", ext: ".png"}, + {file: "../testdata/test_directory/test3.jpg", ext: ".jpg"}, + } + + for _, test := range tests { + ext := ImageFile(test.file).Ext() + if ext != test.ext { + t.Errorf("file: %#v (want: %v, got: %v)", test.file, test.ext, ext) + continue + } + } +} + +func TestImageFile_Name(t *testing.T) { + tests := []struct { + file string + name string + }{ + {file: "../testdata/test1.png", name: "test1"}, + {file: "../testdata/test2.png", name: "test2"}, + {file: "../testdata/test_directory/test3.jpg", name: "test3"}, + } + + for _, test := range tests { + name := ImageFile(test.file).Name() + if name != test.name { + t.Errorf("file: %#v (want: %v, got: %v)", test.file, test.name, name) + continue + } + } +} + +func TestConvertImage_Convert(t *testing.T) { + tests := []struct { + format string + before string + after string + hasError bool + }{ + {format: "jpg", before: "../testdata/test1.png", after: "../testdata/test1.jpg", hasError: false}, + {format: "png", before: "../testdata/test2.jpg", after: "../testdata/test2.png", hasError: false}, + {format: "png", before: "../testdata/test_directory/test3.jpg", after: "../testdata/test_directory/test3.png", hasError: false}, + {format: "pdf", before: "../testdata/test_directory/test4.pdf", after: "../testdata/test_directory/test4.pdf", hasError: true}, + {format: "png", before: "../testdata/test/test5.png", after: "../testdata/test/test5.png", hasError: true}, + } + + for _, test := range tests { + images := &ConvertImage{ + Before: ImageFile(test.before), + After: ImageFile(test.after), + } + + var hasError bool + + if err := images.Convert(test.format); err != nil { + hasError = true + } + + if hasError != test.hasError { + t.Errorf("images: %#v (want: %v, got: %v)", images, test.hasError, hasError) + continue + } + } +} diff --git a/kadai2/micchie/main.go b/kadai2/micchie/main.go new file mode 100644 index 0000000..54e1f74 --- /dev/null +++ b/kadai2/micchie/main.go @@ -0,0 +1,95 @@ +package main + +import ( + "flag" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert" + "github.com/pkg/errors" +) + +var ( + dir string + beforeFormat string + afterFormat string +) + +func init() { + flag.StringVar(&dir, "d", "", "path of images to convert [required]") + flag.StringVar(&beforeFormat, "before", "jpg", "image format before conversion") + flag.StringVar(&afterFormat, "after", "png", "image format after conversion") + flag.Parse() +} + +func main() { + if err := exec(); err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err.Error()) + os.Exit(1) + } +} + +func exec() error { + d := strings.TrimSpace(dir) + if d == "" { + return errors.New("directory is required") + } + + info, err := os.Stat(d) + if os.IsNotExist(err) { + return errors.Wrap(err, "directory does not exist") + } + + if !info.IsDir() { + return errors.New("set a directory") + } + + var bf, af string + bf, err = ValidationFormat(beforeFormat) + if err != nil { + return errors.Wrapf(err, "format: %v", beforeFormat) + } + + af, err = ValidationFormat(afterFormat) + if err != nil { + return errors.Wrapf(err, "format: %v", afterFormat) + } + + return filepath.Walk(d, func(p string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if info.IsDir() { + return nil + } + + img := &imgconvert.ConvertImage{} + img.Before = imgconvert.ImageFile(p) + + if img.Before.Ext() != fmt.Sprintf(".%v", bf) { + return nil + } + + dir, err := img.Before.Directory() + if err != nil { + return nil + } + + img.After = imgconvert.ImageFile(fmt.Sprintf("%v/%v.%v", dir, img.Before.Name(), af)) + + return img.Convert(af) + }) +} + +// ValidationFormat validates the image format. +func ValidationFormat(format string) (string, error) { + f := strings.ToLower(format) + switch { + case f == "jpg" || f == "png": + return f, nil + } + return "", errors.New("format is not valid") +} diff --git a/kadai2/micchie/main_test.go b/kadai2/micchie/main_test.go new file mode 100644 index 0000000..467e563 --- /dev/null +++ b/kadai2/micchie/main_test.go @@ -0,0 +1,34 @@ +package main + +import ( + "testing" +) + +func TestValidationFormat(t *testing.T) { + tests := []struct { + format string + lower string + hasError bool + }{ + {format: "JPG", lower: "jpg", hasError: false}, + {format: "jpg", lower: "jpg", hasError: false}, + {format: "PNG", lower: "png", hasError: false}, + {format: "png", lower: "png", hasError: false}, + {format: "GIF", lower: "", hasError: true}, + {format: "", lower: "", hasError: true}, + } + + for _, test := range tests { + var lower string + var hasError bool + if l, err := ValidationFormat(test.format); err != nil { + lower = l + hasError = true + } + + if hasError != test.hasError { + t.Errorf("format: %#v (want: %v, %v, god: %v, %v)", test.format, test.lower, test.hasError, lower, hasError) + continue + } + } +} diff --git a/kadai2/micchie/profile b/kadai2/micchie/profile new file mode 100644 index 0000000..ed7dd11 --- /dev/null +++ b/kadai2/micchie/profile @@ -0,0 +1,16 @@ +mode: set +github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert/imgconvert.go:16.48,19.2 2 0 +github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert/imgconvert.go:22.33,24.2 1 1 +github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert/imgconvert.go:27.34,30.2 2 1 +github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert/imgconvert.go:39.54,41.16 2 1 +github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert/imgconvert.go:44.2,47.16 3 1 +github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert/imgconvert.go:50.2,53.16 3 1 +github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert/imgconvert.go:57.2,57.16 1 1 +github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert/imgconvert.go:65.2,65.16 1 1 +github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert/imgconvert.go:68.2,68.12 1 1 +github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert/imgconvert.go:41.16,43.3 1 1 +github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert/imgconvert.go:47.16,49.3 1 0 +github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert/imgconvert.go:53.16,55.3 1 1 +github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert/imgconvert.go:58.13,59.28 1 1 +github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert/imgconvert.go:60.13,62.35 2 1 +github.com/gopherdojo/dojo6/kadai2/micchie/imgconvert/imgconvert.go:65.16,67.3 1 0 diff --git a/kadai2/micchie/testdata/test1.jpg b/kadai2/micchie/testdata/test1.jpg new file mode 100644 index 0000000..038e72f Binary files /dev/null and b/kadai2/micchie/testdata/test1.jpg differ diff --git a/kadai2/micchie/testdata/test1.png b/kadai2/micchie/testdata/test1.png new file mode 100644 index 0000000..e06028b Binary files /dev/null and b/kadai2/micchie/testdata/test1.png differ diff --git a/kadai2/micchie/testdata/test2.jpg b/kadai2/micchie/testdata/test2.jpg new file mode 100644 index 0000000..d84410c Binary files /dev/null and b/kadai2/micchie/testdata/test2.jpg differ diff --git a/kadai2/micchie/testdata/test2.png b/kadai2/micchie/testdata/test2.png new file mode 100644 index 0000000..96db90c Binary files /dev/null and b/kadai2/micchie/testdata/test2.png differ diff --git a/kadai2/micchie/testdata/test_directory/test3.jpg b/kadai2/micchie/testdata/test_directory/test3.jpg new file mode 100644 index 0000000..27c1ff5 Binary files /dev/null and b/kadai2/micchie/testdata/test_directory/test3.jpg differ diff --git a/kadai2/micchie/testdata/test_directory/test3.png b/kadai2/micchie/testdata/test_directory/test3.png new file mode 100644 index 0000000..9016555 Binary files /dev/null and b/kadai2/micchie/testdata/test_directory/test3.png differ diff --git a/kadai2/micchie/testdata/test_directory/test4.pdf b/kadai2/micchie/testdata/test_directory/test4.pdf new file mode 100644 index 0000000..e69de29