-
Notifications
You must be signed in to change notification settings - Fork 0
hokita / 課題1 #5
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
base: master
Are you sure you want to change the base?
hokita / 課題1 #5
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# 課題 1 画像変換コマンドを作ろう | ||
|
||
## 課題内容 | ||
### 次の仕様を満たすコマンドを作って下さい | ||
|
||
- ディレクトリを指定する | ||
- 指定したディレクトリ以下の JPG ファイルを PNG に変換(デフォルト) | ||
- ディレクトリ以下は再帰的に処理する | ||
- 変換前と変換後の画像形式を指定できる(オプション) | ||
|
||
### 以下を満たすように開発してください | ||
|
||
- main パッケージと分離する | ||
- 自作パッケージと標準パッケージと準標準パッケージのみ使う | ||
- 準標準パッケージ:golang.org/x 以下のパッケージ | ||
- ユーザ定義型を作ってみる | ||
- GoDoc を生成してみる | ||
- Go Modules を使ってみる | ||
|
||
## 対応したこと | ||
- 画像を変換 | ||
- 現状はjpg, pngのみ | ||
- jpg, png以外はエラー表示 | ||
- 画像出力先は対象画像と同じディレクトリ | ||
- 指定したディレクトリが無いとエラーを表示 | ||
|
||
## 動作 | ||
```shell | ||
$ go build -o test_imgconv | ||
|
||
$ ./test_imgconv -h | ||
Usage of ./test_imgconv: | ||
-from string | ||
Conversion source extension. (default "jpg") | ||
-to string | ||
Conversion target extension. (default "png") | ||
|
||
# testdata内のすべてのjpgファイルをpngに変換する | ||
$ ./test_imgconv testdata | ||
Conversion finished! | ||
|
||
# testdata内のすべてのpngファイルをjpgに変換する | ||
$ ./test_imgconv -from png -to jpg testdata | ||
Conversion finished! | ||
|
||
# ディレクトリの指定が無い場合はエラー | ||
$ ./test_imgconv | ||
Please specify a directory. | ||
|
||
# 存在しないディレクトリの場合はエラー | ||
$ ./test_imgconv non_exist_dir | ||
Cannot find directory. | ||
|
||
# 対応していない拡張子の場合はエラー | ||
$ ./test_imgconv -from txt -to jpg testdata | ||
Selected extension is not supported. | ||
``` | ||
|
||
## 工夫したこと | ||
- png, jpg以外にも拡張子が増えそうなので、`image_type`というinterfaceを作ってみた。 | ||
- 拡張子の微妙な違い(jpg, jpeg, JPGなど)にも対応できるようにした。 | ||
|
||
## わからなかったこと、むずかしかったこと | ||
- go mod initで指定するmodule名に命名規則があるのか。 | ||
- 普段オブジェクト指向(その上動的型付け言語)で書いているので、それがgoらしいコードになっているのか不安。 | ||
- なんでもかんでも構造体メソッドにしたい願望がでてくる |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module imgconv | ||
|
||
go 1.14 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package imgconv | ||
|
||
import ( | ||
"errors" | ||
"image" | ||
"os" | ||
"path/filepath" | ||
) | ||
|
||
func newConverter(from, to string) (*Converter, error) { | ||
fromImage, err := selectImage("." + from) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
toImage, err := selectImage("." + to) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &Converter{fromImage, toImage}, nil | ||
} | ||
|
||
var ErrUnmatchExt = errors.New("ext does not match") | ||
|
||
func IsNotMatchExt(err error) bool { | ||
return errors.Is(err, ErrUnmatchExt) | ||
} | ||
|
||
type Converter struct { | ||
fromImage ImageType | ||
toImage ImageType | ||
} | ||
|
||
func (conv *Converter) Execute(path string) (rerr error) { | ||
// ignore unrelated file | ||
if !conv.fromImage.IsMatchExt(filepath.Ext(path)) { | ||
return ErrUnmatchExt | ||
} | ||
|
||
// file open | ||
file, err := os.Open(path) | ||
defer file.Close() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. defer Closeはエラー処理のあと |
||
if err != nil { | ||
return err | ||
} | ||
|
||
// convert to image obj | ||
img, _, err := image.Decode(file) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// output file | ||
out, err := os.Create(conv.SwitchExt(path)) | ||
defer func() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. defer Closeはエラー処理のあと |
||
if err := out.Close(); err != nil { | ||
rerr = err | ||
} | ||
}() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// output image | ||
if err := conv.toImage.Encode(out, img); err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func (conv *Converter) SwitchExt(path string) string { | ||
ext := filepath.Ext(path) | ||
toExt := conv.toImage.GetMainExt() | ||
|
||
return path[:len(path)-len(ext)] + toExt | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package imgconv | ||
|
||
import ( | ||
"errors" | ||
"image" | ||
"io" | ||
) | ||
|
||
type ImageType interface { | ||
Encode(w io.Writer, m image.Image) error | ||
IsMatchExt(ext string) bool | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
GetMainExt() string | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. こちらも |
||
} | ||
|
||
func selectImage(ext string) (ImageType, error) { | ||
pngImage := PngImage{} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. フィールドの初期化を伴わない構造体リテラル(コンポジットリテラル)での初期化は不要です。 |
||
jpegImage := JpegImage{} | ||
|
||
if pngImage.IsMatchExt(ext) { | ||
return pngImage, nil | ||
} else if jpegImage.IsMatchExt(ext) { | ||
return jpegImage, nil | ||
} | ||
|
||
return nil, errors.New("Selected extension is not supported.") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package imgconv | ||
|
||
import ( | ||
"errors" | ||
"os" | ||
"path/filepath" | ||
) | ||
|
||
func Call(dir, from, to string) error { | ||
if dir == "" { | ||
return errors.New("Please specify a directory.") | ||
} | ||
|
||
if f, err := os.Stat(dir); os.IsNotExist(err) || !f.IsDir() { | ||
return errors.New("Cannot find directory.") | ||
} | ||
|
||
converter, err := newConverter(from, to) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = filepath.Walk(dir, | ||
func(path string, info os.FileInfo, err error) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 引数のエラーのエラー処理。 |
||
err = converter.Execute(path) | ||
if !IsNotMatchExt(err) { | ||
return err | ||
} | ||
return nil | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package imgconv | ||
|
||
import ( | ||
"image" | ||
"image/jpeg" | ||
"io" | ||
) | ||
|
||
const QUALITY = 100 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 定数であってもキャメルケースで書きます。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JPEGのQUALITYっていうのが分かるようにしたほうが良さそうですね。 |
||
|
||
type JpegImage struct{} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JPEGで十分だと思います。Goでは略語はすべて大文字か全部小文字にします。 |
||
|
||
var jpegExt = map[string]bool{ | ||
".jpg": true, | ||
".jpeg": true, | ||
".JPG": true, | ||
".JPEG": true, | ||
} | ||
|
||
func (JpegImage) Encode(w io.Writer, m image.Image) error { | ||
err := jpeg.Encode(w, m, &jpeg.Options{Quality: QUALITY}) | ||
return err | ||
} | ||
|
||
func (ji JpegImage) IsMatchExt(ext string) bool { | ||
return jpegExt[ext] | ||
} | ||
|
||
func (JpegImage) GetMainExt() string { | ||
return ".jpg" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package imgconv | ||
|
||
import ( | ||
"image" | ||
"image/png" | ||
"io" | ||
) | ||
|
||
type PngImage struct{} | ||
|
||
var pngExt = map[string]bool{ | ||
".png": true, | ||
".PNG": true, | ||
} | ||
|
||
func (PngImage) Encode(w io.Writer, m image.Image) error { | ||
err := png.Encode(w, m) | ||
return err | ||
} | ||
|
||
func (pi PngImage) IsMatchExt(ext string) bool { | ||
return pngExt[ext] | ||
} | ||
|
||
func (PngImage) GetMainExt() string { | ||
return ".png" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"imgconv/imgconv" | ||
"os" | ||
) | ||
|
||
const ( | ||
ExitCodeOk = 0 | ||
ExitCodeError = 1 | ||
) | ||
|
||
var from, to string | ||
|
||
func init() { | ||
flag.StringVar(&from, "from", "jpg", "Conversion source extension.") | ||
flag.StringVar(&to, "to", "png", "Conversion target extension.") | ||
} | ||
|
||
func main() { | ||
os.Exit(run()) | ||
} | ||
|
||
func run() int { | ||
flag.Parse() | ||
dir := flag.Arg(0) | ||
|
||
err := imgconv.Call(dir, from, to) | ||
|
||
if err != nil { | ||
fmt.Fprintln(os.Stderr, err) | ||
return ExitCodeError | ||
} | ||
|
||
fmt.Println("Conversion finished!") | ||
return ExitCodeOk | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IsNotMatchExt
関数が公開されているのでこちらは公開しないほうが良いです。