-
Notifications
You must be signed in to change notification settings - Fork 179
Kadai1,2 kotaaaa #87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Kadai1,2 kotaaaa #87
Changes from 11 commits
8d314f1
48d542a
6362fea
f9e6e91
323ac4a
f54bca3
3bf9772
ece43ce
3205044
6ed4ab3
21a2937
f74a772
1b34df3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Specification | ||
- 次の仕様を満たすコマンドを作って下さい | ||
- ディレクトリを指定する | ||
- 指定したディレクトリ以下のJPGファイルをPNGに変換(デフォルト) | ||
- ディレクトリ以下は再帰的に処理する | ||
- 変換前と変換後の画像形式を指定できる(オプション) | ||
- 以下を満たすように開発してください | ||
- mainパッケージと分離する | ||
- 自作パッケージと標準パッケージと準標準パッケージのみ使う | ||
- 準標準パッケージ:golang.org/x以下のパッケージ | ||
- ユーザ定義型を作ってみる | ||
- GoDocを生成してみる | ||
- Go Modulesを使ってみる | ||
|
||
# How to use | ||
``` | ||
$ pwd | ||
(YOUR_PATH)/gopherdojo-studyroom/kadai1/kotaaaa | ||
$ go build -o converter | ||
$ ./converter -path="./testdata/" -srcExt=".jpg" -dstExt=".png" | ||
``` | ||
|
||
# How to test | ||
``` | ||
$ go test ./... --count=1 -cover | ||
? github.com/kotaaaa/gopherdojo-studyroom/kadai1/kotaaaa [no test files] | ||
ok github.com/kotaaaa/gopherdojo-studyroom/kadai1/kotaaaa/convert 8.231s coverage: 70.4% of statements | ||
ok github.com/kotaaaa/gopherdojo-studyroom/kadai1/kotaaaa/search 0.005s coverage: 91.7% of statements | ||
ok github.com/kotaaaa/gopherdojo-studyroom/kadai1/kotaaaa/validator 0.006s coverage: 100.0% of statements | ||
``` | ||
|
||
# Notes | ||
### Extensions that you can convert to. | ||
- .gif, .jpeg, .jpg, .png | ||
|
||
## Help | ||
``` | ||
$./converter --help | ||
Usage of /var/folders/nx/xqljz2y954qbyppfwn4w0tcr0000gn/T/go-build027276676/b001/exe/main: | ||
-dstExt string | ||
変換後の拡張子 (default ".png") | ||
-path string | ||
ファイルパス | ||
-srcExt string | ||
変換前の拡張子 (default ".jpg") | ||
exit status 2 | ||
``` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package convert | ||
|
||
import ( | ||
"image" | ||
"image/gif" | ||
"image/jpeg" | ||
"image/png" | ||
"os" | ||
"path/filepath" | ||
) | ||
|
||
// User create Type | ||
type fileInfo struct { | ||
srcFilename string | ||
dstExt string | ||
basePath string | ||
} | ||
|
||
// Create new FileInfo | ||
func NewFileInfo(srcFilename string, dstExt string, basePath string) *fileInfo { | ||
return &fileInfo{srcFilename, dstExt, basePath} | ||
} | ||
|
||
// Remove file | ||
func removeFile(fileName string) error { | ||
err := os.Remove(fileName) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
// Get strings except for file's extension. | ||
func getFilePathFromBase(path string) string { | ||
return path[:len(path)-len(filepath.Ext(path))] | ||
} | ||
|
||
// Convert file from src extension to dst extension | ||
func (fi *fileInfo) Convert() error { | ||
|
||
dstFileName := getFilePathFromBase(fi.srcFilename) + fi.dstExt | ||
// Open target image file object | ||
srcFile, err := os.Open(fi.basePath + fi.srcFilename) | ||
if err != nil { | ||
return err | ||
} | ||
defer srcFile.Close() | ||
|
||
// Read target image file | ||
img, _, err := image.Decode(srcFile) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Create transformed file object | ||
dstFile, err := os.Create(fi.basePath + dstFileName) | ||
if err != nil { | ||
return err | ||
} | ||
defer dstFile.Close() | ||
|
||
// Execute file transform | ||
switch filepath.Ext(fi.basePath + dstFileName) { | ||
case ".gif": | ||
err = gif.Encode(dstFile, img, nil) | ||
case ".png": | ||
err = png.Encode(dstFile, img) | ||
case ".jpg", "jpeg": | ||
err = jpeg.Encode(dstFile, img, nil) | ||
default: | ||
return err | ||
} | ||
// Remove src file | ||
err = removeFile(fi.basePath + fi.srcFilename) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package convert | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"os" | ||
"testing" | ||
) | ||
|
||
func TestNewFileInfo(t *testing.T) { | ||
srcFilename := "sample.jpg" | ||
dstExt := ".png" | ||
basePath := "./dir" | ||
result := NewFileInfo(srcFilename, dstExt, basePath) | ||
|
||
if result.srcFilename != srcFilename { | ||
t.Errorf("Result: %v, Expected: %v", result.srcFilename, srcFilename) | ||
} | ||
if result.dstExt != dstExt { | ||
t.Errorf("Result: %v, Expected: %v", result.dstExt, dstExt) | ||
} | ||
if result.basePath != basePath { | ||
t.Errorf("Result: %v, Expected: %v", result.basePath, basePath) | ||
} | ||
} | ||
|
||
func TestRemoveFile(t *testing.T) { | ||
fileName := "../testdata/sample.log" | ||
removeFile(fileName) | ||
if _, err := os.Stat(fileName); err == nil { | ||
t.Errorf("File still exists: %v", fileName) | ||
} | ||
fp, err := os.Create(fileName) | ||
if err != nil { | ||
fmt.Println(err) | ||
return | ||
} | ||
defer fp.Close() | ||
fp.WriteString("hello") | ||
} | ||
|
||
func TestGetFilePathFromBaseSuccess(t *testing.T) { | ||
path := "./dir/sample.jpg" | ||
expected := "./dir/sample" | ||
result := getFilePathFromBase(path) | ||
if result != expected { | ||
t.Errorf("Result: %v, Expected: %v", result, expected) | ||
} | ||
} | ||
|
||
func TestGetFilePathFromBaseFail(t *testing.T) { | ||
path := "./dir/sample.jpg" | ||
expected := "./dir/sample.jpg" | ||
result := getFilePathFromBase(path) | ||
if result == expected { | ||
t.Errorf("Result: %v, Expected: %v", result, expected) | ||
} | ||
} | ||
|
||
func TestConvert(t *testing.T) { | ||
|
||
srcFilename := "sample.jpg" | ||
dstFilename := "sample.png" | ||
dstExt := ".png" | ||
basePath := "../testdata/" | ||
|
||
copyFile(basePath+"owl.jpg", basePath+srcFilename) | ||
fileInfo := NewFileInfo(srcFilename, dstExt, basePath) | ||
if !isExist(basePath + srcFilename) { | ||
t.Errorf("File for test is not created.") | ||
} | ||
fileInfo.Convert() | ||
if !isExist(basePath + dstFilename) { | ||
t.Errorf("Expected file is not created: %v", dstFilename) | ||
} | ||
os.Remove(basePath + dstFilename) | ||
} | ||
|
||
func copyFile(srcName string, dstName string) { | ||
src, err := os.Open(srcName) | ||
if err != nil { | ||
panic(err) | ||
} | ||
defer src.Close() | ||
|
||
dst, err := os.Create(dstName) | ||
if err != nil { | ||
panic(err) | ||
} | ||
defer dst.Close() | ||
|
||
_, err = io.Copy(dst, src) | ||
if err != nil { | ||
panic(err) | ||
} | ||
} | ||
|
||
func isExist(f string) bool { | ||
_, err := os.Stat(f) | ||
return err == nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module github.com/kotaaaa/gopherdojo-studyroom/kadai1/kotaaaa | ||
|
||
go 1.14 | ||
|
||
require golang.org/x/tools v0.1.7 // indirect |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
github.com/yuin/goldmark v1.4.0 h1:OtISOGfH6sOWa1/qXqqAiOIAO6Z5J3AEAE18WAq6BiQ= | ||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= | ||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= | ||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= | ||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | ||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= | ||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= | ||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= | ||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= | ||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
|
||
"github.com/kotaaaa/gopherdojo-studyroom/kadai1/kotaaaa/convert" | ||
"github.com/kotaaaa/gopherdojo-studyroom/kadai1/kotaaaa/search" | ||
"github.com/kotaaaa/gopherdojo-studyroom/kadai1/kotaaaa/validator" | ||
) | ||
|
||
var targetPath string | ||
var targetSrcExt string | ||
var targetDstExt string | ||
|
||
func init() { | ||
flag.StringVar(&targetPath, "path", "", "File path") | ||
flag.StringVar(&targetSrcExt, "srcExt", ".jpg", "source file extention") | ||
flag.StringVar(&targetDstExt, "dstExt", ".png", "destination file extention") | ||
|
||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
validator.ValidateArgs(targetPath, targetSrcExt, targetDstExt) | ||
fmt.Println("targetPath:", targetPath) | ||
targetPath = targetPath + "/" | ||
// Get file list (Relative path from basePath) | ||
fileNames := search.GetFiles(targetPath, targetSrcExt) | ||
|
||
for _, fileName := range fileNames { | ||
fileInfo := convert.NewFileInfo(fileName, targetDstExt, targetPath) | ||
// 変換処理 | ||
err := fileInfo.Convert() | ||
if err != nil { | ||
fmt.Println("Error Occuerrd ", err) | ||
} | ||
fmt.Println("Converted ", fileName) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package search | ||
|
||
import ( | ||
"io/ioutil" | ||
"log" | ||
"path/filepath" | ||
) | ||
|
||
// Get a list of files under the directory. | ||
func GetFiles(dir string, ext string) []string { | ||
// Get the files in the target directory | ||
files, err := ioutil.ReadDir(dir) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
var arr []string | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 細かいところになりますが、[]stringはarrayではなくsliceです([3]stringはarrayです)。変数名はarrではなく、slやlist,意味ある名前にするならtargetFilesとかresultsにするのが適切かと思います。変数名をどれにすべきかは場合によるのですが、とりあえずarrayは適切でなさそうとことを覚えておいてもらえると良さそうです |
||
for _, file := range files { | ||
name := file.Name() | ||
// If the file is directory, add files recursively. | ||
if file.IsDir() { | ||
for _, subFile := range GetFiles(dir+name, ext) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. パスの結合はfilepath.Joinを使う方が良いでしょう。ディレクトリの区切りが/以外のプラットフォームでも対処できますし、ディレクトの最後に/がつくつかないで誤動作することがなくなります |
||
arr = append(arr, name+"/"+subFile) | ||
} | ||
} | ||
if filepath.Ext(name) == ext { | ||
arr = append(arr, name) | ||
} | ||
} | ||
return arr | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package search | ||
|
||
import ( | ||
"path/filepath" | ||
"testing" | ||
) | ||
|
||
func TestGetFiles(t *testing.T) { | ||
|
||
extentions := []string{".txt", ".png", ".jpg", ".gif"} | ||
for _, ext := range extentions { | ||
files := GetFiles("../testdata/", ext) | ||
for _, file := range files { | ||
if filepath.Ext(file) != ext { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ここではGetFilesが意図どおりに動くかのテストをすべきかと思うので、拡張子が想定通りかを調べるよりファイル名が想定通りかを調べるほうが望ましいかと思います |
||
t.Errorf("Invalid result. Expected: %v Actual file: %v", ext, file) | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
hello |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package validator | ||
|
||
import ( | ||
"errors" | ||
"os" | ||
) | ||
|
||
func ValidateArgs(targetPath string, targetSrcExt string, targetDstExt string) error { | ||
if _, err := os.Stat(targetPath); err != nil { | ||
return errors.New("Error: Doesn't exists the directory that you specified") | ||
} | ||
if !validateFileFormat(targetSrcExt) || !validateFileFormat(targetDstExt) { | ||
return errors.New("Error: Invalid or Unsupported file format") | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 入力と出力でどちらが問題なのかわかるように分けておいた方が親切かと思います |
||
return nil | ||
} | ||
|
||
func validateFileFormat(target string) bool { | ||
for _, ext := range []string{".jpg", ".jpeg", ".png", ".gif"} { | ||
if ext == target { | ||
return true | ||
} | ||
} | ||
return false | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
エラーはここで処理するのではなく、この関数を
とした上で呼び出し元で処理した方が良いです。理由としてはそのようにエラーハンドリングするのがGoの一般的な書き方であるほか、以下のような問題があるためです