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

kadai2-torotake #19

Open
wants to merge 6 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
1 change: 1 addition & 0 deletions kadai1/torotake/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.vscode/
38 changes: 38 additions & 0 deletions kadai1/torotake/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Gopher道場#6 課題1

## 次の仕様を満たすコマンドを作って下さい
- [x] ディレクトリを指定する
- [x] 指定したディレクトリ以下のJPGファイルをPNGに変換(デフォルト)
- [x] ディレクトリ以下は再帰的に処理する
- [x] 変換前と変換後の画像形式を指定できる(オプション)

## 以下を満たすように開発してください
- [x] mainパッケージと分離する
- [x] 自作パッケージと標準パッケージと準標準パッケージのみ使う
- [x] 準標準パッケージ:golang.org/x以下のパッケージ
- [x] ユーザ定義型を作ってみる
- [x] GoDocを生成してみる

----

## ビルド

```sh
go build imgconv.go
```

## Usage

```sh
imgconv <options> [directory]

options
-i format : input image format "jpg"(default), "png", "gif", "bmp"
-o format : output image format "jpg", "png"(default), "gif", "bmp"
```

----

## 反省
* ユーザー定義型を意味ある形で使えている気がしません
* テストまで書けませんでした…
103 changes: 103 additions & 0 deletions kadai1/torotake/convert/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
Package convert は指定のディレクトリ以下に存在する指定された形式の画像ファイルを
別の形式の画像ファイルに変換するためのパッケージです。
*/
package convert

import (
"errors"
"fmt"
"image"
"image/gif"
"image/jpeg"
"image/png"
"os"
"path/filepath"

"golang.org/x/image/bmp"
)

// Format 画像ファイルの形式
type Format int

const (
// UNKNOWN 形式不明
UNKNOWN Format = iota - 1
// JPEG JPEG形式
JPEG
// PNG PNG形式
PNG
// GIF GIF形式
GIF
// BMP BMP形式
BMP
)

// Options 画像変換のオプション指定
type Options struct {
SrcFiles []string
OutputFormat Format
}

// Convert optionsに指定された内容に従って画像を変換します
func Convert(options Options) {
for _, src := range options.SrcFiles {
dst := getDst(src, options.OutputFormat)
fmt.Printf("convert %s -> %s ...\n", src, dst)
err := convertFormat(src, dst, options.OutputFormat)
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
}
}
}

func getDst(src string, f Format) string {
return src[:len(src)-len(filepath.Ext(src))] + getDstExt(f)
}

func getDstExt(f Format) string {
switch f {
case JPEG:
return ".jpg"
case PNG:
return ".png"
case GIF:
return ".gif"
case BMP:
return ".bmp"
}

return ""
}

func convertFormat(src string, dst string, format Format) error {
srcFp, err := os.Open(src)
if err != nil {
return err
}
defer srcFp.Close()

img, _, err := image.Decode(srcFp)
if err != nil {
return err
}

dstFp, err := os.Create(dst)
if err != nil {
return err
}
defer dstFp.Close()

switch format {
case JPEG:
return jpeg.Encode(dstFp, img, nil)
case PNG:
return png.Encode(dstFp, img)
case GIF:
return gif.Encode(dstFp, img, nil)
case BMP:
return bmp.Encode(dstFp, img)
}

return errors.New("Invalid format")
}
61 changes: 61 additions & 0 deletions kadai1/torotake/convert/files.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package convert

import (
"errors"
"os"
"path/filepath"
"strings"
)

// ListFiles 指定のディレクトリ(dir)以下を再帰的に探索し、指定フォーマット(format)の画像ファイルを列挙します。
func ListFiles(dir string, format Format) ([]string, error) {
if !isDir(dir) {
return nil, errors.New(dir + " is not directory.")
}

var files []string
filepath.Walk(dir, generateWalkFunc(&files, format))

return files, nil
}

func isDir(path string) bool {
info, err := os.Stat(path)
if err != nil {
return false
}

return info.IsDir()
}

func generateWalkFunc(files *[]string, format Format) func(string, os.FileInfo, error) error {
targetExts := getTargetExts(format)
return func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}

ext := strings.ToLower(filepath.Ext(path))
for _, v := range targetExts {
if v == ext {
*files = append(*files, path)
}
}
return nil
}
}

func getTargetExts(format Format) []string {
switch format {
case JPEG:
return []string{".jpeg", ".jpg"}
case PNG:
return []string{".png"}
case GIF:
return []string{".gif"}
case BMP:
return []string{".bmp"}
}

return []string{}
}
74 changes: 74 additions & 0 deletions kadai1/torotake/imgconv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package main

import (
"flag"
"fmt"
"os"
"strings"

"github.com/gopherdojo/dojo6/kadai1/torotake/convert"
)

var (
optInputFormat string
optOutputFormat string
exitCode int
)

func init() {
// -i=[変換前形式] default : jpeg
// -o=[変換後形式] default : png
// 変換対象ディレクトリ
flag.StringVar(&optInputFormat, "i", "jpeg", "input file format.")
flag.StringVar(&optOutputFormat, "o", "png", "output file format.")
flag.Parse()
}

func main() {
exec()
os.Exit(exitCode)
}

func exec() {
args := flag.Args()
inputFormat := getFormat(optInputFormat)
outputFormat := getFormat(optOutputFormat)

if len(args) == 0 {
fmt.Fprintf(os.Stderr, "%s\n", "option error : no target directory")
exitCode = 1
return
}

if inputFormat == convert.UNKNOWN || outputFormat == convert.UNKNOWN || inputFormat == outputFormat {
fmt.Fprintf(os.Stderr, "%s\n", "option error : invalid format")
exitCode = 1
return
}

files, err := convert.ListFiles(args[0], inputFormat)
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
exitCode = 2
return
}

convert.Convert(convert.Options{
SrcFiles: files,
OutputFormat: outputFormat})
}

func getFormat(f string) convert.Format {
f = strings.ToLower(f)
if f == "jpg" || f == "jpeg" {
return convert.JPEG
} else if f == "png" {
return convert.PNG
} else if f == "gif" {
return convert.GIF
} else if f == "bmp" {
return convert.BMP
}

return convert.UNKNOWN
}
Empty file added kadai2/.gitkeep
Empty file.
2 changes: 2 additions & 0 deletions kadai2/torotake/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.vscode/
profile
76 changes: 76 additions & 0 deletions kadai2/torotake/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Gopher道場#6 課題2

## io.Readerとio.Writerについて調べてみよう

### 標準パッケージでどのように使われているか

* 標準入力、標準出力、標準エラー出力 (osパッケージ)
* 暗号や圧縮などデータ変換ソース (crypt, gzipパッケージなど)
* ネットワーク通信の送受信 (netパッケージなど)
* 各種Reader/WriterをラッピングしてI/O処理にバッファリング機能を追加 (bufioパッケージ)


### io.Readerとio.Writerがあることでどういう利点があるのか具体例を挙げて考えてみる

ファイル、標準入出力、ネットワーク通信など様々なIO処理について、同じ取り扱う事が出来、入出力先の切り替えも容易となる。

例えば、fmt.Fprintfでは出力対象としてio.Writerを要求するので渡すものを変えるだけでファイルへの書き込み、標準出力への出力、ネットワークソケットへの送信処理等を行う事が可能。

```go
// 標準出力
fmt.Fprintf(os.Stderr, "Hello World.")

// ファイルへ書き込み
fp := os.Open("hoge.txt")
fmt.Fprintf(fp, "Hello World.")

// ネットワークへ送信
conn, _ := net.Dial("tcp", "example.com:80")
fmt.Fprintf(conn, "Hello World.")
```

----

## 1回目の宿題のテストを作ってみて下さい

- [ ] テストのしやすさを考えてリファクタリングしてみる
- [x] テストのカバレッジを取ってみる
- [x] テーブル駆動テストを行う
- [ ] テストヘルパーを作ってみる

テスト実行
```
$ go test -v github.com/gopherdojo/dojo6/kadai2/torotake/convert

=== RUN TestConvert
=== RUN TestConvert/ConvertJpegToPng
convert testdata/test/test_jpg.jpg -> testdata/test/test_jpg.png ...
=== RUN TestConvert/ConvertJpegToBmp
convert testdata/test/test_jpg.jpg -> testdata/test/test_jpg.bmp ...
=== RUN TestConvert/ConvertJpegToGif
convert testdata/test/test_jpg.jpg -> testdata/test/test_jpg.gif ...
=== RUN TestConvert/ConvertPngToJpeg
convert testdata/test/test_png.png -> testdata/test/test_png.jpg ...
=== RUN TestConvert/ConvertPngToBmp
convert testdata/test/test_png.png -> testdata/test/test_png.bmp ...
=== RUN TestConvert/ConvertPngToGif
convert testdata/test/test_png.png -> testdata/test/test_png.gif ...
--- PASS: TestConvert (1.77s)
--- PASS: TestConvert/ConvertJpegToPng (0.32s)
--- PASS: TestConvert/ConvertJpegToBmp (0.04s)
--- PASS: TestConvert/ConvertJpegToGif (0.70s)
--- PASS: TestConvert/ConvertPngToJpeg (0.06s)
--- PASS: TestConvert/ConvertPngToBmp (0.03s)
--- PASS: TestConvert/ConvertPngToGif (0.59s)
=== RUN TestListFiles
--- PASS: TestListFiles (0.00s)
PASS
ok github.com/gopherdojo/dojo6/kadai2/torotake/convert 1.776s
```

カバレッジ
```
$ go test -coverprofile=profile github.com/gopherdojo/dojo6/kadai2/torotake/convert

ok github.com/gopherdojo/dojo6/kadai2/torotake/convert 1.771s coverage: 77.8% of statements
```
Loading