Full Code of mercari/go-httpdoc for AI

main 76d5c80539b9 cached
26 files
96.8 KB
32.0k tokens
187 symbols
1 requests
Download .txt
Repository: mercari/go-httpdoc
Branch: main
Commit: 76d5c80539b9
Files: 26
Total size: 96.8 KB

Directory structure:
gitextract_wz5xessz/

├── .github/
│   └── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── _example/
│   ├── README.md
│   ├── doc/
│   │   ├── protobuf.md
│   │   ├── simple.md
│   │   └── validate.md
│   ├── handler.go
│   ├── handler_proto_test.go
│   ├── handler_simple_test.go
│   ├── handler_validate_test.go
│   └── message.pb.go
├── doc.go
├── httpdoc.go
├── httpdoc_test.go
├── message_pb_test.go
├── proto/
│   └── message.proto
├── static/
│   ├── bindata.go
│   ├── static.go
│   └── tmpl/
│       └── doc.md.tmpl
├── template.go
├── template_test.go
├── validate.go
└── validate_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
Please read the CLA carefully before submitting your contribution to Mercari.
Under any circumstances, by submitting your contribution, you are deemed to accept and agree to be bound by the terms and conditions of the CLA.

https://www.mercari.com/cla/


================================================
FILE: .gitignore
================================================
httpdoc.md
coverage.txt

================================================
FILE: CHANGELOG.md
================================================
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [0.2.0] - 2018-02-13

In this release, we added breaking changes by [#18](https://github.com/mercari/go-httpdoc/pull/18). Now user can set custom asset function to each test cases. Since this added new field named `AssertFunc` to `TestCase` struct, the code which uses it without specifying field name will be broken. To migrate to new version easily, we add `NewTestCase` function. Check [#18](https://github.com/mercari/go-httpdoc/pull/18) and see how our example migrate to new `TestCase` by it.

### Added 

- Add MkdirAll if not exist output directory [#14](https://github.com/mercari/go-httpdoc/pull/14)

### Changed

- Allow to provide custom assert function to `TestCase` [#18](https://github.com/mercari/go-httpdoc/pull/18)

### Removed

- Stop support Go 1.6.x [#20](https://github.com/mercari/go-httpdoc/pull/20)


## [0.1.0] - 2017-06-01

Initial release. 

### Added 

- Fundamental features


================================================
FILE: LICENSE
================================================
Copyright (c) 2017 Mercari, Inc.

MIT License

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
# go-httpdoc [![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godoc]

[godoc]: http://godoc.org/go.mercari.io/go-httpdoc

`go-httpdoc` is a Golang package to generate API documentation from [`httptest`](https://golang.org/pkg/net/http/httptest/) test cases.

It provides a simple http middleware which records http requests and responses from tests and generates documentation automatically in markdown format. See [Sample Documentation](/_example/doc/validate.md). It also provides a way to validate values are equal to what you expect with annotation (e.g., you can add a description for headers, params or response fields). If you write proper tests, it will generate usable documentation (namely, it forces you to write good tests).

Not only JSON request and response but it also supports [protocol buffer](https://developers.google.com/protocol-buffers/). See [Sample ProtoBuf Documentation](/_example/doc/protobuf.md)).

See usage and example in [GoDoc](https://godoc.org/go.mercari.io/go-httpdoc).

*NOTE*: This package is experimental and may make backward-incompatible changes.

## Prerequisites

go-httpdoc requires Go 1.7 or later.

## Install

Use go get:

```
$ go get -u go.mercari.io/go-httpdoc
```

## Usage

All usage are described in [GoDoc](https://godoc.org/go.mercari.io/go-httpdoc).

To generate documentation, set the following env var:

```bash
$ export HTTPDOC=1
```

## Reference

The original idea came from [r7kamura/autodoc](https://github.com/r7kamura/autodoc) (rack middleware).

For struct inspection in validator, it uses [tenntenn/gpath](https://github.com/tenntenn/gpath) package.


================================================
FILE: _example/README.md
================================================
# httpdoc example

This directory contains some examples of `httpdoc`.

- [`handler_simple_test.go`](/_example/handler_simple_test.go) generates [`doc/simple.md`](/_example/doc/simple.md)
- [`handler_validate_test.go`](/_example/handler_validate_test.go) generates [`doc/validate.md`](/_example/doc/validate.md)
- [`handler_proto_test.go`](/_example/handler_proto_test.go) generates [`doc/protobuf.md`](/_example/doc/protobuf.md)

To generate documentation, run the following command:

```bash
$ env HTTPDOC=1 go test -v
```

One example uses protocol buffer, message definition is in [`../proto`](../proto) directory. To generate code from that, run the following command:

```bash
# Install protoc-gen-go if you don't have it
$ go get -u github.com/golang/protobuf/protoc-gen-go

$ protoc -I=./../proto --gofast_out=./ ../proto/message.proto
```


================================================
FILE: _example/doc/protobuf.md
================================================
# API doc

This is API documentation for Example API (with protobuf). This is generated by `httpdoc`. Don't edit by hand.

## Table of contents

- [[200] GET /v2/user/169743](#200-get-v2user169743)


## [200] GET /v2/user/169743

Get a user

### Request









### Response

Headers

| Name  | Value  | Description |
| ----- | :----- | :--------- |
| Content-Type | application/protobuf |  |



Response fields

| Name  | Value  | Description |
| ----- | :----- | :--------- |
| Name | Immortan Joe | User name |
| Setting.Email | immortan@madmax.com | User email |



Response example

<details>
<summary>Click to expand code.</summary>

```javascript
{
  "id": 169743,
  "name": "Immortan Joe",
  "active": true,
  "setting": {
    "email": "immortan@madmax.com"
  }
}

```

</details>





================================================
FILE: _example/doc/simple.md
================================================
# API doc

This is API documentation for Example API (simple). This is generated by `httpdoc`. Don't edit by hand.

## Table of contents

- [[200] POST /v1/user](#200-post-v1user)


## [200] POST /v1/user

Create a new user

### Request

Parameters

| Name  | Value  | Description |
| ----- | :----- | :--------- |
| token | 12345 |  |


Headers

| Name  | Value  | Description |
| ----- | :----- | :--------- |
| Accept-Encoding | gzip |  |
| User-Agent | Go-http-client/1.1 |  |
| X-Version | 2 |  |





Request example

<details>
<summary>Click to expand code.</summary>

```javascript
{
 "name": "tcnksm",
 "email": "tcnksm@mercari.com",
 "attribute": {
  "birthday": "1988-11-24"
 }
}

```

</details>


### Response

Headers

| Name  | Value  | Description |
| ----- | :----- | :--------- |
| Content-Type | application/json |  |





Response example

<details>
<summary>Click to expand code.</summary>

```javascript
{
 "id": 11241988,
 "name": "tcnksm"
}

```

</details>





================================================
FILE: _example/doc/validate.md
================================================
# API doc

This is API documentation for Example API (with validation). This is generated by `httpdoc`. Don't edit by hand.

## Table of contents

- [[200] POST /v1/user](#200-post-v1user)


## [200] POST /v1/user

Create a new user

### Request

Parameters

| Name  | Value  | Description |
| ----- | :----- | :--------- |
| pretty |  | Pretty print response message |
| token | 12345 | Request token |


Headers

| Name  | Value  | Description |
| ----- | :----- | :--------- |
| X-Version | 2 | Request API version |



Request fields

| Name  | Value  | Description |
| ----- | :----- | :--------- |
| Name | tcnksm | User Name |
| Email | tcnksm@mercari.com | User email address |
| Attribute.Birthday | 1988-11-24 | User birthday YYYY-MM-DD format |



Request example

<details>
<summary>Click to expand code.</summary>

```javascript
{
 "name": "tcnksm",
 "email": "tcnksm@mercari.com",
 "attribute": {
  "birthday": "1988-11-24"
 }
}

```

</details>


### Response

Headers

| Name  | Value  | Description |
| ----- | :----- | :--------- |
| Content-Type | application/json |  |



Response fields

| Name  | Value  | Description |
| ----- | :----- | :--------- |
| ID | 11241988 | User ID assigned |



Response example

<details>
<summary>Click to expand code.</summary>

```javascript
{
 "id": 11241988,
 "name": "tcnksm"
}

```

</details>





================================================
FILE: _example/handler.go
================================================
package main

import (
	"encoding/json"
	"net/http"
)

type createUserRequest struct {
	Name      string    `json:"name"`
	Email     string    `json:"email"`
	Attribute attribute `json:"attribute"`
}

type attribute struct {
	Birthday string `json:"birthday,omitempty"`
	Gender   string `json:"gender,omitempty"`
}

type createUserResponse struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

type userHandler struct {
}

type userProtoHandler struct {
}

func (h *userHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	if v := r.URL.Query().Get("token"); v != "12345" {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	var request createUserRequest
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(&request); err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// Some process...

	response := createUserResponse{
		ID:   11241988,
		Name: request.Name,
	}

	w.WriteHeader(http.StatusOK)
	w.Header().Set("Content-Type", "application/json")

	encoder := json.NewEncoder(w)
	encoder.SetIndent("", " ")
	encoder.Encode(&response)
}

func (h *userProtoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	response := &UserProtoResponse{
		Id:     169743,
		Name:   "Immortan Joe",
		Active: true,
		Setting: &UserProtoResponse_Setting{
			Email: "immortan@madmax.com",
		},
	}
	buf, err := response.Marshal()
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Header().Set("Content-Type", "application/protobuf")
	w.Write(buf)
}


================================================
FILE: _example/handler_proto_test.go
================================================
package main

import (
	"net/http"
	"net/http/httptest"
	"testing"

	httpdoc "go.mercari.io/go-httpdoc"
)

func TestUserHandlerWithProtobuf(t *testing.T) {
	document := &httpdoc.Document{
		Name: "Example API (with protobuf)",
		ExcludeHeaders: []string{
			"Accept-Encoding",
		},
	}
	defer func() {
		if err := document.Generate("doc/protobuf.md"); err != nil {
			t.Fatalf("err: %s", err)
		}
	}()

	mux := http.NewServeMux()
	mux.Handle("/v2/user/", httpdoc.Record(&userProtoHandler{}, document, &httpdoc.RecordOption{
		Description: "Get a user",
		ExcludeHeaders: []string{
			"User-Agent",
			"Content-Length",
		},

		WithValidate: func(validator *httpdoc.Validator) {
			validator.ResponseBody(t, []httpdoc.TestCase{
				httpdoc.NewTestCase("Name", "Immortan Joe", "User name"),
				httpdoc.NewTestCase("Setting.Email", "immortan@madmax.com", "User email")},
				&UserProtoResponse{},
			)
		},

		WithProtoBuffer: &httpdoc.ProtoBufferOption{
			ResponseUnmarshaler: &UserProtoResponse{},
		},
	}))

	testServer := httptest.NewServer(mux)
	defer testServer.Close()

	req, err := http.NewRequest("GET", testServer.URL+"/v2/user/169743", nil)
	if err != nil {
		t.Fatal(err)
	}

	if _, err := http.DefaultClient.Do(req); err != nil {
		t.Fatal(err)
	}
}


================================================
FILE: _example/handler_simple_test.go
================================================
package main

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	httpdoc "go.mercari.io/go-httpdoc"
)

func TestUserHandlerSimple(t *testing.T) {
	document := &httpdoc.Document{
		Name:           "Example API (simple)",
		ExcludeHeaders: []string{"Content-Length"},
	}
	defer func() {
		if err := document.Generate("doc/simple.md"); err != nil {
			t.Fatalf("err: %s", err)
		}
	}()

	mux := http.NewServeMux()
	mux.Handle("/v1/user", httpdoc.Record(&userHandler{}, document, &httpdoc.RecordOption{
		Description: "Create a new user",
	}))

	testServer := httptest.NewServer(mux)
	defer testServer.Close()

	req := testNewRequest(t, testServer.URL+"/v1/user?token=12345")
	if _, err := http.DefaultClient.Do(req); err != nil {
		t.Fatalf("err: %s", err)
	}
}

func testNewRequest(t *testing.T, urlStr string) *http.Request {
	var buf bytes.Buffer
	encoder := json.NewEncoder(&buf)
	encoder.SetIndent("", " ")
	encoder.Encode(&createUserRequest{
		Name:  "tcnksm",
		Email: "tcnksm@mercari.com",
		Attribute: attribute{
			Birthday: "1988-11-24",
		},
	})

	req, err := http.NewRequest("POST", urlStr, &buf)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	req.Header.Add("X-Version", "2")
	return req
}


================================================
FILE: _example/handler_validate_test.go
================================================
package main

import (
	"net/http"
	"net/http/httptest"
	"testing"

	httpdoc "go.mercari.io/go-httpdoc"
)

func TestUserHandlerWithValidate(t *testing.T) {
	document := &httpdoc.Document{
		Name: "Example API (with validation)",
		ExcludeHeaders: []string{
			"Accept-Encoding",
		},
	}
	defer func() {
		if err := document.Generate("doc/validate.md"); err != nil {
			t.Fatalf("err: %s", err)
		}
	}()

	mux := http.NewServeMux()
	mux.Handle("/v1/user", httpdoc.Record(&userHandler{}, document, &httpdoc.RecordOption{
		Description: "Create a new user",
		ExcludeHeaders: []string{
			"User-Agent",
			"Content-Length",
		},

		// WithValidate option, you can validate various http request & parameter values.
		// It checks handler gets the expected value or not and assert when it's different.
		// You can annotate what kind of value you expect (description) in each validation
		// and it will be the document.
		WithValidate: func(validator *httpdoc.Validator) {
			validator.RequestParams(t, []httpdoc.TestCase{
				httpdoc.NewTestCase("token", "12345", "Request token"),
				httpdoc.NewTestCase("pretty", "", "Pretty print response message"),
			})

			validator.RequestHeaders(t, []httpdoc.TestCase{
				httpdoc.NewTestCase("X-Version", "2", "Request API version"),
			})

			validator.RequestBody(t, []httpdoc.TestCase{
				httpdoc.NewTestCase("Name", "tcnksm", "User Name"),
				httpdoc.NewTestCase("Email", "tcnksm@mercari.com", "User email address"),
				httpdoc.NewTestCase("Attribute.Birthday", "1988-11-24", "User birthday YYYY-MM-DD format")},
				&createUserRequest{},
			)

			validator.ResponseStatusCode(t, http.StatusOK)

			validator.ResponseBody(t, []httpdoc.TestCase{
				httpdoc.NewTestCase("ID", 11241988, "User ID assigned")},
				&createUserResponse{},
			)
		},
	}))

	testServer := httptest.NewServer(mux)
	defer testServer.Close()

	req := testNewRequest(t, testServer.URL+"/v1/user?token=12345")
	if _, err := http.DefaultClient.Do(req); err != nil {
		t.Fatalf("err: %s", err)
	}
}


================================================
FILE: _example/message.pb.go
================================================
// Code generated by protoc-gen-gogo.
// source: message.proto
// DO NOT EDIT!

/*
	Package httpdoc is a generated protocol buffer package.

	It is generated from these files:
		message.proto

	It has these top-level messages:
		UserProtoRequest
		UserProtoResponse
*/
package main

import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"

import io "io"

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package

type UserProtoRequest struct {
	Id   int32  `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
	Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
}

func (m *UserProtoRequest) Reset()                    { *m = UserProtoRequest{} }
func (m *UserProtoRequest) String() string            { return proto.CompactTextString(m) }
func (*UserProtoRequest) ProtoMessage()               {}
func (*UserProtoRequest) Descriptor() ([]byte, []int) { return fileDescriptorMessage, []int{0} }

func (m *UserProtoRequest) GetId() int32 {
	if m != nil {
		return m.Id
	}
	return 0
}

func (m *UserProtoRequest) GetName() string {
	if m != nil {
		return m.Name
	}
	return ""
}

type UserProtoResponse struct {
	Id      int32                      `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
	Name    string                     `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
	Active  bool                       `protobuf:"varint,3,opt,name=active,proto3" json:"active,omitempty"`
	Setting *UserProtoResponse_Setting `protobuf:"bytes,4,opt,name=setting" json:"setting,omitempty"`
}

func (m *UserProtoResponse) Reset()                    { *m = UserProtoResponse{} }
func (m *UserProtoResponse) String() string            { return proto.CompactTextString(m) }
func (*UserProtoResponse) ProtoMessage()               {}
func (*UserProtoResponse) Descriptor() ([]byte, []int) { return fileDescriptorMessage, []int{1} }

func (m *UserProtoResponse) GetId() int32 {
	if m != nil {
		return m.Id
	}
	return 0
}

func (m *UserProtoResponse) GetName() string {
	if m != nil {
		return m.Name
	}
	return ""
}

func (m *UserProtoResponse) GetActive() bool {
	if m != nil {
		return m.Active
	}
	return false
}

func (m *UserProtoResponse) GetSetting() *UserProtoResponse_Setting {
	if m != nil {
		return m.Setting
	}
	return nil
}

type UserProtoResponse_Setting struct {
	Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
}

func (m *UserProtoResponse_Setting) Reset()         { *m = UserProtoResponse_Setting{} }
func (m *UserProtoResponse_Setting) String() string { return proto.CompactTextString(m) }
func (*UserProtoResponse_Setting) ProtoMessage()    {}
func (*UserProtoResponse_Setting) Descriptor() ([]byte, []int) {
	return fileDescriptorMessage, []int{1, 0}
}

func (m *UserProtoResponse_Setting) GetEmail() string {
	if m != nil {
		return m.Email
	}
	return ""
}

func init() {
	proto.RegisterType((*UserProtoRequest)(nil), "httpdoc.UserProtoRequest")
	proto.RegisterType((*UserProtoResponse)(nil), "httpdoc.UserProtoResponse")
	proto.RegisterType((*UserProtoResponse_Setting)(nil), "httpdoc.UserProtoResponse.Setting")
}
func (m *UserProtoRequest) Marshal() (dAtA []byte, err error) {
	size := m.Size()
	dAtA = make([]byte, size)
	n, err := m.MarshalTo(dAtA)
	if err != nil {
		return nil, err
	}
	return dAtA[:n], nil
}

func (m *UserProtoRequest) MarshalTo(dAtA []byte) (int, error) {
	var i int
	_ = i
	var l int
	_ = l
	if m.Id != 0 {
		dAtA[i] = 0x8
		i++
		i = encodeVarintMessage(dAtA, i, uint64(m.Id))
	}
	if len(m.Name) > 0 {
		dAtA[i] = 0x12
		i++
		i = encodeVarintMessage(dAtA, i, uint64(len(m.Name)))
		i += copy(dAtA[i:], m.Name)
	}
	return i, nil
}

func (m *UserProtoResponse) Marshal() (dAtA []byte, err error) {
	size := m.Size()
	dAtA = make([]byte, size)
	n, err := m.MarshalTo(dAtA)
	if err != nil {
		return nil, err
	}
	return dAtA[:n], nil
}

func (m *UserProtoResponse) MarshalTo(dAtA []byte) (int, error) {
	var i int
	_ = i
	var l int
	_ = l
	if m.Id != 0 {
		dAtA[i] = 0x8
		i++
		i = encodeVarintMessage(dAtA, i, uint64(m.Id))
	}
	if len(m.Name) > 0 {
		dAtA[i] = 0x12
		i++
		i = encodeVarintMessage(dAtA, i, uint64(len(m.Name)))
		i += copy(dAtA[i:], m.Name)
	}
	if m.Active {
		dAtA[i] = 0x18
		i++
		if m.Active {
			dAtA[i] = 1
		} else {
			dAtA[i] = 0
		}
		i++
	}
	if m.Setting != nil {
		dAtA[i] = 0x22
		i++
		i = encodeVarintMessage(dAtA, i, uint64(m.Setting.Size()))
		n1, err := m.Setting.MarshalTo(dAtA[i:])
		if err != nil {
			return 0, err
		}
		i += n1
	}
	return i, nil
}

func (m *UserProtoResponse_Setting) Marshal() (dAtA []byte, err error) {
	size := m.Size()
	dAtA = make([]byte, size)
	n, err := m.MarshalTo(dAtA)
	if err != nil {
		return nil, err
	}
	return dAtA[:n], nil
}

func (m *UserProtoResponse_Setting) MarshalTo(dAtA []byte) (int, error) {
	var i int
	_ = i
	var l int
	_ = l
	if len(m.Email) > 0 {
		dAtA[i] = 0xa
		i++
		i = encodeVarintMessage(dAtA, i, uint64(len(m.Email)))
		i += copy(dAtA[i:], m.Email)
	}
	return i, nil
}

func encodeFixed64Message(dAtA []byte, offset int, v uint64) int {
	dAtA[offset] = uint8(v)
	dAtA[offset+1] = uint8(v >> 8)
	dAtA[offset+2] = uint8(v >> 16)
	dAtA[offset+3] = uint8(v >> 24)
	dAtA[offset+4] = uint8(v >> 32)
	dAtA[offset+5] = uint8(v >> 40)
	dAtA[offset+6] = uint8(v >> 48)
	dAtA[offset+7] = uint8(v >> 56)
	return offset + 8
}
func encodeFixed32Message(dAtA []byte, offset int, v uint32) int {
	dAtA[offset] = uint8(v)
	dAtA[offset+1] = uint8(v >> 8)
	dAtA[offset+2] = uint8(v >> 16)
	dAtA[offset+3] = uint8(v >> 24)
	return offset + 4
}
func encodeVarintMessage(dAtA []byte, offset int, v uint64) int {
	for v >= 1<<7 {
		dAtA[offset] = uint8(v&0x7f | 0x80)
		v >>= 7
		offset++
	}
	dAtA[offset] = uint8(v)
	return offset + 1
}
func (m *UserProtoRequest) Size() (n int) {
	var l int
	_ = l
	if m.Id != 0 {
		n += 1 + sovMessage(uint64(m.Id))
	}
	l = len(m.Name)
	if l > 0 {
		n += 1 + l + sovMessage(uint64(l))
	}
	return n
}

func (m *UserProtoResponse) Size() (n int) {
	var l int
	_ = l
	if m.Id != 0 {
		n += 1 + sovMessage(uint64(m.Id))
	}
	l = len(m.Name)
	if l > 0 {
		n += 1 + l + sovMessage(uint64(l))
	}
	if m.Active {
		n += 2
	}
	if m.Setting != nil {
		l = m.Setting.Size()
		n += 1 + l + sovMessage(uint64(l))
	}
	return n
}

func (m *UserProtoResponse_Setting) Size() (n int) {
	var l int
	_ = l
	l = len(m.Email)
	if l > 0 {
		n += 1 + l + sovMessage(uint64(l))
	}
	return n
}

func sovMessage(x uint64) (n int) {
	for {
		n++
		x >>= 7
		if x == 0 {
			break
		}
	}
	return n
}
func sozMessage(x uint64) (n int) {
	return sovMessage(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *UserProtoRequest) Unmarshal(dAtA []byte) error {
	l := len(dAtA)
	iNdEx := 0
	for iNdEx < l {
		preIndex := iNdEx
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if shift >= 64 {
				return ErrIntOverflowMessage
			}
			if iNdEx >= l {
				return io.ErrUnexpectedEOF
			}
			b := dAtA[iNdEx]
			iNdEx++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		fieldNum := int32(wire >> 3)
		wireType := int(wire & 0x7)
		if wireType == 4 {
			return fmt.Errorf("proto: UserProtoRequest: wiretype end group for non-group")
		}
		if fieldNum <= 0 {
			return fmt.Errorf("proto: UserProtoRequest: illegal tag %d (wire type %d)", fieldNum, wire)
		}
		switch fieldNum {
		case 1:
			if wireType != 0 {
				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
			}
			m.Id = 0
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				m.Id |= (int32(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
		case 2:
			if wireType != 2 {
				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
			}
			var stringLen uint64
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				stringLen |= (uint64(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			intStringLen := int(stringLen)
			if intStringLen < 0 {
				return ErrInvalidLengthMessage
			}
			postIndex := iNdEx + intStringLen
			if postIndex > l {
				return io.ErrUnexpectedEOF
			}
			m.Name = string(dAtA[iNdEx:postIndex])
			iNdEx = postIndex
		default:
			iNdEx = preIndex
			skippy, err := skipMessage(dAtA[iNdEx:])
			if err != nil {
				return err
			}
			if skippy < 0 {
				return ErrInvalidLengthMessage
			}
			if (iNdEx + skippy) > l {
				return io.ErrUnexpectedEOF
			}
			iNdEx += skippy
		}
	}

	if iNdEx > l {
		return io.ErrUnexpectedEOF
	}
	return nil
}
func (m *UserProtoResponse) Unmarshal(dAtA []byte) error {
	l := len(dAtA)
	iNdEx := 0
	for iNdEx < l {
		preIndex := iNdEx
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if shift >= 64 {
				return ErrIntOverflowMessage
			}
			if iNdEx >= l {
				return io.ErrUnexpectedEOF
			}
			b := dAtA[iNdEx]
			iNdEx++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		fieldNum := int32(wire >> 3)
		wireType := int(wire & 0x7)
		if wireType == 4 {
			return fmt.Errorf("proto: UserProtoResponse: wiretype end group for non-group")
		}
		if fieldNum <= 0 {
			return fmt.Errorf("proto: UserProtoResponse: illegal tag %d (wire type %d)", fieldNum, wire)
		}
		switch fieldNum {
		case 1:
			if wireType != 0 {
				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
			}
			m.Id = 0
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				m.Id |= (int32(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
		case 2:
			if wireType != 2 {
				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
			}
			var stringLen uint64
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				stringLen |= (uint64(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			intStringLen := int(stringLen)
			if intStringLen < 0 {
				return ErrInvalidLengthMessage
			}
			postIndex := iNdEx + intStringLen
			if postIndex > l {
				return io.ErrUnexpectedEOF
			}
			m.Name = string(dAtA[iNdEx:postIndex])
			iNdEx = postIndex
		case 3:
			if wireType != 0 {
				return fmt.Errorf("proto: wrong wireType = %d for field Active", wireType)
			}
			var v int
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				v |= (int(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			m.Active = bool(v != 0)
		case 4:
			if wireType != 2 {
				return fmt.Errorf("proto: wrong wireType = %d for field Setting", wireType)
			}
			var msglen int
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				msglen |= (int(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			if msglen < 0 {
				return ErrInvalidLengthMessage
			}
			postIndex := iNdEx + msglen
			if postIndex > l {
				return io.ErrUnexpectedEOF
			}
			if m.Setting == nil {
				m.Setting = &UserProtoResponse_Setting{}
			}
			if err := m.Setting.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
				return err
			}
			iNdEx = postIndex
		default:
			iNdEx = preIndex
			skippy, err := skipMessage(dAtA[iNdEx:])
			if err != nil {
				return err
			}
			if skippy < 0 {
				return ErrInvalidLengthMessage
			}
			if (iNdEx + skippy) > l {
				return io.ErrUnexpectedEOF
			}
			iNdEx += skippy
		}
	}

	if iNdEx > l {
		return io.ErrUnexpectedEOF
	}
	return nil
}
func (m *UserProtoResponse_Setting) Unmarshal(dAtA []byte) error {
	l := len(dAtA)
	iNdEx := 0
	for iNdEx < l {
		preIndex := iNdEx
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if shift >= 64 {
				return ErrIntOverflowMessage
			}
			if iNdEx >= l {
				return io.ErrUnexpectedEOF
			}
			b := dAtA[iNdEx]
			iNdEx++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		fieldNum := int32(wire >> 3)
		wireType := int(wire & 0x7)
		if wireType == 4 {
			return fmt.Errorf("proto: Setting: wiretype end group for non-group")
		}
		if fieldNum <= 0 {
			return fmt.Errorf("proto: Setting: illegal tag %d (wire type %d)", fieldNum, wire)
		}
		switch fieldNum {
		case 1:
			if wireType != 2 {
				return fmt.Errorf("proto: wrong wireType = %d for field Email", wireType)
			}
			var stringLen uint64
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				stringLen |= (uint64(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			intStringLen := int(stringLen)
			if intStringLen < 0 {
				return ErrInvalidLengthMessage
			}
			postIndex := iNdEx + intStringLen
			if postIndex > l {
				return io.ErrUnexpectedEOF
			}
			m.Email = string(dAtA[iNdEx:postIndex])
			iNdEx = postIndex
		default:
			iNdEx = preIndex
			skippy, err := skipMessage(dAtA[iNdEx:])
			if err != nil {
				return err
			}
			if skippy < 0 {
				return ErrInvalidLengthMessage
			}
			if (iNdEx + skippy) > l {
				return io.ErrUnexpectedEOF
			}
			iNdEx += skippy
		}
	}

	if iNdEx > l {
		return io.ErrUnexpectedEOF
	}
	return nil
}
func skipMessage(dAtA []byte) (n int, err error) {
	l := len(dAtA)
	iNdEx := 0
	for iNdEx < l {
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if shift >= 64 {
				return 0, ErrIntOverflowMessage
			}
			if iNdEx >= l {
				return 0, io.ErrUnexpectedEOF
			}
			b := dAtA[iNdEx]
			iNdEx++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		wireType := int(wire & 0x7)
		switch wireType {
		case 0:
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return 0, ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return 0, io.ErrUnexpectedEOF
				}
				iNdEx++
				if dAtA[iNdEx-1] < 0x80 {
					break
				}
			}
			return iNdEx, nil
		case 1:
			iNdEx += 8
			return iNdEx, nil
		case 2:
			var length int
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return 0, ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return 0, io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				length |= (int(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			iNdEx += length
			if length < 0 {
				return 0, ErrInvalidLengthMessage
			}
			return iNdEx, nil
		case 3:
			for {
				var innerWire uint64
				var start int = iNdEx
				for shift := uint(0); ; shift += 7 {
					if shift >= 64 {
						return 0, ErrIntOverflowMessage
					}
					if iNdEx >= l {
						return 0, io.ErrUnexpectedEOF
					}
					b := dAtA[iNdEx]
					iNdEx++
					innerWire |= (uint64(b) & 0x7F) << shift
					if b < 0x80 {
						break
					}
				}
				innerWireType := int(innerWire & 0x7)
				if innerWireType == 4 {
					break
				}
				next, err := skipMessage(dAtA[start:])
				if err != nil {
					return 0, err
				}
				iNdEx = start + next
			}
			return iNdEx, nil
		case 4:
			return iNdEx, nil
		case 5:
			iNdEx += 4
			return iNdEx, nil
		default:
			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
		}
	}
	panic("unreachable")
}

var (
	ErrInvalidLengthMessage = fmt.Errorf("proto: negative length found during unmarshaling")
	ErrIntOverflowMessage   = fmt.Errorf("proto: integer overflow")
)

func init() { proto.RegisterFile("message.proto", fileDescriptorMessage) }

var fileDescriptorMessage = []byte{
	// 211 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcd, 0x4d, 0x2d, 0x2e,
	0x4e, 0x4c, 0x4f, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xcf, 0x28, 0x29, 0x29, 0x48,
	0xc9, 0x4f, 0x56, 0x32, 0xe3, 0x12, 0x08, 0x2d, 0x4e, 0x2d, 0x0a, 0x00, 0x89, 0x06, 0xa5, 0x16,
	0x96, 0xa6, 0x16, 0x97, 0x08, 0xf1, 0x71, 0x31, 0x65, 0xa6, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0xb0,
	0x06, 0x31, 0x65, 0xa6, 0x08, 0x09, 0x71, 0xb1, 0xe4, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x29, 0x30,
	0x6a, 0x70, 0x06, 0x81, 0xd9, 0x4a, 0xeb, 0x18, 0xb9, 0x04, 0x91, 0x34, 0x16, 0x17, 0xe4, 0xe7,
	0x15, 0xa7, 0x12, 0xa3, 0x53, 0x48, 0x8c, 0x8b, 0x2d, 0x31, 0xb9, 0x24, 0xb3, 0x2c, 0x55, 0x82,
	0x59, 0x81, 0x51, 0x83, 0x23, 0x08, 0xca, 0x13, 0xb2, 0xe1, 0x62, 0x2f, 0x4e, 0x2d, 0x29, 0xc9,
	0xcc, 0x4b, 0x97, 0x60, 0x51, 0x60, 0xd4, 0xe0, 0x36, 0x52, 0xd2, 0x83, 0x3a, 0x52, 0x0f, 0xc3,
	0x22, 0xbd, 0x60, 0x88, 0xca, 0x20, 0x98, 0x16, 0x29, 0x79, 0x2e, 0x76, 0xa8, 0x98, 0x90, 0x08,
	0x17, 0x6b, 0x6a, 0x6e, 0x62, 0x66, 0x0e, 0xd8, 0x1d, 0x9c, 0x41, 0x10, 0x8e, 0x93, 0xc0, 0x89,
	0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe3, 0xb1, 0x1c, 0x43,
	0x12, 0x1b, 0x38, 0x28, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xc5, 0x23, 0xb1, 0xd2, 0x1b,
	0x01, 0x00, 0x00,
}


================================================
FILE: doc.go
================================================
// Package httpdoc is a Golang package for generating API documentation from httptest test cases.
//
// It provides a simple http middleware for recording various http requst & response values you use in your tests
// and automatically arranges and generates them as usable documentation in markdown format. It also provides a way
// to validate values are equal to what you expect with annotation (e.g., you can add a description for headers,
// params or response fields).
//
// See example document output, https://github.com/mercari/go-httpdoc/blob/master/_example/doc/validate.md
package httpdoc // import "go.mercari.io/go-httpdoc"


================================================
FILE: httpdoc.go
================================================
package httpdoc

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sort"

	"github.com/golang/protobuf/proto"
)

const (
	// EnvHTTPDoc is the environmental variable that determines if Generate func generates documentation
	// to the given file or not. By default, it does not generate. If this variable is not empty, then it does.
	EnvHTTPDoc = "HTTPDOC"
)

// Document stores recorded results by Record middleware.
type Document struct {
	// Name is API documentation name.
	Name string

	// ExcludeHeaders is list of headers to exclude from documentation.
	// For example, you may do not need `Content-Length` header. This is applied all entries (endpoints).
	// If you want to exclude header only in specific endpoint, then use `RecordOption.ExcludeHeaders`.
	ExcludeHeaders []string

	// Entries stores all recorded results by Record middleware. Normally, you don't need to modify this.
	// This is exported just for templating.
	Entries []Entry

	// tmpl is template file to use. Currently this is only static/tmpl/doc.md.tmpl
	tmpl string

	logger *log.Logger
}

// Entry is recorded results by Record middleware. Normally, you don't need to modify this.
// All fields are exported just for templating.
type Entry struct {
	// Description is description of endpoint.
	Description string

	// Method is HTTP method.
	Method string

	// Path is request path.
	Path string

	RequestParams  []Data
	RequestHeaders []Data
	RequestFields  []Data

	// RequestExample is request body example. If you use plain text for json for response body
	// it uses it here without modification. If you use protocol buffer format for your request body
	// it unmarshals it in the given struct and encodes it into json format.
	RequestExample string

	ResponseStatusCode int
	ResponseHeaders    []Data
	ResponseFields     []Data

	// ResponseExample is response body example. If you use plain text for json for response body
	// it uses it here without modification. If you use protocol buffer format for your response body
	// it unmarshals it in the given struct and encodes it into json format.
	ResponseExample string
}

// RecordOption is option for Record middleware.
type RecordOption struct {
	// Description is description of endpoint. This is used for Entry.Description.
	Description string

	// ExcludeHeaders is list of headers to exclude from documentation.
	// This is applied only one entry (endpoint). If you want to exclude header in all endpoints
	// use `Document.ExcludeHeaders`.
	ExcludeHeaders []string

	// WithValidate option, you can validate various http request & response parameter values.
	// It inspects values which handler receives and checks it's expected or not.
	// If not it asserts and fails the test. If ok, uses it for documentation entry.
	//
	// Not only validate, you can add an annotation to each values (e.g., what does the header
	// means?) and it's used for documentation.
	//
	// See more usage in Validator methods.
	WithValidate func(*Validator)

	// WithProtoBuffer option is used for protocol buffer request & response.
	WithProtoBuffer *ProtoBufferOption
}

// ProtoBufferOption is option for protocol buffer.
type ProtoBufferOption struct {
	// RequestUnmarshaler is used to unmarshal protocol buffer encoded request body.
	// This is used for generating human readable request example (json format).
	RequestUnmarshaler proto.Unmarshaler

	// ResponseUnmarshaler is used to unmarshal protocol buffer encoded response body.
	// This is used for generating human readable response example (json format).
	ResponseUnmarshaler proto.Unmarshaler
}

// Data represents a request or response parameter value. Normally, you don't need to modify this.
// All fields are exported just for templating.
type Data struct {
	// Name is header or params, field name.
	Name string

	// Value is actual value handler receives.
	Value interface{}

	// Description is description for this data. You can provide this via a validator.
	Description string
}

type byName []Data

func (n byName) Len() int           { return len(n) }
func (n byName) Less(i, j int) bool { return n[i].Name < n[j].Name }
func (n byName) Swap(i, j int)      { n[i], n[j] = n[j], n[i] }

// Record is a http middleware. It records all request & response values which the given http handler
// receives & response and save it in the given Document.
func Record(next http.Handler, document *Document, opt *RecordOption) http.Handler {

	// If not option is provided, initialize it.
	if opt == nil {
		opt = &RecordOption{}
	}

	if document.logger == nil {
		document.logger = log.New(os.Stderr, "", log.LstdFlags)
	}

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Create a new responseWriter it captures status code and response body.
		rw := responseWriter{
			ResponseWriter: w,
		}

		// Create a tee reader and stores request body.
		// Because of this, handler must read request body to record.
		var requestBody bytes.Buffer
		r.Body = ioutil.NopCloser(io.TeeReader(r.Body, &requestBody))

		next.ServeHTTP(&rw, r)

		// If protobuffer option is provided, use protoUnmarshalFunc for
		// validator, by default, use json unmashal func.
		unmarshalFunc := defaultUnmarshalFunc
		if opt.WithProtoBuffer != nil {
			unmarshalFunc = protoUnmarshalFunc
		}

		validator := &Validator{
			record: &record{
				requestParams:  r.URL.Query(),
				requestHeaders: r.Header,
				requestBody:    requestBody.Bytes(),

				responseStatusCode: rw.statusCode,
				responseHeaders:    rw.Header(),
				responseBody:       rw.responseBody,
			},
			unmarshalFunc: unmarshalFunc,
			assertFunc:    defaultAssertFunc,
		}

		if opt.WithValidate != nil {
			opt.WithValidate(validator)
		}

		excludeHeaders := append(opt.ExcludeHeaders, document.ExcludeHeaders...)

		requestParams := mergeData(validator.requestParams, convertHeaders(r.URL.Query()))

		requestHeaders := mergeData(validator.requestHeaders, convertHeaders(r.Header))
		requestHeaders = excludeData(requestHeaders, excludeHeaders)

		responseHeaders := mergeData(validator.responseHeaders, convertHeaders(rw.Header()))
		responseHeaders = excludeData(responseHeaders, excludeHeaders)

		requestExample := requestBody.String()
		responseExample := string(rw.responseBody)
		if opt.WithProtoBuffer != nil {
			// FIXME(tcnksm): Want to use jsonpb but sometimes panic happens while marshalling....
			if unmarshaler := opt.WithProtoBuffer.RequestUnmarshaler; unmarshaler != nil {
				unmarshaler.Unmarshal(requestBody.Bytes())

				var buf bytes.Buffer
				encoder := json.NewEncoder(&buf)
				encoder.Encode(unmarshaler)
				s := buf.String()
				buf.Reset()
				json.Indent(&buf, []byte(s), "", "  ")

				requestExample = buf.String()
			}

			if unmarshaler := opt.WithProtoBuffer.ResponseUnmarshaler; unmarshaler != nil {
				unmarshaler.Unmarshal(rw.responseBody)

				var buf bytes.Buffer
				encoder := json.NewEncoder(&buf)
				encoder.Encode(unmarshaler)
				s := buf.String()
				buf.Reset()
				json.Indent(&buf, []byte(s), "", "  ")

				responseExample = buf.String()
			}
		}

		entry := Entry{
			Description: opt.Description,

			Method: r.Method,
			Path:   r.URL.Path,

			RequestHeaders: requestHeaders,
			RequestParams:  requestParams,
			RequestFields:  validator.requestFields,
			RequestExample: requestExample,

			ResponseStatusCode: rw.statusCode,
			ResponseHeaders:    responseHeaders,
			ResponseFields:     validator.responseFields,
			ResponseExample:    responseExample,
		}
		entry.format()
		document.Entries = append(document.Entries, entry)
	})
}

// format sorts entry data to prevent results updated everytime.
func (e *Entry) format() error {
	sort.Sort(byName(e.RequestHeaders))
	sort.Sort(byName(e.RequestParams))

	return nil
}

type responseWriter struct {
	statusCode   int
	responseBody []byte

	http.ResponseWriter
}

func (w *responseWriter) Write(buf []byte) (int, error) {
	w.responseBody = buf
	return w.ResponseWriter.Write(buf)
}

func (w *responseWriter) WriteHeader(code int) {
	w.statusCode = code
	w.ResponseWriter.WriteHeader(code)
}

// convertHeaders convert HTTP header to httpdoc description format.
func convertHeaders(headers map[string][]string) []Data {
	d := make([]Data, 0, len(headers))
	for k, v := range headers {
		data := Data{
			Name:  k,
			Value: v[0],
		}
		d = append(d, data)
	}
	return d
}

// mergeData merges 2 Data slice into 1 slice without duplication.
// If duplicated, item in 1st slice is used.
func mergeData(a, b []Data) []Data {
	newData := make([]Data, len(a))
	copy(newData, a)
	for _, d1 := range b {
		var contain bool
		for _, d2 := range a {
			if d1.Name == d2.Name {
				contain = true
			}
		}
		if !contain {
			newData = append(newData, d1)
		}
	}
	return newData
}

// excludeData excludes data which is given.
func excludeData(target []Data, excludes ...[]string) []Data {
	newData := make([]Data, 0, len(target))
	for _, d := range target {
		var contain bool
		for _, exclude := range excludes {
			for _, name := range exclude {
				if d.Name == name {
					contain = true
				}
			}
		}

		if !contain {
			newData = append(newData, d)
		}
	}
	return newData
}


================================================
FILE: httpdoc_test.go
================================================
package httpdoc

import (
	"bytes"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"reflect"
	"sort"
	"strings"
	"testing"

	"github.com/golang/protobuf/proto"
)

var (
	testExcludeHeaders = []string{"User-Agent", "Accept-Encoding", "Content-Length"}

	testHandler = func(w http.ResponseWriter, r *http.Request) {
		// To record, request example, request body must be read in handler
		io.Copy(ioutil.Discard, r.Body)

		w.WriteHeader(http.StatusOK)
		w.Header().Add("Content-Type", "text/plain")
		w.Write([]byte("hello"))
	}

	testHandlerProto = func(w http.ResponseWriter, r *http.Request) {
		// To record, request example, request body must be read in handler
		io.Copy(ioutil.Discard, r.Body)

		response := &UserProtoResponse{
			Id:     7089,
			Name:   "tcnksm",
			Active: true,
		}
		buf, _ := response.Marshal()

		w.WriteHeader(http.StatusOK)
		w.Header().Add("Content-Type", "application/protobuf")
		w.Write(buf)
	}
)

func TestRecord(t *testing.T) {
	cases := []struct {
		path          string
		handler       http.HandlerFunc
		recordOption  *RecordOption
		requestMethod string
		requestParam  string
		requestBody   io.Reader
		want          Entry
	}{
		{
			"/v1/hello",
			testHandler,
			nil,
			"GET",
			"?token=123456&pretty=true",
			strings.NewReader("hello"),
			Entry{
				Description: "",
				Method:      "GET",
				Path:        "/v1/hello",

				RequestParams: []Data{
					{"pretty", "true", ""},
					{"token", "123456", ""},
				},
				RequestHeaders: []Data{
					{"Accept-Encoding", "gzip", ""},
					{"Content-Length", "5", ""},
					{"User-Agent", "Go-http-client/1.1", ""},
				},
				RequestFields:  nil,
				RequestExample: "hello",

				ResponseStatusCode: http.StatusOK,
				ResponseHeaders: []Data{
					{"Content-Type", "text/plain", ""},
				},
				ResponseExample: "hello",
			},
		},

		{
			"/v1/hello",
			testHandler,
			&RecordOption{
				ExcludeHeaders: testExcludeHeaders,
			},
			"GET",
			"",
			strings.NewReader("hello"),
			Entry{
				Description: "",
				Method:      "GET",
				Path:        "/v1/hello",

				RequestParams:  []Data{},
				RequestHeaders: []Data{},
				RequestFields:  nil,
				RequestExample: "hello",

				ResponseStatusCode: http.StatusOK,
				ResponseHeaders: []Data{
					{"Content-Type", "text/plain", ""},
				},
				ResponseExample: "hello",
			},
		},

		{
			"/v1/hello",
			testHandler,
			&RecordOption{
				ExcludeHeaders: testExcludeHeaders,
				WithValidate: func(v *Validator) {
					v.RequestParams(t, []TestCase{
						NewTestCase("token", "123456", "Test token"),
					})
				},
			},
			"GET",
			"?token=123456",
			strings.NewReader("hello"),
			Entry{
				Description: "",
				Method:      "GET",
				Path:        "/v1/hello",

				RequestParams: []Data{
					{"token", "123456", "Test token"},
				},
				RequestHeaders: []Data{},
				RequestFields:  nil,
				RequestExample: "hello",

				ResponseStatusCode: http.StatusOK,
				ResponseHeaders: []Data{
					{"Content-Type", "text/plain", ""},
				},
				ResponseExample: "hello",
			},
		},
	}

	for _, tc := range cases {
		document := &Document{}
		mux := http.NewServeMux()
		mux.Handle(tc.path, Record(tc.handler, document, tc.recordOption))
		testServer := httptest.NewServer(mux)

		client := http.DefaultClient
		req, err := http.NewRequest(tc.requestMethod, testServer.URL+tc.path+tc.requestParam, tc.requestBody)
		if err != nil {
			t.Fatal(err)
		}

		if _, err := client.Do(req); err != nil {
			t.Fatal(err)
		}

		if len(document.Entries) != 1 {
			t.Fatalf("expect doc records 1 entry")
		}

		got := document.Entries[0]
		if !reflect.DeepEqual(got, tc.want) {
			t.Fatalf("\ngot  %#v\nwant %#v", got, tc.want)
		}
		testServer.Close()
	}
}

func TestRecord_Proto(t *testing.T) {
	cases := []struct {
		path          string
		handler       http.HandlerFunc
		recordOption  *RecordOption
		requestMethod string
		requestParam  string
		requestBody   proto.Marshaler
		want          Entry
	}{
		{
			"/v1/hello_proto",
			testHandlerProto,
			&RecordOption{
				ExcludeHeaders: testExcludeHeaders,
				WithProtoBuffer: &ProtoBufferOption{
					RequestUnmarshaler:  &UserProtoRequest{},
					ResponseUnmarshaler: &UserProtoResponse{},
				},
			},
			"GET",
			"",
			&UserProtoRequest{
				Id:   7089,
				Name: "tcnksm",
			},
			Entry{
				Description: "",
				Method:      "GET",
				Path:        "/v1/hello_proto",

				RequestParams:  []Data{},
				RequestHeaders: []Data{},
				RequestFields:  nil,
				RequestExample: `{
  "id": 7089,
  "name": "tcnksm"
}
`,

				ResponseStatusCode: http.StatusOK,
				ResponseHeaders: []Data{
					{"Content-Type", "application/protobuf", ""},
				},
				ResponseExample: `{
  "id": 7089,
  "name": "tcnksm",
  "active": true
}
`,
			},
		},
	}

	for _, tc := range cases {
		document := &Document{}
		mux := http.NewServeMux()
		mux.Handle(tc.path, Record(tc.handler, document, tc.recordOption))
		testServer := httptest.NewServer(mux)

		client := http.DefaultClient
		buf, err := tc.requestBody.Marshal()
		if err != nil {
			t.Fatal(err)
		}
		req, err := http.NewRequest(tc.requestMethod, testServer.URL+tc.path+tc.requestParam, bytes.NewReader(buf))
		if err != nil {
			t.Fatal(err)
		}

		if _, err := client.Do(req); err != nil {
			t.Fatal(err)
		}

		if len(document.Entries) != 1 {
			t.Fatalf("expect doc records 1 entry")
		}

		got := document.Entries[0]
		if !reflect.DeepEqual(got, tc.want) {
			t.Fatalf("\ngot  %#v\nwant %#v", got, tc.want)
		}
		testServer.Close()
	}
}

func TestConvertHeaders(t *testing.T) {
	input := map[string][]string{
		"Content-Type":  []string{"application/json"},
		"X-API-Version": []string{"1.1.2"},
	}

	got := convertHeaders(input)
	want := []Data{
		{
			Name:        "Content-Type",
			Value:       "application/json",
			Description: "",
		},
		{
			Name:        "X-API-Version",
			Value:       "1.1.2",
			Description: "",
		},
	}

	sort.Sort(byName(got))

	if !reflect.DeepEqual(got, want) {
		t.Fatalf("got %#v, want %#v", got, want)
	}
}

func TestMergeData(t *testing.T) {
	a := []Data{
		{Name: "1"},
		{Name: "2"},
		{Name: "3", Description: "this is 3"},
		{Name: "4", Description: "this is 4"},
	}

	b := []Data{
		{Name: "3"},
		{Name: "4"},
		{Name: "5"},
	}

	got := mergeData(a, b)
	want := []Data{
		{Name: "1"},
		{Name: "2"},
		{Name: "3", Description: "this is 3"},
		{Name: "4", Description: "this is 4"},
		{Name: "5"},
	}

	if !reflect.DeepEqual(got, want) {
		t.Fatalf("got %#v, want %#v", got, want)
	}
}

func TestExcludeData(t *testing.T) {
	input := []Data{
		{Name: "1"},
		{Name: "2"},
		{Name: "3"},
		{Name: "4"},
		{Name: "5"},
	}

	got := excludeData(input, []string{"1", "2"}, []string{"3"}, []string{"4"})
	want := []Data{
		{
			Name: "5",
		},
	}

	if !reflect.DeepEqual(got, want) {
		t.Fatalf("got %#v, want %#v", got, want)
	}
}

func TestResponseWriter_Write(t *testing.T) {
}

func TestResponseWriter_WriteHeader(t *testing.T) {
}


================================================
FILE: message_pb_test.go
================================================
// Code generated by protoc-gen-gogo.
// source: message.proto
// DO NOT EDIT!

/*
	Package httpdoc is a generated protocol buffer package.

	It is generated from these files:
		message.proto

	It has these top-level messages:
		UserProtoRequest
		UserProtoResponse
*/
package httpdoc

import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"

import io "io"

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package

type UserProtoRequest struct {
	Id   int32  `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
	Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
}

func (m *UserProtoRequest) Reset()                    { *m = UserProtoRequest{} }
func (m *UserProtoRequest) String() string            { return proto.CompactTextString(m) }
func (*UserProtoRequest) ProtoMessage()               {}
func (*UserProtoRequest) Descriptor() ([]byte, []int) { return fileDescriptorMessage, []int{0} }

func (m *UserProtoRequest) GetId() int32 {
	if m != nil {
		return m.Id
	}
	return 0
}

func (m *UserProtoRequest) GetName() string {
	if m != nil {
		return m.Name
	}
	return ""
}

type UserProtoResponse struct {
	Id      int32                      `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
	Name    string                     `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
	Active  bool                       `protobuf:"varint,3,opt,name=active,proto3" json:"active,omitempty"`
	Setting *UserProtoResponse_Setting `protobuf:"bytes,4,opt,name=setting" json:"setting,omitempty"`
}

func (m *UserProtoResponse) Reset()                    { *m = UserProtoResponse{} }
func (m *UserProtoResponse) String() string            { return proto.CompactTextString(m) }
func (*UserProtoResponse) ProtoMessage()               {}
func (*UserProtoResponse) Descriptor() ([]byte, []int) { return fileDescriptorMessage, []int{1} }

func (m *UserProtoResponse) GetId() int32 {
	if m != nil {
		return m.Id
	}
	return 0
}

func (m *UserProtoResponse) GetName() string {
	if m != nil {
		return m.Name
	}
	return ""
}

func (m *UserProtoResponse) GetActive() bool {
	if m != nil {
		return m.Active
	}
	return false
}

func (m *UserProtoResponse) GetSetting() *UserProtoResponse_Setting {
	if m != nil {
		return m.Setting
	}
	return nil
}

type UserProtoResponse_Setting struct {
	Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
}

func (m *UserProtoResponse_Setting) Reset()         { *m = UserProtoResponse_Setting{} }
func (m *UserProtoResponse_Setting) String() string { return proto.CompactTextString(m) }
func (*UserProtoResponse_Setting) ProtoMessage()    {}
func (*UserProtoResponse_Setting) Descriptor() ([]byte, []int) {
	return fileDescriptorMessage, []int{1, 0}
}

func (m *UserProtoResponse_Setting) GetEmail() string {
	if m != nil {
		return m.Email
	}
	return ""
}

func init() {
	proto.RegisterType((*UserProtoRequest)(nil), "httpdoc.UserProtoRequest")
	proto.RegisterType((*UserProtoResponse)(nil), "httpdoc.UserProtoResponse")
	proto.RegisterType((*UserProtoResponse_Setting)(nil), "httpdoc.UserProtoResponse.Setting")
}
func (m *UserProtoRequest) Marshal() (dAtA []byte, err error) {
	size := m.Size()
	dAtA = make([]byte, size)
	n, err := m.MarshalTo(dAtA)
	if err != nil {
		return nil, err
	}
	return dAtA[:n], nil
}

func (m *UserProtoRequest) MarshalTo(dAtA []byte) (int, error) {
	var i int
	_ = i
	var l int
	_ = l
	if m.Id != 0 {
		dAtA[i] = 0x8
		i++
		i = encodeVarintMessage(dAtA, i, uint64(m.Id))
	}
	if len(m.Name) > 0 {
		dAtA[i] = 0x12
		i++
		i = encodeVarintMessage(dAtA, i, uint64(len(m.Name)))
		i += copy(dAtA[i:], m.Name)
	}
	return i, nil
}

func (m *UserProtoResponse) Marshal() (dAtA []byte, err error) {
	size := m.Size()
	dAtA = make([]byte, size)
	n, err := m.MarshalTo(dAtA)
	if err != nil {
		return nil, err
	}
	return dAtA[:n], nil
}

func (m *UserProtoResponse) MarshalTo(dAtA []byte) (int, error) {
	var i int
	_ = i
	var l int
	_ = l
	if m.Id != 0 {
		dAtA[i] = 0x8
		i++
		i = encodeVarintMessage(dAtA, i, uint64(m.Id))
	}
	if len(m.Name) > 0 {
		dAtA[i] = 0x12
		i++
		i = encodeVarintMessage(dAtA, i, uint64(len(m.Name)))
		i += copy(dAtA[i:], m.Name)
	}
	if m.Active {
		dAtA[i] = 0x18
		i++
		if m.Active {
			dAtA[i] = 1
		} else {
			dAtA[i] = 0
		}
		i++
	}
	if m.Setting != nil {
		dAtA[i] = 0x22
		i++
		i = encodeVarintMessage(dAtA, i, uint64(m.Setting.Size()))
		n1, err := m.Setting.MarshalTo(dAtA[i:])
		if err != nil {
			return 0, err
		}
		i += n1
	}
	return i, nil
}

func (m *UserProtoResponse_Setting) Marshal() (dAtA []byte, err error) {
	size := m.Size()
	dAtA = make([]byte, size)
	n, err := m.MarshalTo(dAtA)
	if err != nil {
		return nil, err
	}
	return dAtA[:n], nil
}

func (m *UserProtoResponse_Setting) MarshalTo(dAtA []byte) (int, error) {
	var i int
	_ = i
	var l int
	_ = l
	if len(m.Email) > 0 {
		dAtA[i] = 0xa
		i++
		i = encodeVarintMessage(dAtA, i, uint64(len(m.Email)))
		i += copy(dAtA[i:], m.Email)
	}
	return i, nil
}

func encodeFixed64Message(dAtA []byte, offset int, v uint64) int {
	dAtA[offset] = uint8(v)
	dAtA[offset+1] = uint8(v >> 8)
	dAtA[offset+2] = uint8(v >> 16)
	dAtA[offset+3] = uint8(v >> 24)
	dAtA[offset+4] = uint8(v >> 32)
	dAtA[offset+5] = uint8(v >> 40)
	dAtA[offset+6] = uint8(v >> 48)
	dAtA[offset+7] = uint8(v >> 56)
	return offset + 8
}
func encodeFixed32Message(dAtA []byte, offset int, v uint32) int {
	dAtA[offset] = uint8(v)
	dAtA[offset+1] = uint8(v >> 8)
	dAtA[offset+2] = uint8(v >> 16)
	dAtA[offset+3] = uint8(v >> 24)
	return offset + 4
}
func encodeVarintMessage(dAtA []byte, offset int, v uint64) int {
	for v >= 1<<7 {
		dAtA[offset] = uint8(v&0x7f | 0x80)
		v >>= 7
		offset++
	}
	dAtA[offset] = uint8(v)
	return offset + 1
}
func (m *UserProtoRequest) Size() (n int) {
	var l int
	_ = l
	if m.Id != 0 {
		n += 1 + sovMessage(uint64(m.Id))
	}
	l = len(m.Name)
	if l > 0 {
		n += 1 + l + sovMessage(uint64(l))
	}
	return n
}

func (m *UserProtoResponse) Size() (n int) {
	var l int
	_ = l
	if m.Id != 0 {
		n += 1 + sovMessage(uint64(m.Id))
	}
	l = len(m.Name)
	if l > 0 {
		n += 1 + l + sovMessage(uint64(l))
	}
	if m.Active {
		n += 2
	}
	if m.Setting != nil {
		l = m.Setting.Size()
		n += 1 + l + sovMessage(uint64(l))
	}
	return n
}

func (m *UserProtoResponse_Setting) Size() (n int) {
	var l int
	_ = l
	l = len(m.Email)
	if l > 0 {
		n += 1 + l + sovMessage(uint64(l))
	}
	return n
}

func sovMessage(x uint64) (n int) {
	for {
		n++
		x >>= 7
		if x == 0 {
			break
		}
	}
	return n
}
func sozMessage(x uint64) (n int) {
	return sovMessage(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *UserProtoRequest) Unmarshal(dAtA []byte) error {
	l := len(dAtA)
	iNdEx := 0
	for iNdEx < l {
		preIndex := iNdEx
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if shift >= 64 {
				return ErrIntOverflowMessage
			}
			if iNdEx >= l {
				return io.ErrUnexpectedEOF
			}
			b := dAtA[iNdEx]
			iNdEx++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		fieldNum := int32(wire >> 3)
		wireType := int(wire & 0x7)
		if wireType == 4 {
			return fmt.Errorf("proto: UserProtoRequest: wiretype end group for non-group")
		}
		if fieldNum <= 0 {
			return fmt.Errorf("proto: UserProtoRequest: illegal tag %d (wire type %d)", fieldNum, wire)
		}
		switch fieldNum {
		case 1:
			if wireType != 0 {
				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
			}
			m.Id = 0
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				m.Id |= (int32(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
		case 2:
			if wireType != 2 {
				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
			}
			var stringLen uint64
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				stringLen |= (uint64(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			intStringLen := int(stringLen)
			if intStringLen < 0 {
				return ErrInvalidLengthMessage
			}
			postIndex := iNdEx + intStringLen
			if postIndex > l {
				return io.ErrUnexpectedEOF
			}
			m.Name = string(dAtA[iNdEx:postIndex])
			iNdEx = postIndex
		default:
			iNdEx = preIndex
			skippy, err := skipMessage(dAtA[iNdEx:])
			if err != nil {
				return err
			}
			if skippy < 0 {
				return ErrInvalidLengthMessage
			}
			if (iNdEx + skippy) > l {
				return io.ErrUnexpectedEOF
			}
			iNdEx += skippy
		}
	}

	if iNdEx > l {
		return io.ErrUnexpectedEOF
	}
	return nil
}
func (m *UserProtoResponse) Unmarshal(dAtA []byte) error {
	l := len(dAtA)
	iNdEx := 0
	for iNdEx < l {
		preIndex := iNdEx
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if shift >= 64 {
				return ErrIntOverflowMessage
			}
			if iNdEx >= l {
				return io.ErrUnexpectedEOF
			}
			b := dAtA[iNdEx]
			iNdEx++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		fieldNum := int32(wire >> 3)
		wireType := int(wire & 0x7)
		if wireType == 4 {
			return fmt.Errorf("proto: UserProtoResponse: wiretype end group for non-group")
		}
		if fieldNum <= 0 {
			return fmt.Errorf("proto: UserProtoResponse: illegal tag %d (wire type %d)", fieldNum, wire)
		}
		switch fieldNum {
		case 1:
			if wireType != 0 {
				return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType)
			}
			m.Id = 0
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				m.Id |= (int32(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
		case 2:
			if wireType != 2 {
				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
			}
			var stringLen uint64
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				stringLen |= (uint64(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			intStringLen := int(stringLen)
			if intStringLen < 0 {
				return ErrInvalidLengthMessage
			}
			postIndex := iNdEx + intStringLen
			if postIndex > l {
				return io.ErrUnexpectedEOF
			}
			m.Name = string(dAtA[iNdEx:postIndex])
			iNdEx = postIndex
		case 3:
			if wireType != 0 {
				return fmt.Errorf("proto: wrong wireType = %d for field Active", wireType)
			}
			var v int
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				v |= (int(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			m.Active = bool(v != 0)
		case 4:
			if wireType != 2 {
				return fmt.Errorf("proto: wrong wireType = %d for field Setting", wireType)
			}
			var msglen int
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				msglen |= (int(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			if msglen < 0 {
				return ErrInvalidLengthMessage
			}
			postIndex := iNdEx + msglen
			if postIndex > l {
				return io.ErrUnexpectedEOF
			}
			if m.Setting == nil {
				m.Setting = &UserProtoResponse_Setting{}
			}
			if err := m.Setting.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
				return err
			}
			iNdEx = postIndex
		default:
			iNdEx = preIndex
			skippy, err := skipMessage(dAtA[iNdEx:])
			if err != nil {
				return err
			}
			if skippy < 0 {
				return ErrInvalidLengthMessage
			}
			if (iNdEx + skippy) > l {
				return io.ErrUnexpectedEOF
			}
			iNdEx += skippy
		}
	}

	if iNdEx > l {
		return io.ErrUnexpectedEOF
	}
	return nil
}
func (m *UserProtoResponse_Setting) Unmarshal(dAtA []byte) error {
	l := len(dAtA)
	iNdEx := 0
	for iNdEx < l {
		preIndex := iNdEx
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if shift >= 64 {
				return ErrIntOverflowMessage
			}
			if iNdEx >= l {
				return io.ErrUnexpectedEOF
			}
			b := dAtA[iNdEx]
			iNdEx++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		fieldNum := int32(wire >> 3)
		wireType := int(wire & 0x7)
		if wireType == 4 {
			return fmt.Errorf("proto: Setting: wiretype end group for non-group")
		}
		if fieldNum <= 0 {
			return fmt.Errorf("proto: Setting: illegal tag %d (wire type %d)", fieldNum, wire)
		}
		switch fieldNum {
		case 1:
			if wireType != 2 {
				return fmt.Errorf("proto: wrong wireType = %d for field Email", wireType)
			}
			var stringLen uint64
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				stringLen |= (uint64(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			intStringLen := int(stringLen)
			if intStringLen < 0 {
				return ErrInvalidLengthMessage
			}
			postIndex := iNdEx + intStringLen
			if postIndex > l {
				return io.ErrUnexpectedEOF
			}
			m.Email = string(dAtA[iNdEx:postIndex])
			iNdEx = postIndex
		default:
			iNdEx = preIndex
			skippy, err := skipMessage(dAtA[iNdEx:])
			if err != nil {
				return err
			}
			if skippy < 0 {
				return ErrInvalidLengthMessage
			}
			if (iNdEx + skippy) > l {
				return io.ErrUnexpectedEOF
			}
			iNdEx += skippy
		}
	}

	if iNdEx > l {
		return io.ErrUnexpectedEOF
	}
	return nil
}
func skipMessage(dAtA []byte) (n int, err error) {
	l := len(dAtA)
	iNdEx := 0
	for iNdEx < l {
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if shift >= 64 {
				return 0, ErrIntOverflowMessage
			}
			if iNdEx >= l {
				return 0, io.ErrUnexpectedEOF
			}
			b := dAtA[iNdEx]
			iNdEx++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		wireType := int(wire & 0x7)
		switch wireType {
		case 0:
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return 0, ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return 0, io.ErrUnexpectedEOF
				}
				iNdEx++
				if dAtA[iNdEx-1] < 0x80 {
					break
				}
			}
			return iNdEx, nil
		case 1:
			iNdEx += 8
			return iNdEx, nil
		case 2:
			var length int
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return 0, ErrIntOverflowMessage
				}
				if iNdEx >= l {
					return 0, io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				length |= (int(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			iNdEx += length
			if length < 0 {
				return 0, ErrInvalidLengthMessage
			}
			return iNdEx, nil
		case 3:
			for {
				var innerWire uint64
				var start int = iNdEx
				for shift := uint(0); ; shift += 7 {
					if shift >= 64 {
						return 0, ErrIntOverflowMessage
					}
					if iNdEx >= l {
						return 0, io.ErrUnexpectedEOF
					}
					b := dAtA[iNdEx]
					iNdEx++
					innerWire |= (uint64(b) & 0x7F) << shift
					if b < 0x80 {
						break
					}
				}
				innerWireType := int(innerWire & 0x7)
				if innerWireType == 4 {
					break
				}
				next, err := skipMessage(dAtA[start:])
				if err != nil {
					return 0, err
				}
				iNdEx = start + next
			}
			return iNdEx, nil
		case 4:
			return iNdEx, nil
		case 5:
			iNdEx += 4
			return iNdEx, nil
		default:
			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
		}
	}
	panic("unreachable")
}

var (
	ErrInvalidLengthMessage = fmt.Errorf("proto: negative length found during unmarshaling")
	ErrIntOverflowMessage   = fmt.Errorf("proto: integer overflow")
)

func init() { proto.RegisterFile("message.proto", fileDescriptorMessage) }

var fileDescriptorMessage = []byte{
	// 211 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcd, 0x4d, 0x2d, 0x2e,
	0x4e, 0x4c, 0x4f, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xcf, 0x28, 0x29, 0x29, 0x48,
	0xc9, 0x4f, 0x56, 0x32, 0xe3, 0x12, 0x08, 0x2d, 0x4e, 0x2d, 0x0a, 0x00, 0x89, 0x06, 0xa5, 0x16,
	0x96, 0xa6, 0x16, 0x97, 0x08, 0xf1, 0x71, 0x31, 0x65, 0xa6, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0xb0,
	0x06, 0x31, 0x65, 0xa6, 0x08, 0x09, 0x71, 0xb1, 0xe4, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x29, 0x30,
	0x6a, 0x70, 0x06, 0x81, 0xd9, 0x4a, 0xeb, 0x18, 0xb9, 0x04, 0x91, 0x34, 0x16, 0x17, 0xe4, 0xe7,
	0x15, 0xa7, 0x12, 0xa3, 0x53, 0x48, 0x8c, 0x8b, 0x2d, 0x31, 0xb9, 0x24, 0xb3, 0x2c, 0x55, 0x82,
	0x59, 0x81, 0x51, 0x83, 0x23, 0x08, 0xca, 0x13, 0xb2, 0xe1, 0x62, 0x2f, 0x4e, 0x2d, 0x29, 0xc9,
	0xcc, 0x4b, 0x97, 0x60, 0x51, 0x60, 0xd4, 0xe0, 0x36, 0x52, 0xd2, 0x83, 0x3a, 0x52, 0x0f, 0xc3,
	0x22, 0xbd, 0x60, 0x88, 0xca, 0x20, 0x98, 0x16, 0x29, 0x79, 0x2e, 0x76, 0xa8, 0x98, 0x90, 0x08,
	0x17, 0x6b, 0x6a, 0x6e, 0x62, 0x66, 0x0e, 0xd8, 0x1d, 0x9c, 0x41, 0x10, 0x8e, 0x93, 0xc0, 0x89,
	0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe3, 0xb1, 0x1c, 0x43,
	0x12, 0x1b, 0x38, 0x28, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xc5, 0x23, 0xb1, 0xd2, 0x1b,
	0x01, 0x00, 0x00,
}


================================================
FILE: proto/message.proto
================================================
syntax = "proto3";

package httpdoc;

message UserProtoRequest {
  int32 id = 1;
  string name = 2;
}

message UserProtoResponse {
  message Setting {
    string email = 1;
  }
  
  int32 id = 1;
  string name = 2;
  bool active = 3;
  Setting setting = 4;
}


================================================
FILE: static/bindata.go
================================================
// Code generated by go-bindata.
// sources:
// tmpl/api-blueprint.tmpl
// tmpl/doc.md.tmpl
// DO NOT EDIT!

package static

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"
)

func bindataRead(data []byte, name string) ([]byte, error) {
	gz, err := gzip.NewReader(bytes.NewBuffer(data))
	if err != nil {
		return nil, fmt.Errorf("Read %q: %v", name, err)
	}

	var buf bytes.Buffer
	_, err = io.Copy(&buf, gz)
	clErr := gz.Close()

	if err != nil {
		return nil, fmt.Errorf("Read %q: %v", name, err)
	}
	if clErr != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

type asset struct {
	bytes []byte
	info  os.FileInfo
}

type bindataFileInfo struct {
	name    string
	size    int64
	mode    os.FileMode
	modTime time.Time
}

func (fi bindataFileInfo) Name() string {
	return fi.name
}
func (fi bindataFileInfo) Size() int64 {
	return fi.size
}
func (fi bindataFileInfo) Mode() os.FileMode {
	return fi.mode
}
func (fi bindataFileInfo) ModTime() time.Time {
	return fi.modTime
}
func (fi bindataFileInfo) IsDir() bool {
	return false
}
func (fi bindataFileInfo) Sys() interface{} {
	return nil
}

var _tmplApiBlueprintTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x90\x3d\x4f\xc3\x30\x10\x86\xf7\xfc\x8a\x93\xb2\xb4\xaa\x1a\x76\x36\x3e\x8a\x60\x00\x55\x20\xb1\x1f\xf5\x4b\x31\x4a\x6c\x63\x3b\x12\x28\xca\x7f\x47\x89\xdd\xc6\x4d\xca\xc7\xd0\x2d\xe7\x3c\xf7\xde\x73\x97\xd3\xc5\xfa\x2e\xcb\xf2\x9c\x9a\x86\x8a\x07\xae\x40\x6d\x9b\x65\x4d\x43\x96\xd5\x16\x54\xac\x94\xb7\x12\x8e\x96\xdd\x73\x1e\xb9\x6b\xb8\x8d\x95\xc6\x4b\xad\xa8\x6d\xfb\xa7\x7b\xf8\x37\x2d\x76\xcd\xf2\x95\x8a\x47\x7c\xd4\x70\xfe\x46\xa2\x14\xa1\x7f\x48\x9d\xfe\x5b\xa4\x02\x34\x73\xde\x4a\xb5\x9d\xd3\xf2\xc8\xbc\x2e\x07\x4a\x1c\x7e\x8d\xa6\xae\xd9\x72\xb5\x4b\xee\x0b\x78\x58\x37\x55\x48\x40\xa2\x91\x84\xaa\xab\x17\xd8\xbf\x24\x12\x87\x05\xc5\x54\x9a\xb1\x31\xa5\xdc\x70\x47\x9f\xbd\x3b\xad\xe6\x23\xc1\x5b\xb0\x80\x1d\x06\xc7\x7a\x2a\x78\x08\x52\x6a\x78\xde\x17\xcf\x5c\xd6\xf8\xe9\x2c\x5d\xf4\xa5\x16\x5f\xfb\xd6\x98\xba\xfa\xe4\xca\x94\xd8\x5b\x3b\xa3\x95\x43\x24\x42\xf1\xe4\xd9\xd7\xee\x4a\x8b\x70\x8c\x5f\x16\x0a\xfc\x7f\x36\x3a\x46\x9e\x60\xa5\x10\x9b\xee\x34\xe0\xdf\x01\x00\x00\xff\xff\x6d\xf2\xb7\x0d\xe2\x02\x00\x00")

func tmplApiBlueprintTmplBytes() ([]byte, error) {
	return bindataRead(
		_tmplApiBlueprintTmpl,
		"tmpl/api-blueprint.tmpl",
	)
}

func tmplApiBlueprintTmpl() (*asset, error) {
	bytes, err := tmplApiBlueprintTmplBytes()
	if err != nil {
		return nil, err
	}

	info := bindataFileInfo{name: "tmpl/api-blueprint.tmpl", size: 738, mode: os.FileMode(420), modTime: time.Unix(1496893135, 0)}
	a := &asset{bytes: bytes, info: info}
	return a, nil
}

var _tmplDocMdTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x53\xc1\x6e\xd4\x30\x10\xbd\xcf\x57\x8c\x94\x03\x70\xd8\xf4\x8e\x56\x95\x50\x5b\x04\x07\xd0\xaa\x54\x5c\x2a\xa4\x4c\xe3\xd9\xc6\x90\xd8\xc1\x9e\x40\xab\x4d\xfe\x1d\xd9\xde\xad\x1c\x6d\xf7\x42\x9b\x9c\x66\x9e\xc7\xf3\xde\x9b\x8c\x0b\xfc\xb0\xf9\x8c\xca\xd6\x00\x37\x8d\xf6\xa8\xfd\x01\x18\x3a\x36\x42\xa2\xad\xc1\xad\x75\xb8\xdb\x61\xf9\x95\x3a\xc6\x69\x2a\xf1\x50\x7a\xcf\x86\x1d\x09\x2b\xbc\x7b\xc4\xaa\x11\xe9\x95\xad\xab\x12\x2f\xad\x79\x23\xc8\x4a\x4b\x38\x68\xc8\xa8\x12\xa0\x28\xf0\x86\xee\x5a\x46\xbb\xc5\xda\x1a\x61\x23\x1e\x60\xb7\x43\x47\xe6\x9e\xb1\xbc\x32\xe2\x34\x7b\x5c\x4d\x13\xac\xf0\xf6\x36\x30\x5e\xb3\xef\xad\xf1\xfc\x4d\x48\x06\x7f\x61\x55\xe0\xff\x11\xc5\x7c\x61\x69\xac\xc2\x69\x8a\xd9\x86\xa4\x09\x47\x6f\x8b\x93\xd7\x56\xd9\xad\x11\x5b\xfb\x97\xdd\x01\x8d\xb7\x47\xf4\xe2\x74\xef\x5b\xf2\x4d\x56\xf0\x2e\x48\x64\x13\x98\x4e\xa9\x2d\x0a\xfc\x2f\xb5\xb1\x5f\x79\xc9\xbe\x76\xba\x8f\x93\x0e\x58\x51\x14\x78\xcd\xbf\x07\xf6\x12\x0b\xf4\x36\x74\x8e\xf9\x86\x1c\x75\x89\x33\x86\x2c\xec\x3c\xc0\x88\xf1\xcf\xe0\x88\xdf\xa9\x1d\x62\x90\x37\x1d\x61\xc4\x55\xf8\x70\xc4\xf7\xf3\x20\x25\x99\xad\x63\xa2\x31\xff\xf5\x98\xb2\x44\xf3\x94\xce\x1d\xa4\x7e\x69\x62\xf3\xd1\x65\x4e\x3e\x31\x29\x76\x89\x61\x1f\x2f\xe1\x23\xa7\x79\x91\x11\x38\xe9\xe4\xa3\xe6\x56\x25\x86\x3d\x82\xdb\x08\x2d\xe1\x27\x23\x5b\xc8\xce\xd5\x03\x75\x7d\xcb\x33\x3f\x9c\x30\x80\xb5\x62\x21\xdd\xfa\x73\x58\xfb\xa1\xeb\xc8\x3d\x9e\x5f\xb4\xba\xfe\x85\x62\x91\x1f\x7a\x32\x0a\x6b\xab\xb8\x5c\x9f\x1d\x8e\x01\xaa\xaa\xfa\x49\x7f\x28\x29\x81\xf4\x4c\x66\x4c\xd3\x14\x6a\x00\xd6\x67\x4f\xdd\x33\x75\xe9\x35\xa4\x77\x95\x69\x4d\xc0\xf2\x5b\x74\xcc\xf3\xfa\x73\x4f\x1c\xb3\x3d\x4a\xd0\x32\x8b\x74\x44\xb7\x94\xa3\xf9\x2a\xed\x2d\xbd\xfa\x2e\xcd\xb9\x9e\x59\xa6\x67\xb5\xfe\x0b\x00\x00\xff\xff\x2f\x57\xc7\x87\xf8\x06\x00\x00")

func tmplDocMdTmplBytes() ([]byte, error) {
	return bindataRead(
		_tmplDocMdTmpl,
		"tmpl/doc.md.tmpl",
	)
}

func tmplDocMdTmpl() (*asset, error) {
	bytes, err := tmplDocMdTmplBytes()
	if err != nil {
		return nil, err
	}

	info := bindataFileInfo{name: "tmpl/doc.md.tmpl", size: 1784, mode: os.FileMode(420), modTime: time.Unix(1496894277, 0)}
	a := &asset{bytes: bytes, info: info}
	return a, nil
}

// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
	cannonicalName := strings.Replace(name, "\\", "/", -1)
	if f, ok := _bindata[cannonicalName]; ok {
		a, err := f()
		if err != nil {
			return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
		}
		return a.bytes, nil
	}
	return nil, fmt.Errorf("Asset %s not found", name)
}

// MustAsset is like Asset but panics when Asset would return an error.
// It simplifies safe initialization of global variables.
func MustAsset(name string) []byte {
	a, err := Asset(name)
	if err != nil {
		panic("asset: Asset(" + name + "): " + err.Error())
	}

	return a
}

// AssetInfo loads and returns the asset info for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) {
	cannonicalName := strings.Replace(name, "\\", "/", -1)
	if f, ok := _bindata[cannonicalName]; ok {
		a, err := f()
		if err != nil {
			return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
		}
		return a.info, nil
	}
	return nil, fmt.Errorf("AssetInfo %s not found", name)
}

// AssetNames returns the names of the assets.
func AssetNames() []string {
	names := make([]string, 0, len(_bindata))
	for name := range _bindata {
		names = append(names, name)
	}
	return names
}

// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
	"tmpl/api-blueprint.tmpl": tmplApiBlueprintTmpl,
	"tmpl/doc.md.tmpl": tmplDocMdTmpl,
}

// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
// following hierarchy:
//     data/
//       foo.txt
//       img/
//         a.png
//         b.png
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
// AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) {
	node := _bintree
	if len(name) != 0 {
		cannonicalName := strings.Replace(name, "\\", "/", -1)
		pathList := strings.Split(cannonicalName, "/")
		for _, p := range pathList {
			node = node.Children[p]
			if node == nil {
				return nil, fmt.Errorf("Asset %s not found", name)
			}
		}
	}
	if node.Func != nil {
		return nil, fmt.Errorf("Asset %s not found", name)
	}
	rv := make([]string, 0, len(node.Children))
	for childName := range node.Children {
		rv = append(rv, childName)
	}
	return rv, nil
}

type bintree struct {
	Func     func() (*asset, error)
	Children map[string]*bintree
}
var _bintree = &bintree{nil, map[string]*bintree{
	"tmpl": &bintree{nil, map[string]*bintree{
		"api-blueprint.tmpl": &bintree{tmplApiBlueprintTmpl, map[string]*bintree{}},
		"doc.md.tmpl": &bintree{tmplDocMdTmpl, map[string]*bintree{}},
	}},
}}

// RestoreAsset restores an asset under the given directory
func RestoreAsset(dir, name string) error {
	data, err := Asset(name)
	if err != nil {
		return err
	}
	info, err := AssetInfo(name)
	if err != nil {
		return err
	}
	err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
	if err != nil {
		return err
	}
	err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
	if err != nil {
		return err
	}
	return nil
}

// RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error {
	children, err := AssetDir(name)
	// File
	if err != nil {
		return RestoreAsset(dir, name)
	}
	// Dir
	for _, child := range children {
		err = RestoreAssets(dir, filepath.Join(name, child))
		if err != nil {
			return err
		}
	}
	return nil
}

func _filePath(dir, name string) string {
	cannonicalName := strings.Replace(name, "\\", "/", -1)
	return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}



================================================
FILE: static/static.go
================================================
package static

//go:generate go-bindata -pkg=static ./tmpl/...


================================================
FILE: static/tmpl/doc.md.tmpl
================================================
# API doc

This is API documentation for {{ .Name }}. This is generated by `httpdoc`. Don't edit by hand.

## Table of contents

{{ range .Entries -}}
- [[{{ .ResponseStatusCode }}] {{ .Method }} {{ .Path }}](#{{ .ResponseStatusCode }}-{{ .Method | lower }}-{{ .Path | stripslash | lower }})
{{ end }}

{{ range .Entries -}}
## [{{ .ResponseStatusCode }}] {{ .Method }} {{ .Path }}

{{ .Description }}

### Request

{{ if .RequestParams -}}
Parameters

| Name  | Value  | Description |
| ----- | :----- | :--------- |
{{ range .RequestParams -}}
| {{ .Name }} | {{ .Value }} | {{ .Description }} |
{{ end }}{{ end }}

{{ if .RequestHeaders -}}
Headers

| Name  | Value  | Description |
| ----- | :----- | :--------- |
{{ range .RequestHeaders -}}
| {{ .Name }} | {{ .Value }} | {{ .Description }} |
{{ end }}
{{ end }}

{{ if .RequestFields -}}
Request fields

| Name  | Value  | Description |
| ----- | :----- | :--------- |
{{ range .RequestFields -}}
| {{ .Name }} | {{ .Value }} | {{ .Description }} |
{{ end }}
{{ end }}

{{ if .RequestExample -}}
Request example

<details>
<summary>Click to expand code.</summary>

```javascript
{{ .RequestExample }}
```

</details>
{{ end }}

### Response

{{ if .ResponseHeaders -}}
Headers

| Name  | Value  | Description |
| ----- | :----- | :--------- |
{{ range .ResponseHeaders -}}
| {{ .Name }} | {{ .Value }} | {{ .Description }} |
{{ end }}
{{ end }}

{{ if .ResponseFields -}}
Response fields

| Name  | Value  | Description |
| ----- | :----- | :--------- |
{{ range .ResponseFields -}}
| {{ .Name }} | {{ .Value }} | {{ .Description }} |
{{ end }}
{{ end }}

{{ if .ResponseExample -}}
Response example

<details>
<summary>Click to expand code.</summary>

```javascript
{{ .ResponseExample }}
```

</details>

{{ end }}
{{ end }}


================================================
FILE: template.go
================================================
package httpdoc

import (
	"io"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"go.mercari.io/go-httpdoc/static"
)

// defaultTmpl is default template file to use.
var defaultTmpl = "tmpl/doc.md.tmpl"

// Generate writes documentation into the given file. Generation is skipped
// if EnvHTTPDoc is empty. If directory does not exist or any, it returns error.
func (d *Document) Generate(path string) error {

	// Only generate documentation when EnvHttpDoc has non-empty value
	if os.Getenv(EnvHTTPDoc) == "" {
		return nil
	}

	path, _ = filepath.Abs(path)
	if _, err := os.Stat(filepath.Dir(path)); err != nil && os.IsNotExist(err) {
		if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
			return err
		}
	}
	f, err := os.Create(path)
	if err != nil {
		return err
	}

	return d.generate(f)
}

func (d *Document) generate(w io.Writer) error {
	if d.tmpl == "" {
		d.tmpl = defaultTmpl
	}

	buf, err := static.Asset(d.tmpl)
	if err != nil {
		return err
	}

	return d.tmplExecute(w, string(buf))
}

func (d *Document) tmplExecute(w io.Writer, text string) error {
	tmpl, err := template.New("httpdoc").Funcs(funcMap()).Parse(text)
	if err != nil {
		return err
	}

	if err := tmpl.Execute(w, d); err != nil {
		return err
	}
	return nil
}

func funcMap() template.FuncMap {
	return template.FuncMap{
		"lower": strings.ToLower,
		"stripslash": func(s string) string {
			return strings.Replace(s, "/", "", -1)
		},
	}
}


================================================
FILE: template_test.go
================================================
package httpdoc

import (
	"io/ioutil"
	"os"
	"testing"
)

func setEnv(t *testing.T, k, v string) func() {
	preV := os.Getenv(k)
	if err := os.Setenv(k, v); err != nil {
		t.Fatal(err)
	}

	return func() {
		if err := os.Setenv(k, preV); err != nil {
			t.Fatal(err)
		}
	}
}

func TestDocument_Generate(t *testing.T) {
	resetF := setEnv(t, EnvHTTPDoc, "test")
	defer resetF()

	f, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatal(err)
	}

	doc := &Document{}
	if err := doc.Generate(f.Name()); err != nil {
		t.Fatal(err)
	}

	fi, err := os.Stat(f.Name())
	if err != nil {
		t.Fatal(err)
	}

	if fi.Size() == 0 {
		t.Fatalf("expect doc to be generated")
	}
}

func TestDocument_Generate_noEnv(t *testing.T) {
	resetF := setEnv(t, EnvHTTPDoc, "")
	defer resetF()

	f, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatal(err)
	}

	doc := &Document{}
	if err := doc.Generate(f.Name()); err != nil {
		t.Fatal(err)
	}

	fi, err := os.Stat(f.Name())
	if err != nil {
		t.Fatal(err)
	}

	if fi.Size() > 0 {
		t.Fatalf("expect doc not to be generated")
	}
}

func TestFuncMap(t *testing.T) {
	m := funcMap()
	lower := m["lower"].(func(s string) string)
	if got, want := lower("DOC"), "doc"; got != want {
		t.Fatalf("got %q, want %q", got, want)
	}

	stripslash := m["stripslash"].(func(s string) string)
	if got, want := stripslash("/v2/user/contact"), "v2usercontact"; got != want {
		t.Fatalf("got %q, want %q", got, want)
	}
}

func TestTemplateGenerate_NotExistDir(t *testing.T) {
	resetF := setEnv(t, EnvHTTPDoc, "1")
	defer resetF()

	doc := &Document{}
	if err := doc.Generate("/tmp/httpdoc/no-such-file-or-directory"); err != nil {
		t.Fatalf("expect to be failed")
	}
	defer os.RemoveAll("/tmp/httpdoc")
}

func TestTemplateGenerate_InvalidTmpl(t *testing.T) {
	doc := &Document{
		tmpl: "no-such-template",
	}
	if err := doc.generate(ioutil.Discard); err == nil {
		t.Fatalf("expect to be failed")
	}
}

func TestTmplExecute_InvalidTemplate(t *testing.T) {
	cases := []struct {
		text string
	}{
		{
			`{{ .Name }`,
		},
		{
			`{{ .Name.NoSuchField }}`,
		},
	}

	doc := &Document{}
	for _, tc := range cases {
		if err := doc.tmplExecute(ioutil.Discard, tc.text); err == nil {
			t.Fatalf("expect to be failed")
		}
	}
}


================================================
FILE: validate.go
================================================
package httpdoc

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"reflect"
	"testing"

	"github.com/golang/protobuf/proto"
	"github.com/tenntenn/gpath"
)

var (
	defaultUnmarshalFunc = json.Unmarshal

	defaultAssertFunc = func(t *testing.T, expected, actual interface{}, desc string) {
		if !reflect.DeepEqual(expected, actual) {
			tFatalf(t, "%s: got %#v(%T), want %#v(%T)", desc, actual, actual, expected, expected)
		}
	}

	defaultFatalFunc = func(t *testing.T, format string, args ...interface{}) {
		t.Fatalf(format, args...)
	}

	protoUnmarshalFunc = func(data []byte, v interface{}) error {
		unmashaler, ok := v.(proto.Unmarshaler)
		if !ok {
			return fmt.Errorf("failed to type assert to Unmashaler: %T must implement proto.Unmarshaler interface", v)
		}
		return unmashaler.Unmarshal(data)
	}
)

var tFatalf fatalFunc = defaultFatalFunc

type (
	assertFunc    func(t *testing.T, expected, actual interface{}, desc string)
	fatalFunc     func(t *testing.T, format string, args ...interface{})
	unmarshalFunc func(data []byte, v interface{}) error
)

// Validator takes test cases and checks whether recorded values are equal to the given expected values.
// If not, it fails in the given test context. If ok, it uses the result for documentation.
type Validator struct {
	record *record

	unmarshalFunc unmarshalFunc
	assertFunc    assertFunc

	requestParams  []Data
	requestHeaders []Data
	requestFields  []Data

	responseHeaders []Data
	responseFields  []Data
}

type record struct {
	requestParams  url.Values
	requestHeaders http.Header
	requestBody    []byte

	responseStatusCode int
	responseHeaders    http.Header
	responseBody       []byte
}

// TestCase is test case validator uses. Validator inspects and extract request & response value based on
// Target (e.g, when testing request params, target is parameter name. when testing response
// body, target is filed name) and asserts with Expected value.
//
// TestCase can be used like table-driven way.
//
//   validator.RequestParams(t, []httpdoc.TestCase{
//       NewTestCase("token","12345","Request token"),
//       NewTestCase("pretty","true","Pretty print response message"),
//	 })
//
type TestCase struct {
	Target      string
	Expected    interface{}
	Description string
	AssertFunc  assertFunc
}

// NewTestCase returns new TestCase.
func NewTestCase(target string, expected interface{}, description string) TestCase {
	return TestCase{Target: target, Expected: expected, Description: description}
}

func newValidator() *Validator {
	return &Validator{
		unmarshalFunc: defaultUnmarshalFunc,
		assertFunc:    defaultAssertFunc,
		record:        &record{},
	}
}

// ResponseStatusCode validates response status code is expected or not.
func (v *Validator) ResponseStatusCode(t *testing.T, expected int) {
	v.assertFunc(t, expected, v.record.responseStatusCode, "response status code")
}

// RequestParams validated request params are expected or not.
func (v *Validator) RequestParams(t *testing.T, cases []TestCase) {
	for _, tc := range cases {
		data := Data{
			Name:        tc.Target,
			Value:       tc.Expected,
			Description: tc.Description,
		}
		v.requestParams = append(v.requestParams, data)
		pickAssertFunc(&tc, v)(t, tc.Expected, v.record.requestParams.Get(tc.Target), tc.Description)
	}
}

// RequestHeaders validates request headers are expected or not.
func (v *Validator) RequestHeaders(t *testing.T, cases []TestCase) {
	for _, tc := range cases {
		data := Data{
			Name:        tc.Target,
			Value:       tc.Expected,
			Description: tc.Description,
		}
		v.requestHeaders = append(v.requestHeaders, data)

		actual := v.record.requestHeaders.Get(tc.Target)
		if actual == "" {
			h, ok := v.record.requestHeaders[tc.Target]
			if !ok || len(h) == 0 {
				tFatalf(t, "request header %q is not found", tc.Target)
				return
			}
			actual = h[0]
		}

		pickAssertFunc(&tc, v)(t, tc.Expected, actual, tc.Description)
	}
}

// ResponseHeaders validates response headers are expected or not.
func (v *Validator) ResponseHeaders(t *testing.T, cases []TestCase) {
	for _, tc := range cases {
		data := Data{
			Name:        tc.Target,
			Value:       tc.Expected,
			Description: tc.Description,
		}
		v.responseHeaders = append(v.responseHeaders, data)

		actual := v.record.responseHeaders.Get(tc.Target)
		if actual == "" {
			h, ok := v.record.responseHeaders[tc.Target]
			if !ok || len(h) == 0 {
				tFatalf(t, "request header %q is not found", tc.Target)
				return
			}
			actual = h[0]
		}
		pickAssertFunc(&tc, v)(t, tc.Expected, actual, tc.Description)
	}
}

// RequestBody validates request body's fileds are expected or not. The request body
// is unmarshaled to the given struct. To extract a filed to validate, this uses dot-seprated
// expression in TestCase.Target. For example, if you want to access `Email` value in the
// following struct use `Setting.Name` in Target.
//
//   type User struct {
//       Setting Setting
//   }
//
//   type Setting struct {
//       Email string
//   }
//
func (v *Validator) RequestBody(t *testing.T, cases []TestCase, request interface{}) {
	// Unmarshal request body into the given struct
	if err := v.unmarshalFunc(v.record.requestBody, request); err != nil {
		tFatalf(t, "Failed to unmarshal request body: %s", err)
		return
	}
	v.validateFields(t, cases, request, &v.requestFields)
}

// ResponseBody validates response body's fields are expected or not. The response body
// is unmarshaled to the given struct. To extract a filed to validate, this uses dot-seprated
// expression in TestCase.Target. For example, if you want to access `Email` value in the
// following struct use `Setting.Name` in Target.
//
//   type User struct {
//       Setting Setting
//   }
//
//   type Setting struct {
//       Email string
//   }
//
func (v *Validator) ResponseBody(t *testing.T, cases []TestCase, response interface{}) {
	// Unmarshal request body into the given struct
	if err := v.unmarshalFunc(v.record.responseBody, response); err != nil {
		tFatalf(t, "Failed to unmarshal response body: %s", err)
	}
	v.validateFields(t, cases, response, &v.responseFields)
}

func (vl *Validator) validateFields(t *testing.T, cases []TestCase, v interface{}, fields *[]Data) {
	for _, tc := range cases {
		data := Data{
			Name:        tc.Target,
			Value:       tc.Expected,
			Description: tc.Description,
		}
		*fields = append(*fields, data)
		actual, _ := gpath.At(v, tc.Target)
		pickAssertFunc(&tc, vl)(t, tc.Expected, actual, tc.Description)
	}
}

func pickAssertFunc(tc *TestCase, v *Validator) assertFunc {
	if tc.AssertFunc != nil {
		return tc.AssertFunc
	}
	return v.assertFunc
}


================================================
FILE: validate_test.go
================================================
package httpdoc

import (
	"bytes"
	"fmt"
	"io"
	"math/rand"
	"reflect"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/golang/protobuf/proto"
)

type User struct {
	ID         int            `json:"id"`
	Name       string         `json:"name"`
	Active     bool           `json:"active"`
	Setting    *Setting       `json:"setting"`
	Permission []string       `json:"permission"`
	Preference map[string]int `json:"preference"`
}

type Setting struct {
	Email string `json:"email"`
	SNS   SNS    `json:"sns"`
}

type SNS struct {
	Twitter  string `json:"twitter"`
	Facebook string `json:"facebook"`
}

// testAssertWithCount returns assertFunc it counts failed test instead of fail.
func testAssertWithCount(fails *int) assertFunc {
	return func(t *testing.T, expected, actual interface{}, desc string) {
		if !reflect.DeepEqual(expected, actual) {
			*fails++
		}
	}
}

func fprintFatalFunc(w io.Writer) fatalFunc {
	return func(t *testing.T, format string, args ...interface{}) {
		fmt.Fprintf(w, format, args...)
	}
}

func TestValidator_ResponseStatusCode(t *testing.T) {
	validator := newValidator()

	validator.record.responseStatusCode = 200
	validator.ResponseStatusCode(t, 200)

	validator.record.responseStatusCode = 500
	validator.ResponseStatusCode(t, 500)

	var got int
	validator.assertFunc = testAssertWithCount(&got)
	validator.record.responseStatusCode = 500
	validator.ResponseStatusCode(t, 200)
	if want := 1; got != want {
		t.Fatalf("expect valiate fails %d, got %d", want, got)
	}
}

func TestValidator_RequestParams(t *testing.T) {
	validator := newValidator()
	validator.record.requestParams = map[string][]string{
		"token":  []string{"12345"},
		"pretty": []string{"true"},
		"year":   []string{strconv.Itoa(time.Now().Year())},
	}
	thisYearcalledAssertFunc := false
	validator.RequestParams(t, []TestCase{
		NewTestCase("token", "12345", ""),
		NewTestCase("pretty", "true", ""),
		{"year", "thisyear", "", func(t *testing.T, expected, actual interface{}, desc string) {
			if expected != "thisyear" {
				t.Fatal("expected is not thisyear")
			}
			thisYearcalledAssertFunc = true
		}},
	})

	if thisYearcalledAssertFunc == false {
		t.Fatal("thisYear AssertFunc should be called.")
	}

	var got int
	validator.assertFunc = testAssertWithCount(&got)
	validator.RequestParams(t, []TestCase{
		NewTestCase("token", "8976", ""),
		NewTestCase("pretty", "", ""),
		NewTestCase("id", "u8988", ""),
	})
	if want := 3; got != want {
		t.Fatalf("expect valiate fails %d, got %d", want, got)
	}
}

func TestValidator_RequestHeaders(t *testing.T) {
	validator := newValidator()
	validator.record.requestHeaders = map[string][]string{
		"User-Agent":    []string{"Googlebot/2.1"},
		"Content-Type":  []string{"application/json"},
		"X-API-Version": []string{"1.1.2"},
	}
	validator.RequestHeaders(t, []TestCase{
		NewTestCase("User-Agent", "Googlebot/2.1", ""),
		NewTestCase("Content-Type", "application/json", ""),
		NewTestCase("X-API-Version", "1.1.2", ""),
	})

	var got int
	validator.assertFunc = testAssertWithCount(&got)
	validator.RequestHeaders(t, []TestCase{
		NewTestCase("User-Agent", []string{"curl"}, ""),
		NewTestCase("Content-Type", []string{"application/protobuf"}, ""),
		NewTestCase("X-API-Version", []string{"3.0"}, ""),
	})
	if want := 3; got != want {
		t.Fatalf("expect valiate fails %d, got %d", want, got)
	}

	var buf bytes.Buffer
	tFatalf = fprintFatalFunc(&buf)
	validator.RequestHeaders(t, []TestCase{
		NewTestCase("Not-Found", []string{""}, ""),
	})

	if got, want := buf.String(), "not found"; !strings.Contains(got, want) {
		t.Fatalf("expect %q to contain %q", got, want)
	}
}

func TestValidator_ResponseHeaders(t *testing.T) {
	validator := newValidator()
	rand.Seed(time.Now().UnixNano())
	length := 0
	for {
		length = rand.Intn(100000)
		if length > 0 {
			break
		}
	}

	validator.record.responseHeaders = map[string][]string{
		"Content-Type":   []string{"application/json"},
		"X-API-Version":  []string{"1.1.2"},
		"Content-Length": []string{strconv.Itoa(length)},
	}
	contentLengthCalledAssertFunc := false
	validator.ResponseHeaders(t, []TestCase{
		NewTestCase("Content-Type", "application/json", ""),
		NewTestCase("X-API-Version", "1.1.2", ""),
		{"Content-Length", []string{"content length"}, "length is change every time", func(t *testing.T, expected, actual interface{}, desc string) {
			contentLength, err := strconv.Atoi(actual.(string))
			if err != nil {
				t.Fatal("actual is not number")
			}
			if contentLength <= 0 {
				t.Fatal("actual must be greater than 0")
			}
			contentLengthCalledAssertFunc = true
		}},
	})

	if contentLengthCalledAssertFunc == false {
		t.Fatal("content length AssertFunc should be called.")
	}

	var got int
	validator.assertFunc = testAssertWithCount(&got)
	validator.ResponseHeaders(t, []TestCase{
		NewTestCase("Content-Type", []string{"application/protobuf"}, ""),
	})
	if want := 1; got != want {
		t.Fatalf("expect valiate fails %d, got %d", want, got)
	}

	var buf bytes.Buffer
	tFatalf = fprintFatalFunc(&buf)
	validator.ResponseHeaders(t, []TestCase{
		NewTestCase("Not-Found", []string{""}, ""),
	})

	if got, want := buf.String(), "not found"; !strings.Contains(got, want) {
		t.Fatalf("expect %q to contain %q", got, want)
	}
}

func TestValidator_RequestBody(t *testing.T) {
	validator := newValidator()
	validator.record.requestBody = []byte(`{
  "id": 910,
  "setting": {
    "email": "taichi@mercari.com"
  }
}
`)
	validator.RequestBody(t, []TestCase{
		NewTestCase("ID", 910, ""),
		NewTestCase("Setting.Email", "taichi@mercari.com", ""),
	}, &User{})

	var got int
	validator.assertFunc = testAssertWithCount(&got)
	validator.RequestBody(t, []TestCase{
		NewTestCase("ID", 123, ""),
		NewTestCase("Active", true, ""),
		NewTestCase("Setting.Email", "deeeet@gmail.com", ""),
	}, &User{})

	if want := 3; got != want {
		t.Fatalf("expect valiate fails %d, got %d", want, got)
	}

	var buf bytes.Buffer
	tFatalf = fprintFatalFunc(&buf)
	validator.RequestBody(t, []TestCase{}, struct{}{})
	if got, want := buf.String(), "Failed to unmarshal request"; !strings.Contains(got, want) {
		t.Fatalf("expect %q to contain %q", got, want)
	}
}

func TestValidator_ResponseBody(t *testing.T) {
	validator := newValidator()
	validator.record.responseBody = []byte(`{
  "id": 789,
  "active": false,
  "setting": {
    "email": "tcnksm@mercari.com"
  },
  "permission": ["write","read"],
  "preference": {
    "email": 0
  }
}
`)
	custommailCalledAssertFunc := false
	validator.ResponseBody(t, []TestCase{
		NewTestCase("ID", 789, ""),
		NewTestCase("Active", false, ""),
		NewTestCase("Setting.Email", "tcnksm@mercari.com", ""),
		{"Setting.Email", "custommail", "", func(t *testing.T, expected, actual interface{}, desc string) {
			if expected != "custommail" {
				t.Fatal("Setting.Email is not custommail")
			}
			custommailCalledAssertFunc = true
		}},
		NewTestCase("Permission[1]", "read", ""),
		{`Preference["email"]`, 0, "", nil},
	}, &User{})

	if custommailCalledAssertFunc == false {
		t.Fatal("custom mail AssertFunc should be called.")
	}

	var got int
	validator.assertFunc = testAssertWithCount(&got)
	validator.ResponseBody(t, []TestCase{
		NewTestCase("ID", 123, ""),
		NewTestCase("Active", true, ""),
		NewTestCase("Setting.Email", "deeeet@gmail.com", ""),
		NewTestCase("Permission[1]", "write", ""),
		{`Preference["email"]`, 1, "", nil},
	}, &User{})

	if want := 5; got != want {
		t.Fatalf("expect valiate fails %d, got %d", want, got)
	}

	var buf bytes.Buffer
	tFatalf = fprintFatalFunc(&buf)
	validator.ResponseBody(t, []TestCase{}, struct{}{})
	if got, want := buf.String(), "Failed to unmarshal response"; !strings.Contains(got, want) {
		t.Fatalf("expect %q to contain %q", got, want)
	}
}

func TestValidateFields(t *testing.T) {
	testUser := &User{
		ID:     12345,
		Name:   "tcnksm",
		Active: true,
		Setting: &Setting{
			Email: "tcnksm@example.com",
			SNS: SNS{
				Twitter: "@deeeet",
			},
		},
		Permission: []string{"write", "read"},
		Preference: map[string]int{
			"email": 0,
			"push":  1,
		},
	}

	activeCalledAssertFunc := false
	validator := newValidator()
	validator.validateFields(t, []TestCase{
		NewTestCase("ID", 12345, ""),
		NewTestCase("Name", "tcnksm", ""),
		NewTestCase("Active", true, ""),
		{"Active", "customactive", "", func(t *testing.T, expected, actual interface{}, desc string) {
			if expected != "customactive" {
				t.Fatal("Acitve is not customactive")
			}
			activeCalledAssertFunc = true
		}},
		NewTestCase("Setting.Email", "tcnksm@example.com", ""),
		NewTestCase("Setting.SNS.Twitter", "@deeeet", ""),
		NewTestCase("Permission[0]", "write", ""),
		{`Preference["email"]`, 0, "", nil},
	}, testUser, &[]Data{})

	if activeCalledAssertFunc == false {
		t.Fatal("active AssertFunc should be called.")
	}
}

func TestValidator_RequestBody_Proto(t *testing.T) {
	buf, err := proto.Marshal(&UserProtoRequest{
		Id:   12345,
		Name: "tcnksm",
	})
	if err != nil {
		t.Fatal(err)
	}

	validator := newValidator()
	validator.record.requestBody = buf
	validator.unmarshalFunc = protoUnmarshalFunc
	customIDCalledAssertFunc := false
	validator.RequestBody(t, []TestCase{
		NewTestCase("Id", int32(12345), ""),
		NewTestCase("Name", "tcnksm", ""),
		{"Id", "customid", "custom assert func test", func(t *testing.T, expected, actual interface{}, desc string) {
			if expected != "customid" {
				t.Fatal("expected is not customid")
			}
			customIDCalledAssertFunc = true
		}},
	}, &UserProtoRequest{})

	if customIDCalledAssertFunc == false {
		t.Fatal("custom id AssertFunc should be called.")
	}

	var got int
	validator.assertFunc = testAssertWithCount(&got)
	validator.RequestBody(t, []TestCase{
		NewTestCase("Id", 123, ""),
	}, &UserProtoRequest{})

	if want := 1; got != want {
		t.Fatalf("expect valiate fails %d, got %d", want, got)
	}
}

func TestValidator_ResponseBody_Proto(t *testing.T) {
	buf, err := proto.Marshal(&UserProtoResponse{
		Id: 667854,
		Setting: &UserProtoResponse_Setting{
			Email: "httpdoc@example.com",
		},
	})
	if err != nil {
		t.Fatal(err)
	}

	validator := newValidator()
	validator.unmarshalFunc = protoUnmarshalFunc
	validator.record.responseBody = buf
	validator.ResponseBody(t, []TestCase{
		NewTestCase("Id", int32(667854), ""),
		NewTestCase("Setting.Email", "httpdoc@example.com", ""),
	}, &UserProtoResponse{})

	var got int
	validator.assertFunc = testAssertWithCount(&got)
	validator.ResponseBody(t, []TestCase{
		NewTestCase("Id", 123, ""),
		NewTestCase("Setting.Email", "deeeet@gmail.com", ""),
	}, &UserProtoResponse{})

	if want := 2; got != want {
		t.Fatalf("expect valiate fails %d, got %d", want, got)
	}
}

func TestUnmarshallerFunc(t *testing.T) {
	unmarshalFunc := protoUnmarshalFunc
	if err := unmarshalFunc([]byte(""), &User{}); err == nil {
		t.Fatal("expect to be failed")
	}
}

func TestAssertFunc(t *testing.T) {
	var buf bytes.Buffer
	tFatalf = fprintFatalFunc(&buf)
	defaultAssertFunc(t, 1, 2, "test-assert")
	if got, want := buf.String(), "test-assert: got 2(int), want 1(int)"; !strings.Contains(got, want) {
		t.Fatalf("expect %q to contain %q", got, want)
	}

}
Download .txt
gitextract_wz5xessz/

├── .github/
│   └── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── _example/
│   ├── README.md
│   ├── doc/
│   │   ├── protobuf.md
│   │   ├── simple.md
│   │   └── validate.md
│   ├── handler.go
│   ├── handler_proto_test.go
│   ├── handler_simple_test.go
│   ├── handler_validate_test.go
│   └── message.pb.go
├── doc.go
├── httpdoc.go
├── httpdoc_test.go
├── message_pb_test.go
├── proto/
│   └── message.proto
├── static/
│   ├── bindata.go
│   ├── static.go
│   └── tmpl/
│       └── doc.md.tmpl
├── template.go
├── template_test.go
├── validate.go
└── validate_test.go
Download .txt
SYMBOL INDEX (187 symbols across 13 files)

FILE: _example/handler.go
  type createUserRequest (line 8) | type createUserRequest struct
  type attribute (line 14) | type attribute struct
  type createUserResponse (line 19) | type createUserResponse struct
  type userHandler (line 24) | type userHandler struct
    method ServeHTTP (line 30) | func (h *userHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  type userProtoHandler (line 27) | type userProtoHandler struct
    method ServeHTTP (line 63) | func (h *userProtoHandler) ServeHTTP(w http.ResponseWriter, r *http.Re...

FILE: _example/handler_proto_test.go
  function TestUserHandlerWithProtobuf (line 11) | func TestUserHandlerWithProtobuf(t *testing.T) {

FILE: _example/handler_simple_test.go
  function TestUserHandlerSimple (line 13) | func TestUserHandlerSimple(t *testing.T) {
  function testNewRequest (line 38) | func testNewRequest(t *testing.T, urlStr string) *http.Request {

FILE: _example/handler_validate_test.go
  function TestUserHandlerWithValidate (line 11) | func TestUserHandlerWithValidate(t *testing.T) {

FILE: _example/message.pb.go
  constant _ (line 32) | _ = proto.ProtoPackageIsVersion2
  type UserProtoRequest (line 34) | type UserProtoRequest struct
    method Reset (line 39) | func (m *UserProtoRequest) Reset()                    { *m = UserProto...
    method String (line 40) | func (m *UserProtoRequest) String() string            { return proto.C...
    method ProtoMessage (line 41) | func (*UserProtoRequest) ProtoMessage()               {}
    method Descriptor (line 42) | func (*UserProtoRequest) Descriptor() ([]byte, []int) { return fileDes...
    method GetId (line 44) | func (m *UserProtoRequest) GetId() int32 {
    method GetName (line 51) | func (m *UserProtoRequest) GetName() string {
    method Marshal (line 121) | func (m *UserProtoRequest) Marshal() (dAtA []byte, err error) {
    method MarshalTo (line 131) | func (m *UserProtoRequest) MarshalTo(dAtA []byte) (int, error) {
    method Size (line 250) | func (m *UserProtoRequest) Size() (n int) {
    method Unmarshal (line 306) | func (m *UserProtoRequest) Unmarshal(dAtA []byte) error {
  type UserProtoResponse (line 58) | type UserProtoResponse struct
    method Reset (line 65) | func (m *UserProtoResponse) Reset()                    { *m = UserProt...
    method String (line 66) | func (m *UserProtoResponse) String() string            { return proto....
    method ProtoMessage (line 67) | func (*UserProtoResponse) ProtoMessage()               {}
    method Descriptor (line 68) | func (*UserProtoResponse) Descriptor() ([]byte, []int) { return fileDe...
    method GetId (line 70) | func (m *UserProtoResponse) GetId() int32 {
    method GetName (line 77) | func (m *UserProtoResponse) GetName() string {
    method GetActive (line 84) | func (m *UserProtoResponse) GetActive() bool {
    method GetSetting (line 91) | func (m *UserProtoResponse) GetSetting() *UserProtoResponse_Setting {
    method Marshal (line 150) | func (m *UserProtoResponse) Marshal() (dAtA []byte, err error) {
    method MarshalTo (line 160) | func (m *UserProtoResponse) MarshalTo(dAtA []byte) (int, error) {
    method Size (line 263) | func (m *UserProtoResponse) Size() (n int) {
    method Unmarshal (line 404) | func (m *UserProtoResponse) Unmarshal(dAtA []byte) error {
  type UserProtoResponse_Setting (line 98) | type UserProtoResponse_Setting struct
    method Reset (line 102) | func (m *UserProtoResponse_Setting) Reset()         { *m = UserProtoRe...
    method String (line 103) | func (m *UserProtoResponse_Setting) String() string { return proto.Com...
    method ProtoMessage (line 104) | func (*UserProtoResponse_Setting) ProtoMessage()    {}
    method Descriptor (line 105) | func (*UserProtoResponse_Setting) Descriptor() ([]byte, []int) {
    method GetEmail (line 109) | func (m *UserProtoResponse_Setting) GetEmail() string {
    method Marshal (line 199) | func (m *UserProtoResponse_Setting) Marshal() (dAtA []byte, err error) {
    method MarshalTo (line 209) | func (m *UserProtoResponse_Setting) MarshalTo(dAtA []byte) (int, error) {
    method Size (line 283) | func (m *UserProtoResponse_Setting) Size() (n int) {
    method Unmarshal (line 555) | func (m *UserProtoResponse_Setting) Unmarshal(dAtA []byte) error {
  function init (line 116) | func init() {
  function encodeFixed64Message (line 223) | func encodeFixed64Message(dAtA []byte, offset int, v uint64) int {
  function encodeFixed32Message (line 234) | func encodeFixed32Message(dAtA []byte, offset int, v uint32) int {
  function encodeVarintMessage (line 241) | func encodeVarintMessage(dAtA []byte, offset int, v uint64) int {
  function sovMessage (line 293) | func sovMessage(x uint64) (n int) {
  function sozMessage (line 303) | func sozMessage(x uint64) (n int) {
  function skipMessage (line 634) | func skipMessage(dAtA []byte) (n int, err error) {
  function init (line 739) | func init() { proto.RegisterFile("message.proto", fileDescriptorMessage) }

FILE: httpdoc.go
  constant EnvHTTPDoc (line 19) | EnvHTTPDoc = "HTTPDOC"
  type Document (line 23) | type Document struct
  type Entry (line 44) | type Entry struct
    method format (line 241) | func (e *Entry) format() error {
  type RecordOption (line 74) | type RecordOption struct
  type ProtoBufferOption (line 98) | type ProtoBufferOption struct
  type Data (line 110) | type Data struct
  type byName (line 121) | type byName
    method Len (line 123) | func (n byName) Len() int           { return len(n) }
    method Less (line 124) | func (n byName) Less(i, j int) bool { return n[i].Name < n[j].Name }
    method Swap (line 125) | func (n byName) Swap(i, j int)      { n[i], n[j] = n[j], n[i] }
  function Record (line 129) | func Record(next http.Handler, document *Document, opt *RecordOption) ht...
  type responseWriter (line 248) | type responseWriter struct
    method Write (line 255) | func (w *responseWriter) Write(buf []byte) (int, error) {
    method WriteHeader (line 260) | func (w *responseWriter) WriteHeader(code int) {
  function convertHeaders (line 266) | func convertHeaders(headers map[string][]string) []Data {
  function mergeData (line 280) | func mergeData(a, b []Data) []Data {
  function excludeData (line 298) | func excludeData(target []Data, excludes ...[]string) []Data {

FILE: httpdoc_test.go
  function TestRecord (line 46) | func TestRecord(t *testing.T) {
  function TestRecord_Proto (line 178) | func TestRecord_Proto(t *testing.T) {
  function TestConvertHeaders (line 264) | func TestConvertHeaders(t *testing.T) {
  function TestMergeData (line 291) | func TestMergeData(t *testing.T) {
  function TestExcludeData (line 319) | func TestExcludeData(t *testing.T) {
  function TestResponseWriter_Write (line 340) | func TestResponseWriter_Write(t *testing.T) {
  function TestResponseWriter_WriteHeader (line 343) | func TestResponseWriter_WriteHeader(t *testing.T) {

FILE: message_pb_test.go
  constant _ (line 32) | _ = proto.ProtoPackageIsVersion2
  type UserProtoRequest (line 34) | type UserProtoRequest struct
    method Reset (line 39) | func (m *UserProtoRequest) Reset()                    { *m = UserProto...
    method String (line 40) | func (m *UserProtoRequest) String() string            { return proto.C...
    method ProtoMessage (line 41) | func (*UserProtoRequest) ProtoMessage()               {}
    method Descriptor (line 42) | func (*UserProtoRequest) Descriptor() ([]byte, []int) { return fileDes...
    method GetId (line 44) | func (m *UserProtoRequest) GetId() int32 {
    method GetName (line 51) | func (m *UserProtoRequest) GetName() string {
    method Marshal (line 121) | func (m *UserProtoRequest) Marshal() (dAtA []byte, err error) {
    method MarshalTo (line 131) | func (m *UserProtoRequest) MarshalTo(dAtA []byte) (int, error) {
    method Size (line 250) | func (m *UserProtoRequest) Size() (n int) {
    method Unmarshal (line 306) | func (m *UserProtoRequest) Unmarshal(dAtA []byte) error {
  type UserProtoResponse (line 58) | type UserProtoResponse struct
    method Reset (line 65) | func (m *UserProtoResponse) Reset()                    { *m = UserProt...
    method String (line 66) | func (m *UserProtoResponse) String() string            { return proto....
    method ProtoMessage (line 67) | func (*UserProtoResponse) ProtoMessage()               {}
    method Descriptor (line 68) | func (*UserProtoResponse) Descriptor() ([]byte, []int) { return fileDe...
    method GetId (line 70) | func (m *UserProtoResponse) GetId() int32 {
    method GetName (line 77) | func (m *UserProtoResponse) GetName() string {
    method GetActive (line 84) | func (m *UserProtoResponse) GetActive() bool {
    method GetSetting (line 91) | func (m *UserProtoResponse) GetSetting() *UserProtoResponse_Setting {
    method Marshal (line 150) | func (m *UserProtoResponse) Marshal() (dAtA []byte, err error) {
    method MarshalTo (line 160) | func (m *UserProtoResponse) MarshalTo(dAtA []byte) (int, error) {
    method Size (line 263) | func (m *UserProtoResponse) Size() (n int) {
    method Unmarshal (line 404) | func (m *UserProtoResponse) Unmarshal(dAtA []byte) error {
  type UserProtoResponse_Setting (line 98) | type UserProtoResponse_Setting struct
    method Reset (line 102) | func (m *UserProtoResponse_Setting) Reset()         { *m = UserProtoRe...
    method String (line 103) | func (m *UserProtoResponse_Setting) String() string { return proto.Com...
    method ProtoMessage (line 104) | func (*UserProtoResponse_Setting) ProtoMessage()    {}
    method Descriptor (line 105) | func (*UserProtoResponse_Setting) Descriptor() ([]byte, []int) {
    method GetEmail (line 109) | func (m *UserProtoResponse_Setting) GetEmail() string {
    method Marshal (line 199) | func (m *UserProtoResponse_Setting) Marshal() (dAtA []byte, err error) {
    method MarshalTo (line 209) | func (m *UserProtoResponse_Setting) MarshalTo(dAtA []byte) (int, error) {
    method Size (line 283) | func (m *UserProtoResponse_Setting) Size() (n int) {
    method Unmarshal (line 555) | func (m *UserProtoResponse_Setting) Unmarshal(dAtA []byte) error {
  function init (line 116) | func init() {
  function encodeFixed64Message (line 223) | func encodeFixed64Message(dAtA []byte, offset int, v uint64) int {
  function encodeFixed32Message (line 234) | func encodeFixed32Message(dAtA []byte, offset int, v uint32) int {
  function encodeVarintMessage (line 241) | func encodeVarintMessage(dAtA []byte, offset int, v uint64) int {
  function sovMessage (line 293) | func sovMessage(x uint64) (n int) {
  function sozMessage (line 303) | func sozMessage(x uint64) (n int) {
  function skipMessage (line 634) | func skipMessage(dAtA []byte) (n int, err error) {
  function init (line 739) | func init() { proto.RegisterFile("message.proto", fileDescriptorMessage) }

FILE: static/bindata.go
  function bindataRead (line 21) | func bindataRead(data []byte, name string) ([]byte, error) {
  type asset (line 41) | type asset struct
  type bindataFileInfo (line 46) | type bindataFileInfo struct
    method Name (line 53) | func (fi bindataFileInfo) Name() string {
    method Size (line 56) | func (fi bindataFileInfo) Size() int64 {
    method Mode (line 59) | func (fi bindataFileInfo) Mode() os.FileMode {
    method ModTime (line 62) | func (fi bindataFileInfo) ModTime() time.Time {
    method IsDir (line 65) | func (fi bindataFileInfo) IsDir() bool {
    method Sys (line 68) | func (fi bindataFileInfo) Sys() interface{} {
  function tmplApiBlueprintTmplBytes (line 74) | func tmplApiBlueprintTmplBytes() ([]byte, error) {
  function tmplApiBlueprintTmpl (line 81) | func tmplApiBlueprintTmpl() (*asset, error) {
  function tmplDocMdTmplBytes (line 94) | func tmplDocMdTmplBytes() ([]byte, error) {
  function tmplDocMdTmpl (line 101) | func tmplDocMdTmpl() (*asset, error) {
  function Asset (line 115) | func Asset(name string) ([]byte, error) {
  function MustAsset (line 129) | func MustAsset(name string) []byte {
  function AssetInfo (line 141) | func AssetInfo(name string) (os.FileInfo, error) {
  function AssetNames (line 154) | func AssetNames() []string {
  function AssetDir (line 181) | func AssetDir(name string) ([]string, error) {
  type bintree (line 203) | type bintree struct
  function RestoreAsset (line 215) | func RestoreAsset(dir, name string) error {
  function RestoreAssets (line 240) | func RestoreAssets(dir, name string) error {
  function _filePath (line 256) | func _filePath(dir, name string) string {

FILE: template.go
  method Generate (line 18) | func (d *Document) Generate(path string) error {
  method generate (line 39) | func (d *Document) generate(w io.Writer) error {
  method tmplExecute (line 52) | func (d *Document) tmplExecute(w io.Writer, text string) error {
  function funcMap (line 64) | func funcMap() template.FuncMap {

FILE: template_test.go
  function setEnv (line 9) | func setEnv(t *testing.T, k, v string) func() {
  function TestDocument_Generate (line 22) | func TestDocument_Generate(t *testing.T) {
  function TestDocument_Generate_noEnv (line 46) | func TestDocument_Generate_noEnv(t *testing.T) {
  function TestFuncMap (line 70) | func TestFuncMap(t *testing.T) {
  function TestTemplateGenerate_NotExistDir (line 83) | func TestTemplateGenerate_NotExistDir(t *testing.T) {
  function TestTemplateGenerate_InvalidTmpl (line 94) | func TestTemplateGenerate_InvalidTmpl(t *testing.T) {
  function TestTmplExecute_InvalidTemplate (line 103) | func TestTmplExecute_InvalidTemplate(t *testing.T) {

FILE: validate.go
  type assertFunc (line 40) | type assertFunc
  type fatalFunc (line 41) | type fatalFunc
  type unmarshalFunc (line 42) | type unmarshalFunc
  type Validator (line 47) | type Validator struct
    method ResponseStatusCode (line 103) | func (v *Validator) ResponseStatusCode(t *testing.T, expected int) {
    method RequestParams (line 108) | func (v *Validator) RequestParams(t *testing.T, cases []TestCase) {
    method RequestHeaders (line 121) | func (v *Validator) RequestHeaders(t *testing.T, cases []TestCase) {
    method ResponseHeaders (line 145) | func (v *Validator) ResponseHeaders(t *testing.T, cases []TestCase) {
    method RequestBody (line 180) | func (v *Validator) RequestBody(t *testing.T, cases []TestCase, reques...
    method ResponseBody (line 202) | func (v *Validator) ResponseBody(t *testing.T, cases []TestCase, respo...
    method validateFields (line 210) | func (vl *Validator) validateFields(t *testing.T, cases []TestCase, v ...
  type record (line 61) | type record struct
  type TestCase (line 82) | type TestCase struct
  function NewTestCase (line 90) | func NewTestCase(target string, expected interface{}, description string...
  function newValidator (line 94) | func newValidator() *Validator {
  function pickAssertFunc (line 223) | func pickAssertFunc(tc *TestCase, v *Validator) assertFunc {

FILE: validate_test.go
  type User (line 17) | type User struct
  type Setting (line 26) | type Setting struct
  type SNS (line 31) | type SNS struct
  function testAssertWithCount (line 37) | func testAssertWithCount(fails *int) assertFunc {
  function fprintFatalFunc (line 45) | func fprintFatalFunc(w io.Writer) fatalFunc {
  function TestValidator_ResponseStatusCode (line 51) | func TestValidator_ResponseStatusCode(t *testing.T) {
  function TestValidator_RequestParams (line 69) | func TestValidator_RequestParams(t *testing.T) {
  function TestValidator_RequestHeaders (line 104) | func TestValidator_RequestHeaders(t *testing.T) {
  function TestValidator_ResponseHeaders (line 139) | func TestValidator_ResponseHeaders(t *testing.T) {
  function TestValidator_RequestBody (line 195) | func TestValidator_RequestBody(t *testing.T) {
  function TestValidator_ResponseBody (line 229) | func TestValidator_ResponseBody(t *testing.T) {
  function TestValidateFields (line 284) | func TestValidateFields(t *testing.T) {
  function TestValidator_RequestBody_Proto (line 325) | func TestValidator_RequestBody_Proto(t *testing.T) {
  function TestValidator_ResponseBody_Proto (line 364) | func TestValidator_ResponseBody_Proto(t *testing.T) {
  function TestUnmarshallerFunc (line 395) | func TestUnmarshallerFunc(t *testing.T) {
  function TestAssertFunc (line 402) | func TestAssertFunc(t *testing.T) {
Condensed preview — 26 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (112K chars).
[
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 253,
    "preview": "Please read the CLA carefully before submitting your contribution to Mercari.\nUnder any circumstances, by submitting you"
  },
  {
    "path": ".gitignore",
    "chars": 23,
    "preview": "httpdoc.md\ncoverage.txt"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 1158,
    "preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
  },
  {
    "path": "LICENSE",
    "chars": 1070,
    "preview": "Copyright (c) 2017 Mercari, Inc.\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining\na c"
  },
  {
    "path": "README.md",
    "chars": 1663,
    "preview": "# go-httpdoc [![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godoc]\n\n[god"
  },
  {
    "path": "_example/README.md",
    "chars": 848,
    "preview": "# httpdoc example\n\nThis directory contains some examples of `httpdoc`.\n\n- [`handler_simple_test.go`](/_example/handler_s"
  },
  {
    "path": "_example/doc/protobuf.md",
    "chars": 793,
    "preview": "# API doc\n\nThis is API documentation for Example API (with protobuf). This is generated by `httpdoc`. Don't edit by hand"
  },
  {
    "path": "_example/doc/simple.md",
    "chars": 985,
    "preview": "# API doc\n\nThis is API documentation for Example API (simple). This is generated by `httpdoc`. Don't edit by hand.\n\n## T"
  },
  {
    "path": "_example/doc/validate.md",
    "chars": 1357,
    "preview": "# API doc\n\nThis is API documentation for Example API (with validation). This is generated by `httpdoc`. Don't edit by ha"
  },
  {
    "path": "_example/handler.go",
    "chars": 1724,
    "preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n)\n\ntype createUserRequest struct {\n\tName      string    `json:\"name\""
  },
  {
    "path": "_example/handler_proto_test.go",
    "chars": 1261,
    "preview": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\thttpdoc \"go.mercari.io/go-httpdoc\"\n)\n\nfunc TestUser"
  },
  {
    "path": "_example/handler_simple_test.go",
    "chars": 1232,
    "preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\thttpdoc \"go.mercari.io/go"
  },
  {
    "path": "_example/handler_validate_test.go",
    "chars": 2016,
    "preview": "package main\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\thttpdoc \"go.mercari.io/go-httpdoc\"\n)\n\nfunc TestUser"
  },
  {
    "path": "_example/message.pb.go",
    "chars": 17614,
    "preview": "// Code generated by protoc-gen-gogo.\n// source: message.proto\n// DO NOT EDIT!\n\n/*\n\tPackage httpdoc is a generated proto"
  },
  {
    "path": "doc.go",
    "chars": 638,
    "preview": "// Package httpdoc is a Golang package for generating API documentation from httptest test cases.\n//\n// It provides a si"
  },
  {
    "path": "httpdoc.go",
    "chars": 9179,
    "preview": "package httpdoc\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"sort\"\n\n\t\"github.com/go"
  },
  {
    "path": "httpdoc_test.go",
    "chars": 6941,
    "preview": "package httpdoc\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\t\"t"
  },
  {
    "path": "message_pb_test.go",
    "chars": 17617,
    "preview": "// Code generated by protoc-gen-gogo.\n// source: message.proto\n// DO NOT EDIT!\n\n/*\n\tPackage httpdoc is a generated proto"
  },
  {
    "path": "proto/message.proto",
    "chars": 259,
    "preview": "syntax = \"proto3\";\n\npackage httpdoc;\n\nmessage UserProtoRequest {\n  int32 id = 1;\n  string name = 2;\n}\n\nmessage UserProto"
  },
  {
    "path": "static/bindata.go",
    "chars": 9165,
    "preview": "// Code generated by go-bindata.\n// sources:\n// tmpl/api-blueprint.tmpl\n// tmpl/doc.md.tmpl\n// DO NOT EDIT!\n\npackage sta"
  },
  {
    "path": "static/static.go",
    "chars": 64,
    "preview": "package static\n\n//go:generate go-bindata -pkg=static ./tmpl/...\n"
  },
  {
    "path": "static/tmpl/doc.md.tmpl",
    "chars": 1784,
    "preview": "# API doc\n\nThis is API documentation for {{ .Name }}. This is generated by `httpdoc`. Don't edit by hand.\n\n## Table of c"
  },
  {
    "path": "template.go",
    "chars": 1438,
    "preview": "package httpdoc\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"text/template\"\n\n\t\"go.mercari.io/go-httpdoc/static\"\n)"
  },
  {
    "path": "template_test.go",
    "chars": 2244,
    "preview": "package httpdoc\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc setEnv(t *testing.T, k, v string) func() {\n\tpreV := os.G"
  },
  {
    "path": "validate.go",
    "chars": 6651,
    "preview": "package httpdoc\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/golang/prot"
  },
  {
    "path": "validate_test.go",
    "chars": 11124,
    "preview": "package httpdoc\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gith"
  }
]

About this extraction

This page contains the full source code of the mercari/go-httpdoc GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 26 files (96.8 KB), approximately 32.0k tokens, and a symbol index with 187 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!