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

kynefuk / 課題2 #18

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
27 changes: 27 additions & 0 deletions kadai2/kynefuk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Created by https://www.toptal.com/developers/gitignore/api/go
# Edit at https://www.toptal.com/developers/gitignore?templates=go

### Go ###
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

### Go Patch ###
/vendor/
/Godeps/

img

# End of https://www.toptal.com/developers/gitign
40 changes: 40 additions & 0 deletions kadai2/kynefuk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# <課題 2> io.Reader と io.Writer

### io.Reader とは

一連のデータ(バイト列)の読み込み処理を抽象化したインターフェース。
読み込まれる対象データとしてバイト型の配列を受け取り、読み込んだバイト数とエラーを戻り値とする。

```
type Reader interface {
Read(p []byte) (n int, err error)
}
```

### io.Writer とは

一連のデータ(バイト列)の書き込み処理を抽象化したインターフェース
書き込まれる対象データとしてバイト型の配列を受け取り、書き込んだバイト数とエラーを戻り値とする。

```
type Writer interface {
Write(p []byte) (n int, err error)
}
```

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

データの入出力を表現する様々なパッケージで io.Reader や io.Writer が実装している。
(標準入力を表す`os.Stdin`、標準出力を表す`os.Stdout`、ファイルの入出力を表現する`os.File`や、通信の入出力を表現する`net.Conn`など)

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

入力処理と出力処理の仕様をインターフェースとして定義しておくことで、様々な入出力処理を統一的に扱うことができ、プログラムの柔軟性が高くなる。
例えば、何らかの読み込み処理を行う関数の引数の型を`io.Reader`とすることで、`io.Reader`を実装している型であれば何でも受けることができる。
コマンドラインのの処理内容を出力する標準出力や、エラーを表示する標準エラー出力のフィールドの型として io.Writer を指定することで、テスト時に、`bytes.Buffer`などに切り替えることができ、テスト容易性を高めることができる。

```
type Command struct {
OutStream, ErrStream io.Writer
}
```
44 changes: 44 additions & 0 deletions kadai2/kynefuk/cli/args.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package cli

import (
"fmt"
)

// Args represents CLI's arguments object
type Args struct {
DirecTory, From, To string
}

// Validate is a validation method
func (args *Args) Validate() error {
fileExtMap := createFileExtMap()
if _, ok := fileExtMap[args.From]; !ok {
return fmt.Errorf("argument of \"-f, --From\" is not valid file format. invalid format: %s", args.From)
}
if _, ok := fileExtMap[args.To]; !ok {
return fmt.Errorf("argument of \"-t, --To\" is not valid file format. invalid format: %s", args.To)
}

return nil
}

func createFileExtMap() map[string]string {
fileExtMap := make(map[string]string)
list := []string{
"jpg",
"jpeg",
"png",
"gif",
"bmp",
"tiff",
}
for _, v := range list {
fileExtMap[v] = ""
}
return fileExtMap
}

// NewArgs is a construcTor of Args
func NewArgs(DirecTory, From, To string) *Args {
return &Args{DirecTory: DirecTory, From: From, To: To}
}
31 changes: 31 additions & 0 deletions kadai2/kynefuk/cli/args_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cli

import (
"testing"

"github.com/gopherdojo/dojo8/kadai2/kynefuk/helper"
)

const dummyDir = "dummy"

func TestArgs(t *testing.T) {
cases := []struct{ name, fromFormat, toFormat, expectedMessage string }{
{name: "valid arguments", fromFormat: "jpeg", toFormat: "png", expectedMessage: ""},
{name: "invalid from format", fromFormat: "hoge", toFormat: "jpg", expectedMessage: "argument of \"-f, --From\" is not valid file format. invalid format: hoge"},
{name: "invalid to format", fromFormat: "gif", toFormat: "hage", expectedMessage: "argument of \"-t, --To\" is not valid file format. invalid format: hage"},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
args := NewArgs(dummyDir, c.fromFormat, c.toFormat)
actual := args.Validate()
if actual == nil {
if c.expectedMessage != "" {
helper.ErrorHelper(t, actual, c.expectedMessage)
}
} else if actual.Error() != c.expectedMessage {
helper.ErrorHelper(t, actual, c.expectedMessage)
}
})
}
}
45 changes: 45 additions & 0 deletions kadai2/kynefuk/cli/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package cli

import (
"fmt"
"io"

"github.com/gopherdojo/dojo8/kadai2/kynefuk/converter"
"github.com/gopherdojo/dojo8/kadai2/kynefuk/walker"
)

// it represents Exit Code
const (
ExitCodeOK = iota
ExitCodeError
)

// Command represents CLI object
type Command struct {
OutStream, ErrStream io.Writer
}

// Run invoke cli main logic
func (cli *Command) Run(directory, from, to string) int {
dirWalker := walker.NewWalker(from)
files, err := dirWalker.Dirwalk(directory)
if err != nil {
fmt.Fprintf(cli.ErrStream, "failed to read directory: %s, err: %s\n", directory, err)
return ExitCodeError
}

imgConverter := converter.NewConverter(from, to)
for _, file := range files {
dstPath := converter.ConvertExt(file, from, to)
if err := imgConverter.ConvertFormat(file, dstPath); err != nil {
fmt.Fprintf(cli.ErrStream, "failed to convert img, err: %s\n", err)
return ExitCodeError
}
}
return ExitCodeOK
}

// NewCommand is a constructor of CLI
func NewCommand(outStream, errStream io.Writer) *Command {
return &Command{OutStream: outStream, ErrStream: errStream}
}
63 changes: 63 additions & 0 deletions kadai2/kynefuk/cli/cli_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package cli

import (
"bytes"
"os"
"strings"
"testing"

"github.com/gopherdojo/dojo8/kadai2/kynefuk/helper"
)

var fileExtList = []string{
"jpg",
"jpeg",
"png",
"gif",
"bmp",
"tiff",
}

func ConvertExt(filepath, to string) string {
return strings.Replace(filepath, ".png", to, 1)
}

func TestCLI(t *testing.T) {
testDir := helper.CreateTmpDir()
defer os.RemoveAll(testDir)
outStream, errStream := new(bytes.Buffer), new(bytes.Buffer)
command := NewCommand(outStream, errStream)

cases := []struct {
name string
directory string
fromFormat string
toFormat string
outStream string
errStream string
exitCode int
}{
{name: "success", directory: testDir, fromFormat: "png", toFormat: "jpg", outStream: "", errStream: "", exitCode: 0},
{name: "invalid directory", directory: "dummyDir", fromFormat: "jpg", toFormat: "png", outStream: "", errStream: "failed to read directory: dummyDir, err: open dummyDir: no such file or directory", exitCode: 1},
{name: "invalid to format", directory: testDir, fromFormat: "jpg", toFormat: "hoge", outStream: "", errStream: "failed to convert img, err: unknown format type", exitCode: 1},
{name: "invalid from format", directory: testDir, fromFormat: "hoge", toFormat: "png", outStream: "", errStream: "", exitCode: 1},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
tmpFile := helper.CreateTmpFile(testDir, c.fromFormat)
defer os.Remove(tmpFile.Name())
status := command.Run(c.directory, c.fromFormat, c.toFormat)

if status != c.exitCode {
helper.ErrorHelper(t, c.exitCode, status)
}
if !strings.Contains(outStream.String(), c.outStream) {
helper.ErrorHelper(t, c.outStream, outStream.String())
}
if !strings.Contains(errStream.String(), c.errStream) {
helper.ErrorHelper(t, c.errStream, errStream.String())
}
})
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
66 changes: 66 additions & 0 deletions kadai2/kynefuk/converter/converter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package converter

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

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

// Converter converts image file
type Converter struct {
From, To string
}

// ConvertFormat converts file format
func (converter *Converter) ConvertFormat(filepath, dst string) error {
file, err := os.Open(filepath)
if err != nil {
return fmt.Errorf("failed to open file. file: %s", filepath)
}
defer file.Close()

img, _, err := image.Decode(file)
if err != nil {
return fmt.Errorf("failed to decode file. file: %s", filepath)
}

out, err := os.Create(dst)
if err != nil {
return fmt.Errorf("failed to create output file. file: %s", dst)
}
defer out.Close()

switch converter.To {
case "jpg", "jpeg":
err = jpeg.Encode(out, img, &jpeg.Options{Quality: 100})
case "png":
err = png.Encode(out, img)
case "gif":
err = gif.Encode(out, img, &gif.Options{NumColors: 256, Quantizer: nil, Drawer: nil})
case "bmp":
err = bmp.Encode(out, img)
case "tiff":
err = tiff.Encode(out, img, nil)
default:
return fmt.Errorf("unknown format type")
}

return err
}

// ConvertExt converts file format extention
func ConvertExt(filepath, from, to string) string {
return strings.Replace(filepath, from, to, 1)
}

// NewConverter is a Constructor of Converter
func NewConverter(from, to string) *Converter {
return &Converter{From: from, To: to}
}
42 changes: 42 additions & 0 deletions kadai2/kynefuk/converter/converter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package converter

import (
"os"

"github.com/gopherdojo/dojo8/kadai2/kynefuk/helper"

"testing"
)

var fileExtList = []string{
"jpg",
"jpeg",
"png",
"gif",
"bmp",
"tiff",
}

const testDirPath = "../testdata"

func TestConverter(t *testing.T) {
tmpDir := helper.CreateTmpDir()
defer os.RemoveAll(tmpDir)

// from→toに画像変換できているかテスト
for _, from := range helper.FileExtList {
for _, to := range helper.FileExtList {
tmpFile := helper.CreateTmpFile(tmpDir, from)
defer os.Remove(tmpFile.Name())
imgConverter := NewConverter(from, to)
err := imgConverter.ConvertFormat(tmpFile.Name(), ConvertExt(tmpFile.Name(), from, to))
if err != nil {
t.Errorf("failed to test. error: %s", err)
}
exists := helper.Exists(ConvertExt(tmpFile.Name(), from, to))
if !exists {
t.Errorf("want true, but got %v", exists)
}
}
}
}
5 changes: 5 additions & 0 deletions kadai2/kynefuk/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/gopherdojo/dojo8/kadai2/kynefuk

go 1.14

require golang.org/x/image v0.0.0-20200618115811-c13761719519
4 changes: 4 additions & 0 deletions kadai2/kynefuk/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/gopherdojo/dojo8 v0.0.0-20200703052727-6a79d18126bf h1:lpYevjFQMxI5VNBc3WXV6Z5pDDrdppdDKwmeBoyt5BE=
golang.org/x/image v0.0.0-20200618115811-c13761719519 h1:1e2ufUJNM3lCHEY5jIgac/7UTjd6cgJNdatjPdFWf34=
golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Loading