diff --git a/kadai2/sminamot/README.md b/kadai2/sminamot/README.md new file mode 100644 index 0000000..f60f831 --- /dev/null +++ b/kadai2/sminamot/README.md @@ -0,0 +1,16 @@ +# 画像変換コマンドの作成 + +## Usage +``` +$ ./main [OPTIONS] TARGET_DIR +``` +OPTIONS +``` + -dst string + output format (default "png") + -src string + input format (default "jpg") +``` + +## その他 +* "jpg", "png", "gif"のみ対応 diff --git a/kadai2/sminamot/convert/convert.go b/kadai2/sminamot/convert/convert.go new file mode 100644 index 0000000..50703c3 --- /dev/null +++ b/kadai2/sminamot/convert/convert.go @@ -0,0 +1,93 @@ +package convert + +import ( + "errors" + "fmt" + "image" + "image/gif" + "image/jpeg" + "image/png" + "os" + "path/filepath" + "strings" +) + +var ( + ErrWrongInputSrcExtention = errors.New("src extension is wrong") + ErrWrongInputDstExtention = errors.New("dst extension is wrong") +) + +// A Conversion represents an conversion object that includes target file paths to convert and input/output formats +type Conversion struct { + Files []string + Src string + Dst string +} + +// New returns a new Conversion that includes target file paths to convert and input/output formats +func New(dir, src, dst string) (*Conversion, error) { + if !validateExtension(src) { + return nil, ErrWrongInputSrcExtention + } + if !validateExtension(dst) { + return nil, ErrWrongInputDstExtention + } + + files := []string{} + err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if filepath.Ext(path) == "."+src { + files = append(files, path) + } + return nil + }) + if err != nil { + return nil, err + } + + return &Conversion{ + Files: files, + Src: src, + Dst: dst, + }, nil +} + +func validateExtension(e string) bool { + return (e == "jpg" || e == "png" || e == "gif") +} + +func filename(f, src, dst string) string { + return strings.TrimSuffix(f, src) + dst +} + +// Convert executes image conversion +func (c *Conversion) Convert() { + for _, v := range c.Files { + sf, err := os.Open(v) + if err != nil { + fmt.Fprintln(os.Stderr, err) + continue + } + defer sf.Close() + img, _, err := image.Decode(sf) + if err != nil { + fmt.Fprintln(os.Stderr, err) + continue + } + + df, err := os.Create(filename(v, c.Src, c.Dst)) + if err != nil { + fmt.Fprintln(os.Stderr, err) + continue + } + defer df.Close() + + switch c.Dst { + case "jpg": + jpeg.Encode(df, img, nil) + case "gif": + gif.Encode(df, img, nil) + case "png": + png.Encode(df, img) + } + } +} diff --git a/kadai2/sminamot/convert/convert_test.go b/kadai2/sminamot/convert/convert_test.go new file mode 100644 index 0000000..5b82dd7 --- /dev/null +++ b/kadai2/sminamot/convert/convert_test.go @@ -0,0 +1,112 @@ +package convert + +import ( + "sort" + "testing" + + "github.com/google/go-cmp/cmp" +) + +type testNewStruct struct { + name string + inputDir string + inputSrc string + inputDst string + wantErr error + wantConversion *Conversion +} + +func TestNew(t *testing.T) { + tests := []testNewStruct{ + { + name: "srcが想定外", + inputDir: "./testdata", + inputSrc: "txt", + inputDst: "png", + wantErr: ErrWrongInputSrcExtention, + wantConversion: nil, + }, + { + name: "dstが想定外", + inputDir: "./testdata", + inputSrc: "png", + inputDst: "txt", + wantErr: ErrWrongInputDstExtention, + wantConversion: nil, + }, + { + name: "png->jpg", + inputDir: "./testdata", + inputSrc: "png", + inputDst: "jpg", + wantErr: nil, + wantConversion: &Conversion{ + Files: []string{"testdata/talks.png", "testdata/subdir/ref.png"}, + Src: "png", + Dst: "jpg", + }, + }, + } + + // sort Files + // https://godoc.org/github.com/google/go-cmp/cmp#example-Option--SortedSlice + trans := cmp.Transformer("Sort", func(in []string) []string { + out := append([]string(nil), in...) + sort.Strings(out) + return out + }) + + for _, tt := range tests { + testNew(t, tt, trans) + } +} + +func testNew(t *testing.T, tt testNewStruct, tr cmp.Option) { + t.Helper() + ret, err := New(tt.inputDir, tt.inputSrc, tt.inputDst) + if err != tt.wantErr { + t.Errorf(`%s: New("%s", "%s", "%s") => error:%v, want error: %v`, tt.name, tt.inputDir, tt.inputSrc, tt.inputDst, err, tt.wantErr) + } + diff := cmp.Diff(ret, tt.wantConversion, tr) + if diff != "" { + t.Errorf(`%s: New("%s", "%s", "%s"): Conversion diff:%s`, tt.name, tt.inputDir, tt.inputSrc, tt.inputDst, diff) + } +} + +func TestValidateExtension(t *testing.T) { + tests := []struct { + input string + want bool + }{ + {input: "jpg", want: true}, + {input: "png", want: true}, + {input: "gif", want: true}, + {input: "txt", want: false}, + } + + for _, tt := range tests { + ret := validateExtension(tt.input) + if ret != tt.want { + t.Errorf(`validateExtension("%s") => %v, want %v`, tt.input, ret, tt.want) + } + } +} + +func TestFilename(t *testing.T) { + tests := []struct { + inputName string + inputSrc string + inputDst string + want string + }{ + {inputName: "testdata/talks.png", inputSrc: "png", inputDst: "jpg", want: "testdata/talks.jpg"}, + {inputName: "testdata/subdir/ref.png", inputSrc: "png", inputDst: "gif", want: "testdata/subdir/ref.gif"}, + } + + for _, tt := range tests { + ret := filename(tt.inputName, tt.inputSrc, tt.inputDst) + if ret != tt.want { + t.Errorf(`filename("%s", "%s", "%s") => %s, want %s`, tt.inputName, tt.inputSrc, tt.inputDst, ret, tt.want) + } + } +} diff --git a/kadai2/sminamot/convert/testdata/fiveyears.jpg b/kadai2/sminamot/convert/testdata/fiveyears.jpg new file mode 100644 index 0000000..df10648 Binary files /dev/null and b/kadai2/sminamot/convert/testdata/fiveyears.jpg differ diff --git a/kadai2/sminamot/convert/testdata/subdir/ref.png b/kadai2/sminamot/convert/testdata/subdir/ref.png new file mode 100644 index 0000000..0508f6e Binary files /dev/null and b/kadai2/sminamot/convert/testdata/subdir/ref.png differ diff --git a/kadai2/sminamot/convert/testdata/talks.png b/kadai2/sminamot/convert/testdata/talks.png new file mode 100644 index 0000000..589db47 Binary files /dev/null and b/kadai2/sminamot/convert/testdata/talks.png differ diff --git a/kadai2/sminamot/main.go b/kadai2/sminamot/main.go new file mode 100644 index 0000000..e532f4a --- /dev/null +++ b/kadai2/sminamot/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "flag" + "log" + + "github.com/sminamot/dojo4/kadai1/sminamot/convert" +) + +func main() { + var src, dst string + + flag.StringVar(&src, "src", "jpg", "input format") + flag.StringVar(&dst, "dst", "png", "output format") + flag.Parse() + if len(flag.Args()) == 0 { + log.Fatal("need to specify target dir") + } + dir := flag.Args()[0] + + c, err := convert.New(dir, src, dst) + if err != nil { + log.Fatal(err) + } + c.Convert() +}