diff --git a/kadai1/pei/Makefile b/kadai1/pei/Makefile new file mode 100644 index 0000000..278a0d4 --- /dev/null +++ b/kadai1/pei/Makefile @@ -0,0 +1,17 @@ +ROOT=github.com/gopherdojo/dojo6/kadai1/pei +BIN=bin/imgconv +MAIN=cmd/cli.go +TEST=... + +.PHONY: build +build: ${MAIN} + go build -o ${BIN} ${GOPATH}/src/${ROOT}/$? + +.PHONY: test +test: + go test -v -cover ${ROOT}/${TEST} + +.PHONY: clean +clean: + rm ${BIN} + go clean diff --git a/kadai1/pei/README.md b/kadai1/pei/README.md new file mode 100644 index 0000000..cfb6136 --- /dev/null +++ b/kadai1/pei/README.md @@ -0,0 +1,46 @@ +# 課題1 画像変換コマンドを作ろう + +- 次の仕様を満たすコマンドを作って下さい + - ディレクトリを指定する + - 指定したディレクトリ以下のJPGファイルをPNGに変換(デフォルト) + - ディレクトリ以下は再帰的に処理する + - 変換前と変換後の画像形式を指定できる(オプション) +- 以下を満たすように開発してください + - mainパッケージと分離する + - 自作パッケージと標準パッケージと準標準パッケージのみ使う + - 準標準パッケージ:golang.org/x以下のパッケージ + - ユーザ定義型を作ってみる + - GoDocを生成してみる + +# imgconv + +## Usage + +``` +Usage: + imgconv [-in_ext] [-out_ext] [-leave] DIR +Arguments: + -in_ext input extension (jpg, png, gif) [default: jpg] + -out_ext output extension (jpg, png, gif) [default: png] + -leave whether to leave input [default: fasle] +``` + +## Development + +### Build + +``` +make build +``` + +### Test + +``` +make test +``` + +### Clean + +``` +make clean +``` diff --git a/kadai1/pei/cmd/cli.go b/kadai1/pei/cmd/cli.go new file mode 100644 index 0000000..2482ab7 --- /dev/null +++ b/kadai1/pei/cmd/cli.go @@ -0,0 +1,37 @@ +package main + +import ( + "flag" + "os" + + "github.com/gopherdojo/dojo6/kadai1/pei/pkg/convdir" + "github.com/gopherdojo/dojo6/kadai1/pei/pkg/imgconv" +) + +const ( + usageInExt = `input extension (jpg, png, gif)` + usageOutExt = `output extension (jpg, png, gif)` + usageLeave = `whether to leave input` +) + +func main() { + var ( + inputExtension = flag.String("in_ext", "jpg", usageInExt) + outputExtension = flag.String("out_ext", "png", usageOutExt) + leaveInput = flag.Bool("leave", false, usageLeave) + ) + flag.Parse() + args := flag.Args() + + if len(args) == 0 { + os.Exit(1) + } + + cd := convdir.ConverterWithDir{ + Dir: args[0], + InputExtension: imgconv.ParseImgExtension(*inputExtension), + OutputExtension: imgconv.ParseImgExtension(*outputExtension), + LeaveInput: *leaveInput, + } + cd.Convert() +} diff --git a/kadai1/pei/pkg/convdir/convdir.go b/kadai1/pei/pkg/convdir/convdir.go new file mode 100644 index 0000000..a05eb35 --- /dev/null +++ b/kadai1/pei/pkg/convdir/convdir.go @@ -0,0 +1,72 @@ +package convdir + +import ( + "fmt" + "log" + "os" + "path/filepath" + "strings" + + "github.com/gopherdojo/dojo6/kadai1/pei/pkg/imgconv" +) + +// ImgConverter With Directory struct has target directory and 2 extention and option. +type ConverterWithDir struct { + Dir string + InputExtension imgconv.ImgExtension + OutputExtension imgconv.ImgExtension + LeaveInput bool +} + +// Result struct has output image path. +type ConvertedResult struct { + OutputPath string +} + +// Convert image file in target directory. +func (cd ConverterWithDir) Convert() (results []ConvertedResult) { + files, err := cd.getImageFilePaths() + if err != nil { + log.Fatal(err) + } + + for _, f := range files { + + inputPath := fmt.Sprintf("%s/%s", cd.Dir, f) + outputPath := fmt.Sprintf("%s.%s", strings.TrimSuffix(inputPath, filepath.Ext(inputPath)), cd.OutputExtension) + + ic := imgconv.ImgConverter{ + InputPath: inputPath, + InputExtension: cd.InputExtension, + OutputPath: outputPath, + OutputExtension: cd.OutputExtension, + LeaveInput: cd.LeaveInput, + } + err = ic.Convert() + if err != nil { + log.Fatal(err) + } + + results = append(results, ConvertedResult{OutputPath: outputPath}) + } + + return +} + +func (cd ConverterWithDir) getImageFilePaths() (files []string, err error) { + _, err = os.Stat(cd.Dir) + if err != nil { + return + } + + err = filepath.Walk(cd.Dir, + func(path string, info os.FileInfo, err error) error { + relPath, err := filepath.Rel(cd.Dir, path) + if !info.IsDir() && err == nil && imgconv.ParseImgExtension(relPath) == cd.InputExtension { + files = append(files, relPath) + } + return nil + }) + + return +} diff --git a/kadai1/pei/pkg/convdir/convdir_test.go b/kadai1/pei/pkg/convdir/convdir_test.go new file mode 100644 index 0000000..a143047 --- /dev/null +++ b/kadai1/pei/pkg/convdir/convdir_test.go @@ -0,0 +1,29 @@ +package convdir + +import ( + "os" + "reflect" + "testing" + + "github.com/gopherdojo/dojo6/kadai1/pei/pkg/imgconv" +) + +func TestConverterWithDir_Convert(t *testing.T) { + cd := ConverterWithDir{ + Dir: "../../testdata", + InputExtension: imgconv.JPEG, + OutputExtension: imgconv.PNG, + LeaveInput: true, + } + got := cd.Convert() + want := []ConvertedResult{ + {OutputPath: "../../testdata/sample.png"}, + {OutputPath: "../../testdata/testdir1/testdir2/sample.png"}, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("got: %v, want: %v", got, want) + } + + os.Remove(want[0].OutputPath) + os.Remove(want[1].OutputPath) +} diff --git a/kadai1/pei/pkg/imgconv/imgconv.go b/kadai1/pei/pkg/imgconv/imgconv.go new file mode 100644 index 0000000..b64322b --- /dev/null +++ b/kadai1/pei/pkg/imgconv/imgconv.go @@ -0,0 +1,80 @@ +package imgconv + +import ( + "fmt" + "image" + "image/gif" + "image/jpeg" + "image/png" + "log" + "os" +) + +// ImgConverter struct has 2 paths and 2 extensions for input and output. +type ImgConverter struct { + // + InputPath string + InputExtension ImgExtension + OutputPath string + OutputExtension ImgExtension + LeaveInput bool +} + +// Convert image file. +func (ic *ImgConverter) Convert() error { + inFile, err := os.Open(ic.InputPath) + if err != nil { + log.Fatal(err) + } + defer inFile.Close() + + inImg, err := decodeImg(inFile, ic.InputExtension) + if err != nil { + log.Fatal(err) + } + + outFile, err := os.Create(ic.OutputPath) + if err != nil { + log.Fatal(err) + } + defer outFile.Close() + + err = encodeImg(outFile, ic.OutputExtension, &inImg) + if err != nil { + log.Fatal(err) + } + + if !ic.LeaveInput { + os.Remove(ic.InputPath) + } + + return nil +} + +func encodeImg(file *os.File, ext ImgExtension, img *image.Image) (err error) { + switch ext { + case JPEG: + err = jpeg.Encode(file, *img, nil) + case PNG: + err = png.Encode(file, *img) + case GIF: + err = gif.Encode(file, *img, nil) + default: + err = fmt.Errorf("%s is not supported", ext) + } + return +} + +func decodeImg(file *os.File, ext ImgExtension) (img image.Image, err error) { + switch ext { + case JPEG: + img, err = jpeg.Decode(file) + case PNG: + img, err = png.Decode(file) + case GIF: + img, err = gif.Decode(file) + default: + err = fmt.Errorf("%s is not supported", ext) + } + return +} diff --git a/kadai1/pei/pkg/imgconv/imgconv_test.go b/kadai1/pei/pkg/imgconv/imgconv_test.go new file mode 100644 index 0000000..6e09208 --- /dev/null +++ b/kadai1/pei/pkg/imgconv/imgconv_test.go @@ -0,0 +1,51 @@ +package imgconv + +import ( + "os" + "testing" +) + +func TestImgConverter_Convert(t *testing.T) { + const ( + jpgfile = "../../testdata/sample.jpg" + pngfile = "../../testdata/sample.png" + giffile = "../../testdata/sample.gif" + + errorMessage = "Unexpected behavior" + ) + defer func() { + os.Remove(pngfile) + os.Remove(giffile) + }() + + var ( + ic ImgConverter + err error + ) + + // jpg -> png + ic = ImgConverter{ + InputPath: jpgfile, + InputExtension: JPEG, + OutputPath: pngfile, + OutputExtension: PNG, + LeaveInput: true, + } + err = ic.Convert() + if err != nil { + t.Errorf(errorMessage) + } + + // png -> gif + ic = ImgConverter{ + InputPath: pngfile, + InputExtension: PNG, + OutputPath: giffile, + OutputExtension: GIF, + LeaveInput: false, + } + err = ic.Convert() + if err != nil { + t.Errorf(errorMessage) + } +} diff --git a/kadai1/pei/pkg/imgconv/imgext.go b/kadai1/pei/pkg/imgconv/imgext.go new file mode 100644 index 0000000..65c1f0b --- /dev/null +++ b/kadai1/pei/pkg/imgconv/imgext.go @@ -0,0 +1,46 @@ +package imgconv + +import ( + "strings" +) + +// ImgExtension is enum type for image extension. +type ImgExtension int + +const ( + UNDEF ImgExtension = iota + JPEG + PNG + GIF +) + +// ImgExtension => String +func (ie ImgExtension) String() string { + switch ie { + case JPEG: + return "jpeg" + case PNG: + return "png" + case GIF: + return "gif" + default: + return "undefined" + } +} + +// String => ImgExtension +func ParseImgExtension(s string) ImgExtension { + stringList := strings.Split(s, ".") + ext := stringList[len(stringList)-1] + ext = strings.ToLower(ext) + switch ext { + case "jpg", "jpeg": + return JPEG + case "png": + return PNG + case "gif": + return GIF + default: + return UNDEF + } +} diff --git a/kadai1/pei/testdata/sample.jpg b/kadai1/pei/testdata/sample.jpg new file mode 100755 index 0000000..30a491d Binary files /dev/null and b/kadai1/pei/testdata/sample.jpg differ diff --git a/kadai1/pei/testdata/testdir1/testdir2/sample.jpg b/kadai1/pei/testdata/testdir1/testdir2/sample.jpg new file mode 100755 index 0000000..30a491d Binary files /dev/null and b/kadai1/pei/testdata/testdir1/testdir2/sample.jpg differ diff --git a/kadai2/.gitkeep b/kadai2/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/kadai2/pei/Makefile b/kadai2/pei/Makefile new file mode 100644 index 0000000..f6a44f4 --- /dev/null +++ b/kadai2/pei/Makefile @@ -0,0 +1,17 @@ +ROOT=github.com/gopherdojo/dojo6/kadai2/pei +BIN=bin/imgconv +MAIN=cmd/cli.go +TEST=... + +.PHONY: build +build: ${MAIN} + go build -o ${BIN} ${GOPATH}/src/${ROOT}/$? + +.PHONY: test +test: + go test -v -cover ${ROOT}/${TEST} + +.PHONY: clean +clean: + rm ${BIN} + go clean diff --git a/kadai2/pei/README.md b/kadai2/pei/README.md new file mode 100644 index 0000000..cfb6136 --- /dev/null +++ b/kadai2/pei/README.md @@ -0,0 +1,46 @@ +# 課題1 画像変換コマンドを作ろう + +- 次の仕様を満たすコマンドを作って下さい + - ディレクトリを指定する + - 指定したディレクトリ以下のJPGファイルをPNGに変換(デフォルト) + - ディレクトリ以下は再帰的に処理する + - 変換前と変換後の画像形式を指定できる(オプション) +- 以下を満たすように開発してください + - mainパッケージと分離する + - 自作パッケージと標準パッケージと準標準パッケージのみ使う + - 準標準パッケージ:golang.org/x以下のパッケージ + - ユーザ定義型を作ってみる + - GoDocを生成してみる + +# imgconv + +## Usage + +``` +Usage: + imgconv [-in_ext] [-out_ext] [-leave] DIR +Arguments: + -in_ext input extension (jpg, png, gif) [default: jpg] + -out_ext output extension (jpg, png, gif) [default: png] + -leave whether to leave input [default: fasle] +``` + +## Development + +### Build + +``` +make build +``` + +### Test + +``` +make test +``` + +### Clean + +``` +make clean +``` diff --git a/kadai2/pei/cmd/cli.go b/kadai2/pei/cmd/cli.go new file mode 100644 index 0000000..2bfb9b4 --- /dev/null +++ b/kadai2/pei/cmd/cli.go @@ -0,0 +1,37 @@ +package main + +import ( + "flag" + "os" + + "github.com/gopherdojo/dojo6/kadai2/pei/pkg/convdir" + "github.com/gopherdojo/dojo6/kadai2/pei/pkg/imgconv" +) + +const ( + usageInExt = `input extension (jpg, png, gif)` + usageOutExt = `output extension (jpg, png, gif)` + usageLeave = `whether to leave input` +) + +func main() { + var ( + inputExtension = flag.String("in_ext", "jpg", usageInExt) + outputExtension = flag.String("out_ext", "png", usageOutExt) + leaveInput = flag.Bool("leave", false, usageLeave) + ) + flag.Parse() + args := flag.Args() + + if len(args) == 0 { + os.Exit(1) + } + + cd := convdir.ConverterWithDir{ + Dir: args[0], + InputExtension: imgconv.ParseImgExtension(*inputExtension), + OutputExtension: imgconv.ParseImgExtension(*outputExtension), + LeaveInput: *leaveInput, + } + cd.Convert() +} diff --git a/kadai2/pei/pkg/convdir/convdir.go b/kadai2/pei/pkg/convdir/convdir.go new file mode 100644 index 0000000..fdee69c --- /dev/null +++ b/kadai2/pei/pkg/convdir/convdir.go @@ -0,0 +1,72 @@ +package convdir + +import ( + "fmt" + "log" + "os" + "path/filepath" + "strings" + + "github.com/gopherdojo/dojo6/kadai2/pei/pkg/imgconv" +) + +// ImgConverter With Directory struct has target directory and 2 extention and option. +type ConverterWithDir struct { + Dir string + InputExtension imgconv.ImgExtension + OutputExtension imgconv.ImgExtension + LeaveInput bool +} + +// Result struct has output image path. +type ConvertedResult struct { + OutputPath string +} + +// Convert image file in target directory. +func (cd ConverterWithDir) Convert() (results []ConvertedResult) { + files, err := cd.getImageFilePaths() + if err != nil { + log.Fatal(err) + } + + for _, f := range files { + + inputPath := fmt.Sprintf("%s/%s", cd.Dir, f) + outputPath := fmt.Sprintf("%s.%s", strings.TrimSuffix(inputPath, filepath.Ext(inputPath)), cd.OutputExtension) + + ic := imgconv.ImgConverter{ + InputPath: inputPath, + InputExtension: cd.InputExtension, + OutputPath: outputPath, + OutputExtension: cd.OutputExtension, + LeaveInput: cd.LeaveInput, + } + err = ic.Convert() + if err != nil { + log.Fatal(err) + } + + results = append(results, ConvertedResult{OutputPath: outputPath}) + } + + return +} + +func (cd ConverterWithDir) getImageFilePaths() (files []string, err error) { + _, err = os.Stat(cd.Dir) + if err != nil { + return + } + + err = filepath.Walk(cd.Dir, + func(path string, info os.FileInfo, err error) error { + relPath, err := filepath.Rel(cd.Dir, path) + if !info.IsDir() && err == nil && imgconv.ParseImgExtension(relPath) == cd.InputExtension { + files = append(files, relPath) + } + return nil + }) + + return +} diff --git a/kadai2/pei/pkg/convdir/convdir_test.go b/kadai2/pei/pkg/convdir/convdir_test.go new file mode 100644 index 0000000..9118d2f --- /dev/null +++ b/kadai2/pei/pkg/convdir/convdir_test.go @@ -0,0 +1,29 @@ +package convdir + +import ( + "os" + "reflect" + "testing" + + "github.com/gopherdojo/dojo6/kadai2/pei/pkg/imgconv" +) + +func TestConverterWithDir_Convert(t *testing.T) { + cd := ConverterWithDir{ + Dir: "../../testdata", + InputExtension: imgconv.JPEG, + OutputExtension: imgconv.PNG, + LeaveInput: true, + } + got := cd.Convert() + want := []ConvertedResult{ + {OutputPath: "../../testdata/sample.png"}, + {OutputPath: "../../testdata/testdir1/testdir2/sample.png"}, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("got: %v, want: %v", got, want) + } + + os.Remove(want[0].OutputPath) + os.Remove(want[1].OutputPath) +} diff --git a/kadai2/pei/pkg/imgconv/imgconv.go b/kadai2/pei/pkg/imgconv/imgconv.go new file mode 100644 index 0000000..b64322b --- /dev/null +++ b/kadai2/pei/pkg/imgconv/imgconv.go @@ -0,0 +1,80 @@ +package imgconv + +import ( + "fmt" + "image" + "image/gif" + "image/jpeg" + "image/png" + "log" + "os" +) + +// ImgConverter struct has 2 paths and 2 extensions for input and output. +type ImgConverter struct { + // + InputPath string + InputExtension ImgExtension + OutputPath string + OutputExtension ImgExtension + LeaveInput bool +} + +// Convert image file. +func (ic *ImgConverter) Convert() error { + inFile, err := os.Open(ic.InputPath) + if err != nil { + log.Fatal(err) + } + defer inFile.Close() + + inImg, err := decodeImg(inFile, ic.InputExtension) + if err != nil { + log.Fatal(err) + } + + outFile, err := os.Create(ic.OutputPath) + if err != nil { + log.Fatal(err) + } + defer outFile.Close() + + err = encodeImg(outFile, ic.OutputExtension, &inImg) + if err != nil { + log.Fatal(err) + } + + if !ic.LeaveInput { + os.Remove(ic.InputPath) + } + + return nil +} + +func encodeImg(file *os.File, ext ImgExtension, img *image.Image) (err error) { + switch ext { + case JPEG: + err = jpeg.Encode(file, *img, nil) + case PNG: + err = png.Encode(file, *img) + case GIF: + err = gif.Encode(file, *img, nil) + default: + err = fmt.Errorf("%s is not supported", ext) + } + return +} + +func decodeImg(file *os.File, ext ImgExtension) (img image.Image, err error) { + switch ext { + case JPEG: + img, err = jpeg.Decode(file) + case PNG: + img, err = png.Decode(file) + case GIF: + img, err = gif.Decode(file) + default: + err = fmt.Errorf("%s is not supported", ext) + } + return +} diff --git a/kadai2/pei/pkg/imgconv/imgconv_test.go b/kadai2/pei/pkg/imgconv/imgconv_test.go new file mode 100644 index 0000000..6e09208 --- /dev/null +++ b/kadai2/pei/pkg/imgconv/imgconv_test.go @@ -0,0 +1,51 @@ +package imgconv + +import ( + "os" + "testing" +) + +func TestImgConverter_Convert(t *testing.T) { + const ( + jpgfile = "../../testdata/sample.jpg" + pngfile = "../../testdata/sample.png" + giffile = "../../testdata/sample.gif" + + errorMessage = "Unexpected behavior" + ) + defer func() { + os.Remove(pngfile) + os.Remove(giffile) + }() + + var ( + ic ImgConverter + err error + ) + + // jpg -> png + ic = ImgConverter{ + InputPath: jpgfile, + InputExtension: JPEG, + OutputPath: pngfile, + OutputExtension: PNG, + LeaveInput: true, + } + err = ic.Convert() + if err != nil { + t.Errorf(errorMessage) + } + + // png -> gif + ic = ImgConverter{ + InputPath: pngfile, + InputExtension: PNG, + OutputPath: giffile, + OutputExtension: GIF, + LeaveInput: false, + } + err = ic.Convert() + if err != nil { + t.Errorf(errorMessage) + } +} diff --git a/kadai2/pei/pkg/imgconv/imgext.go b/kadai2/pei/pkg/imgconv/imgext.go new file mode 100644 index 0000000..65c1f0b --- /dev/null +++ b/kadai2/pei/pkg/imgconv/imgext.go @@ -0,0 +1,46 @@ +package imgconv + +import ( + "strings" +) + +// ImgExtension is enum type for image extension. +type ImgExtension int + +const ( + UNDEF ImgExtension = iota + JPEG + PNG + GIF +) + +// ImgExtension => String +func (ie ImgExtension) String() string { + switch ie { + case JPEG: + return "jpeg" + case PNG: + return "png" + case GIF: + return "gif" + default: + return "undefined" + } +} + +// String => ImgExtension +func ParseImgExtension(s string) ImgExtension { + stringList := strings.Split(s, ".") + ext := stringList[len(stringList)-1] + ext = strings.ToLower(ext) + switch ext { + case "jpg", "jpeg": + return JPEG + case "png": + return PNG + case "gif": + return GIF + default: + return UNDEF + } +} diff --git a/kadai2/pei/testdata/sample.jpg b/kadai2/pei/testdata/sample.jpg new file mode 100755 index 0000000..30a491d Binary files /dev/null and b/kadai2/pei/testdata/sample.jpg differ diff --git a/kadai2/pei/testdata/testdir1/testdir2/sample.jpg b/kadai2/pei/testdata/testdir1/testdir2/sample.jpg new file mode 100755 index 0000000..30a491d Binary files /dev/null and b/kadai2/pei/testdata/testdir1/testdir2/sample.jpg differ