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

tanaka0325 / 課題2 #16

Open
wants to merge 27 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
33 changes: 33 additions & 0 deletions kadai2/tanaka0325/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# 課題2

## 【TRY】io.Readerとio.Writer

```
- io.Readerとio.Writerについて調べてみよう
- 標準パッケージでどのように使われているか
- io.Readerとio.Writerがあることでどういう利点があるのか具体例を挙げて考えてみる
```

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

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

身近なものでいうと `fmt` の `Fprint` 系の関数は `io.Writer` を引数にとり、そこに対して書き込みを行っている。
例えば `Println` は `io.Writer` として `os.Stdout` を `Fprintln` に渡して処理を行っている。

標準バッケージでは上記のほかにも、画像やファイルやhttpのリクエスト/レスポンスなど「何か読み書きできるやつ」を抽象化して扱えるように `io.Reader`, `io.Writer` が使われている。

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

- 「読み書きできるやつ」と抽象化することができるので、例えば書き込みをする関数を1つ作れば複数の構造体に対応できる
- `io.Writer` がない場合、「ファイルに対して書き込みする関数」「画像に対して書き込みする関数」のように構造体ごとに関数を準備しなくてはならない
- 抽象化することにより、具体的な構造体に依存しなくなるため、テスト時に差し替えることができモックなどでテストがしやくすなる
- 例えば画像を扱うテストをしたい時に、実際に画像を準備する必要がない


## 感想

- どういう構成にするのが良いのか考えながら何度もいじっていたらどんどん深みにハマり、何が良いのか全然わからなくなってしまった...
- `main` パッケージにはテストを書くほどの処理は書かないで複雑な処理を書きたいなら別途パッケージを切るべきかな?と思ったので今回は `main` パッケージのテストを書いていないがそれで良いのかわからず
- 現状で `main` には受け取ったオプション、引数まわりの処理などをしているので、もしパッケージを作るなら `cli` みたいなパッケージを作ろうと思ってますが、今くらいの規模であれば作らなくてよいかなと思いました

146 changes: 146 additions & 0 deletions kadai2/tanaka0325/imgconv/cmd/imgconv/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package main

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

"github.com/gopherdojo/dojo8/kadai2/tanaka0325/imgconv"
)

var (
args []string

from string
to string
dryRun bool
)

const (
fromUsageText = "before extension"
toUsageText = "after extension"
dryRunUsageText = "with dry-run"
)

var allowedExts = map[string]bool{
"png": true,
"jpg": true,
"jpeg": true,
"gif": true,
"bmp": true,
"tiff": true,
"tif": true,
}

func init() {
flag.StringVar(&from, "from", "jpg", fromUsageText)
flag.StringVar(&from, "f", "jpg", fromUsageText+" (short)")
flag.StringVar(&to, "to", "png", toUsageText)
flag.StringVar(&to, "t", "png", toUsageText+" (short)")
flag.BoolVar(&dryRun, "dry-run", false, dryRunUsageText)
flag.BoolVar(&dryRun, "n", false, dryRunUsageText+" (short)")
flag.Parse()

args = flag.Args()
}

func main() {
// validate options
if ok := isAllowedFileType(from); !ok {
onExit(fmt.Errorf("%s is invalid filetype", from))
}
if ok := isAllowedFileType(to); !ok {
onExit(fmt.Errorf("%s is invalid filetype", to))
}

// validate arguments
dirnames := uniq(args)
for _, dirname := range dirnames {
ok, err := isDir(dirname)
if err != nil {
onExit(err)
}
if !ok {
onExit(fmt.Errorf("%s is not a directory", dirname))
}
}

paths, err := getTargetFilepaths(dirnames, from)
if err != nil {
onExit(err)
}

for _, path := range paths {
param := imgconv.ConvertParam{
Path: path,
FileHandler: imgconv.NewFile(),
BeforeFormat: imgconv.NewImage(from),
AfterFormat: imgconv.NewImage(to),
}

if !dryRun {
if err := imgconv.Do(param); err != nil {
onExit(err)
}
} else {
e := len(param.Path) - len(from)
fmt.Printf("%s => %s%s\n", path, path[:e], to)
}
}
}

func isAllowedFileType(ft string) bool {
return allowedExts[ft]
}

func onExit(err error) {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

func uniq([]string) []string {
m := map[string]bool{}
u := []string{}

for _, v := range args {
if !m[v] {
m[v] = true

u = append(u, v)
}
}

return u
}

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

return fi.IsDir(), nil
}

func getTargetFilepaths(ds []string, ext string) ([]string, error) {
var names []string

for _, n := range ds {
if err := filepath.Walk(n, func(name string, info os.FileInfo, err error) error {
if err != nil {
return err
}

if filepath.Ext(name) == "."+ext {
names = append(names, name)
}

return nil
}); err != nil {
return nil, err
}
}

return names, nil
}
Loading