Skip to content
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

Kadai1-fujiokayu #6

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions kadai1/fujiokayu/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LDFLAGS := -ldflags="-s -w"

.PHONY: build
build:
go build $(LDFLAGS)
37 changes: 37 additions & 0 deletions kadai1/fujiokayu/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# myConverter

## how to build
```
make
```

## usage

```
# convert
./myConverter [-from ext] [-to ext] directory

# show help
./myConverter -h

# example
./myConverter -from png -to jpg testdir
```

## Specification
- 引数で指定されたディレクトリを再帰的に走査し、-from オプションで指定された画像形式のファイルを -to オプションで指定された画像形式のファイルに変換します。
- オプションで指定できるフォーマットは gif、jpg(jpeg)、png のみです。
- オプションを指定しなかった場合は jpg -> png に変換します。
- 変換後のファイルは変換元のファイルと同じディレクトリに出力されます。
- 変換元のファイルは削除されず、そのまま残ります。
- 変換中にディスクサイズが足りなくなった場合の挙動は(恐らく)処理系定義になります。

## learning memo
- ディレクトリの再帰的な探索は [path/filepath.Walk](https://golang.org/pkg/path/filepath/#Walk) を使うのが一番近道だと思った。
- しかし、filepath.Walk 内で呼び出せる関数が [WalkFunc](https://golang.org/pkg/path/filepath/#WalkFunc) に限定されているようで、かつ WalkFunc の型が決まっていたので少し使い辛かった。
- 一方で、これは[先月の Software Design](https://gihyo.jp/magazine/SD/archive/2019/201905) で見た Generator Pattern を試すと Go らしくなるのでは考えた。
- [同じことを考える人](https://gist.github.com/sethamclean/9475737)が既に居たので、参考にした。
- ~~flag パッケージに少し使い辛さを感じる。~~
- ~~特に、フラグ無し引数が一つ入ると以降のフラグも全て Parse できなくなるのが少し使いづらい。~~
- sh-tatsuno さんのコードで使われていた [NewFlagSet](https://golang.org/pkg/flag/#NewFlagSet) を使うともっと柔軟な操作が可能になるようでした。
勉強になりました。
58 changes: 58 additions & 0 deletions kadai1/fujiokayu/args/args.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package args

import (
"flag"
"fmt"
"io"
"log"
"os"
)

//Args struct has parsed args.
type Args struct {
DecodeType string
EncodeType string
RootFolderName []string
}

func usage() {
_, err := io.WriteString(os.Stderr, usageString)
if err != nil {
log.Fatal(err)
}
flag.PrintDefaults()
}

const usageString = `Usage of myConverter:
# convert
./myConverter [-from ext] [-to ext] directory

# example
./myConverter -from png -to jpg testdir

# args
`

// ParseArgs is the constructor of struct "args"
func ParseArgs() (*Args, error) {
flag.Usage = usage
arg1 := flag.String("from", "jpg", "original file type to convert")
arg2 := flag.String("to", "png", "file type you want to convert")

flag.Parse()

// フォルダが指定されているかチェックする
var err error
folder := flag.Args()
if len(folder) == 0 {
err = fmt.Errorf("specify target directory")
}

newArgs := &Args{
DecodeType: *arg1,
EncodeType: *arg2,
RootFolderName: flag.Args(),
}

return newArgs, err
}
80 changes: 80 additions & 0 deletions kadai1/fujiokayu/converter/converter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
Package converter

converter は特定の画像形式のファイルを変換するためのパッケージです。

How to use

string 型の3つの引数を指定して Convert 関数を呼び出してください。
Convert(filePath string, decodeType string, encodeType string)

利用できるファイル形式は gif、jpg(jpeg)、png だけです。
*/
package converter

import (
"fmt"
"image"
"image/gif"
"image/jpeg"
"image/png"
"os"
"path"
"strings"
)

//dec: Decode filePath file
func dec(filePath string, decodeType string) (image.Image, error) {
reader, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer reader.Close()

switch decodeType {
case "gif":
return gif.Decode(reader)
case "jpeg", "jpg":
return jpeg.Decode(reader)
case "png":
return png.Decode(reader)
}
return nil, err
}

//enc: Encode m to encodeType
func enc(filePath string, encodeType string, m image.Image) error {
writer, err := os.Create(strings.TrimSuffix(filePath, path.Ext(filePath)) + "." + encodeType)
if err != nil {
return err
}
defer writer.Close()

switch encodeType {
case "gif":
return gif.Encode(writer, m, nil)
case "jpeg", "jpg":
return jpeg.Encode(writer, m, nil)
case "png":
return png.Encode(writer, m)
}
return nil
}

//Convert : convert decodeType file to encodeType file
func Convert(filePath string, decodeType string, encodeType string) error {
fmt.Println("Converting", filePath)

m, err := dec(filePath, decodeType)
if err != nil {
return err
}

err = enc(filePath, encodeType, m)
if err != nil {
return err
}

fmt.Println("Converted", filePath)
return nil
}
31 changes: 31 additions & 0 deletions kadai1/fujiokayu/converter/converter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package converter

import (
"os"
"testing"
)

const decodeFile string = "../testdata/cat.jpg"
const encodeFile string = "../testdata/cat.png"

func Test_Convert(t *testing.T) {
err := Convert(decodeFile, "jpg", "png")
if err != nil {
t.Fatal("failed test: Convert error")
}

// 変換後のファイルが存在するかチェック
info, err := os.Stat(encodeFile)
if err != nil {
t.Fatal("failed test: File not generated")
}
// 変換後のファイルサイズが0バイトではないかチェック
if info.Size() <= 0 {
t.Fatal("failed test: Encoded file is invalid")
}
// テストで生成したファイルを削除する。
err = os.Remove(encodeFile)
if err != nil {
t.Fatal("failed to remove the file")
}
}
3 changes: 3 additions & 0 deletions kadai1/fujiokayu/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module myConverter

go 1.12
40 changes: 40 additions & 0 deletions kadai1/fujiokayu/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"log"
"myConverter/args"
"myConverter/converter"
"myConverter/walker"
"path/filepath"
"strings"
)

// フォルダ探索によって見つかったファイルの拡張子が、decodeType と一致した場合に converter を起動する。
func execute(filePath string, decodeType string, encodeType string) {
// filepath.Ext が抽出する拡張子は "." を含むため、オプションで指定された拡張子と揃えるために "." を除去する。
ext := strings.ToLower(strings.Trim(filepath.Ext(filePath), "."))

if ext == strings.ToLower(decodeType) {
err := converter.Convert(filePath, ext, encodeType)
if err != nil {
log.Fatal(err)
}
}
}

func main() {
args, err := args.ParseArgs()
if err != nil {
log.Fatal(err)
}

folder := strings.Join(args.RootFolderName, " ")

ch, err := walker.Walk(folder)
if err != nil {
log.Fatal(err)
}
for filePath := range ch {
execute(filePath, args.DecodeType, args.EncodeType)
}
}
Empty file.
Empty file.
Binary file added kadai1/fujiokayu/testdata/cat.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
43 changes: 43 additions & 0 deletions kadai1/fujiokayu/walker/walker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package walker

import (
"fmt"
"os"
"path/filepath"
)

// assertDir: 引数の文字列が有効なディレクトリか検証する
func assertDir(path string) error {
info, err := os.Stat(path)
if err == nil {
if !info.IsDir() {
err = fmt.Errorf("directory is not valid")
}
}
return err
}

// Walk :指定されたディレクトリを再帰的に操作し、見つかったファイルをチャネルで返す。
func Walk(rootPath string) (chan string, error) {

ch := make(chan string)
err := assertDir(rootPath)
if err != nil {
return ch, err
}

// Todo: Add error handling
go func() {
err = filepath.Walk(rootPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
ch <- path
}
return nil
})
defer close(ch)
}()
return ch, err
}
24 changes: 24 additions & 0 deletions kadai1/fujiokayu/walker/walker_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package walker

import (
"fmt"
"testing"
)

func Test_Walk(t *testing.T) {
ch, err := Walk("../testdata")
if err != nil {
t.Fatal(err)
}

count := 0
for filePath := range ch {
fmt.Println(filePath)
count++
}

if count != 4 {
fmt.Println(len(ch))
t.Fatal("failed test")
}
}