Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions kadai1/kotaaaa/README.md
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
```

80 changes: 80 additions & 0 deletions kadai1/kotaaaa/convert/convert.go
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
}
101 changes: 101 additions & 0 deletions kadai1/kotaaaa/convert/convert_test.go
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
}
Binary file added kadai1/kotaaaa/converter
Binary file not shown.
5 changes: 5 additions & 0 deletions kadai1/kotaaaa/go.mod
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
29 changes: 29 additions & 0 deletions kadai1/kotaaaa/go.sum
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=
40 changes: 40 additions & 0 deletions kadai1/kotaaaa/main.go
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)
}
}
31 changes: 31 additions & 0 deletions kadai1/kotaaaa/search/search.go
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)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

エラーはここで処理するのではなく、この関数を

func GetFiles(dir string, ext string) ([]string,error) {

とした上で呼び出し元で処理した方が良いです。理由としてはそのようにエラーハンドリングするのがGoの一般的な書き方であるほか、以下のような問題があるためです

  • エラーのハンドリングを呼び出し元でコントロールできない
  • この関数をCLI以外で使うときに中断されると困るケースがある
  • ログを出したくないときに使いづらい
  • log.Fatal(err)は内部でos.Exit(1)を呼び出すため、main()関数以外で使用すると他のゴルーチンの終了を待たずして大元であるmain()ゴルーチンを終了させてしまう

}

var arr []string

Choose a reason for hiding this comment

The 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) {

Choose a reason for hiding this comment

The 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
}
19 changes: 19 additions & 0 deletions kadai1/kotaaaa/search/search_test.go
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 {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここではGetFilesが意図どおりに動くかのテストをすべきかと思うので、拡張子が想定通りかを調べるよりファイル名が想定通りかを調べるほうが望ましいかと思います

t.Errorf("Invalid result. Expected: %v Actual file: %v", ext, file)
}
}
}
}
Binary file added kadai1/kotaaaa/testdata/dir1/owl2.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added kadai1/kotaaaa/testdata/owl.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions kadai1/kotaaaa/testdata/sample.log
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello
25 changes: 25 additions & 0 deletions kadai1/kotaaaa/validator/validator.go
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")
}

Choose a reason for hiding this comment

The 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
}
Loading