diff --git a/kadai1/.gitignore b/kadai1/.gitignore new file mode 100644 index 00000000..808d8dca --- /dev/null +++ b/kadai1/.gitignore @@ -0,0 +1,2 @@ +image-converter +images/** \ No newline at end of file diff --git a/kadai1/README.md b/kadai1/README.md new file mode 100644 index 00000000..b5cf9506 --- /dev/null +++ b/kadai1/README.md @@ -0,0 +1,44 @@ +# 画像変換コマンド + +## 次の仕様を満たすコマンドを作ってください + +- ディレクトリを指定する +- 指定したディレクトリ以下の JPG ファイルを PNG に変換(デフォルト) +- ディレクトリ以下は再帰的に処理する +- 変換前と変換後の画像形式を指定できる(オプション) + +## 以下を満たすように開発してください + +- main パッケージと分離する +- 自作パッケージと標準パッケージと準標準パッケージのみ使う +- ユーザ定義型を作ってみる +- GoDoc を生成してみる +- Go Modules を使ってみる + +## 使い方 + +### ビルドして使う場合 + +- ビルド + +``` +go build -o image-converter +``` + +- 例 + +``` +image-converter -d sample -f .png -t .jpg +``` + +### ビルドしない場合 + +``` +go run main.go -d [ディレクトリ] -f [変換前の拡張子] -t [変換後の拡張子] +``` + +- 例 + +``` +go run main.go -d sample -f .png -t .jpg +``` diff --git a/kadai1/cli/cli.go b/kadai1/cli/cli.go new file mode 100644 index 00000000..52f519ee --- /dev/null +++ b/kadai1/cli/cli.go @@ -0,0 +1,96 @@ +package cli + +import ( + "flag" + "fmt" + + "io" + "io/fs" + "path/filepath" + + "github.com/kynefuk/gopherdojo-studyroom/kadai1/converter" +) + +// Exit codes are values that represents an exit code +const ( + ExitOk = 0 + ExitErr = 1 +) + +// CLI is a struct of cli +type CLI struct { + OutStream io.Writer + ErrStream io.Writer +} + +// Run is a main func of CLI +func (c *CLI) Run(args []string) int { + var ( + targetDir string + fromExt string + toExt string + ) + + flag.CommandLine.SetOutput(c.ErrStream) + flag.Usage = func() { + fmt.Fprintf(c.ErrStream, helpText) + } + + flag.StringVar(&targetDir, "d", "", "specify target directory") + flag.StringVar(&fromExt, "f", converter.ExtJPG, "specify \"fromExt\"") + flag.StringVar(&toExt, "t", converter.ExtPNG, "specify \"toExt\"") + + flag.Parse() + + if ok := converter.IsConvertible(fromExt); !ok { + fmt.Printf("fromExt format is not convertible: %v\n", fromExt) + return ExitErr + } + + if ok := converter.IsConvertible(toExt); !ok { + fmt.Printf("toExt format is not convertible: %v\n", toExt) + return ExitErr + } + + // walking directory to collect target image files + var targetFiles []string + err := filepath.WalkDir(targetDir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + fmt.Printf("prevent panic by handling failure accessing a path %q, error: %v\n", path, err) + return err + } + + if ext := filepath.Ext(path); ext == fromExt { + targetFiles = append(targetFiles, path) + return nil + } + return nil + }) + + if err != nil { + fmt.Printf("failed to walk dir: %v\n", err) + return ExitErr + } + + // converting + for _, f := range targetFiles { + con := converter.Converter{FromExt: fromExt, ToExt: toExt, TargetFilePath: f} + + if err := con.Convert(); err != nil { + fmt.Printf("failed to convert: %v\n", err) + continue + } + } + return ExitOk +} + +var helpText = `Usage: image-convert [options...] +image-convert +Options: +-d + specify target dir in which images will be converted +-f + specify image ext which convert from +-t + specify image ext which convert to +` diff --git a/kadai1/converter/converter.go b/kadai1/converter/converter.go new file mode 100644 index 00000000..80d645bf --- /dev/null +++ b/kadai1/converter/converter.go @@ -0,0 +1,92 @@ +package converter + +import ( + "fmt" + "image" + "image/gif" + "image/jpeg" + "image/png" + "os" + "strings" +) + +// Ext codes represents convertible file ext +const ( + ExtJPG = ".jpg" + ExtJPEG = ".jpeg" + ExtPNG = ".png" + ExtGIF = ".gif" +) + +// IsConvertible check that specified ext is convertible +func IsConvertible(ext string) bool { + switch ext { + case ExtJPG, ExtJPEG, ExtPNG, ExtGIF: + return true + default: + return false + } +} + +// Converter is ... +type Converter struct { + FromExt string + ToExt string + TargetFilePath string +} + +// Convert is a main func of image convert +func (c *Converter) Convert() error { + var err error + + reader, err := os.Open(c.TargetFilePath) + if err != nil { + fmt.Printf("failed to open file: %v. error: %v", c.TargetFilePath, err) + return err + } + defer reader.Close() + + // decode + var img image.Image + + switch c.FromExt { + case ExtJPG, ExtJPEG: + img, err = jpeg.Decode(reader) + case ExtPNG: + img, err = png.Decode(reader) + case ExtGIF: + img, err = gif.Decode(reader) + } + + if err != nil { + fmt.Printf("failed to decode: %v\n", err) + return err + } + + // create dist file + fileName := strings.Replace(c.TargetFilePath, c.FromExt, c.ToExt, 1) + dist, err := os.Create(fileName) + if err != nil { + return err + } + defer func() { + if err := dist.Close(); err != nil { + fmt.Printf("failed to close file: %v\n", err) + } + }() + + // encode + switch c.ToExt { + case ExtJPG, ExtJPEG: + err = jpeg.Encode(dist, img, &jpeg.Options{}) + case ExtPNG: + err = png.Encode(dist, img) + case ExtGIF: + err = gif.Encode(dist, img, &gif.Options{}) + } + + if err != nil { + return err + } + return nil +} diff --git a/kadai1/go.mod b/kadai1/go.mod new file mode 100644 index 00000000..0e6642be --- /dev/null +++ b/kadai1/go.mod @@ -0,0 +1,3 @@ +module github.com/kynefuk/gopherdojo-studyroom/kadai1 + +go 1.17 diff --git a/kadai1/main.go b/kadai1/main.go new file mode 100644 index 00000000..12f9b267 --- /dev/null +++ b/kadai1/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "os" + + "github.com/kynefuk/gopherdojo-studyroom/kadai1/cli" +) + +func main() { + cli := cli.CLI{ + OutStream: os.Stdout, + ErrStream: os.Stderr, + } + os.Exit(cli.Run(os.Args)) +}