Skip to content

Kadai1 tetuyosi #67

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 4 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
16 changes: 16 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}",
"args": ["testJpg"]
}
]
}
2 changes: 2 additions & 0 deletions kadai1/tetuyosi/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bin
main
2 changes: 2 additions & 0 deletions kadai1/tetuyosi/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build:
go build main.go
37 changes: 37 additions & 0 deletions kadai1/tetuyosi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
### 画像変換コマンドを作ろう

## 次の仕様を満たすコマンドを作って下さい

* ディレクトリを指定する
* 指定したディレクトリ以下のJPGファイルをPNGに変換(デフォルト)
* ディレクトリ以下は再帰的に処理する
* 変換前と変換後の画像形式を指定できる(オプション)

## 以下を満たすように開発してください

* mainパッケージと分離する
* 自作パッケージと標準パッケージと準標準パッケージのみ使う
- 準標準パッケージ:golang.org/x以下のパッケージ
* ユーザ定義型を作ってみる
* GoDocを生成してみる
* Go Modulesを使ってみる

## 利用方法

1. build
```
make build
```

2. 実行

- jpg -> png
```
./main <変換対象画像の入ったフォルダ>
```

- png -> jpg
```
./main <変換対象画像の入ったフォルダ> -srcExt png -destExt jpg
```

3 changes: 3 additions & 0 deletions kadai1/tetuyosi/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/tetuyosi/gopherdojo-studyroom/kadai1/tetuyosi

go 1.16
Empty file added kadai1/tetuyosi/go.sum
Empty file.
101 changes: 101 additions & 0 deletions kadai1/tetuyosi/iconv/iconv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// package iconv implements functions to convert images to a desired format.
package iconv

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

// A Convert image interface
type Conv struct {
FilePath string
SrcExt string
DestExt string
image image.Image
}

// jpeg or jpg return same extension
var extMap = map[string]string{
"jpg": "jpg",
"jpeg": "jpg",
"png": "png",
"gif": "gif",
}

// Return Conv object
func New(path string, srcExt string, destExt string) (*Conv, error) {
fileExt := strings.ToLower(strings.TrimLeft(filepath.Ext(path), "."))
srcExt = strings.ToLower(srcExt)
destExt = strings.ToLower(destExt)
if !isConvertible(fileExt) {
return nil, fmt.Errorf("変換ファイル拡張子が不正です。")
}
if !isConvertible(srcExt) {
return nil, fmt.Errorf("変換元フォーマット指定の誤りです。")
}
if !isConvertible(destExt) {
return nil, fmt.Errorf("変換先フォーマット指定の誤りです。")
}
return &Conv{FilePath: path, SrcExt: srcExt, DestExt: destExt}, nil
}

// Check convertible extension
func isConvertible(ext string) bool {
_, isExist := extMap[ext]
return isExist
}

// Read file and convert internal image
func (c *Conv) Imaging() error {
f, err := os.Open(c.FilePath)
if err != nil {
return fmt.Errorf("指定ファイルが開けません")
}
defer f.Close()

switch extMap[c.SrcExt] {
case "jpg":
c.image, err = jpeg.Decode(f)
case "png":
c.image, err = png.Decode(f)
case "gif":
c.image, err = gif.Decode(f)
default:
return fmt.Errorf("変換前拡張子が不正です")
}
if err != nil {
return fmt.Errorf("画像読み込みに失敗しました")
}
return nil
}

// internal image convert to desired format
func (c *Conv) Convert() error {
name := strings.TrimSuffix(filepath.Base(c.FilePath), c.SrcExt) + c.DestExt

f, err := os.Create(filepath.Dir(c.FilePath) + "/" + name)
if err != nil {
return fmt.Errorf("ファイル作成に失敗しました")
}
defer f.Close()
switch extMap[c.DestExt] {
case "jpg":
err = jpeg.Encode(f, c.image, &jpeg.Options{})
case "png":
err = png.Encode(f, c.image)
case "gif":
err = gif.Encode(f, c.image, &gif.Options{})
default:
return fmt.Errorf("変換後拡張子が不正です")
}
if err != nil {
return fmt.Errorf("画像変換に失敗しました")
}
return nil
}
132 changes: 132 additions & 0 deletions kadai1/tetuyosi/iconv/iconv_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package iconv_test

import (
"image"
"image/color"
"image/gif"
"image/jpeg"
"image/png"
"os"
"reflect"
"testing"

iconv "github.com/tetuyosi/gopherdojo-studyroom/kadai1/tetuyosi/iconv"
)

func TestMain(m *testing.M) {
code := m.Run()
os.Exit(code)
}

// 正常ケース
func Test_New_01(t *testing.T) {
actual, _ := iconv.New("test.jpg", "jpg", "png")
expected := iconv.Conv{
FilePath: "test.jpg",
SrcExt: "jpg",
DestExt: "png",
}
if !reflect.DeepEqual(actual, &expected) {
t.Errorf("got: %v\nwant: %v", actual, &expected)
}
}

// エラーケース
func Test_New_02(t *testing.T) {
_, actual := iconv.New("test.txt", "jpg", "png")
expected := "変換ファイル拡張子が不正です。"
if actual.Error() != expected {
t.Errorf("got: %v\nwant: %v", actual.Error(), expected)
}
}

func Test_New_03(t *testing.T) {
_, actual := iconv.New("test.jpg", "txt", "png")
expected := "変換元フォーマット指定の誤りです。"
if actual.Error() != expected {
t.Errorf("got: %v\nwant: %v", actual.Error(), expected)
}
}

func Test_New_04(t *testing.T) {
_, actual := iconv.New("test.jpg", "jpg", "txt")
expected := "変換先フォーマット指定の誤りです。"
if actual.Error() != expected {
t.Errorf("got: %v\nwant: %v", actual.Error(), expected)
}
}

func Test_New_05(t *testing.T) {
actual, _ := iconv.New("test.JPEG", "jpg", "png")
expected := iconv.Conv{
FilePath: "test.JPEG",
SrcExt: "jpg",
DestExt: "png",
}
if !reflect.DeepEqual(actual, &expected) {
t.Errorf("got: %v\nwant: %v", actual, &expected)
}
}

// test jpg -> png
func Test_Convert_01(t *testing.T) {
makeImage("jpg")
c, _ := iconv.New("test.jpg", "jpg", "png")
c.Imaging()
c.Convert()
_, actual := os.Stat("test.png")
if actual != nil {
t.Errorf("convert file not created")
}
defer func() {
os.Remove("test.jpg")
os.Remove("test.png")
}()
}

// test png -> jpg
func Test_Convert_02(t *testing.T) {
makeImage("png")
c, _ := iconv.New("test.png", "png", "jpg")
c.Imaging()
c.Convert()
_, actual := os.Stat("test.jpg")
if actual != nil {
t.Errorf("convert file not created")
}
defer func() {
os.Remove("test.jpg")
os.Remove("test.png")
}()
}

// test gif -> jpg
func Test_Convert_03(t *testing.T) {
makeImage("gif")
c, _ := iconv.New("test.gif", "gif", "jpg")
c.Imaging()
c.Convert()
_, actual := os.Stat("test.jpg")
if actual != nil {
t.Errorf("convert file not created")
}
defer func() {
os.Remove("test.jpg")
os.Remove("test.gif")
}()
}

func makeImage(t string) {
img := image.NewRGBA(image.Rect(0, 0, 100, 50))
img.Set(2, 3, color.RGBA{255, 0, 0, 255})
f, _ := os.OpenFile("test."+t, os.O_WRONLY|os.O_CREATE, 0600)
defer f.Close()
switch t {
case "jpg":
jpeg.Encode(f, img, &jpeg.Options{})
case "png":
png.Encode(f, img)
case "gif":
gif.Encode(f, img, &gif.Options{})
}
}
70 changes: 70 additions & 0 deletions kadai1/tetuyosi/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

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

iconv "github.com/tetuyosi/gopherdojo-studyroom/kadai1/tetuyosi/iconv"
)

// 変換対象ディレクトリ名
var dir string

// 変換対象拡張子(jpg,jpeg,png,gif)
var srcExt string

// 変換後拡張子(jpg,jpeg,png,gif)
var destExt string

func init() {
flag.StringVar(&srcExt, "srcExt", "jpg", "変換前画像フォーマット")
flag.StringVar(&destExt, "destExt", "png", "変換後画像フォーマット")
}

func main() {
flag.Parse()

dir = flag.Arg(0)
// 引数のチェック
if dir == "" {
fmt.Printf("変換対象ディレクトリ名を入れてください。")
os.Exit(1)
}
_, err := os.Stat(dir)
// フォルダチェック
if os.IsNotExist(err) {
fmt.Printf("指定ディレクトリは存在しません。%s", dir)
os.Exit(1)
}

// 画像変換
err = filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
c, err := iconv.New(path, srcExt, destExt)
if err != nil {
return nil
}
err = c.Imaging()
if err != nil {
return err
}
err = c.Convert()
if err != nil {
return err
}
return nil
})
if err != nil {
fmt.Printf("%s", err)
os.Exit(1)
}
os.Exit(0)
}