Open13

Goのあれこれ

utf-8の文字列から指定した位置から部分抽出する

package main

import "fmt"

func main() {
	s := "テスト文字列で指定の文字だけ抽出したい"
	fmt.Println(string([]rune(s)[2:5]))
}

// output:
// ト文字

https://go.dev/play/p/WcflojCwlFi

type switcher

var x interface{} = "foo"

switch v := x.(type) {
case nil:
    fmt.Println("x is nil")            // here v has type interface{}
case int: 
    fmt.Println("x is", v)             // here v has type int
case bool, string:
    fmt.Println("x is bool or string") // here v has type interface{}
default:
    fmt.Println("type unknown")        // here v has type interface{}
}

file uploaderのテスト例

    filePath := "file.jpg"
    fieldName := "file"
    body := new(bytes.Buffer)

    mw := multipart.NewWriter(body)

    file, err := os.Open(filePath)
    if err != nil {
        t.Fatal(err)
    }

    w, err := mw.CreateFormFile(fieldName, filePath)
    if err != nil {
        t.Fatal(err)
    }

    if _, err := io.Copy(w, file); err != nil {
        t.Fatal(err)
    }

    // close the writer before making the request
    mw.Close()

    req := httptest.NewRequest(http.MethodPost, "/upload", body)
    
    req.Header.Add("Content-Type", mw.FormDataContentType())

    res := httptest.NewRecorder()

    // router is of type http.Handler
    router.ServeHTTP(res, req)

https://stackoverflow.com/questions/43904974/testing-go-http-request-formfile
  • bodyにstringで詰め合わせればいいんだ
const (
	fileaContents = "This is a test file."
	filebContents = "Another test file."
	textaValue    = "foo"
	textbValue    = "bar"
	boundary      = `MyBoundary`
)

const message = `
--MyBoundary
Content-Disposition: form-data; name="filea"; filename="filea.txt"
Content-Type: text/plain

` + fileaContents + `
--MyBoundary
Content-Disposition: form-data; name="fileb"; filename="fileb.txt"
Content-Type: text/plain

` + filebContents + `
--MyBoundary
Content-Disposition: form-data; name="texta"

` + textaValue + `
--MyBoundary
Content-Disposition: form-data; name="textb"

` + textbValue + `
--MyBoundary--
`

func newTestMultipartRequest(t *testing.T) *Request {
	b := strings.NewReader(strings.ReplaceAll(message, "\n", "\r\n"))
	req, err := NewRequest("POST", "/", b)
	if err != nil {
		t.Fatal("NewRequest:", err)
	}
	ctype := fmt.Sprintf(`multipart/form-data; boundary="%s"`, boundary)
	req.Header.Set("Content-type", ctype)
	return req
}

https://go.dev/src/net/http/request_test.go

APIでNullを扱う

ケース

  • request.body設定がない場合string:nullが入る。
    • pointer型にするのが王道。
    • ただ、keyがoptionalな場合、keyが含まれない場合とnullで区別ができない(両方nilになる)
package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
    "gopkg.in/guregu/null.v3"
    "gopkg.in/guregu/null.v3/zero"
)

type (
    User struct {
        Name null.String `json:"name"`
        Age null.Int `json:"age"`
    }
)

func main() {
    db, err := sql.Open("mysql", "root:@/guregu")
    if err != nil {
        panic(err)
    }
    defer db.Close()

    rows, err := db.Query("SELECT * FROM user")
    if err != nil {
        return
    }

    for rows.Next() {
        var user User
        rows.Scan(&user.Name, &user.Age)
        JSON, err := json.Marshal(user)
        if err != nil {
            continue
        }
        fmt.Println(string(JSON))
    }
}
{"name":"tarou","age":12}
{"name":null,"age":null}
{"name":"","age":0}
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
)

type NullSegments struct {
    Segments Segments
    Valid    bool // Valid is true if Segments is not NULL
}

type Range struct {
    From int `json:"from"`
    To   int `json:"to"`
}

type Segments struct {
    Status int   `json:"status"`
    Range  Range `json:"range"`
}

type Settings struct {
    Segments NullSegments `json:"segments"`
}

var nullLiteral = []byte("null")

func (s *NullSegments) UnmarshalJSON(b []byte) error {
    if bytes.Equal(b, nullLiteral) {
        return nil
    }

    err := json.Unmarshal(b, &s.Segments)
    if err == nil {
        s.Valid = true
        return nil
    }

    return err
}

func (s NullSegments) MarshalJSON() ([]byte, error) {
    if s.Valid {
        return json.Marshal(s.Segments)
    } else {
        return nullLiteral, nil
    }
}

func main() {
    b := []byte(`{"segments": {"status": 1, "range": {"from": 10, "to": 20}}}`)
    // b := []byte(`{"segments": null }`)

    s := &Settings{}

    json.Unmarshal(b, s)

    bb, _ := json.Marshal(s)

    fmt.Println(string(bb))
}

...引数(スプレット構文的使用法)

package main

import (
	"fmt"
)

func Greeting(prefix string, who ...string) {
	for _, w := range who {
		fmt.Printf("%s %s\n", prefix, w)
	}
}

func main() {
	Greeting("hello:", "Joe", "Anna", "Eileen")
	s := []string{"James", "Jasmine", "John", "Jules"}
	Greeting("good afternoon:", s...)
	Greeting("goodbye:", s[2:]...)
	Greeting("こんにちは", "私")
}
hello: Joe
hello: Anna
hello: Eileen
good afternoon: James
good afternoon: Jasmine
good afternoon: John
good afternoon: Jules
goodbye: John
goodbye: Jules
こんにちは 私

Program exited.

https://go.dev/play/p/UuFGV5uJIpA

スプレット構文となっている第二引数は存在しなくても実行可能

package main

import (
	"fmt"
)

func Greeting(prefix string, who ...string) {
	for _, w := range who {
		fmt.Printf("%s %s\n", prefix, w)
	}
}

func main() {
	Greeting("こんにちは")
}
Program exited.

Mapの要素数取得

package main

import "fmt"

func main() {
	m := map[string]int{"apple": 150, "banana": 300, "lemon": 300}

	fmt.Println(len(m))
}
3

Program exited.

github.com/volatiletech/null/v8

  • nullでない場合、Validがtrueになる
package main

import (
	"fmt"

	"github.com/volatiletech/null/v8"
)

func main() {
	i := null.IntFrom(12)
	fmt.Printf("%+v", i)
}
{Int:12 Valid:true}
Program exited.

https://go.dev/play/p/c7bm8aL__i-

go testを複数回実行する

makeで実行

.PHONY: go-test-carefully
go-test-c:
	for ((i=0; i<100; i++)); \
	do go test -count=1; \
	done
ログインするとコメントできます