From 2c0ce8ae5a34048ea30da9151ab3691a4439ff4d Mon Sep 17 00:00:00 2001 From: tsuchinaga Date: Mon, 6 Jul 2020 15:07:22 +0900 Subject: [PATCH 01/13] =?UTF-8?q?=E8=AA=B2=E9=A1=8C1=E3=81=AE=E5=AE=9F?= =?UTF-8?q?=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai1/tsuchinaga/.idea/$CACHE_FILE$ | 6 + kadai1/tsuchinaga/.idea/.gitignore | 8 ++ .../inspectionProfiles/Project_Default.xml | 20 +++ kadai1/tsuchinaga/.idea/misc.xml | 84 ++++++++++++ kadai1/tsuchinaga/.idea/modules.xml | 8 ++ kadai1/tsuchinaga/.idea/tsuchinaga.iml | 9 ++ kadai1/tsuchinaga/.idea/vcs.xml | 6 + kadai1/tsuchinaga/.idea/watcherTasks.xml | 29 ++++ kadai1/tsuchinaga/README.md | 41 ++++++ kadai1/tsuchinaga/conv/converter.go | 125 ++++++++++++++++++ kadai1/tsuchinaga/go.mod | 3 + kadai1/tsuchinaga/main.go | 35 +++++ kadai1/tsuchinaga/testdata/Example.jpg | Bin 0 -> 9022 bytes kadai1/tsuchinaga/testdata/Example.png | Bin 0 -> 2335 bytes .../tsuchinaga/testdata/subdir1/Example.jpg | Bin 0 -> 9022 bytes .../tsuchinaga/testdata/subdir1/Example.png | Bin 0 -> 2335 bytes .../testdata/subdir1/subdir1_1/Example.jpg | Bin 0 -> 9022 bytes .../subdir1/subdir1_1/subdir1_1_1/Example.png | Bin 0 -> 2335 bytes .../testdata/subdir2/subdir2_1/Example.jpg | Bin 0 -> 9022 bytes .../testdata/subdir2/subdir2_1/Example.png | Bin 0 -> 2335 bytes 20 files changed, 374 insertions(+) create mode 100644 kadai1/tsuchinaga/.idea/$CACHE_FILE$ create mode 100644 kadai1/tsuchinaga/.idea/.gitignore create mode 100644 kadai1/tsuchinaga/.idea/inspectionProfiles/Project_Default.xml create mode 100644 kadai1/tsuchinaga/.idea/misc.xml create mode 100644 kadai1/tsuchinaga/.idea/modules.xml create mode 100644 kadai1/tsuchinaga/.idea/tsuchinaga.iml create mode 100644 kadai1/tsuchinaga/.idea/vcs.xml create mode 100644 kadai1/tsuchinaga/.idea/watcherTasks.xml create mode 100644 kadai1/tsuchinaga/README.md create mode 100644 kadai1/tsuchinaga/conv/converter.go create mode 100644 kadai1/tsuchinaga/go.mod create mode 100644 kadai1/tsuchinaga/main.go create mode 100644 kadai1/tsuchinaga/testdata/Example.jpg create mode 100644 kadai1/tsuchinaga/testdata/Example.png create mode 100644 kadai1/tsuchinaga/testdata/subdir1/Example.jpg create mode 100644 kadai1/tsuchinaga/testdata/subdir1/Example.png create mode 100644 kadai1/tsuchinaga/testdata/subdir1/subdir1_1/Example.jpg create mode 100644 kadai1/tsuchinaga/testdata/subdir1/subdir1_1/subdir1_1_1/Example.png create mode 100644 kadai1/tsuchinaga/testdata/subdir2/subdir2_1/Example.jpg create mode 100644 kadai1/tsuchinaga/testdata/subdir2/subdir2_1/Example.png diff --git a/kadai1/tsuchinaga/.idea/$CACHE_FILE$ b/kadai1/tsuchinaga/.idea/$CACHE_FILE$ new file mode 100644 index 0000000..6cb8985 --- /dev/null +++ b/kadai1/tsuchinaga/.idea/$CACHE_FILE$ @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/kadai1/tsuchinaga/.idea/.gitignore b/kadai1/tsuchinaga/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/kadai1/tsuchinaga/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/kadai1/tsuchinaga/.idea/inspectionProfiles/Project_Default.xml b/kadai1/tsuchinaga/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..e62755d --- /dev/null +++ b/kadai1/tsuchinaga/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/kadai1/tsuchinaga/.idea/misc.xml b/kadai1/tsuchinaga/.idea/misc.xml new file mode 100644 index 0000000..7f8d96a --- /dev/null +++ b/kadai1/tsuchinaga/.idea/misc.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kadai1/tsuchinaga/.idea/modules.xml b/kadai1/tsuchinaga/.idea/modules.xml new file mode 100644 index 0000000..37d8c23 --- /dev/null +++ b/kadai1/tsuchinaga/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/kadai1/tsuchinaga/.idea/tsuchinaga.iml b/kadai1/tsuchinaga/.idea/tsuchinaga.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/kadai1/tsuchinaga/.idea/tsuchinaga.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/kadai1/tsuchinaga/.idea/vcs.xml b/kadai1/tsuchinaga/.idea/vcs.xml new file mode 100644 index 0000000..b2bdec2 --- /dev/null +++ b/kadai1/tsuchinaga/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/kadai1/tsuchinaga/.idea/watcherTasks.xml b/kadai1/tsuchinaga/.idea/watcherTasks.xml new file mode 100644 index 0000000..97ad6d2 --- /dev/null +++ b/kadai1/tsuchinaga/.idea/watcherTasks.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/kadai1/tsuchinaga/README.md b/kadai1/tsuchinaga/README.md new file mode 100644 index 0000000..d774351 --- /dev/null +++ b/kadai1/tsuchinaga/README.md @@ -0,0 +1,41 @@ +# 課題1 tsuchinaga + +## 要件 + +### 次の仕様を満たすコマンドを作って下さい +* ディレクトリを指定する +* 指定したディレクトリ以下のJPGファイルをPNGに変換(デフォルト) +* ディレクトリ以下は再帰的に処理する +* 変換前と変換後の画像形式を指定できる(オプション) + +### 以下を満たすように開発してください +* mainパッケージと分離する +* 自作パッケージと標準パッケージと準標準パッケージのみ使う + * 準標準パッケージ:golang.org/x以下のパッケージ +* ユーザ定義型を作ってみる +* GoDocを生成してみる +* Go Modulesを使ってみる + +## TODO +* [x] コマンドラインで実行できる +* [x] パラメータでディレクトリを指定できる + * [x] ディレクトリの指定がない場合はディレクトリを指定するようにメッセージを出して終了 + * [x] ディレクトリではなくファイルを指定されたらディレクトリを指定するようメッセージを出して終了 + * [x] 指定されたディレクトリ、ファイルが存在しない場合は存在しない旨をメッセージを出して終了 +* [x] 変換元の画像形式が指定できる + * [x] 指定しなかった場合はJPG + * [x] 指定可能な画像形式はJPG、PNG + * [x] 指定可能でない画像形式が指定されたら指定できる画像形式をメッセージで出して終了 +* [x] 変換後の画像形式が指定できる + * [x] 指定しなかった場合はPNG + * [x] 指定可能な画像形式はJPG、PNG + * [x] 指定可能でない画像形式が指定されたら指定できる画像形式をメッセージで出して終了 + * [x] 変換元と同じ画像形式を指定されたら違い画像形式を選ぶようメッセージを出して終了 +* [x] 指定されたディレクトリの配下にある変換元で指定された画像形式の画像を、変換後で指定された画像形式の画像に変換する + * [x] 指定されたディレクトリの配下にサブディレクトリがあればサブディレクトリ内の画像に対しても変換する + * [x] サブディレクトリ内のディレクトリに対しても同様に処理する = 指定されたディレクトリ配下に対して再帰的に実行する + * [x] 指定されたディレクトリ配下に画像がない、もしくは指定された画像形式のファイルがなければ何もしない +* [x] 画像の変換ができる + * [x] 変換元画像が開けなかったらその旨を表示して次のファイルに進む + * [x] 変換後の画像が作れなかったらその旨を表示して次のファイルに進む + * [x] 変換出来たら変換前のパスと変換後のパスを表示して次のがいるに進む diff --git a/kadai1/tsuchinaga/conv/converter.go b/kadai1/tsuchinaga/conv/converter.go new file mode 100644 index 0000000..1451f4d --- /dev/null +++ b/kadai1/tsuchinaga/conv/converter.go @@ -0,0 +1,125 @@ +package conv + +import ( + "fmt" + "image" + "image/jpeg" + "image/png" + "io/ioutil" + "log" + "os" + "path/filepath" +) + +var validFileTypes = []string{"jpeg", "png"} + +// IsValidFileType - 指定されたファイルタイプが利用可能かを返す +func IsValidFileType(fileType string) bool { + for _, t := range validFileTypes { + if t == fileType { + return true + } + } + return false +} + +// IsDir - pathがディレクトリかどうか +func IsDir(path string) bool { + _, err := ioutil.ReadDir(path) + return err == nil +} + +// ExecConvert - 変換の実行 +func ExecConvert(dir, src, dest string) { + c := converter{ + dirList: []string{dir}, + srcFileType: src, + destFileType: dest, + } + c.exec() +} + +// converter - 変換機能の実装 +type converter struct { + dirList []string + srcFileType string + destFileType string +} + +// exec - ディレクトリをたどりながら変換を実行 +func (c *converter) exec() { + for len(c.dirList) > 0 { + dirPath := c.dirList[0] + c.dirList = c.dirList[1:] + + files, err := ioutil.ReadDir(dirPath) + if err != nil { + log.Printf("ディレクトリ: %sが読み込めなかったためスキップします\n", dirPath) + continue + } + + for _, file := range files { + path := filepath.Join(dirPath, file.Name()) + if file.IsDir() { + c.dirList = append(c.dirList, path) + } else { + c.convert(path) + } + } + } +} + +// convert - 変換処理 +func (c converter) convert(path string) { + f, err := os.Open(path) + if err != nil { // 開けない + log.Printf("ファイル: %sが開けなかったためスキップします\n", path) + return + } + defer f.Close() + + if getFileType(path) == c.srcFileType { + img, _, err := image.Decode(f) + if err != nil { + log.Printf("ファイル: %sが読み込めなかったためスキップします\n", path) + return + } + + newFilePath := fmt.Sprintf("%s.%s", path, c.destFileType) + o, err := os.Create(newFilePath) + if err != nil { + log.Printf("変換後ファイル: %sが作成できなかったためスキップします\n", newFilePath) + return + } + defer o.Close() + + err = nil + switch c.destFileType { + case "jpeg": + err = jpeg.Encode(o, img, nil) + + case "png": + err = png.Encode(o, img) + } + if err != nil { + log.Printf("ファイル: %sの変換に失敗しました\n", path) + } else { + log.Printf("%s => %s\n", path, newFilePath) + } + } +} + +// getFileType - 画像ファイルの型を得る +func getFileType(path string) string { + f, err := os.Open(path) + if err != nil { // 開けない + return "" + } + defer f.Close() + + _, format, err := image.DecodeConfig(f) + if err != nil { // 画像じゃない + return "" + } + return format +} diff --git a/kadai1/tsuchinaga/go.mod b/kadai1/tsuchinaga/go.mod new file mode 100644 index 0000000..84868fb --- /dev/null +++ b/kadai1/tsuchinaga/go.mod @@ -0,0 +1,3 @@ +module github.com/gopherdojo/dojo8/kadai1/tsuchinaga + +go 1.14 diff --git a/kadai1/tsuchinaga/main.go b/kadai1/tsuchinaga/main.go new file mode 100644 index 0000000..984d51c --- /dev/null +++ b/kadai1/tsuchinaga/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "flag" + "github.com/gopherdojo/dojo8/kadai1/tsuchinaga/conv" + "log" +) + +func main() { + var dir, src, dest string + flag.StringVar(&dir, "dir", "", "変換する画像のあるディレクトリ") + flag.StringVar(&src, "src", "jpeg", "optional 変換元の画像形式 jpeg|png") + flag.StringVar(&dest, "dest", "png", "optional 変換後の画像形式 jpeg|png") + flag.Parse() + + // validation + if dir == "" { + log.Fatalln("dirの指定は必須です") + } + if !conv.IsDir(dir) { + log.Fatalf("%sは存在しないかディレクトリではありません\n", dir) + } + if !conv.IsValidFileType(src) { + log.Fatalf("%sは許可されていない画像形式です\n", src) + } + if !conv.IsValidFileType(dest) { + log.Fatalf("%sは許可されていない画像形式です", dest) + } + if src == dest { + log.Fatalln("srcとdestで違う画像形式を選択してください") + } + + // 変換実行 + conv.ExecConvert(dir, src, dest) +} diff --git a/kadai1/tsuchinaga/testdata/Example.jpg b/kadai1/tsuchinaga/testdata/Example.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cacc09aa4804fcb30bae7ac36a2a59520e1d06e9 GIT binary patch literal 9022 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 Date: Tue, 7 Jul 2020 15:07:29 +0900 Subject: [PATCH 02/13] =?UTF-8?q?intellij=E9=96=A2=E9=80=A3=E3=81=AE?= =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92=E9=99=A4=E5=A4=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai1/tsuchinaga/.gitignore | 3 + kadai1/tsuchinaga/.idea/$CACHE_FILE$ | 6 -- kadai1/tsuchinaga/.idea/.gitignore | 8 -- .../inspectionProfiles/Project_Default.xml | 20 ----- kadai1/tsuchinaga/.idea/misc.xml | 84 ------------------- kadai1/tsuchinaga/.idea/modules.xml | 8 -- kadai1/tsuchinaga/.idea/tsuchinaga.iml | 9 -- kadai1/tsuchinaga/.idea/vcs.xml | 6 -- kadai1/tsuchinaga/.idea/watcherTasks.xml | 29 ------- 9 files changed, 3 insertions(+), 170 deletions(-) create mode 100644 kadai1/tsuchinaga/.gitignore delete mode 100644 kadai1/tsuchinaga/.idea/$CACHE_FILE$ delete mode 100644 kadai1/tsuchinaga/.idea/.gitignore delete mode 100644 kadai1/tsuchinaga/.idea/inspectionProfiles/Project_Default.xml delete mode 100644 kadai1/tsuchinaga/.idea/misc.xml delete mode 100644 kadai1/tsuchinaga/.idea/modules.xml delete mode 100644 kadai1/tsuchinaga/.idea/tsuchinaga.iml delete mode 100644 kadai1/tsuchinaga/.idea/vcs.xml delete mode 100644 kadai1/tsuchinaga/.idea/watcherTasks.xml diff --git a/kadai1/tsuchinaga/.gitignore b/kadai1/tsuchinaga/.gitignore new file mode 100644 index 0000000..d7340f0 --- /dev/null +++ b/kadai1/tsuchinaga/.gitignore @@ -0,0 +1,3 @@ +### for IntelliJ +.idea +*.iml diff --git a/kadai1/tsuchinaga/.idea/$CACHE_FILE$ b/kadai1/tsuchinaga/.idea/$CACHE_FILE$ deleted file mode 100644 index 6cb8985..0000000 --- a/kadai1/tsuchinaga/.idea/$CACHE_FILE$ +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/kadai1/tsuchinaga/.idea/.gitignore b/kadai1/tsuchinaga/.idea/.gitignore deleted file mode 100644 index 73f69e0..0000000 --- a/kadai1/tsuchinaga/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/kadai1/tsuchinaga/.idea/inspectionProfiles/Project_Default.xml b/kadai1/tsuchinaga/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index e62755d..0000000 --- a/kadai1/tsuchinaga/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - \ No newline at end of file diff --git a/kadai1/tsuchinaga/.idea/misc.xml b/kadai1/tsuchinaga/.idea/misc.xml deleted file mode 100644 index 7f8d96a..0000000 --- a/kadai1/tsuchinaga/.idea/misc.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/kadai1/tsuchinaga/.idea/modules.xml b/kadai1/tsuchinaga/.idea/modules.xml deleted file mode 100644 index 37d8c23..0000000 --- a/kadai1/tsuchinaga/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/kadai1/tsuchinaga/.idea/tsuchinaga.iml b/kadai1/tsuchinaga/.idea/tsuchinaga.iml deleted file mode 100644 index d6ebd48..0000000 --- a/kadai1/tsuchinaga/.idea/tsuchinaga.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/kadai1/tsuchinaga/.idea/vcs.xml b/kadai1/tsuchinaga/.idea/vcs.xml deleted file mode 100644 index b2bdec2..0000000 --- a/kadai1/tsuchinaga/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/kadai1/tsuchinaga/.idea/watcherTasks.xml b/kadai1/tsuchinaga/.idea/watcherTasks.xml deleted file mode 100644 index 97ad6d2..0000000 --- a/kadai1/tsuchinaga/.idea/watcherTasks.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - \ No newline at end of file From fffa8c533ff345727bd100e3682ad7df4c4df41d Mon Sep 17 00:00:00 2001 From: tsuchinaga Date: Wed, 8 Jul 2020 21:50:44 +0900 Subject: [PATCH 03/13] =?UTF-8?q?=E6=9C=89=E5=8A=B9=E3=81=AA=E3=83=95?= =?UTF-8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=BF=E3=82=A4=E3=83=97=E3=82=92?= =?UTF-8?q?map=E3=81=A7=E7=AE=A1=E7=90=86=E3=81=99=E3=82=8B=E3=81=93?= =?UTF-8?q?=E3=81=A8=E3=81=A7=E3=82=B7=E3=83=B3=E3=83=97=E3=83=AB=E3=81=AB?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF=E3=82=BF?= =?UTF-8?q?=E3=83=AA=E3=83=B3=E3=82=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai1/tsuchinaga/conv/converter.go | 9 ++------ kadai1/tsuchinaga/conv/converter_test.go | 29 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 kadai1/tsuchinaga/conv/converter_test.go diff --git a/kadai1/tsuchinaga/conv/converter.go b/kadai1/tsuchinaga/conv/converter.go index 1451f4d..0f6e595 100644 --- a/kadai1/tsuchinaga/conv/converter.go +++ b/kadai1/tsuchinaga/conv/converter.go @@ -11,16 +11,11 @@ import ( "path/filepath" ) -var validFileTypes = []string{"jpeg", "png"} +var validFileTypes = map[string]bool{"jpeg": true, "png": true} // IsValidFileType - 指定されたファイルタイプが利用可能かを返す func IsValidFileType(fileType string) bool { - for _, t := range validFileTypes { - if t == fileType { - return true - } - } - return false + return validFileTypes[fileType] } // IsDir - pathがディレクトリかどうか diff --git a/kadai1/tsuchinaga/conv/converter_test.go b/kadai1/tsuchinaga/conv/converter_test.go new file mode 100644 index 0000000..26eda9c --- /dev/null +++ b/kadai1/tsuchinaga/conv/converter_test.go @@ -0,0 +1,29 @@ +package conv + +import "testing" + +func TestIsValidFileType(t *testing.T) { + t.Parallel() + testTable := []struct { + desc string + arg string + expect bool + }{ + {desc: "jpegがtrueで返される", arg: "jpeg", expect: true}, + {desc: "pngがtrueで返される", arg: "png", expect: true}, + {desc: "jpgがfalseで返される", arg: "jpg", expect: false}, + {desc: "gifがfalseで返される", arg: "gif", expect: false}, + {desc: "空文字がfalseで返される", arg: "", expect: false}, + } + + for _, test := range testTable { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + actual := IsValidFileType(test.arg) + if test.expect != actual { + t.Errorf("%s 失敗\n期待: %v\n実際: %v\n", t.Name(), test.expect, actual) + } + }) + } +} From 0a52dfb8fee8c0165b4434acd1cb984763131aa1 Mon Sep 17 00:00:00 2001 From: tsuchinaga Date: Wed, 8 Jul 2020 21:50:52 +0900 Subject: [PATCH 04/13] =?UTF-8?q?go.sum=E8=BF=BD=E5=8A=A0=E3=81=97?= =?UTF-8?q?=E5=BF=98=E3=82=8C=E3=81=A6=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai1/tsuchinaga/go.sum | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 kadai1/tsuchinaga/go.sum diff --git a/kadai1/tsuchinaga/go.sum b/kadai1/tsuchinaga/go.sum new file mode 100644 index 0000000..e69de29 From 6a2ec4274bff8c417aadc3d468209395daa668ff Mon Sep 17 00:00:00 2001 From: tsuchinaga Date: Wed, 8 Jul 2020 22:12:06 +0900 Subject: [PATCH 05/13] =?UTF-8?q?=E3=83=87=E3=82=A3=E3=83=AC=E3=82=AF?= =?UTF-8?q?=E3=83=88=E3=83=AA=E3=81=8B=E3=81=AE=E3=83=81=E3=82=A7=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=82=92os.Stat=E3=82=92=E4=BD=BF=E3=81=86=E3=82=88?= =?UTF-8?q?=E3=81=86=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai1/tsuchinaga/conv/converter.go | 14 ++++++++--- kadai1/tsuchinaga/conv/converter_test.go | 32 +++++++++++++++++++++++- kadai1/tsuchinaga/go.mod | 2 ++ kadai1/tsuchinaga/go.sum | 2 ++ kadai1/tsuchinaga/main.go | 2 +- 5 files changed, 47 insertions(+), 5 deletions(-) diff --git a/kadai1/tsuchinaga/conv/converter.go b/kadai1/tsuchinaga/conv/converter.go index 0f6e595..8be9e51 100644 --- a/kadai1/tsuchinaga/conv/converter.go +++ b/kadai1/tsuchinaga/conv/converter.go @@ -2,6 +2,7 @@ package conv import ( "fmt" + "golang.org/x/xerrors" "image" "image/jpeg" "image/png" @@ -11,6 +12,10 @@ import ( "path/filepath" ) +var ( + FileStatError = xerrors.New("file stat error") +) + var validFileTypes = map[string]bool{"jpeg": true, "png": true} // IsValidFileType - 指定されたファイルタイプが利用可能かを返す @@ -19,9 +24,12 @@ func IsValidFileType(fileType string) bool { } // IsDir - pathがディレクトリかどうか -func IsDir(path string) bool { - _, err := ioutil.ReadDir(path) - return err == nil +func IsDir(path string) (bool, error) { + fi, err := os.Stat(path) + if err != nil { + return false, xerrors.Errorf("%+v: %w", err, FileStatError) + } + return fi.IsDir(), nil } // ExecConvert - 変換の実行 diff --git a/kadai1/tsuchinaga/conv/converter_test.go b/kadai1/tsuchinaga/conv/converter_test.go index 26eda9c..17d2fb8 100644 --- a/kadai1/tsuchinaga/conv/converter_test.go +++ b/kadai1/tsuchinaga/conv/converter_test.go @@ -1,6 +1,9 @@ package conv -import "testing" +import ( + "errors" + "testing" +) func TestIsValidFileType(t *testing.T) { t.Parallel() @@ -27,3 +30,30 @@ func TestIsValidFileType(t *testing.T) { }) } } + +func TestIsDir(t *testing.T) { + t.Parallel() + testTable := []struct { + desc string + arg string + expect1 bool + expect2 error + }{ + {desc: "ディレクトリを指定したらtrue", arg: "../testdata", expect1: true}, + {desc: "ファイルを指定したらfalse", arg: "../testdata/Example.jpg", expect1: false}, + {desc: "存在しないパスを指定したらエラー", arg: "./testdata/NotFound.jpg", expect2: FileStatError}, + {desc: "空文字を指定したらエラー", arg: "", expect2: FileStatError}, + // {desc: "不良セクタを指定したらfalse", arg: "", expect1: false}, // やり方わからない + } + + for _, test := range testTable { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + actual1, actual2 := IsDir(test.arg) + if test.expect1 != actual1 || !errors.Is(actual2, test.expect2) { + t.Errorf("%s 失敗\n期待: %v, %v\n実際: %v, %v\n", t.Name(), test.expect1, test.expect2, actual1, actual2) + } + }) + } +} diff --git a/kadai1/tsuchinaga/go.mod b/kadai1/tsuchinaga/go.mod index 84868fb..0c22f52 100644 --- a/kadai1/tsuchinaga/go.mod +++ b/kadai1/tsuchinaga/go.mod @@ -1,3 +1,5 @@ module github.com/gopherdojo/dojo8/kadai1/tsuchinaga go 1.14 + +require golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 diff --git a/kadai1/tsuchinaga/go.sum b/kadai1/tsuchinaga/go.sum index e69de29..3ab73ea 100644 --- a/kadai1/tsuchinaga/go.sum +++ b/kadai1/tsuchinaga/go.sum @@ -0,0 +1,2 @@ +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/kadai1/tsuchinaga/main.go b/kadai1/tsuchinaga/main.go index 984d51c..a7748d2 100644 --- a/kadai1/tsuchinaga/main.go +++ b/kadai1/tsuchinaga/main.go @@ -17,7 +17,7 @@ func main() { if dir == "" { log.Fatalln("dirの指定は必須です") } - if !conv.IsDir(dir) { + if isDir, err := conv.IsDir(dir); err != nil || !isDir { log.Fatalf("%sは存在しないかディレクトリではありません\n", dir) } if !conv.IsValidFileType(src) { From 943bc4bc63ca1b282a180b3dfd8e34f92520120d Mon Sep 17 00:00:00 2001 From: tsuchinaga Date: Wed, 8 Jul 2020 22:13:29 +0900 Subject: [PATCH 06/13] =?UTF-8?q?=E3=83=91=E3=83=83=E3=82=B1=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E5=90=8D=E3=80=81=E5=AE=9F=E8=A1=8C=E9=96=A2=E6=95=B0?= =?UTF-8?q?=E3=81=AE=E5=90=8D=E5=89=8D=E3=82=92=E5=A4=89=E6=9B=B4=E3=81=97?= =?UTF-8?q?=E3=80=81=E6=84=8F=E5=91=B3=E3=81=AE=E9=87=8D=E8=A4=87=E3=82=92?= =?UTF-8?q?=E5=9B=9E=E9=81=BF=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai1/tsuchinaga/{conv => imgconv}/converter.go | 6 +++--- kadai1/tsuchinaga/{conv => imgconv}/converter_test.go | 2 +- kadai1/tsuchinaga/main.go | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) rename kadai1/tsuchinaga/{conv => imgconv}/converter.go (96%) rename kadai1/tsuchinaga/{conv => imgconv}/converter_test.go (99%) diff --git a/kadai1/tsuchinaga/conv/converter.go b/kadai1/tsuchinaga/imgconv/converter.go similarity index 96% rename from kadai1/tsuchinaga/conv/converter.go rename to kadai1/tsuchinaga/imgconv/converter.go index 8be9e51..e9f406c 100644 --- a/kadai1/tsuchinaga/conv/converter.go +++ b/kadai1/tsuchinaga/imgconv/converter.go @@ -1,4 +1,4 @@ -package conv +package imgconv import ( "fmt" @@ -32,8 +32,8 @@ func IsDir(path string) (bool, error) { return fi.IsDir(), nil } -// ExecConvert - 変換の実行 -func ExecConvert(dir, src, dest string) { +// Do - 変換の実行 +func Do(dir, src, dest string) { c := converter{ dirList: []string{dir}, srcFileType: src, diff --git a/kadai1/tsuchinaga/conv/converter_test.go b/kadai1/tsuchinaga/imgconv/converter_test.go similarity index 99% rename from kadai1/tsuchinaga/conv/converter_test.go rename to kadai1/tsuchinaga/imgconv/converter_test.go index 17d2fb8..1c5ee69 100644 --- a/kadai1/tsuchinaga/conv/converter_test.go +++ b/kadai1/tsuchinaga/imgconv/converter_test.go @@ -1,4 +1,4 @@ -package conv +package imgconv import ( "errors" diff --git a/kadai1/tsuchinaga/main.go b/kadai1/tsuchinaga/main.go index a7748d2..f106107 100644 --- a/kadai1/tsuchinaga/main.go +++ b/kadai1/tsuchinaga/main.go @@ -2,7 +2,7 @@ package main import ( "flag" - "github.com/gopherdojo/dojo8/kadai1/tsuchinaga/conv" + "github.com/gopherdojo/dojo8/kadai1/tsuchinaga/imgconv" "log" ) @@ -17,13 +17,13 @@ func main() { if dir == "" { log.Fatalln("dirの指定は必須です") } - if isDir, err := conv.IsDir(dir); err != nil || !isDir { + if isDir, err := imgconv.IsDir(dir); err != nil || !isDir { log.Fatalf("%sは存在しないかディレクトリではありません\n", dir) } - if !conv.IsValidFileType(src) { + if !imgconv.IsValidFileType(src) { log.Fatalf("%sは許可されていない画像形式です\n", src) } - if !conv.IsValidFileType(dest) { + if !imgconv.IsValidFileType(dest) { log.Fatalf("%sは許可されていない画像形式です", dest) } if src == dest { @@ -31,5 +31,5 @@ func main() { } // 変換実行 - conv.ExecConvert(dir, src, dest) + imgconv.Do(dir, src, dest) } From bcbc6c4c81ed3189b982dc60002bc95302edc733 Mon Sep 17 00:00:00 2001 From: tsuchinaga Date: Wed, 8 Jul 2020 22:21:02 +0900 Subject: [PATCH 07/13] =?UTF-8?q?goimports=E3=81=A7import=E6=96=87?= =?UTF-8?q?=E3=82=92=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai1/tsuchinaga/imgconv/converter.go | 3 ++- kadai1/tsuchinaga/main.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kadai1/tsuchinaga/imgconv/converter.go b/kadai1/tsuchinaga/imgconv/converter.go index e9f406c..eede9bb 100644 --- a/kadai1/tsuchinaga/imgconv/converter.go +++ b/kadai1/tsuchinaga/imgconv/converter.go @@ -2,7 +2,6 @@ package imgconv import ( "fmt" - "golang.org/x/xerrors" "image" "image/jpeg" "image/png" @@ -10,6 +9,8 @@ import ( "log" "os" "path/filepath" + + "golang.org/x/xerrors" ) var ( diff --git a/kadai1/tsuchinaga/main.go b/kadai1/tsuchinaga/main.go index f106107..0f1672b 100644 --- a/kadai1/tsuchinaga/main.go +++ b/kadai1/tsuchinaga/main.go @@ -2,8 +2,9 @@ package main import ( "flag" - "github.com/gopherdojo/dojo8/kadai1/tsuchinaga/imgconv" "log" + + "github.com/gopherdojo/dojo8/kadai1/tsuchinaga/imgconv" ) func main() { From 6b17c74cc4a10b6ce6024780ff927cb87d3ef6f0 Mon Sep 17 00:00:00 2001 From: tsuchinaga Date: Wed, 8 Jul 2020 22:25:49 +0900 Subject: [PATCH 08/13] =?UTF-8?q?log.Fatal=E7=B3=BB=E3=82=92=E5=91=BC?= =?UTF-8?q?=E3=82=93=E3=81=A7=E3=81=84=E3=81=9F=E3=81=A8=E3=81=93=E3=82=8D?= =?UTF-8?q?=E3=82=92Print=E7=B3=BB+Exit=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai1/tsuchinaga/main.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/kadai1/tsuchinaga/main.go b/kadai1/tsuchinaga/main.go index 0f1672b..9f9b4f0 100644 --- a/kadai1/tsuchinaga/main.go +++ b/kadai1/tsuchinaga/main.go @@ -3,6 +3,7 @@ package main import ( "flag" "log" + "os" "github.com/gopherdojo/dojo8/kadai1/tsuchinaga/imgconv" ) @@ -16,19 +17,24 @@ func main() { // validation if dir == "" { - log.Fatalln("dirの指定は必須です") + log.Println("dirの指定は必須です") + os.Exit(2) } if isDir, err := imgconv.IsDir(dir); err != nil || !isDir { - log.Fatalf("%sは存在しないかディレクトリではありません\n", dir) + log.Printf("%sは存在しないかディレクトリではありません\n", dir) + os.Exit(2) } if !imgconv.IsValidFileType(src) { - log.Fatalf("%sは許可されていない画像形式です\n", src) + log.Printf("%sは許可されていない画像形式です\n", src) + os.Exit(2) } if !imgconv.IsValidFileType(dest) { - log.Fatalf("%sは許可されていない画像形式です", dest) + log.Printf("%sは許可されていない画像形式です", dest) + os.Exit(2) } if src == dest { - log.Fatalln("srcとdestで違う画像形式を選択してください") + log.Println("srcとdestで違う画像形式を選択してください") + os.Exit(2) } // 変換実行 From 6dae5bb58ccfcc7dc2ab892fb3f6cfc7809d28e8 Mon Sep 17 00:00:00 2001 From: tsuchinaga Date: Thu, 9 Jul 2020 12:43:44 +0900 Subject: [PATCH 09/13] =?UTF-8?q?=E3=81=A8=E3=82=8A=E3=81=82=E3=81=88?= =?UTF-8?q?=E3=81=9A=E5=85=A8=E3=81=A6=E3=81=AE=E3=82=A8=E3=83=A9=E3=83=BC?= =?UTF-8?q?=E3=82=92=E4=B8=8A=E3=81=AB=E8=BF=94=E3=81=97=E3=81=A6=E3=81=84?= =?UTF-8?q?=E3=81=8F=E3=82=88=E3=81=86=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai1/tsuchinaga/imgconv/converter.go | 37 ++++++++++++++------------ kadai1/tsuchinaga/main.go | 5 +++- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/kadai1/tsuchinaga/imgconv/converter.go b/kadai1/tsuchinaga/imgconv/converter.go index eede9bb..f3e00c5 100644 --- a/kadai1/tsuchinaga/imgconv/converter.go +++ b/kadai1/tsuchinaga/imgconv/converter.go @@ -14,7 +14,12 @@ import ( ) var ( - FileStatError = xerrors.New("file stat error") + ReadDirError = xerrors.New("read dir error") + FileStatError = xerrors.New("file stat error") + OpenSourceFileError = xerrors.New("open source file error") + CreateDestinationFileError = xerrors.New("create destination file error") + ReadImageError = xerrors.New("read image error") + EncodeImageError = xerrors.New("encode image error") ) var validFileTypes = map[string]bool{"jpeg": true, "png": true} @@ -34,13 +39,13 @@ func IsDir(path string) (bool, error) { } // Do - 変換の実行 -func Do(dir, src, dest string) { +func Do(dir, src, dest string) error { c := converter{ dirList: []string{dir}, srcFileType: src, destFileType: dest, } - c.exec() + return c.exec() } // converter - 変換機能の実装 @@ -51,15 +56,14 @@ type converter struct { } // exec - ディレクトリをたどりながら変換を実行 -func (c *converter) exec() { +func (c *converter) exec() error { for len(c.dirList) > 0 { dirPath := c.dirList[0] c.dirList = c.dirList[1:] files, err := ioutil.ReadDir(dirPath) if err != nil { - log.Printf("ディレクトリ: %sが読み込めなかったためスキップします\n", dirPath) - continue + return xerrors.Errorf("%+v: %w", err, ReadDirError) } for _, file := range files { @@ -67,33 +71,33 @@ func (c *converter) exec() { if file.IsDir() { c.dirList = append(c.dirList, path) } else { - c.convert(path) + if err := c.convert(path); err != nil { + return err + } } } } + return nil } // convert - 変換処理 -func (c converter) convert(path string) { +func (c converter) convert(path string) error { f, err := os.Open(path) if err != nil { // 開けない - log.Printf("ファイル: %sが開けなかったためスキップします\n", path) - return + return xerrors.Errorf("%+v: %w", err, OpenSourceFileError) } defer f.Close() if getFileType(path) == c.srcFileType { img, _, err := image.Decode(f) if err != nil { - log.Printf("ファイル: %sが読み込めなかったためスキップします\n", path) - return + return xerrors.Errorf("%+v: %w", err, ReadImageError) } newFilePath := fmt.Sprintf("%s.%s", path, c.destFileType) o, err := os.Create(newFilePath) if err != nil { - log.Printf("変換後ファイル: %sが作成できなかったためスキップします\n", newFilePath) - return + return xerrors.Errorf("%+v: %w", err, CreateDestinationFileError) } defer o.Close() @@ -101,16 +105,15 @@ func (c converter) convert(path string) { switch c.destFileType { case "jpeg": err = jpeg.Encode(o, img, nil) - case "png": err = png.Encode(o, img) } if err != nil { log.Printf("ファイル: %sの変換に失敗しました\n", path) - } else { - log.Printf("%s => %s\n", path, newFilePath) + return xerrors.Errorf("%+v: %w", err, EncodeImageError) } } + return nil } // getFileType - 画像ファイルの型を得る diff --git a/kadai1/tsuchinaga/main.go b/kadai1/tsuchinaga/main.go index 9f9b4f0..3f36d43 100644 --- a/kadai1/tsuchinaga/main.go +++ b/kadai1/tsuchinaga/main.go @@ -38,5 +38,8 @@ func main() { } // 変換実行 - imgconv.Do(dir, src, dest) + if err := imgconv.Do(dir, src, dest); err != nil { + log.Println(err) + os.Exit(2) + } } From 9c8786d09a42a32fe0e631aa453e3783485f005c Mon Sep 17 00:00:00 2001 From: tsuchinaga Date: Thu, 9 Jul 2020 12:46:57 +0900 Subject: [PATCH 10/13] =?UTF-8?q?=E6=9B=B8=E3=81=8D=E8=BE=BC=E3=81=BF?= =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=81=AE=E3=82=A8=E3=83=A9?= =?UTF-8?q?=E3=83=BC=E3=82=92=E8=BF=94=E3=81=99=E3=81=9F=E3=82=81=E3=81=AB?= =?UTF-8?q?=E5=90=8D=E5=89=8D=E4=BB=98=E3=81=8D=E3=81=AE=E6=88=BB=E3=82=8A?= =?UTF-8?q?=E5=80=A4=E3=82=92=E4=BD=BF=E3=81=86=E3=82=88=E3=81=86=E5=A4=89?= =?UTF-8?q?=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai1/tsuchinaga/imgconv/converter.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kadai1/tsuchinaga/imgconv/converter.go b/kadai1/tsuchinaga/imgconv/converter.go index f3e00c5..dc746b0 100644 --- a/kadai1/tsuchinaga/imgconv/converter.go +++ b/kadai1/tsuchinaga/imgconv/converter.go @@ -81,7 +81,7 @@ func (c *converter) exec() error { } // convert - 変換処理 -func (c converter) convert(path string) error { +func (c converter) convert(path string) (err error) { f, err := os.Open(path) if err != nil { // 開けない return xerrors.Errorf("%+v: %w", err, OpenSourceFileError) @@ -99,7 +99,9 @@ func (c converter) convert(path string) error { if err != nil { return xerrors.Errorf("%+v: %w", err, CreateDestinationFileError) } - defer o.Close() + defer func() { + err = o.Close() + }() err = nil switch c.destFileType { From d17fd4618a12bcb89691bc633f50c881166d4e12 Mon Sep 17 00:00:00 2001 From: tsuchinaga Date: Thu, 9 Jul 2020 12:47:58 +0900 Subject: [PATCH 11/13] =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E3=82=92?= =?UTF-8?q?=E3=81=BE=E3=81=A8=E3=82=81=E3=81=A6=E4=B8=80=E3=81=8B=E6=89=80?= =?UTF-8?q?=E3=81=A7=E5=87=A6=E7=90=86=E3=81=9B=E3=81=9A=E3=80=81=E3=82=A8?= =?UTF-8?q?=E3=83=A9=E3=83=BC=E7=99=BA=E7=94=9F=E5=80=8B=E6=89=80=E3=81=A7?= =?UTF-8?q?=E5=87=A6=E7=90=86=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E5=A4=89?= =?UTF-8?q?=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai1/tsuchinaga/imgconv/converter.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/kadai1/tsuchinaga/imgconv/converter.go b/kadai1/tsuchinaga/imgconv/converter.go index dc746b0..50c3628 100644 --- a/kadai1/tsuchinaga/imgconv/converter.go +++ b/kadai1/tsuchinaga/imgconv/converter.go @@ -6,7 +6,6 @@ import ( "image/jpeg" "image/png" "io/ioutil" - "log" "os" "path/filepath" @@ -103,16 +102,15 @@ func (c converter) convert(path string) (err error) { err = o.Close() }() - err = nil switch c.destFileType { case "jpeg": - err = jpeg.Encode(o, img, nil) + if err = jpeg.Encode(o, img, nil); err != nil { + return xerrors.Errorf("%+v: %w", err, EncodeImageError) + } case "png": - err = png.Encode(o, img) - } - if err != nil { - log.Printf("ファイル: %sの変換に失敗しました\n", path) - return xerrors.Errorf("%+v: %w", err, EncodeImageError) + if err = png.Encode(o, img); err != nil { + return xerrors.Errorf("%+v: %w", err, EncodeImageError) + } } } return nil From 872cef76018428d9d267c078c14d6148982c1fb5 Mon Sep 17 00:00:00 2001 From: tsuchinaga Date: Thu, 9 Jul 2020 12:49:59 +0900 Subject: [PATCH 12/13] =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E3=82=92?= =?UTF-8?q?=E6=8F=A1=E3=82=8A=E3=81=A4=E3=81=B6=E3=81=95=E3=81=9A=E3=80=81?= =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E3=81=A8=E3=81=97=E3=81=A6=E8=BF=94?= =?UTF-8?q?=E3=81=99=E3=82=88=E3=81=86=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai1/tsuchinaga/imgconv/converter.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/kadai1/tsuchinaga/imgconv/converter.go b/kadai1/tsuchinaga/imgconv/converter.go index 50c3628..0322aa6 100644 --- a/kadai1/tsuchinaga/imgconv/converter.go +++ b/kadai1/tsuchinaga/imgconv/converter.go @@ -15,10 +15,11 @@ import ( var ( ReadDirError = xerrors.New("read dir error") FileStatError = xerrors.New("file stat error") - OpenSourceFileError = xerrors.New("open source file error") + OpenFileError = xerrors.New("open source file error") CreateDestinationFileError = xerrors.New("create destination file error") ReadImageError = xerrors.New("read image error") EncodeImageError = xerrors.New("encode image error") + NoImageError = xerrors.New("no image error") ) var validFileTypes = map[string]bool{"jpeg": true, "png": true} @@ -83,11 +84,15 @@ func (c *converter) exec() error { func (c converter) convert(path string) (err error) { f, err := os.Open(path) if err != nil { // 開けない - return xerrors.Errorf("%+v: %w", err, OpenSourceFileError) + return xerrors.Errorf("%+v: %w", err, OpenFileError) } defer f.Close() - if getFileType(path) == c.srcFileType { + ft, err := getFileType(path) + if err != nil { + return err + } + if ft == c.srcFileType { img, _, err := image.Decode(f) if err != nil { return xerrors.Errorf("%+v: %w", err, ReadImageError) @@ -117,16 +122,16 @@ func (c converter) convert(path string) (err error) { } // getFileType - 画像ファイルの型を得る -func getFileType(path string) string { +func getFileType(path string) (string, error) { f, err := os.Open(path) if err != nil { // 開けない - return "" + return "", OpenFileError } defer f.Close() _, format, err := image.DecodeConfig(f) if err != nil { // 画像じゃない - return "" + return "", NoImageError } - return format + return format, nil } From 37228b3f8cb1848b706aac03a42c001abdebd6a1 Mon Sep 17 00:00:00 2001 From: tsuchinaga Date: Fri, 10 Jul 2020 09:24:34 +0900 Subject: [PATCH 13/13] =?UTF-8?q?imgconv=E3=83=91=E3=83=83=E3=82=B1?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=81=AFchan=20error=E3=82=92=E8=BF=94?= =?UTF-8?q?=E3=81=99=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=80=81main?= =?UTF-8?q?=E3=81=8C=E3=82=A8=E3=83=A9=E3=83=BC=E3=82=92=E3=83=8F=E3=83=B3?= =?UTF-8?q?=E3=83=89=E3=83=AA=E3=83=B3=E3=82=B0=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai1/tsuchinaga/imgconv/converter.go | 15 ++++++++++++--- kadai1/tsuchinaga/main.go | 9 ++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/kadai1/tsuchinaga/imgconv/converter.go b/kadai1/tsuchinaga/imgconv/converter.go index 0322aa6..9349557 100644 --- a/kadai1/tsuchinaga/imgconv/converter.go +++ b/kadai1/tsuchinaga/imgconv/converter.go @@ -39,13 +39,21 @@ func IsDir(path string) (bool, error) { } // Do - 変換の実行 -func Do(dir, src, dest string) error { +func Do(dir, src, dest string) chan error { c := converter{ dirList: []string{dir}, srcFileType: src, destFileType: dest, + ch: make(chan error), } - return c.exec() + go func() { + defer close(c.ch) + err := c.exec() + if err != nil { + c.ch <- err + } + }() + return c.ch } // converter - 変換機能の実装 @@ -53,6 +61,7 @@ type converter struct { dirList []string srcFileType string destFileType string + ch chan error } // exec - ディレクトリをたどりながら変換を実行 @@ -72,7 +81,7 @@ func (c *converter) exec() error { c.dirList = append(c.dirList, path) } else { if err := c.convert(path); err != nil { - return err + c.ch <- err // エラーをチャネルに渡して続きを進める } } } diff --git a/kadai1/tsuchinaga/main.go b/kadai1/tsuchinaga/main.go index 3f36d43..ea05b7e 100644 --- a/kadai1/tsuchinaga/main.go +++ b/kadai1/tsuchinaga/main.go @@ -2,6 +2,7 @@ package main import ( "flag" + "fmt" "log" "os" @@ -38,8 +39,10 @@ func main() { } // 変換実行 - if err := imgconv.Do(dir, src, dest); err != nil { - log.Println(err) - os.Exit(2) + ch := imgconv.Do(dir, src, dest) + for err := range ch { + if err != nil { + fmt.Println(err) + } } }