Skip to content

kadai2-micchie #17

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

Open
wants to merge 8 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
14 changes: 14 additions & 0 deletions kadai1/micchie/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Gopher 道場
## 6-1: 画像変換コマンドを作ろう
### 1. 次の仕様を満たすコマンドを作って下さい
- ディレクトリを指定する
- 指定したディレクトリ以下の JPG ファイルをPNGに変換 (デフォルト)
- ディレクトリ以下は再帰的に処理する
- 変換前と変換後の画像形式を指定できる (オプション)

### 2. 以下を満たすように開発してください
- mainパッケージと分離する
- 自作パッケージと標準パッケージと準標準パッケージのみ使う
- 準標準パッケージ: `golang.org/x` 以下のパッケージ
- ユーザ定義型を作ってみる
- GoDoc を生成してみる
5 changes: 5 additions & 0 deletions kadai1/micchie/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/gopherdojo/dojo6/kadai1/micchie

go 1.12

require github.com/pkg/errors v0.8.1
2 changes: 2 additions & 0 deletions kadai1/micchie/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
69 changes: 69 additions & 0 deletions kadai1/micchie/imgconvert/imgconvert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package imgconvert

import (
"image"
"image/jpeg"
"image/png"
"os"
"path/filepath"
"strings"
)

// ImageFile is the Image file.
type ImageFile string

// Directory is the File directory.
func (i ImageFile) Directory() (string, error) {
p, err := filepath.Abs(filepath.Clean(string(i)))
return filepath.Dir(p), err
}

// Ext is the File extention.
func (i ImageFile) Ext() string {
return strings.ToLower(filepath.Ext(string(i)))
}

// Name is the File name without extension.
func (i ImageFile) Name() string {
file := string(i)
return filepath.Base(file[:len(file)-len(i.Ext())])
}

// ConvertImage configures convert images.
type ConvertImage struct {
Before ImageFile
After ImageFile
}

// Convert writes the Image to format.
func (cnv ConvertImage) Convert(format string) error {
bp, err := os.Open(string(cnv.Before))
if err != nil {
return err
}
defer bp.Close()

ap, err := os.Create(string(cnv.After))
if err != nil {
return err
}
defer ap.Close()

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

switch format {
case "png":
err = png.Encode(ap, img)
case "jpg":
opts := &jpeg.Options{Quality: jpeg.DefaultQuality}
err = jpeg.Encode(ap, img, opts)
}

if err != nil {
return err
}
return nil
}
94 changes: 94 additions & 0 deletions kadai1/micchie/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package main

import (
"flag"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/gopherdojo/dojo6/kadai1/micchie/imgconvert"
"github.com/pkg/errors"
)

var (
dir string
beforeFormat string
afterFormat string
)

func init() {
flag.StringVar(&dir, "d", "", "path of images to convert [required]")
flag.StringVar(&beforeFormat, "before", "jpg", "image format before conversion")
flag.StringVar(&afterFormat, "after", "png", "image format after conversion")
flag.Parse()
}

func main() {
if err := exec(); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err.Error())
os.Exit(1)
}
}

func exec() error {
d := strings.TrimSpace(dir)
if d == "" {
return errors.New("directory is required")
}

info, err := os.Stat(d)
if os.IsNotExist(err) {
return errors.Wrap(err, "directory does not exist")
}

if !info.IsDir() {
return errors.New("set a directory")
}

var bf, af string
bf, err = validationFormat(beforeFormat)
if err != nil {
return errors.Wrapf(err, "format: %v", beforeFormat)
}

af, err = validationFormat(afterFormat)
if err != nil {
return errors.Wrapf(err, "format: %v", afterFormat)
}

return filepath.Walk(d, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}

if info.IsDir() {
return nil
}

img := &imgconvert.ConvertImage{}
img.Before = imgconvert.ImageFile(p)

if img.Before.Ext() != fmt.Sprintf(".%v", bf) {
return nil
}

dir, err := img.Before.Directory()
if err != nil {
return nil
}

img.After = imgconvert.ImageFile(fmt.Sprintf("%v/%v.%v", dir, img.Before.Name(), af))

return img.Convert(af)
})
}

func validationFormat(format string) (string, error) {
f := strings.ToLower(format)
switch {
case f == "jpg" || f == "png":
return f, nil
}
return "", errors.New("format is not valid")
}
29 changes: 29 additions & 0 deletions kadai2/micchie/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Gopher 道場
## 6-2-1: io.Readerとio.Writer
io.Reader と io.Writer について調べてみよう。

### 1. 標準パッケージでどのように使われているか
- バッファ付入出力:
- https://golang.org/pkg/bufio/
- 文字列フォーマット:
https://golang.org/pkg/fmt/
- Fprint, Fprintf, Fprintln
- Fscan, Fscanf, Fscanln
- JSON エンコード・デコード:
- https://golang.org/pkg/encoding/json/
- NewDecorder, NewEncoder
- HTTP:
- https://golang.org/pkg/net/http/

### 2. io.Reader と io.Writer があることでどういう利点があるのか具体例を挙げて考えてみる
- 入出力を一律 io.Reader, io.Writer のインターフェースで抽象化しているため, 「読み書きをする」ことに対してはすべて共通して扱える。
- io.Pipe を利用することで, 複数の Read, Write を効率よくつなげて処理できるので, 大量のデータを扱う場合はメモリの節約ができるのでは。
- https://golang.org/pkg/io/#Pipe

## 6-2-2: テストを書いてみよう
1回目の宿題のテストを作ってみて下さい。

- テストのしやすさを考えてリファクタリングしてみる
- テストのカバレッジを取ってみる
- テーブル駆動テストを行う
- テストヘルパーを作ってみる
5 changes: 5 additions & 0 deletions kadai2/micchie/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/gopherdojo/dojo6/kadai2/micchie

go 1.12

require github.com/pkg/errors v0.8.1
3 changes: 3 additions & 0 deletions kadai2/micchie/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
github.com/gopherdojo/dojo6 v0.0.0-20190710155631-1b40d3406c2f h1:UOg/ZpRwvokKADOjfCukjqQXYHb7HOdVSgZGABZc2BQ=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
69 changes: 69 additions & 0 deletions kadai2/micchie/imgconvert/imgconvert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package imgconvert

import (
"image"
"image/jpeg"
"image/png"
"os"
"path/filepath"
"strings"
)

// ImageFile is the Image file.
type ImageFile string

// Directory is the File directory.
func (i ImageFile) Directory() (string, error) {
p, err := filepath.Abs(filepath.Clean(string(i)))
return filepath.Dir(p), err
}

// Ext is the File extention.
func (i ImageFile) Ext() string {
return strings.ToLower(filepath.Ext(string(i)))
}

// Name is the File name without extension.
func (i ImageFile) Name() string {
file := string(i)
return filepath.Base(file[:len(file)-len(i.Ext())])
}

// ConvertImage configures convert images.
type ConvertImage struct {
Before ImageFile
After ImageFile
}

// Convert writes the Image to format.
func (cnv ConvertImage) Convert(format string) error {
bp, err := os.Open(string(cnv.Before))
if err != nil {
return err
}
defer bp.Close()

ap, err := os.Create(string(cnv.After))
if err != nil {
return err
}
defer ap.Close()

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

switch format {
case "png":
err = png.Encode(ap, img)
case "jpg":
opts := &jpeg.Options{Quality: jpeg.DefaultQuality}
err = jpeg.Encode(ap, img, opts)
}

if err != nil {
return err
}
return nil
}
74 changes: 74 additions & 0 deletions kadai2/micchie/imgconvert/imgconvert_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package imgconvert

import "testing"

func TestImageFile_Ext(t *testing.T) {
tests := []struct {
file string
ext string
}{
{file: "../testdata/test1.png", ext: ".png"},
{file: "../testdata/test2.png", ext: ".png"},
{file: "../testdata/test_directory/test3.jpg", ext: ".jpg"},
}

for _, test := range tests {
ext := ImageFile(test.file).Ext()
if ext != test.ext {
t.Errorf("file: %#v (want: %v, got: %v)", test.file, test.ext, ext)
continue
}
}
}

func TestImageFile_Name(t *testing.T) {
tests := []struct {
file string
name string
}{
{file: "../testdata/test1.png", name: "test1"},
{file: "../testdata/test2.png", name: "test2"},
{file: "../testdata/test_directory/test3.jpg", name: "test3"},
}

for _, test := range tests {
name := ImageFile(test.file).Name()
if name != test.name {
t.Errorf("file: %#v (want: %v, got: %v)", test.file, test.name, name)
continue
}
}
}

func TestConvertImage_Convert(t *testing.T) {
tests := []struct {
format string
before string
after string
hasError bool
}{
{format: "jpg", before: "../testdata/test1.png", after: "../testdata/test1.jpg", hasError: false},
{format: "png", before: "../testdata/test2.jpg", after: "../testdata/test2.png", hasError: false},
{format: "png", before: "../testdata/test_directory/test3.jpg", after: "../testdata/test_directory/test3.png", hasError: false},
{format: "pdf", before: "../testdata/test_directory/test4.pdf", after: "../testdata/test_directory/test4.pdf", hasError: true},
{format: "png", before: "../testdata/test/test5.png", after: "../testdata/test/test5.png", hasError: true},
}

for _, test := range tests {
images := &ConvertImage{
Before: ImageFile(test.before),
After: ImageFile(test.after),
}

var hasError bool

if err := images.Convert(test.format); err != nil {
hasError = true
}

if hasError != test.hasError {
t.Errorf("images: %#v (want: %v, got: %v)", images, test.hasError, hasError)
continue
}
}
}
Loading