diff --git a/kadai1/sadah/.gitignore b/kadai1/sadah/.gitignore new file mode 100644 index 0000000..496ee2c --- /dev/null +++ b/kadai1/sadah/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git a/kadai1/sadah/README.md b/kadai1/sadah/README.md new file mode 100644 index 0000000..2402b32 --- /dev/null +++ b/kadai1/sadah/README.md @@ -0,0 +1,100 @@ +# imgconv + +imgconv is an image converter. + +# Install + +Use go get to install this package: + +``` +$ go get github.com/gopherdojo/dojo8/kadai1/sadah/imgconv +``` + +# Build + + +``` +$ git clone git@github.com:gopherdojo/dojo8.git +$ cd dojo8/kadai1/sadah/cmd/imgconv +$ go build -o imgconv +``` + +# Usage + +``` +./imgconv [options...] +``` + +To check the all available options, + +``` +$ imgconv -h +imgconv is an image converter +Usage: imgconv [options...] [path] +Use "imgconv --help" for more information about a command. + +Supported formats: [.jpg .jpeg .JPG .JPEG .png .PNG .gif .GIF .bmp .BMP .tiff .TIFF] + + -s string + Set a source extension (default "jpg") + -source string + Set a source extension (default "jpg") + -t string + Set a target extension (default "png") + -target string + Set a target extension (default "png") + -v Show version + -verbose + Print verbose messages + -version + Show version +``` + +# Spec + +## Required Spec + +次の仕様を満たすコマンドを作って下さい + +* [x] ディレクトリを指定する +* [x] 指定したディレクトリ以下のJPGファイルをPNGに変換(デフォルト) +* [x] ディレクトリ以下は再帰的に処理する +* [x] 変換前と変換後の画像形式を指定できる(オプション) + +以下を満たすように開発してください +* [x] mainパッケージと分離する +* [x] 自作パッケージと標準パッケージと準標準パッケージのみ使う + * 準標準パッケージ:golang.org/x以下のパッケージ +* [x] ユーザ定義型を作ってみる +* [x] GoDocを生成してみる +* [x] Go Modulesを使ってみる + +## Featured Spec + +* コマンドライン引数で PATH を指定。指定がない場合はカレントディレクトリが対象となる +* ディレクトリ以下は再帰的に処理する +* デフォルトでは指定したディレクトリ以下のJPGファイルをPNGに変換 +* `-s` で変換前、 `-t` 変換後の画像形式を指定できる + * jpg, png, gif, bmp, tiffに対応 +* main パッケージと imgconv パッケージを分離 +* 自作パッケージと標準パッケージと準標準パッケージのみ使う +* 拡張子リストに `exts` というユーザ定義型を利用 +* GoDocを生成した +* Go Modulesを使った + +# 感想 + +`type` をどういったときに使うとよいのか、まだいまひとつわかっていない。 + + +struct の初期化で、flagから受け取った値を使いたかったが、よい使い方が思いつかなかった。 + +愚直に代入すればできるけど、ポインタをうまく使う方法がわからなかった。以下の `&verbose` を struct に定義したかった…。 + +```go +flag.BoolVar(&verbose, "verbose", false, optVerboseText) +``` + +# Auther + +@sadah \ No newline at end of file diff --git a/kadai1/sadah/cmd/imgconv/imgconv b/kadai1/sadah/cmd/imgconv/imgconv new file mode 100755 index 0000000..f2f87ae Binary files /dev/null and b/kadai1/sadah/cmd/imgconv/imgconv differ diff --git a/kadai1/sadah/cmd/imgconv/main.go b/kadai1/sadah/cmd/imgconv/main.go new file mode 100644 index 0000000..1aa9e2c --- /dev/null +++ b/kadai1/sadah/cmd/imgconv/main.go @@ -0,0 +1,110 @@ +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + + "github.com/gopherdojo/dojo8/kadai1/sadah/imgconv" +) + +const ( + name = "imgconv" + version = "0.0.1" + defalutPath = "./" + defalutSrcExt = "jpg" + defalutTarExt = "png" + usage = `imgconv is an image converter +Usage: imgconv [options...] [path] +Use "imgconv --help" for more information about a command. + +Supported formats:` + optVerboseText = "Print verbose messages" + optShowVersionText = "Show version" + optSrcExtText = "Set a source extension" + optTarExtText = "Set a target extension" +) + +var ( + verbose bool + showVersion bool + srcExt string + tarExt string + path string +) + +func init() { + flag.BoolVar(&verbose, "verbose", false, optVerboseText) + + flag.BoolVar(&showVersion, "v", false, optShowVersionText) + flag.BoolVar(&showVersion, "version", false, optShowVersionText) + + flag.StringVar(&srcExt, "s", defalutSrcExt, optSrcExtText) + flag.StringVar(&srcExt, "source", defalutSrcExt, optSrcExtText) + + flag.StringVar(&tarExt, "t", defalutTarExt, optTarExtText) + flag.StringVar(&tarExt, "target", defalutTarExt, optTarExtText) + + flag.Usage = func() { + usageTxt := usage + fmt.Fprintf(os.Stderr, "%s %s\n\n", usageTxt, imgconv.SupportedExts) + flag.PrintDefaults() + } + flag.Parse() + + if verbose { + log.SetOutput(os.Stderr) + } else { + log.SetOutput(ioutil.Discard) + } + + if showVersion { + fmt.Println(version) + os.Exit(0) + } + + arg0 := flag.Arg(0) + if arg0 == "" { + path = defalutPath + } else { + path = arg0 + } + + srcExt = "." + srcExt + tarExt = "." + tarExt +} + +func run() (err error) { + log.Println("Target Path: " + path) + log.Println("Source Extension: " + srcExt) + log.Println("Target Extension: " + tarExt) + + if err = imgconv.VerifySupportedExt(srcExt); err != nil { + return + } + + if err = imgconv.VerifySupportedExt(tarExt); err != nil { + return + } + + return filepath.Walk(path, + func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if filepath.Ext(path) == srcExt { + return imgconv.Conv(path, tarExt) + } + return nil + }) +} + +func main() { + if err := run(); err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err.Error()) + os.Exit(1) + } +} diff --git a/kadai1/sadah/go.mod b/kadai1/sadah/go.mod new file mode 100644 index 0000000..39e7fa5 --- /dev/null +++ b/kadai1/sadah/go.mod @@ -0,0 +1,5 @@ +module github.com/gopherdojo/dojo8/kadai1/sadah + +go 1.14 + +require golang.org/x/image v0.0.0-20200618115811-c13761719519 diff --git a/kadai1/sadah/go.sum b/kadai1/sadah/go.sum new file mode 100644 index 0000000..394251b --- /dev/null +++ b/kadai1/sadah/go.sum @@ -0,0 +1,3 @@ +golang.org/x/image v0.0.0-20200618115811-c13761719519 h1:1e2ufUJNM3lCHEY5jIgac/7UTjd6cgJNdatjPdFWf34= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/kadai1/sadah/imgconv/imgconv.go b/kadai1/sadah/imgconv/imgconv.go new file mode 100644 index 0000000..f383488 --- /dev/null +++ b/kadai1/sadah/imgconv/imgconv.go @@ -0,0 +1,95 @@ +package imgconv + +import ( + "fmt" + "image" + "image/gif" + "image/jpeg" + "image/png" + "log" + "os" + "path/filepath" + "strings" + + "golang.org/x/image/bmp" + "golang.org/x/image/tiff" +) + +const ( + canNotOpenImageFile = "Can not open an image file: %s" + canNotWriteImageFile = "Can not write an image file: %s" + unsupportedExtension = "Unsupported extension: %s" +) + +type exts []string + +var ( + // SupportedExts is a list of supported extensions + SupportedExts exts = []string{ + ".jpg", ".jpeg", ".JPG", ".JPEG", + ".png", ".PNG", + ".gif", ".GIF", + ".bmp", ".BMP", + ".tiff", ".TIFF", + } +) + +// VerifySupportedExt verifies that the extension is supported. +func VerifySupportedExt(ext string) error { + var isValidExt bool + + for _, e := range SupportedExts { + if e == ext { + isValidExt = true + } + } + if !isValidExt { + return fmt.Errorf(unsupportedExtension, ext) + } + return nil +} + +// Conv converts the image file to tarExt format. +func Conv(path, tarExt string) error { + src := path + dst := path[:len(path)-len(filepath.Ext(path))] + tarExt + + log.Printf("Source file: %s", src) + log.Printf("Target file: %s", dst) + + sf, err := os.Open(src) + if err != nil { + return fmt.Errorf(canNotOpenImageFile, src) + } + defer sf.Close() + + df, err := os.Create(dst) + if err != nil { + return fmt.Errorf(canNotWriteImageFile, dst) + } + defer df.Close() + + img, _, err := image.Decode(sf) + if err != nil { + return err + } + + switch strings.ToLower(filepath.Ext(dst)) { + case ".png": + err = png.Encode(df, img) + case ".jpeg", ".jpg": + err = jpeg.Encode(df, img, &jpeg.Options{Quality: jpeg.DefaultQuality}) + case ".gif": + err = gif.Encode(df, img, &gif.Options{NumColors: 256, Quantizer: nil, Drawer: nil}) + case ".bmp": + err = bmp.Encode(df, img) + case ".tiff": + err = tiff.Encode(df, img, nil) + } + + if err != nil { + return fmt.Errorf(canNotWriteImageFile, dst) + } + + return nil +} diff --git a/kadai1/sadah/testdata/a/hero02.jpg b/kadai1/sadah/testdata/a/hero02.jpg new file mode 100644 index 0000000..55abfd8 Binary files /dev/null and b/kadai1/sadah/testdata/a/hero02.jpg differ diff --git a/kadai1/sadah/testdata/hero01.jpg b/kadai1/sadah/testdata/hero01.jpg new file mode 100644 index 0000000..e7e36c8 Binary files /dev/null and b/kadai1/sadah/testdata/hero01.jpg differ