diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b54a79f --- /dev/null +++ b/go.mod @@ -0,0 +1 @@ +module dojo6 diff --git a/kadai2/hisamura/README.md b/kadai2/hisamura/README.md new file mode 100644 index 0000000..d6f595a --- /dev/null +++ b/kadai2/hisamura/README.md @@ -0,0 +1,27 @@ + +### 標準パッケージでどのように使われているか + +fmt.Fprintでは以下のように記述されている。 +``` +func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + p := newPrinter() + p.doPrintf(format, a) + n, err = w.Write(p.buf) + p.free() + return +} +``` +同じファイル内のFprint内での呼び出しは以下。 + +``` +Fprintf(os.Stdout, format, a...) +``` + +io.writerのインターフェースはwriteをメソッドに持っている型なら引数に設定できるので、os.Stdoutを引数に指定し、呼び出している。 + +### io.Readerとio.Writerがあることでどういう利点があるのか具体例を挙げて考えてみる + +- コードが簡潔にできるために使用することができる。 +- interfaceがないと、それぞれの型を引数に指定し、複数の関数を作らなくてはいけない。 +- それがinterfaceを設定できることで、interfaceの関数は使用できることが約束される。 +- io.Readerの型を満たしているということはReadメソッドが使用できることが約束されており、引数にio.Readerを指定することで関数内でその引数の型の違いを気にすることなく、Readメソッドを使うことができ、シンプルにコードを記述することができる。 diff --git a/kadai2/hisamura/convert/convert.go b/kadai2/hisamura/convert/convert.go new file mode 100644 index 0000000..a2f9504 --- /dev/null +++ b/kadai2/hisamura/convert/convert.go @@ -0,0 +1,125 @@ +// Package convert is image conversion processing +// +// 画像変換処理。 +// 変換前と変換後の拡張子を指定し、画像変換を行います。 +// ディレクトリを選択し、再帰的にディレクトリ探索を行い変換します。 +// 変換前のファイルを削除したい場合は、remove(-r)フラグを指定します。 +package convert + +import ( + "fmt" + "image" + "image/gif" + "image/jpeg" + "image/png" + "io/ioutil" + "os" + "path/filepath" + "strings" +) + +// FlagOps is flag option +type FlagOps struct { + Dir string + Src string + Dest string + Remove bool +} + +// FileDetails have Extionsion and FileName info +type FileDetails struct { + Extension string + FileName string +} + +// Convert is image conversion processing +func Convert(flagOps FlagOps) (string, error) { + + files := dirwalk(flagOps.Dir) + var convertedName string + + for _, file := range files { + fileDetails := FileDetails{ + filepath.Ext(file), + filepath.Clean(file), + } + + if strings.HasSuffix(fileDetails.Extension, flagOps.Src) { + + decodeImg, err := decodeImage(fileDetails) + if err != nil { + return "", err + } + + if flagOps.Remove { + defer os.Remove(fileDetails.FileName) + } + + fileNameRemoveExt := strings.Replace(fileDetails.FileName, flagOps.Src, "", 1) + dstFile, err := os.Create(fmt.Sprintf(fileNameRemoveExt + flagOps.Dest)) + if err != nil { + return "", err + } + convertedName = dstFile.Name() + defer dstFile.Close() + + err = encodeImage(decodeImg, flagOps, dstFile) + if err != nil { + return "", err + } + } + } + return convertedName, nil + +} + +func decodeImage(fileDetails FileDetails) (image.Image, error) { + srcFile, err := os.Open(fileDetails.FileName) + + defer srcFile.Close() + + if err != nil { + return nil, err + } + + decodeImg, _, err := image.Decode(srcFile) + return decodeImg, err +} + +func encodeImage(decodeImg image.Image, flagOps FlagOps, dstFile *os.File) error { + switch flagOps.Dest { + case "jpeg", "jpg": + err := jpeg.Encode(dstFile, decodeImg, nil) + return err + + case "gif": + err := gif.Encode(dstFile, decodeImg, nil) + return err + + case "png": + err := png.Encode(dstFile, decodeImg) + return err + + default: + return fmt.Errorf("Error: invalid extension") + } +} + +//指定したディレクトリ配下のファイルを取得 +func dirwalk(dir string) []string { + files, err := ioutil.ReadDir(dir) + if err != nil { + panic(err) + } + + var paths []string + for _, file := range files { + if file.IsDir() { + paths = append(paths, dirwalk(filepath.Join(dir, file.Name()))...) + continue + } + paths = append(paths, filepath.Join(dir, file.Name())) + } + + return paths +} diff --git a/kadai2/hisamura/convert/convert_test.go b/kadai2/hisamura/convert/convert_test.go new file mode 100644 index 0000000..39f23f5 --- /dev/null +++ b/kadai2/hisamura/convert/convert_test.go @@ -0,0 +1,70 @@ +package convert + +import ( + "testing" +) + +type testImage struct { + name string + src string + dest string + expected string +} + +func TestSuccess(t *testing.T) { + + cases := []testImage{ + testImage{"jpgToGif", "jpg", "gif", "../testdata/test.gif"}, + testImage{"jpgToGif", "jpg", "gif", "../testdata/test.gif"}, + testImage{"jpgToPng", "jpg", "png", "../testdata/test.png"}, + testImage{"gifToJpg", "gif", "jpg", "../testdata/test.jpg"}, + testImage{"gifToPng", "gif", "png", "../testdata/test.png"}, + testImage{"pngToGif", "png", "gif", "../testdata/test.gif"}, + testImage{"pngToJpg", "gif", "png", "../testdata/test.png"}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + testSuccessConvert(t, c) + }) + } +} + +func testSuccessConvert(t *testing.T, c testImage) { + t.Helper() + myflag := FlagOps{"../testdata", c.src, c.dest, false} + convertedNmae, err := Convert(myflag) + + if convertedNmae != c.expected { + t.Fatal("differnt name") + } + if err != nil { + t.Fatal(err) + } +} + +func TestFail(t *testing.T) { + cases := []testImage{ + testImage{"jpgToGif", "jpg", "img", "../testdata/test.img"}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + testFailConvert(t, c) + }) + } + +} + +func testFailConvert(t *testing.T, c testImage) { + t.Helper() + myflag := FlagOps{"../testdata", c.src, c.dest, false} + convertedNmae, err := Convert(myflag) + + if convertedNmae != "" { + t.Fatal("not empty name") + } + if err == nil { + t.Fatal("err nil") + } +} diff --git a/kadai2/hisamura/go.mod b/kadai2/hisamura/go.mod new file mode 100644 index 0000000..f1175e9 --- /dev/null +++ b/kadai2/hisamura/go.mod @@ -0,0 +1 @@ +module dojo6/kadai2/hisamura diff --git a/kadai2/hisamura/main.go b/kadai2/hisamura/main.go new file mode 100644 index 0000000..4358d83 --- /dev/null +++ b/kadai2/hisamura/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "dojo6/kadai2/hisamura/convert" + "flag" + "fmt" +) + +func main() { + + var ( + dir = flag.String("dir", "./", "変換したいディレクトリ配下") + src = flag.String("s", "png", "変換前の拡張子") + dest = flag.String("d", "jpg", "変換後の拡張子") + remove = flag.Bool("r", false, "変換前の拡張子ファイルを削除するかのflag") + ) + + flag.Parse() + + flagOps := convert.FlagOps{Dir: *dir, Src: *src, Dest: *dest, Remove: *remove} + + result := validation(flagOps) + if result { + fmt.Print("invalid extension") + return + } + + convertFile, err := convert.Convert(flagOps) + + if err != nil { + fmt.Println(err) + } + + fmt.Printf("convert to %v", convertFile) +} + +func validation(flagOps convert.FlagOps) bool { + if flagOps.Dest != "png" && flagOps.Dest != "jpg" && flagOps.Dest != "jpeg" && flagOps.Dest != "gif" { + return true + } + return false +} diff --git a/kadai2/hisamura/testdata/test.gif b/kadai2/hisamura/testdata/test.gif new file mode 100644 index 0000000..cdf4964 Binary files /dev/null and b/kadai2/hisamura/testdata/test.gif differ diff --git a/kadai2/hisamura/testdata/test.img b/kadai2/hisamura/testdata/test.img new file mode 100644 index 0000000..e69de29 diff --git a/kadai2/hisamura/testdata/test.jpg b/kadai2/hisamura/testdata/test.jpg new file mode 100644 index 0000000..ff8b172 Binary files /dev/null and b/kadai2/hisamura/testdata/test.jpg differ diff --git a/kadai2/hisamura/testdata/test.png b/kadai2/hisamura/testdata/test.png new file mode 100644 index 0000000..99b6912 Binary files /dev/null and b/kadai2/hisamura/testdata/test.png differ