diff --git a/.codecov.yml b/.codecov.yml
new file mode 100644
index 00000000..0ac10dcf
--- /dev/null
+++ b/.codecov.yml
@@ -0,0 +1,10 @@
+coverage:
+ status:
+ project:
+ default:
+ threshold: 1%
+ server:
+ threshold: 1%
+ paths:
+ - pkg/grapiserver/*
+ patch: off
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..3d1589ad
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,7 @@
+.travis.yml
+.reviewdog.yml
+_tests
+bin
+vendor
+README.md
+LICENSE
diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml
new file mode 100644
index 00000000..dab76d35
--- /dev/null
+++ b/.github/workflows/review.yml
@@ -0,0 +1,11 @@
+name: Review
+on: [pull_request]
+jobs:
+
+ golangci-lint:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: reviewdog/action-golangci-lint@v1
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 00000000..fbf805e9
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,72 @@
+name: CI
+on: [push, pull_request]
+jobs:
+
+ test:
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ go-version: ['1.11.x', '1.12.x', '1.13.x']
+ test-task: ['test', 'test-e2e']
+
+ steps:
+ - uses: actions/setup-go@v1
+ with:
+ go-version: ${{ matrix.go-version }}
+
+ - uses: actions/checkout@v1
+
+ - name: Install protobuf
+ run: |
+ version=3.10.0
+ archive=protoc-${version}-linux-x86_64
+ curl -O -L https://github.com/protocolbuffers/protobuf/releases/download/v${version}/${archive}.zip
+ sudo unzip -d '/usr/local' ${archive}.zip 'bin/*' 'include/*'
+ rm -rf $archive.zip
+ protoc --version
+ if: matrix.test-task == 'test-e2e'
+
+ - run: go mod download
+
+ - run: echo "TARGET_REVISION=${{ github.sha }}" >> $GITHUB_ENV
+ if: "github.event.push"
+
+ - run: echo "GRAPI_URL=$(pwd)" >> $GITHUB_ENV
+ if: "!github.event.push"
+
+ - run: make ${{ matrix.test-task }}
+ env:
+ COVER: ${{ matrix.go-version == '1.13.x' && matrix.test-task == 'test' }}
+
+ - run: curl -s https://codecov.io/bash | bash -s -- -t $CODECOV_TOKEN
+ if: matrix.go-version == '1.13.x' && matrix.test-task == 'test'
+
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/setup-go@v1
+ with:
+ go-version: 1.13
+
+ - uses: actions/checkout@v1
+
+ - run: go mod download
+
+ - uses: izumin5210/action-go-crossbuild@releases/v1
+ with:
+ package: ./cmd/grapi
+
+ - uses: softprops/action-gh-release@v1
+ with:
+ files: './dist/*'
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ if: startsWith(github.ref, 'refs/tags/')
+
+ - uses: izumin5210/action-homebrew@releases/v0
+ with:
+ tap: izumin5210/homebrew-tools
+ token: ${{ secrets.GITHUB_TOKEN }}
+ tap-token: ${{ secrets.TAP_GITHUB_TOKEN }}
+ if: startsWith(github.ref, 'refs/tags/')
diff --git a/.gitignore b/.gitignore
index fe814633..19d9de15 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@
/vendor
/coverage.txt
/bin
+/dist
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 00000000..9ffbe349
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,12 @@
+output:
+ format: colored-line-number
+
+linters-settings:
+ lll:
+ line-length: 140
+
+linters:
+ enable-all: true
+ disable:
+ - gochecknoglobals
+ - gochecknoinits
diff --git a/.reviewdog.yml b/.reviewdog.yml
new file mode 100644
index 00000000..a4f5554b
--- /dev/null
+++ b/.reviewdog.yml
@@ -0,0 +1,11 @@
+runner:
+ golangci:
+ cmd: golangci-lint run ./...
+ errorformat:
+ - '%E%f:%l:%c: %m'
+ - '%E%f:%l: %m'
+ - '%C%.%#'
+ wraperr:
+ cmd: wraperr ./...
+ errorformat:
+ - "%f:%l:%c:%m"
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 88d8b4a9..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,69 +0,0 @@
-language: go
-
-go: "1.10"
-
-env:
- global:
- - DEP_VERSION="0.4.1"
- - FILE_TO_DEPLOY="dist/*"
- - PROTOBUF_VERSION="3.5.1"
- - CANARY_TAG="canary"
-
- # GITHUB_TOKEN
- - secure: "MhmvXAAzOA5HY7koCfagX1wJi2mBVQsVF5cCMaNx73l+7uDgNzGYfTn4OGKmckduiGB/mp5bTJ1DeMbPq+TDX1n/RE6kndu/Q/1vw4pbxm9BsmO9b3DizIFoWlnG+EABdAZa9igbCAfv+Jj57a0WjKGaiLazylj1mb7AYj6Vao+1zvm2ufoZvpKJcnKPqcWTsx/enJD3wx0LbqTpN5a/EdynJF9kj9Z97cGk9lS/hQHqmYVUYLYG5ZIvPjkuc6ho6pYaerupZ8aQgwraupRrNAzh70C3QgxnrCK+6RRmBMchhBsHOZq1MGhbN48ttlSMKow2NyVp8mK8+wLUnQgxEvYjVNJBXf5iKMmCTBiTO8IqgAKkkMgLaB3H0UpkeOoUQNTACPxR42+FJcwObmxYRSekTGFPwAAwnZV/1BuPrpxpT7JHa9ELlShz2OVEDz9aK/WC28/oEmtYKN8s9koKr1sx4OT5c0F/XG+er2idgCWwvfK5A0Om7Fudur+bbp1a38QWb00cAu8dPTIONe01vGXQ04d+NyohS2bcvK3iehVpa+WZ4CHkjRRuv6vQGvFMNCtwwQjXopBM99+yAykLm7yqOewbzbxFI7nCHNBc1zHvI13j7yniEoI/vdWk43e2H3Az0OOtdVASNmmp5Avwo/UWzjVACvlyNK1CST4pqYQ="
-
-branches:
- except:
- - canary
-
-cache:
- directories:
- - $GOPATH/pkg/dep
- - $HOME/include
- - $HOME/bin
-
-before_install:
-- export PATH=$PWD/bin:$HOME/bin:$PATH
-- ./_script/ci-install
-- go get -u github.com/golang/lint/golint
-
-install:
-- make setup
-
-script:
-- make lint
-- make cover
-- make
-- make test-integration
-
-after_success:
-- bash <(curl -s https://codecov.io/bash)
-
-before_deploy:
-- make packages
-# for canary build, ref: https://github.com/oliexdev/openScale/pull/121
-- git remote add gh https://${TRAVIS_REPO_SLUG%/*}:${GITHUB_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git
-- git tag -f $CANARY_TAG
-- git push -f gh $CANARY_TAG
-- git remote remove gh
-
-deploy:
-- provider: releases
- skip_cleanup: true
- api_key: $GITHUB_TOKEN
- file_glob: true
- file: $FILE_TO_DEPLOY
- on:
- tags: true
-- provider: releases
- skip_cleanup: true
- api_key: $GITHUB_TOKEN
- file_glob: true
- file: $FILE_TO_DEPLOY
- prerelease: true
- overwrite: true
- name: canary
- body: canary build of $TRAVIS_BRANCH ($TRAVIS_COMMIT) built by Travis CI on $(date +'%F %T %Z').
- target_commitish: $TRAVIS_COMMIT
- on:
- branch: master
diff --git a/Gopkg.lock b/Gopkg.lock
deleted file mode 100644
index 9c90d283..00000000
--- a/Gopkg.lock
+++ /dev/null
@@ -1,381 +0,0 @@
-# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
-
-
-[[projects]]
- name = "github.com/bradleyjkemp/cupaloy"
- packages = ["."]
- revision = "0d797813a76b4573317a916b0c5816dc55fff62d"
- version = "v2.0.0"
-
-[[projects]]
- name = "github.com/davecgh/go-spew"
- packages = ["spew"]
- revision = "346938d642f2ec3594ed81d874461961cd0faa76"
- version = "v1.1.0"
-
-[[projects]]
- name = "github.com/fatih/color"
- packages = ["."]
- revision = "507f6050b8568533fb3f5504de8e5205fa62a114"
- version = "v1.6.0"
-
-[[projects]]
- name = "github.com/fsnotify/fsnotify"
- packages = ["."]
- revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
- version = "v1.4.7"
-
-[[projects]]
- name = "github.com/golang/mock"
- packages = [
- "gomock",
- "mockgen",
- "mockgen/model"
- ]
- revision = "13f360950a79f5864a972c786a10a50e44b69541"
- version = "v1.0.0"
-
-[[projects]]
- name = "github.com/golang/protobuf"
- packages = [
- "jsonpb",
- "proto",
- "protoc-gen-go/descriptor",
- "ptypes",
- "ptypes/any",
- "ptypes/duration",
- "ptypes/empty",
- "ptypes/struct",
- "ptypes/timestamp"
- ]
- revision = "925541529c1fa6821df4e44ce2723319eb2be768"
- version = "v1.0.0"
-
-[[projects]]
- name = "github.com/google/go-cmp"
- packages = [
- "cmp",
- "cmp/internal/diff",
- "cmp/internal/function",
- "cmp/internal/value"
- ]
- revision = "3af367b6b30c263d47e8895973edcca9a49cf029"
- version = "v0.2.0"
-
-[[projects]]
- branch = "master"
- name = "github.com/grpc-ecosystem/go-grpc-middleware"
- packages = ["."]
- revision = "d0c54e68681ec7999ac17864470f3bee6521ba2b"
-
-[[projects]]
- name = "github.com/grpc-ecosystem/grpc-gateway"
- packages = [
- "runtime",
- "runtime/internal",
- "utilities"
- ]
- revision = "07f5e79768022f9a3265235f0db4ac8c3f675fec"
- version = "v1.3.1"
-
-[[projects]]
- branch = "master"
- name = "github.com/hashicorp/hcl"
- packages = [
- ".",
- "hcl/ast",
- "hcl/parser",
- "hcl/scanner",
- "hcl/strconv",
- "hcl/token",
- "json/parser",
- "json/scanner",
- "json/token"
- ]
- revision = "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8"
-
-[[projects]]
- name = "github.com/inconshreveable/mousetrap"
- packages = ["."]
- revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
- version = "v1.0"
-
-[[projects]]
- name = "github.com/izumin5210/clicontrib"
- packages = [
- "pkg/cbuild",
- "pkg/ccmd",
- "pkg/clog"
- ]
- revision = "fdfd45e15564e04a472c90a37fb1a5d8035fe050"
- version = "v0.1.0"
-
-[[projects]]
- branch = "master"
- name = "github.com/jessevdk/go-assets"
- packages = ["."]
- revision = "4f4301a06e153ff90e17793577ab6bf79f8dc5c5"
-
-[[projects]]
- branch = "master"
- name = "github.com/jessevdk/go-assets-builder"
- packages = ["."]
- revision = "b8483521738fd2198ecfc378067a4e8a6079f8e5"
-
-[[projects]]
- name = "github.com/jessevdk/go-flags"
- packages = ["."]
- revision = "96dc06278ce32a0e9d957d590bb987c81ee66407"
- version = "v1.3.0"
-
-[[projects]]
- branch = "master"
- name = "github.com/jinzhu/inflection"
- packages = ["."]
- revision = "04140366298a54a039076d798123ffa108fff46c"
-
-[[projects]]
- name = "github.com/magiconair/properties"
- packages = ["."]
- revision = "c3beff4c2358b44d0493c7dda585e7db7ff28ae6"
- version = "v1.7.6"
-
-[[projects]]
- name = "github.com/mattn/go-colorable"
- packages = ["."]
- revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
- version = "v0.0.9"
-
-[[projects]]
- name = "github.com/mattn/go-isatty"
- packages = ["."]
- revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
- version = "v0.0.3"
-
-[[projects]]
- name = "github.com/mitchellh/gox"
- packages = ["."]
- revision = "c9740af9c6574448fd48eb30a71f964014c7a837"
- version = "v0.4.0"
-
-[[projects]]
- branch = "master"
- name = "github.com/mitchellh/iochan"
- packages = ["."]
- revision = "87b45ffd0e9581375c491fef3d32130bb15c5bd7"
-
-[[projects]]
- branch = "master"
- name = "github.com/mitchellh/mapstructure"
- packages = ["."]
- revision = "00c29f56e2386353d58c599509e8dc3801b0d716"
-
-[[projects]]
- name = "github.com/pelletier/go-toml"
- packages = ["."]
- revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8"
- version = "v1.1.0"
-
-[[projects]]
- name = "github.com/pkg/errors"
- packages = ["."]
- revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
- version = "v0.8.0"
-
-[[projects]]
- name = "github.com/pmezard/go-difflib"
- packages = ["difflib"]
- revision = "792786c7400a136282c1664665ae0a8db921c6c2"
- version = "v1.0.0"
-
-[[projects]]
- branch = "master"
- name = "github.com/serenize/snaker"
- packages = ["."]
- revision = "a683aaf2d516deecd70cad0c72e3ca773ecfcef0"
-
-[[projects]]
- name = "github.com/soheilhy/cmux"
- packages = ["."]
- revision = "e09e9389d85d8492d313d73d1469c029e710623f"
- version = "v0.1.4"
-
-[[projects]]
- name = "github.com/spf13/afero"
- packages = [
- ".",
- "mem"
- ]
- revision = "bb8f1927f2a9d3ab41c9340aa034f6b803f4359c"
- version = "v1.0.2"
-
-[[projects]]
- name = "github.com/spf13/cast"
- packages = ["."]
- revision = "8965335b8c7107321228e3e3702cab9832751bac"
- version = "v1.2.0"
-
-[[projects]]
- name = "github.com/spf13/cobra"
- packages = ["."]
- revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
- version = "v0.0.1"
-
-[[projects]]
- branch = "master"
- name = "github.com/spf13/jwalterweatherman"
- packages = ["."]
- revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
-
-[[projects]]
- name = "github.com/spf13/pflag"
- packages = ["."]
- revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66"
- version = "v1.0.0"
-
-[[projects]]
- name = "github.com/spf13/viper"
- packages = ["."]
- revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7"
- version = "v1.0.0"
-
-[[projects]]
- branch = "master"
- name = "github.com/tcnksm/go-input"
- packages = ["."]
- revision = "bbe13e92fa7dda71a4e012e3f427df11866c7651"
-
-[[projects]]
- name = "go.uber.org/atomic"
- packages = ["."]
- revision = "8474b86a5a6f79c443ce4b2992817ff32cf208b8"
- version = "v1.3.1"
-
-[[projects]]
- name = "go.uber.org/multierr"
- packages = ["."]
- revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a"
- version = "v1.1.0"
-
-[[projects]]
- name = "go.uber.org/zap"
- packages = [
- ".",
- "buffer",
- "internal/bufferpool",
- "internal/color",
- "internal/exit",
- "zapcore"
- ]
- revision = "35aad584952c3e7020db7b839f6b102de6271f89"
- version = "v1.7.1"
-
-[[projects]]
- branch = "master"
- name = "golang.org/x/crypto"
- packages = ["ssh/terminal"]
- revision = "91a49db82a88618983a78a06c1cbd4e00ab749ab"
-
-[[projects]]
- branch = "master"
- name = "golang.org/x/net"
- packages = [
- "context",
- "http2",
- "http2/hpack",
- "idna",
- "internal/timeseries",
- "lex/httplex",
- "trace"
- ]
- revision = "cbe0f9307d0156177f9dd5dc85da1a31abc5f2fb"
-
-[[projects]]
- branch = "master"
- name = "golang.org/x/sync"
- packages = ["errgroup"]
- revision = "fd80eb99c8f653c847d294a001bdf2a3a6f768f5"
-
-[[projects]]
- branch = "master"
- name = "golang.org/x/sys"
- packages = [
- "unix",
- "windows"
- ]
- revision = "88d2dcc510266da9f7f8c7f34e1940716cab5f5c"
-
-[[projects]]
- name = "golang.org/x/text"
- packages = [
- "collate",
- "collate/build",
- "internal/colltab",
- "internal/gen",
- "internal/tag",
- "internal/triegen",
- "internal/ucd",
- "language",
- "secure/bidirule",
- "transform",
- "unicode/bidi",
- "unicode/cldr",
- "unicode/norm",
- "unicode/rangetable"
- ]
- revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
- version = "v0.3.0"
-
-[[projects]]
- branch = "master"
- name = "google.golang.org/genproto"
- packages = [
- "googleapis/api/annotations",
- "googleapis/rpc/status"
- ]
- revision = "2b5a72b8730b0b16380010cfe5286c42108d88e7"
-
-[[projects]]
- name = "google.golang.org/grpc"
- packages = [
- ".",
- "balancer",
- "balancer/base",
- "balancer/roundrobin",
- "codes",
- "connectivity",
- "credentials",
- "encoding",
- "encoding/proto",
- "grpclb/grpc_lb_v1/messages",
- "grpclog",
- "internal",
- "keepalive",
- "metadata",
- "naming",
- "peer",
- "reflection",
- "reflection/grpc_reflection_v1alpha",
- "resolver",
- "resolver/dns",
- "resolver/passthrough",
- "stats",
- "status",
- "tap",
- "transport"
- ]
- revision = "8e4536a86ab602859c20df5ebfd0bd4228d08655"
- version = "v1.10.0"
-
-[[projects]]
- name = "gopkg.in/yaml.v2"
- packages = ["."]
- revision = "7f97868eec74b32b0982dd158a51a446d1da7eb5"
- version = "v2.1.1"
-
-[solve-meta]
- analyzer-name = "dep"
- analyzer-version = 1
- inputs-digest = "2c857d3a14b94944969786dc15eae617a0804401018097555db7b338471d5328"
- solver-name = "gps-cdcl"
- solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
deleted file mode 100644
index f052fd54..00000000
--- a/Gopkg.toml
+++ /dev/null
@@ -1,91 +0,0 @@
-# Gopkg.toml example
-#
-# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
-# for detailed Gopkg.toml documentation.
-#
-# required = ["github.com/user/thing/cmd/thing"]
-# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
-#
-# [[constraint]]
-# name = "github.com/user/project"
-# version = "1.0.0"
-#
-# [[constraint]]
-# name = "github.com/user/project2"
-# branch = "dev"
-# source = "github.com/myfork/project2"
-#
-# [[override]]
-# name = "github.com/x/y"
-# version = "2.4.0"
-#
-# [prune]
-# non-go = false
-# go-tests = true
-# unused-packages = true
-
-required = [
- "github.com/golang/mock/mockgen",
- "github.com/jessevdk/go-assets-builder",
- "github.com/mitchellh/gox",
-]
-
-[[constraint]]
- branch = "master"
- name = "github.com/grpc-ecosystem/go-grpc-middleware"
-
-[[constraint]]
- name = "github.com/grpc-ecosystem/grpc-gateway"
- version = "1.3.1"
-
-[[constraint]]
- name = "github.com/pkg/errors"
- version = "0.8.0"
-
-[[constraint]]
- name = "google.golang.org/grpc"
- version = "1.10.0"
-
-[prune]
- go-tests = true
- unused-packages = true
-
-[[constraint]]
- name = "github.com/spf13/cobra"
- version = "0.0.1"
-
-[[constraint]]
- name = "github.com/fatih/color"
- version = "1.6.0"
-
-[[constraint]]
- name = "github.com/izumin5210/clicontrib"
- version = "0.1.0"
-
-[[constraint]]
- branch = "master"
- name = "golang.org/x/sync"
-
-[[constraint]]
- branch = "master"
- name = "github.com/tcnksm/go-input"
-
-[[constraint]]
- name = "github.com/golang/mock"
- version = "1.0.0"
-
-[[constraint]]
- name = "github.com/google/go-cmp"
- version = "0.2.0"
-
-[[constraint]]
- name = "github.com/bradleyjkemp/cupaloy"
- version = "2.0.0"
-
-[[constraint]]
- branch = "master"
- name = "github.com/jinzhu/inflection"
-
-[[constraint]]
- name = "github.com/soheilhy/cmux"
- version = "0.1.4"
diff --git a/Makefile b/Makefile
index 849b68bf..7dea79ad 100644
--- a/Makefile
+++ b/Makefile
@@ -1,143 +1,43 @@
-.DEFAULT_GOAL := all
+PATH := ${PWD}/bin:${PATH}
+export PATH
-VERSION_MAJOR ?= 0
-VERSION_MINOR ?= 2
-VERSION_BUILD ?= 2
+.DEFAULT_GOAL := build
-VERSION ?= v$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_BUILD)
-REVISION ?= $(shell git describe --always)
-BUILD_DATE ?= $(shell date +'%Y-%m-%dT%H:%M:%SZ')
-RELEASE_TYPE ?= $(if $(shell git tag --contains $(REVISION) | grep $(VERSION)),stable,canary)
-
-ORG := github.com/izumin5210
-PROJECT := grapi
-ROOT_PKG ?= $(ORG)/$(PROJECT)
-
-TEMPLATE_PKG := pkg/grapicmd/internal/module/generator/template
-MOCK_PKG := pkg/grapicmd/internal/module/testing
-GENERATED_PKGS := $(TEMPLATE_PKG)
-GENERATED_PKGS += $(MOCK_PKG)
-
-SRC_FILES := $(shell git ls-files --cached --others --exclude-standard | grep -E "\.go$$" | grep -v ".snapshot")
-GOFMT_TARGET := $(SRC_FILES)
-$(foreach pkg,$(GENERATED_PKGS),$(eval GOFMT_TARGET := $(filter-out $(pkg)/%,$(GOFMT_TARGET))))
-GOLINT_TARGET := $(shell go list ./... | grep -v "$(pkg)/testing")
-$(foreach pkg,$(GENERATED_PKGS),$(eval GOLINT_TARGET := $(filter-out $(ROOT_PKG)/$(pkg),$(GOLINT_TARGET))))
-
-GO_BUILD_FLAGS := -v
-GO_TEST_FLAGS := -v
-GO_COVER_FLAGS := -coverpkg $(shell echo $(GOLINT_TARGET) | tr ' ' ',') -coverprofile coverage.txt -covermode atomic
-
-XC_ARCH := 386 amd64
-XC_OS := darwin linux windows
-
-# Utils
-#----------------------------------------------------------------
-define section
- @printf "\e[34m--> $1\e[0m\n"
-endef
-
-# dep
-#----------------------------------------------------------------
-DEP_BIN_DIR := ./vendor/.bin/
-DEP_SRCS := \
- github.com/golang/mock/mockgen \
- github.com/jessevdk/go-assets-builder \
- github.com/mitchellh/gox
-
-DEP_BINS := $(addprefix $(DEP_BIN_DIR),$(notdir $(DEP_SRCS)))
-
-define dep-bin-tmpl
-$(eval OUT := $(addprefix $(DEP_BIN_DIR),$(notdir $(1))))
-$(OUT): dep
- $(call section,Installing $(OUT))
- @cd vendor/$(1) && GOBIN="$(shell pwd)/$(DEP_BIN_DIR)" go install .
-endef
-
-$(foreach src,$(DEP_SRCS),$(eval $(call dep-bin-tmpl,$(src))))
-
-
-# App
-#----------------------------------------------------------------
-BIN_DIR := ./bin/
-OUT_DIR := ./dist
-GENERATED_BINS :=
-PACKAGES :=
-CMDS := $(wildcard ./cmd/*)
-
-define cmd-tmpl
-
-$(eval NAME := $(notdir $(1)))
-$(eval OUT := $(addprefix $(BIN_DIR),$(NAME)))
-$(eval LDFLAGS := -ldflags "-X main.name=$(NAME) -X main.version=$(VERSION) -X main.revision=$(REVISION) -X main.buildDate=$(BUILD_DATE) -X main.releaseType=$(RELEASE_TYPE)")
-$(eval GENERATED_BINS += $(OUT))
-$(OUT): $(SRC_FILES)
- $(call section,Building $(OUT))
- @go build $(GO_BUILD_FLAGS) $(LDFLAGS) -o $(OUT) $(1)
-
-.PHONY: $(NAME)
-$(NAME): $(OUT)
-
-$(eval PACKAGES += $(NAME)-package)
-
-.PHONY: $(NAME)-package
-$(NAME)-package: $(NAME)
- @PATH=$(shell pwd)/$(DEP_BIN_DIR):$$$$PATH gox \
- $(LDFLAGS) \
- -os="$(XC_OS)" \
- -arch="$(XC_ARCH)" \
- -output="$(OUT_DIR)/$(NAME)_{{.OS}}_{{.Arch}}" \
- $(1)
-endef
-
-$(foreach src,$(CMDS),$(eval $(call cmd-tmpl,$(src))))
-
-.PHONY: all
-all: $(GENERATED_BINS)
-
-
-# Commands
-#----------------------------------------------------------------
-.PHONY: setup
-setup: dep $(DEP_BINS)
+.PHONY: tools
+tools:
+ go generate ./tools.go
.PHONY: clean
clean:
- rm -rf $(BIN_DIR)/*
-
-.PHONY: clobber
-clobber: clean
- rm -rf vendor
-
-.PHONY: dep
-dep: Gopkg.toml Gopkg.lock
- $(call section,Installing dependencies)
- @dep ensure -v -vendor-only
+ rm -rf ./bin/*
.PHONY: gen
-gen:
- @PATH=$(shell pwd)/$(DEP_BIN_DIR):$$PATH go generate ./...
+gen: tools
+ go generate ./...
+
+.PHONY: grapi
+build:
+ go build -v -o ./bin/grapi ./cmd/grapi
.PHONY: lint
-lint:
- $(call section,Linting)
- @gofmt -e -d -s $(GOFMT_TARGET) | awk '{ e = 1; print $0 } END { if (e) exit(1) }'
- @echo $(GOLINT_TARGET) | xargs -n1 golint -set_exit_status
+lint: ./bin/reviewdog ./bin/golangci-lint
+ reviewdog -diff="git diff master"
.PHONY: test
test:
- $(call section,Testing)
- @go test $(GO_TEST_FLAGS) ./...
-
-.PHONY: cover
-cover:
- $(call section,Testing with coverage)
- @go test $(GO_TEST_FLAGS) $(GO_COVER_FLAGS) ./...
-
-.PHONY: test-integration
-test-integration:
- $(call section,Integration Testing)
- cd _tests && go test $(GO_TEST_FLAGS) ./...
-
-.PHONY: packages
-packages: $(PACKAGES)
+ifeq ($(COVER),true)
+ go test -v -coverprofile coverage.txt -covermode atomic ./...
+else
+ go test -v ./...
+endif
+
+.PHONY: test-e2e
+test-e2e: build
+ go test -v -timeout 4m ./_tests/e2e --grapi=$$PWD/bin/grapi --grapi-url="$(GRAPI_URL)" --revision="$(TARGET_REVISION)"
+
+# linters
+bin/reviewdog:
+ curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b ./bin v0.9.12
+
+bin/golangci-lint:
+ curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b ./bin v1.17.1
diff --git a/README.md b/README.md
index 81ef16e5..9b8cc5ff 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# ![grapi](./grapi.png)
-[![Build Status](https://travis-ci.org/izumin5210/grapi.svg?branch=master)](https://travis-ci.org/izumin5210/grapi)
+[![CI](https://github.com/izumin5210/grapi/workflows/CI/badge.svg)](https://github.com/izumin5210/grapi/actions?workflow=CI)
[![GoDoc](https://godoc.org/github.com/izumin5210/grapi/pkg/grapiserver?status.svg)](https://godoc.org/github.com/izumin5210/grapi/pkg/grapiserver)
[![Go Report Card](https://goreportcard.com/badge/github.com/izumin5210/grapi)](https://goreportcard.com/report/github.com/izumin5210/grapi)
-[![Go project version](https://badge.fury.io/go/github.com%2Fizumin5210%2Fgrapi.svg)](https://badge.fury.io/go/github.com%2Fizumin5210%2Fgrapi)
+[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/izumin5210/grapi)](http://github.com/izumin5210/grapi/releases/latest)
[![license](https://img.shields.io/github/license/izumin5210/grapi.svg)](./LICENSE)
:open_mouth: A surprisingly easy API server and generator in gRPC and Go
@@ -18,22 +18,172 @@
[![asciicast](https://asciinema.org/a/176280.png)](https://asciinema.org/a/176280)
-## Getting Started
-### Installation
-#### For Homebrew users
+## :warning: Migrate 0.4.x -> 0.5.x :warning:
+[grapiserver](https://godoc.org/github.com/izumin5210/grapi/pkg/grapiserver) will not handle os signals from v0.5.x.
+We recommend to use [`appctx.Global()`](https://godoc.org/github.com/srvc/appctx#Global) if you want to handle them.
-```
-$ brew install protobuf
-$ brew install izumin5210/tools/grapi
-```
+
+:memo: How to migrate
+
+0. Bump grapi version
+ - `go get -u github.com/izumin5210/grapi@v0.5'
+1. Update `cmd/server/run.go`
+ - ```diff
+ // Application context
+ - ctx := context.Background()
+ + ctx := appctx.Global()
+ ```
+ - ```diff
+ - return s.ServeContext(ctx)
+ + return s.Serve(ctx)
+ ```
+
+
+
+
+## :warning: Migrate 0.3.x -> 0.4.x :warning:
+Some tools that are depended by grapi are updated. If you have a grapi project <=v0.3.x, you should migrate it.
+
+
+:memo: How to migrate
+
+0. Bump grapi version
+ - If you use [dep](https://golang.github.io/dep/), update `Gopkg.toml`
+ ```diff
+ [[constraint]]
+ name = "github.com/izumin5210/grapi"
+ - version = "0.3.0"
+ + version = "0.4.0"
+ ```
+ - and run `dep ensure`
+1. Update [gex](https://github.com/izumin5210/gex) and `tools.go`
+ - ```
+ go get -u github.com/izumin5210/gex/cmd/gex
+ gex --regen
+ ```
+1. Initialize [Go Modules](https://github.com/golang/go/wiki/Modules)
+ - ```
+ go mod init
+ go mod tidy
+ ```
+1. Update `grapi.toml`
+ - ```diff
+ package = "yourcompany.yourappname"
+
+ [grapi]
+ server_dir = "./app/server"
+
+ [protoc]
+ protos_dir = "./api/protos"
+ out_dir = "./api"
+ import_dirs = [
+ "./api/protos",
+ - "./vendor/github.com/grpc-ecosystem/grpc-gateway",
+ - "./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis",
+ + '{{ module "github.com/grpc-ecosystem/grpc-gateway" }}',
+ + '{{ module "github.com/grpc-ecosystem/grpc-gateway" }}/third_party/googleapis',
+ ]
+
+ [[protoc.plugins]]
+ name = "go"
+ args = { plugins = "grpc", paths = "source_relative" }
+
+ [[protoc.plugins]]
+ name = "grpc-gateway"
+ args = { logtostderr = true, paths = "source_relative" }
+
+ [[protoc.plugins]]
+ name = "swagger"
+ args = { logtostderr = true }
+ ```
+1. Drop dep
+ - ```
+ rm Gopkg.*
+ ```
+
+
+
-#### Download built binary
-You should install `protoc` command from [google/protobuf](https://github.com/google/protobuf).
+## :warning: Migrate 0.2.x -> 0.3.x :warning:
+grapi v0.3.0 has some breaking changes. If you have a grapi project <=v0.2.x, you should migrate it.
-- Linux:
- - `curl -Lo grapi https://github.com/izumin5210/grapi/releases/download/v0.2.2/grapi_linux_amd64 && chmod +x grapi && sudo mv grapi /usr/local/bin`
-- masOS:
- - `curl -Lo grapi https://github.com/izumin5210/grapi/releases/download/v0.2.2/grapi_darwin_amd64 && chmod +x grapi && sudo mv grapi /usr/local/bin`
+
+:memo: How to migrate
+
+0. Bump grapi version
+ - If you use [dep](https://golang.github.io/dep/), update `Gopkg.toml`
+ ```diff
+ [[constraint]]
+ name = "github.com/izumin5210/grapi"
+ - version = "0.2.2"
+ + version = "0.3.0"
+ ```
+ - and run `dep ensure`
+1. Introduce [gex](https://github.com/izumin5210/gex)
+ - ```
+ go get github.com/izumin5210/gex/cmd/gex
+ ```
+1. Add defualt generator plugins:
+ - ```
+ gex \
+ --add github.com/izumin5210/grapi/cmd/grapi \
+ --add github.com/izumin5210/grapi/cmd/grapi-gen-command \
+ --add github.com/izumin5210/grapi/cmd/grapi-gen-service \
+ --add github.com/izumin5210/grapi/cmd/grapi-gen-scaffold-service \
+ --add github.com/izumin5210/grapi/cmd/grapi-gen-type
+ ```
+1. Add protoc plugins via gex
+ - ```
+ gex \
+ --add github.com/golang/protobuf/protoc-gen-go \
+ --add github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway \
+ --add github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
+ ```
+ - Remove protoc plugins from `Gopkg.toml`
+ ```diff
+ -required = [
+ - "github.com/golang/protobuf/protoc-gen-go",
+ - "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway",
+ - "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger",
+ -]
+ ```
+1. Update `grapi.toml`
+ - ```diff
+ +package = "yourcompany.yourappname"
+ +
+ [grapi]
+ server_dir = "./app/server"
+
+ [protoc]
+ protos_dir = "./api/protos"
+ out_dir = "./api"
+ import_dirs = [
+ + "./api/protos",
+ "./vendor/github.com/grpc-ecosystem/grpc-gateway",
+ "./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis",
+ ]
+
+ [[protoc.plugins]]
+ - path = "./vendor/github.com/golang/protobuf/protoc-gen-go"
+ name = "go"
+ args = { plugins = "grpc", paths = "source_relative" }
+
+ [[protoc.plugins]]
+ - path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"
+ name = "grpc-gateway"
+ - args = { logtostderr = true }
+ + args = { logtostderr = true, paths = "source_relative" }
+
+ [[protoc.plugins]]
+ - path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"
+ name = "swagger"
+ args = { logtostderr = true }
+ ```
+
+
+
+
+## Getting Started
### Create a new application
```
@@ -95,3 +245,26 @@ $ grapi import-books # run the command
$ grapi build
```
+## Installation
+
+1. **grapi**
+ - Linux
+ - `curl -Lo grapi https://github.com/izumin5210/grapi/releases/download/v0.2.2/grapi_linux_amd64 && chmod +x grapi && sudo mv grapi /usr/local/bin`
+ - macOS
+ - `brew install izumin5210/tools/grapi`
+ - others
+ - `go get github.com/izumin5210/grapi/cmd/grapi`
+1. **dep** or **Modules**
+ - [dep](https://golang.github.io/dep/)
+ - macOS
+ - `brew install dep`
+ - others
+ - See [Installation ยท dep](https://golang.github.io/dep/docs/installation.html)
+ - `curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh`
+ - [Modules](https://github.com/golang/go/wiki/Modules) (experimental)
+ - Use Go 1.11 and set `GO111MODULE=on` your env vars
+1. **protoc**
+ - macOS
+ - `brew install protobuf`
+ - others
+ - Download and install from [google/protobuf](https://github.com/google/protobuf)
diff --git a/_script/ci-install b/_script/ci-install
deleted file mode 100755
index e8ec9332..00000000
--- a/_script/ci-install
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env bash
-
-set -eu
-set -o pipefail
-
-BIN_DIR=$HOME/bin
-
-section() {
- printf "\e[34m--> $1\e[0m\n"
-}
-
-install_protoc() {
- section "Installing protoc"
-
- if [ ! -d "${BIN_DIR}/protoc" ]; then
- curl -L -s https://github.com/google/protobuf/releases/download/v$PROTOBUF_VERSION/protoc-$PROTOBUF_VERSION-linux-x86_64.zip -o protoc-$PROTOBUF_VERSION.zip
- unzip -o protoc-$PROTOBUF_VERSION.zip -d $HOME
- else
- echo "skipped"
- fi
-
- protoc --version
-}
-
-install_dep() {
- section "Installing dep"
-
- if [ ! -d "${BIN_DIR}/dep" ]; then
- curl -L -s https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 -o ${BIN_DIR}/dep
- chmod +x ${BIN_DIR}/dep
- else
- echo "skipped"
- fi
-
- dep version
-}
-
-main() {
- install_protoc
- install_dep
-}
-
-main
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-cmd-server-run.go b/_tests/e2e/.snapshots/TestE2E_withModules--cmd-server-main.go
similarity index 64%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-cmd-server-run.go
rename to _tests/e2e/.snapshots/TestE2E_withModules--cmd-server-main.go
index bd59a657..76322ecc 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-cmd-server-run.go
+++ b/_tests/e2e/.snapshots/TestE2E_withModules--cmd-server-main.go
@@ -4,20 +4,13 @@ import (
"os"
"google.golang.org/grpc/grpclog"
-
- "testapp/app"
)
func main() {
- os.Exit(run())
-}
-
-func run() int {
- err := app.Run()
+ err := run()
if err != nil {
grpclog.Errorf("server was shutdown with errors: %v", err)
- return 1
+ os.Exit(1)
}
- return 0
}
diff --git a/pkg/grapicmd/internal/module/generator/template/init/app/run.go.tmpl b/_tests/e2e/.snapshots/TestE2E_withModules--cmd-server-run.go
similarity index 55%
rename from pkg/grapicmd/internal/module/generator/template/init/app/run.go.tmpl
rename to _tests/e2e/.snapshots/TestE2E_withModules--cmd-server-run.go
index c9c90169..485f8be3 100644
--- a/pkg/grapicmd/internal/module/generator/template/init/app/run.go.tmpl
+++ b/_tests/e2e/.snapshots/TestE2E_withModules--cmd-server-run.go
@@ -1,16 +1,21 @@
-package app
+package main
import (
+ "github.com/srvc/appctx"
+
"github.com/izumin5210/grapi/pkg/grapiserver"
)
-// Run starts the grapiserver.
-func Run() error {
+func run() error {
+ // Application context
+ ctx := appctx.Global()
+
s := grapiserver.New(
grapiserver.WithDefaultLogger(),
grapiserver.WithServers(
// TODO
),
)
- return s.Serve()
+ return s.Serve(ctx)
}
+
diff --git a/_tests/e2e/.snapshots/TestE2E_withModules--grapi.toml b/_tests/e2e/.snapshots/TestE2E_withModules--grapi.toml
new file mode 100644
index 00000000..5b763733
--- /dev/null
+++ b/_tests/e2e/.snapshots/TestE2E_withModules--grapi.toml
@@ -0,0 +1,26 @@
+package = "testuser.sampleapp"
+
+[grapi]
+server_dir = "./app/server"
+
+[protoc]
+protos_dir = "./api/protos"
+out_dir = "./api"
+import_dirs = [
+ "./api/protos",
+ '{{ module "github.com/grpc-ecosystem/grpc-gateway" }}',
+ '{{ module "github.com/grpc-ecosystem/grpc-gateway" }}/third_party/googleapis',
+]
+
+ [[protoc.plugins]]
+ name = "go"
+ args = { plugins = "grpc", paths = "source_relative" }
+
+ [[protoc.plugins]]
+ name = "grpc-gateway"
+ args = { logtostderr = true, paths = "source_relative" }
+
+ [[protoc.plugins]]
+ name = "swagger"
+ args = { logtostderr = true }
+
diff --git a/_tests/e2e/.snapshots/TestE2E_withModules--tools.go b/_tests/e2e/.snapshots/TestE2E_withModules--tools.go
new file mode 100644
index 00000000..210f0e62
--- /dev/null
+++ b/_tests/e2e/.snapshots/TestE2E_withModules--tools.go
@@ -0,0 +1,32 @@
+// Code generated by github.com/izumin5210/gex. DO NOT EDIT.
+
+// +build tools
+
+package tools
+
+// tool dependencies
+import (
+ _ "github.com/golang/protobuf/protoc-gen-go"
+ _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"
+ _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"
+ _ "github.com/izumin5210/gex/cmd/gex"
+ _ "github.com/izumin5210/grapi/cmd/grapi"
+ _ "github.com/izumin5210/grapi/cmd/grapi-gen-command"
+ _ "github.com/izumin5210/grapi/cmd/grapi-gen-scaffold-service"
+ _ "github.com/izumin5210/grapi/cmd/grapi-gen-service"
+ _ "github.com/izumin5210/grapi/cmd/grapi-gen-type"
+)
+
+// If you want to use tools, please run the following command:
+// go generate ./tools.go
+//
+//go:generate go build -v -o=./bin/protoc-gen-go github.com/golang/protobuf/protoc-gen-go
+//go:generate go build -v -o=./bin/protoc-gen-grpc-gateway github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
+//go:generate go build -v -o=./bin/protoc-gen-swagger github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
+//go:generate go build -v -o=./bin/gex github.com/izumin5210/gex/cmd/gex
+//go:generate go build -v -o=./bin/grapi github.com/izumin5210/grapi/cmd/grapi
+//go:generate go build -v -o=./bin/grapi-gen-command github.com/izumin5210/grapi/cmd/grapi-gen-command
+//go:generate go build -v -o=./bin/grapi-gen-scaffold-service github.com/izumin5210/grapi/cmd/grapi-gen-scaffold-service
+//go:generate go build -v -o=./bin/grapi-gen-service github.com/izumin5210/grapi/cmd/grapi-gen-service
+//go:generate go build -v -o=./bin/grapi-gen-type github.com/izumin5210/grapi/cmd/grapi-gen-type
+
diff --git a/_tests/e2e/main_test.go b/_tests/e2e/main_test.go
new file mode 100644
index 00000000..cea7139d
--- /dev/null
+++ b/_tests/e2e/main_test.go
@@ -0,0 +1,359 @@
+package main
+
+import (
+ "bytes"
+ "context"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/bradleyjkemp/cupaloy/v2"
+ "golang.org/x/tools/go/packages/packagestest"
+)
+
+var (
+ grapiCmd = flag.String("grapi", "grapi", "path of grapi command")
+ grapiURL = flag.String("grapi-url", "", "url for replacing github.com/izumin5210/grapi")
+ revision = flag.String("revision", "", "target revision")
+)
+
+func TestE2E_withModules(t *testing.T) {
+ invokeE2ETest(t, packagestest.Modules)
+}
+
+func TestE2E_withDep(t *testing.T) {
+ t.SkipNow()
+ invokeE2ETest(t, packagestest.GOPATH)
+}
+
+func invokeE2ETest(t *testing.T, exporter packagestest.Exporter) {
+ t.Helper()
+
+ exported := packagestest.Export(t, exporter, []packagestest.Module{
+ {Name: "sampleapp", Files: map[string]interface{}{".keep": ""}},
+ })
+ defer exported.Cleanup()
+
+ rootPath := exported.Config.Dir
+ exported.Config.Dir = filepath.Dir(rootPath)
+ checkNoErr(t, os.RemoveAll(rootPath))
+
+ // init
+ {
+ args := []string{"--debug", "init", "--package", "testuser.sampleapp"}
+ if *grapiURL != "" {
+ // Add replacements later
+ args = append(args, "--replace-grapi="+*grapiURL)
+ } else if *revision != "" {
+ args = append(args, "--revision="+*revision)
+ } else {
+ args = append(args, "--HEAD")
+ }
+ if exporter.Name() == "GOPATH" {
+ args = append(args, "--use-dep")
+ }
+ args = append(args, filepath.Base(rootPath))
+ invoke(t, exported, exec.Command(*grapiCmd, args...))
+ checkExistence(t, rootPath)
+ t.Log("Initialize a project successfully")
+ }
+
+ exported.Config.Dir = rootPath
+
+ ignoreFiles := map[string]struct{}{
+ "/go.mod": struct{}{},
+ "/go.sum": struct{}{},
+ }
+
+ err := filepath.Walk(rootPath, func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if info.IsDir() {
+ return nil
+ }
+ rel := strings.TrimPrefix(path, rootPath)
+ if _, ok := ignoreFiles[rel]; ok {
+ return nil
+ }
+ if strings.HasPrefix(rel, "/bin/") {
+ return nil
+ }
+
+ t.Run(rel, func(t *testing.T) {
+ data, err := ioutil.ReadFile(path)
+ if err != nil {
+ t.Errorf("failed to open %s: %v", path, err)
+ }
+
+ cupaloy.SnapshotT(t, string(data))
+ })
+
+ return nil
+ })
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ // generate service
+ {
+ invoke(t, exported, exec.Command(*grapiCmd, "--debug", "g", "service", "book", "list"))
+ checkExistence(t, filepath.Join(rootPath, "app", "server", "book_server.go"))
+ t.Log("Generate a service successfully")
+ }
+
+ port := getFreePort(t)
+ updateRun(t, rootPath, port)
+ updateServerImpl(t, rootPath)
+
+ // run server
+ {
+ t.Log("Start the server")
+ cmd := exec.Command(*grapiCmd, "--debug", "server")
+ sdCh := make(chan struct{}, 1)
+ go func() {
+ defer close(sdCh)
+ invoke(t, exported, cmd)
+ }()
+
+ startedAt := time.Now()
+ var (
+ resp *http.Response
+ retryCnt int
+ err error
+ )
+
+ for {
+ func() {
+ defer recover()
+ resp, err = http.Get(fmt.Sprintf("http://localhost:%d/books", port))
+ }()
+ if err != nil && time.Since(startedAt) < 120*time.Second {
+ time.Sleep(5 * time.Second)
+ retryCnt++
+ } else {
+ break
+ }
+ }
+
+ if err != nil {
+ t.Fatalf("Unexpected error (retry count: %d): %v", retryCnt, err)
+ }
+
+ if got, want := resp.StatusCode, 200; got != want {
+ t.Errorf("Response status is %d, want %d", got, want)
+ }
+
+ t.Log("HTTP Request successfully")
+
+ sendSignal(t, cmd, os.Interrupt)
+ toCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ defer cancel()
+ select {
+ case <-sdCh:
+ t.Log("Shutdown server successfully")
+ case <-toCtx.Done():
+ t.Log("Deadline exceeded stopping server")
+ sendSignal(t, cmd, os.Kill)
+ <-sdCh
+ }
+ }
+}
+
+func invoke(t *testing.T, exported *packagestest.Exported, cmd *exec.Cmd) {
+ t.Helper()
+ cmd.Dir = exported.Config.Dir
+ for _, kv := range exported.Config.Env {
+ if strings.HasPrefix(kv, "GOPROXY=") {
+ continue
+ }
+ cmd.Env = append(cmd.Env, kv)
+ }
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ err := cmd.Run()
+ if err != nil {
+ t.Fatalf("failed to execute command %v: %v", cmd, err)
+ }
+}
+
+func checkNoErr(t *testing.T, err error) {
+ t.Helper()
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+}
+
+func checkExistence(t *testing.T, path string) {
+ t.Helper()
+ _, err := os.Stat(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ t.Fatalf("%s does not exist: %v", path, err)
+ }
+ t.Fatalf("failed to check file existence: %v", err)
+ }
+}
+
+func getFreePort(t *testing.T) int {
+ t.Helper()
+ lis, err := net.Listen("tcp", ":0")
+ checkNoErr(t, err)
+ defer lis.Close()
+
+ return lis.Addr().(*net.TCPAddr).Port
+}
+
+func sendSignal(t *testing.T, cmd *exec.Cmd, sig os.Signal) {
+ t.Helper()
+ checkNoErr(t, cmd.Process.Signal(sig))
+}
+
+type visitor struct {
+ VisitFunc func(ast.Visitor, ast.Node) ast.Visitor
+}
+
+func (v *visitor) Visit(node ast.Node) ast.Visitor {
+ return v.VisitFunc(v, node)
+}
+
+func updateRun(t *testing.T, rootPath string, port int) {
+ data, err := ioutil.ReadFile(filepath.Join(rootPath, "cmd", "server", "run.go"))
+ checkNoErr(t, err)
+
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "", data, parser.DeclarationErrors)
+ checkNoErr(t, err)
+
+ ast.Walk(&visitor{
+ VisitFunc: func(v ast.Visitor, n ast.Node) ast.Visitor {
+ switch n := n.(type) {
+ case *ast.GenDecl:
+ if n.Tok == token.IMPORT {
+ n.Specs = append(n.Specs, &ast.ImportSpec{
+ Path: &ast.BasicLit{
+ Kind: token.STRING,
+ Value: strconv.Quote("sampleapp/app/server"),
+ },
+ })
+ }
+ case *ast.CallExpr:
+ switch fun := n.Fun.(type) {
+ case *ast.SelectorExpr:
+ switch fun.Sel.Name {
+ case "New":
+ n.Args = append(n.Args, &ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("grapiserver"),
+ Sel: ast.NewIdent("WithGatewayAddr"),
+ },
+ Args: []ast.Expr{
+ &ast.BasicLit{
+ Kind: token.STRING,
+ Value: strconv.Quote("tcp"),
+ },
+ &ast.BasicLit{
+ Kind: token.STRING,
+ Value: strconv.Quote(fmt.Sprintf(":%d", port)),
+ },
+ },
+ })
+ case "WithServers":
+ n.Args = append(n.Args, &ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("server"),
+ Sel: ast.NewIdent("NewBookServiceServer"),
+ },
+ })
+ }
+ }
+ }
+ return v
+ },
+ }, f)
+
+ buf := new(bytes.Buffer)
+ err = format.Node(buf, token.NewFileSet(), f)
+ checkNoErr(t, err)
+ err = ioutil.WriteFile(filepath.Join(rootPath, "cmd", "server", "run.go"), buf.Bytes(), 0755)
+ checkNoErr(t, err)
+}
+
+func updateServerImpl(t *testing.T, rootPath string) {
+ data, err := ioutil.ReadFile(filepath.Join(rootPath, "app", "server", "book_server.go"))
+ checkNoErr(t, err)
+
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "", data, parser.DeclarationErrors)
+ checkNoErr(t, err)
+
+ ast.Walk(&visitor{
+ VisitFunc: func(v ast.Visitor, n ast.Node) ast.Visitor {
+ switch n := n.(type) {
+ case *ast.GenDecl:
+ if n.Tok == token.IMPORT {
+ n.Specs = []ast.Spec{
+ &ast.ImportSpec{
+ Path: &ast.BasicLit{
+ Kind: token.STRING,
+ Value: strconv.Quote("context"),
+ },
+ },
+ &ast.ImportSpec{
+ Path: &ast.BasicLit{
+ Kind: token.STRING,
+ Value: strconv.Quote("github.com/izumin5210/grapi/pkg/grapiserver"),
+ },
+ },
+ &ast.ImportSpec{
+ Name: &ast.Ident{Name: "api_pb"},
+ Path: &ast.BasicLit{
+ Kind: token.STRING,
+ Value: strconv.Quote("sampleapp/api"),
+ },
+ },
+ }
+ }
+ case *ast.FuncDecl:
+ if n.Name.Name == "ListBooks" {
+ n.Body.List = []ast.Stmt{
+ &ast.ReturnStmt{
+ Results: []ast.Expr{
+ &ast.UnaryExpr{
+ X: &ast.CompositeLit{
+ Type: &ast.SelectorExpr{
+ X: ast.NewIdent("api_pb"),
+ Sel: ast.NewIdent("ListBooksResponse"),
+ },
+ },
+ Op: token.AND,
+ },
+ &ast.Ident{Name: "nil"},
+ },
+ },
+ }
+ }
+ }
+ return v
+ },
+ }, f)
+
+ buf := new(bytes.Buffer)
+ err = format.Node(buf, token.NewFileSet(), f)
+ checkNoErr(t, err)
+ err = ioutil.WriteFile(filepath.Join(rootPath, "app", "server", "book_server.go"), buf.Bytes(), 0755)
+ checkNoErr(t, err)
+}
diff --git a/_tests/integration/main_test.go b/_tests/integration/main_test.go
deleted file mode 100644
index aa118b01..00000000
--- a/_tests/integration/main_test.go
+++ /dev/null
@@ -1,275 +0,0 @@
-package main
-
-import (
- "bytes"
- "context"
- "fmt"
- "go/ast"
- "go/format"
- "go/parser"
- "go/token"
- "net/http"
- "os"
- "os/exec"
- "path/filepath"
- "runtime"
- "strconv"
- "testing"
- "time"
-
- "github.com/spf13/afero"
-)
-
-func Test_Integration(t *testing.T) {
- _, testfilepath, _, _ := runtime.Caller(0)
- wd := filepath.Dir(testfilepath)
- bin := filepath.Join(wd, "..", "..", "bin", "grapi")
- gopath := filepath.Join(wd, "go")
- srcDir := filepath.Join(gopath, "src")
-
- fs := afero.NewOsFs()
- name := "sample"
- rootPath := filepath.Join(srcDir, name)
-
- fs.MkdirAll(srcDir, 0755)
- defer fs.RemoveAll(gopath)
-
- cmd := exec.Command(bin, "--debug", "init", "--HEAD", name)
- cmd.Dir = srcDir
- cmd.Env = append(os.Environ(), "GOPATH="+gopath)
- out, err := cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("failed to initialize project: %v\n%s", err, string(out))
- }
-
- if ok, err := afero.DirExists(fs, rootPath); err != nil || !ok {
- t.Fatalf("%s does not exist: %v", rootPath, err)
- }
- t.Log("Initialize a project successfully")
-
- cmd = exec.Command(bin, "--debug", "g", "service", "book", "list")
- cmd.Dir = rootPath
- cmd.Env = append(os.Environ(), "GOPATH="+gopath)
- out, err = cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("failed to generate service: %v\n%s", err, string(out))
- }
-
- svrPath := filepath.Join(rootPath, "app", "server", "book_server.go")
- if ok, err := afero.Exists(fs, svrPath); err != nil || !ok {
- t.Fatalf("%s does not exist: %v", svrPath, err)
- }
- t.Log("Generate a service successfully")
-
- port := 15261
-
- updateRun(t, fs, rootPath, port)
- updateServerImpl(t, fs, rootPath)
-
- t.Log("Start the server")
- svrCtx, cancel := context.WithCancel(context.Background())
- cmd = exec.CommandContext(svrCtx, bin, "--debug", "server")
- cmd.Dir = rootPath
- cmd.Env = append(os.Environ(), "GOPATH="+gopath)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- err = cmd.Start()
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- defer func() {
- if cmd.Process != nil && cmd.ProcessState != nil && !cmd.ProcessState.Exited() {
- cmd.Process.Kill()
- }
- }()
-
- startedAt := time.Now()
- var resp *http.Response
- var retryCnt int
-
- for {
- func() {
- defer recover()
- resp, err = http.Get(fmt.Sprintf("http://localhost:%d/books", port))
- }()
- if err != nil && time.Since(startedAt) < 120*time.Second {
- time.Sleep(5 * time.Second)
- retryCnt++
- } else {
- break
- }
- }
-
- if err != nil {
- t.Fatalf("Unexpected error (retry count: %d): %v", retryCnt, err)
- }
-
- if got, want := resp.StatusCode, 200; got != want {
- t.Errorf("Response status is %d, want %d", got, want)
- }
-
- t.Log("HTTP Request successfully")
-
- cancel()
- toCtx, _ := context.WithTimeout(context.Background(), 30*time.Second)
- select {
- case <-svrCtx.Done():
- t.Log("Shutdown server successfully")
- case <-toCtx.Done():
- t.Log("Deadline exceeded stopping server")
- cmd.Process.Signal(os.Kill)
- }
- err = cmd.Wait()
-}
-
-type visitor struct {
- VisitFunc func(ast.Visitor, ast.Node) ast.Visitor
-}
-
-func (v *visitor) Visit(node ast.Node) ast.Visitor {
- return v.VisitFunc(v, node)
-}
-
-func updateRun(t *testing.T, fs afero.Fs, rootPath string, port int) {
- data, err := afero.ReadFile(fs, filepath.Join(rootPath, "app", "run.go"))
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
-
- fset := token.NewFileSet()
- f, err := parser.ParseFile(fset, "", data, parser.DeclarationErrors)
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
-
- ast.Walk(&visitor{
- VisitFunc: func(v ast.Visitor, n ast.Node) ast.Visitor {
- switch n := n.(type) {
- case *ast.GenDecl:
- if n.Tok == token.IMPORT {
- n.Specs = append(n.Specs, &ast.ImportSpec{
- Path: &ast.BasicLit{
- Kind: token.STRING,
- Value: strconv.Quote("sample/app/server"),
- },
- })
- }
- case *ast.CallExpr:
- switch fun := n.Fun.(type) {
- case *ast.SelectorExpr:
- switch fun.Sel.Name {
- case "New":
- n.Args = append(n.Args, &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent("grapiserver"),
- Sel: ast.NewIdent("WithGatewayAddr"),
- },
- Args: []ast.Expr{
- &ast.BasicLit{
- Kind: token.STRING,
- Value: strconv.Quote("tcp"),
- },
- &ast.BasicLit{
- Kind: token.STRING,
- Value: strconv.Quote(fmt.Sprintf(":%d", port)),
- },
- },
- })
- case "WithServers":
- n.Args = append(n.Args, &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent("server"),
- Sel: ast.NewIdent("NewBookServiceServer"),
- },
- })
- }
- }
- }
- return v
- },
- }, f)
-
- buf := new(bytes.Buffer)
- err = format.Node(buf, token.NewFileSet(), f)
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- err = afero.WriteFile(fs, filepath.Join(rootPath, "app", "run.go"), buf.Bytes(), 0755)
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
-}
-
-func updateServerImpl(t *testing.T, fs afero.Fs, rootPath string) {
- data, err := afero.ReadFile(fs, filepath.Join(rootPath, "app", "server", "book_server.go"))
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
-
- fset := token.NewFileSet()
- f, err := parser.ParseFile(fset, "", data, parser.DeclarationErrors)
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
-
- ast.Walk(&visitor{
- VisitFunc: func(v ast.Visitor, n ast.Node) ast.Visitor {
- switch n := n.(type) {
- case *ast.GenDecl:
- if n.Tok == token.IMPORT {
- n.Specs = []ast.Spec{
- &ast.ImportSpec{
- Path: &ast.BasicLit{
- Kind: token.STRING,
- Value: strconv.Quote("context"),
- },
- },
- &ast.ImportSpec{
- Path: &ast.BasicLit{
- Kind: token.STRING,
- Value: strconv.Quote("github.com/izumin5210/grapi/pkg/grapiserver"),
- },
- },
- &ast.ImportSpec{
- Name: &ast.Ident{Name: "api_pb"},
- Path: &ast.BasicLit{
- Kind: token.STRING,
- Value: strconv.Quote("sample/api"),
- },
- },
- }
- }
- case *ast.FuncDecl:
- if n.Name.Name == "ListBooks" {
- n.Body.List = []ast.Stmt{
- &ast.ReturnStmt{
- Results: []ast.Expr{
- &ast.UnaryExpr{
- X: &ast.CompositeLit{
- Type: &ast.SelectorExpr{
- X: ast.NewIdent("api_pb"),
- Sel: ast.NewIdent("ListBooksResponse"),
- },
- },
- Op: token.AND,
- },
- &ast.Ident{Name: "nil"},
- },
- },
- }
- }
- }
- return v
- },
- }, f)
-
- buf := new(bytes.Buffer)
- err = format.Node(buf, token.NewFileSet(), f)
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- err = afero.WriteFile(fs, filepath.Join(rootPath, "app", "server", "book_server.go"), buf.Bytes(), 0755)
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
-}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_CommandGenerator-Generate-cmd-foo-run.go b/cmd/grapi-gen-command/.snapshots/TestCommand-simple-generate-cmd-foo-run.go
similarity index 100%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_CommandGenerator-Generate-cmd-foo-run.go
rename to cmd/grapi-gen-command/.snapshots/TestCommand-simple-generate-cmd-foo-run.go
diff --git a/cmd/grapi-gen-command/main.go b/cmd/grapi-gen-command/main.go
new file mode 100644
index 00000000..2a1d7a2a
--- /dev/null
+++ b/cmd/grapi-gen-command/main.go
@@ -0,0 +1,45 @@
+package main
+
+import (
+ "github.com/spf13/cobra"
+
+ _ "github.com/izumin5210/grapi/cmd/grapi-gen-command/template"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+)
+
+func main() {
+ buildCommand().MustExecute()
+}
+
+func buildCommand(opts ...gencmd.Option) gencmd.Executor {
+ return gencmd.New(
+ "command",
+ newGenerateCommand(),
+ newDestroyCommand(),
+ opts...,
+ )
+}
+
+func newGenerateCommand() *gencmd.Command {
+ return &gencmd.Command{
+ Use: "generate NAME",
+ Short: "Generate a new command",
+ Args: cobra.ExactArgs(1),
+ ShouldInsideApp: true,
+ BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) {
+ return map[string]string{"name": args[0]}, nil
+ },
+ }
+}
+
+func newDestroyCommand() *gencmd.Command {
+ return &gencmd.Command{
+ Use: "destroy NAME",
+ Short: "Destroy a existing command",
+ Args: cobra.ExactArgs(1),
+ ShouldInsideApp: true,
+ BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) {
+ return map[string]string{"name": args[0]}, nil
+ },
+ }
+}
diff --git a/cmd/grapi-gen-command/main_test.go b/cmd/grapi-gen-command/main_test.go
new file mode 100644
index 00000000..c9884ce8
--- /dev/null
+++ b/cmd/grapi-gen-command/main_test.go
@@ -0,0 +1,93 @@
+package main
+
+import (
+ "testing"
+
+ "github.com/bradleyjkemp/cupaloy/v2"
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ gencmdtesting "github.com/izumin5210/grapi/pkg/gencmd/testing"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/spf13/afero"
+)
+
+func TestCommand(t *testing.T) {
+ cases := []struct {
+ test string
+ args []string
+ files []string
+ }{
+ {
+ test: "simple",
+ args: []string{"foo"},
+ files: []string{"cmd/foo/run.go"},
+ },
+ }
+
+ rootDir := cli.RootDir{clib.Path("/home/src/testapp")}
+
+ createGenApp := func(cmd *gencmd.Command) (*gencmd.App, error) {
+ return gencmdtesting.NewTestApp(cmd, cli.NopUI)
+ }
+ createCmd := func(t *testing.T, fs afero.Fs) gencmd.Executor {
+ ctx := &grapicmd.Ctx{
+ FS: fs,
+ RootDir: rootDir,
+ }
+ return buildCommand(gencmd.WithGrapiCtx(ctx), gencmd.WithCreateAppFunc(createGenApp))
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.test, func(t *testing.T) {
+ fs := afero.NewMemMapFs()
+ afero.WriteFile(fs, rootDir.Join("grapi.toml").String(), []byte{}, 0755)
+
+ t.Run("generate", func(t *testing.T) {
+ cmd := createCmd(t, fs)
+ cmd.Command().SetArgs(append([]string{"generate"}, tc.args...))
+ err := cmd.Execute()
+
+ if err != nil {
+ t.Errorf("returned an error: %+v", err)
+ }
+
+ for _, file := range tc.files {
+ t.Run(file, func(t *testing.T) {
+ data, err := afero.ReadFile(fs, rootDir.Join(file).String())
+
+ if err != nil {
+ t.Errorf("returned an error: %v", err)
+ }
+
+ cupaloy.SnapshotT(t, string(data))
+ })
+ }
+ })
+
+ t.Run("destroy", func(t *testing.T) {
+ cmd := createCmd(t, fs)
+ cmd.Command().SetArgs(append([]string{"destroy"}, tc.args...))
+ err := cmd.Execute()
+
+ if err != nil {
+ t.Errorf("returned an error: %+v", err)
+ }
+
+ for _, file := range tc.files {
+ t.Run(file, func(t *testing.T) {
+ ok, err := afero.Exists(fs, rootDir.Join(file).String())
+
+ if err != nil {
+ t.Errorf("Exists(fs, %q) returned an error: %v", file, err)
+ }
+
+ if ok {
+ t.Errorf("%q should not exist", file)
+ }
+ })
+ }
+ })
+ })
+ }
+}
diff --git a/pkg/grapicmd/internal/module/generator/template/init/api/protos/.keep.tmpl b/cmd/grapi-gen-command/template/.gitignore
similarity index 100%
rename from pkg/grapicmd/internal/module/generator/template/init/api/protos/.keep.tmpl
rename to cmd/grapi-gen-command/template/.gitignore
diff --git a/pkg/grapicmd/internal/module/generator/template/command/cmd/{{ .name }}/run.go.tmpl b/cmd/grapi-gen-command/template/_data/cmd/{{ .name }}/run.go.tmpl
similarity index 100%
rename from pkg/grapicmd/internal/module/generator/template/command/cmd/{{ .name }}/run.go.tmpl
rename to cmd/grapi-gen-command/template/_data/cmd/{{ .name }}/run.go.tmpl
diff --git a/cmd/grapi-gen-command/template/gen.go b/cmd/grapi-gen-command/template/gen.go
new file mode 100644
index 00000000..ba18632b
--- /dev/null
+++ b/cmd/grapi-gen-command/template/gen.go
@@ -0,0 +1,3 @@
+//go:generate statik -src ./_data -dest .. -p template -f -m
+
+package template
diff --git a/cmd/grapi-gen-command/template/statik.go b/cmd/grapi-gen-command/template/statik.go
new file mode 100644
index 00000000..68e3dd19
--- /dev/null
+++ b/cmd/grapi-gen-command/template/statik.go
@@ -0,0 +1,13 @@
+// Code generated by statik. DO NOT EDIT.
+
+// Package statik contains static assets.
+package template
+
+import (
+ "github.com/rakyll/statik/fs"
+)
+
+func init() {
+ data := "PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b\x00 \x00cmd/{{ .name }}/run.go.tmplUT\x05\x00\x01\x80Cm8,\xcaA\n\xc20\x10\x05\xd0u\xfe)\xc6Y%\x9b\xe2%\\\xb8\xf3\n\xa142\xd4L\xcad\x82\x82\xf4\xeeb\xe9\xf2\xc1\xdb\xf2\xbc\xe6\xe7B5\x8b\x02R\xb7fN\x11\x81KuF\xe0\xd6\x19 (C\xe7\xe3\xc4D_\x84\xd6\xa7\xdbG<\xda\xd0\x98\x12\xf63\x1c$Q\xff\x9fR}z\x98\xa8\xbf4\xf2\xdd\xe9\xddl\xed\x17N\x08\xb6\xf80\xa5+v\xfc\x02\x00\x00\xff\xffPK\x07\x08\xe5<\xbb\x9ao\x00\x00\x00\x7f\x00\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xe5<\xbb\x9ao\x00\x00\x00\x7f\x00\x00\x00\x1b\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00cmd/{{ .name }}/run.go.tmplUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00R\x00\x00\x00\xc1\x00\x00\x00\x00\x00"
+ fs.Register(data)
+}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-api-protos-foo-bar_baz.proto
similarity index 69%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-api-protos-foo-bar_baz.proto
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-api-protos-foo-bar_baz.proto
index d19d50bd..2590d59c 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-api-protos-foo-bar_baz.proto
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-api-protos-foo-bar_baz.proto
@@ -1,7 +1,10 @@
syntax = "proto3";
-option go_package = "foo_pb";
+
package testapp.api.foo;
+option go_package = "testapp/api/foo;foo_pb";
+
+
import "google/api/annotations.proto";
service BarBazService {
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server.go
similarity index 65%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server.go
index 6b5bba6d..fa385d3b 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server.go
@@ -10,11 +10,14 @@ import (
foo_pb "testapp/api/foo"
)
-// NewBarBazServiceServer creates a new BarBazServiceServer instance.
-func NewBarBazServiceServer() interface {
+// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server.
+type BarBazServiceServer interface {
foo_pb.BarBazServiceServer
grapiserver.Server
-} {
+}
+
+// NewBarBazServiceServer creates a new BarBazServiceServer instance.
+func NewBarBazServiceServer() BarBazServiceServer {
return &barBazServiceServerImpl{}
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_register_funcs.go
similarity index 90%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server_register_funcs.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_register_funcs.go
index 9bd7e8ae..1c820532 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server_register_funcs.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_register_funcs.go
@@ -1,3 +1,5 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
package foo
import (
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_test.go
similarity index 100%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server_test.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_test.go
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-api-protos-foo-bar.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-api-protos-foo-bar.proto
similarity index 68%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-api-protos-foo-bar.proto
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-api-protos-foo-bar.proto
index 80a6b442..230cdcd3 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-api-protos-foo-bar.proto
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-api-protos-foo-bar.proto
@@ -1,7 +1,10 @@
syntax = "proto3";
-option go_package = "foo_pb";
+
package testapp.api.foo;
+option go_package = "testapp/api/foo;foo_pb";
+
+
import "google/api/annotations.proto";
service BarService {
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server.go
similarity index 66%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server.go
index b35236b8..68b67795 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server.go
@@ -10,11 +10,14 @@ import (
foo_pb "testapp/api/foo"
)
-// NewBarServiceServer creates a new BarServiceServer instance.
-func NewBarServiceServer() interface {
+// BarServiceServer is a composite interface of foo_pb.BarServiceServer and grapiserver.Server.
+type BarServiceServer interface {
foo_pb.BarServiceServer
grapiserver.Server
-} {
+}
+
+// NewBarServiceServer creates a new BarServiceServer instance.
+func NewBarServiceServer() BarServiceServer {
return &barServiceServerImpl{}
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_register_funcs.go
similarity index 90%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server_register_funcs.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_register_funcs.go
index 6d73d59d..27421437 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server_register_funcs.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_register_funcs.go
@@ -1,3 +1,5 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
package foo
import (
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_test.go
similarity index 100%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server_test.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_test.go
diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-api-protos-foo-bar.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-api-protos-foo-bar.proto
new file mode 100644
index 00000000..2775531f
--- /dev/null
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-api-protos-foo-bar.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package testcompany.testapp.foo;
+
+option go_package = "testapp/api/foo;foo_pb";
+
+
+import "google/api/annotations.proto";
+
+service BarService {
+}
+
diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server.go
new file mode 100644
index 00000000..68b67795
--- /dev/null
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server.go
@@ -0,0 +1,26 @@
+package foo
+
+import (
+ "context"
+
+ "github.com/izumin5210/grapi/pkg/grapiserver"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ foo_pb "testapp/api/foo"
+)
+
+// BarServiceServer is a composite interface of foo_pb.BarServiceServer and grapiserver.Server.
+type BarServiceServer interface {
+ foo_pb.BarServiceServer
+ grapiserver.Server
+}
+
+// NewBarServiceServer creates a new BarServiceServer instance.
+func NewBarServiceServer() BarServiceServer {
+ return &barServiceServerImpl{}
+}
+
+type barServiceServerImpl struct {
+}
+
diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_register_funcs.go
new file mode 100644
index 00000000..27421437
--- /dev/null
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_register_funcs.go
@@ -0,0 +1,23 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
+package foo
+
+import (
+ "context"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "google.golang.org/grpc"
+
+ foo_pb "testapp/api/foo"
+)
+
+// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
+func (s *barServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) {
+ foo_pb.RegisterBarServiceServer(grpcSvr, s)
+}
+
+// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.
+func (s *barServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return foo_pb.RegisterBarServiceHandler(ctx, mux, conn)
+}
+
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_test.go
similarity index 100%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server_test.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_test.go
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-api-protos-book.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-api-protos-book.proto
similarity index 96%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-api-protos-book.proto
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-api-protos-book.proto
index 2f046f00..767d59bc 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-api-protos-book.proto
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-api-protos-book.proto
@@ -1,7 +1,10 @@
syntax = "proto3";
-option go_package = "api_pb";
+
package testapp.api;
+option go_package = "testapp/api;api_pb";
+
+
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-api-protos-foo.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-api-protos-foo.proto
similarity index 70%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-api-protos-foo.proto
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-api-protos-foo.proto
index dedcc441..e94131cc 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-api-protos-foo.proto
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-api-protos-foo.proto
@@ -1,7 +1,10 @@
syntax = "proto3";
-option go_package = "api_pb";
+
package testapp.api;
+option go_package = "testapp/api;api_pb";
+
+
import "google/api/annotations.proto";
service FooService {
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server.go
similarity index 89%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server.go
index 1080bbe6..09152b99 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server.go
@@ -11,11 +11,14 @@ import (
api_pb "testapp/api"
)
-// NewBookServiceServer creates a new BookServiceServer instance.
-func NewBookServiceServer() interface {
+// BookServiceServer is a composite interface of api_pb.BookServiceServer and grapiserver.Server.
+type BookServiceServer interface {
api_pb.BookServiceServer
grapiserver.Server
-} {
+}
+
+// NewBookServiceServer creates a new BookServiceServer instance.
+func NewBookServiceServer() BookServiceServer {
return &bookServiceServerImpl{}
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server_register_funcs.go
similarity index 90%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server_register_funcs.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server_register_funcs.go
index eb34b1e1..0c8e7832 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server_register_funcs.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server_register_funcs.go
@@ -1,3 +1,5 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
package server
import (
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server_test.go
similarity index 95%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server_test.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server_test.go
index 5bda729f..2423f050 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server_test.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server_test.go
@@ -4,8 +4,6 @@ import (
"context"
"testing"
- "github.com/golang/protobuf/ptypes/empty"
-
api_pb "testapp/api"
)
@@ -17,6 +15,8 @@ func Test_BookServiceServer_ListBooks(t *testing.T) {
resp, err := svr.ListBooks(ctx, req)
+ t.SkipNow()
+
if err != nil {
t.Errorf("returned an error %v", err)
}
@@ -34,6 +34,8 @@ func Test_BookServiceServer_GetBook(t *testing.T) {
resp, err := svr.GetBook(ctx, req)
+ t.SkipNow()
+
if err != nil {
t.Errorf("returned an error %v", err)
}
@@ -51,6 +53,8 @@ func Test_BookServiceServer_CreateBook(t *testing.T) {
resp, err := svr.CreateBook(ctx, req)
+ t.SkipNow()
+
if err != nil {
t.Errorf("returned an error %v", err)
}
@@ -68,6 +72,8 @@ func Test_BookServiceServer_UpdateBook(t *testing.T) {
resp, err := svr.UpdateBook(ctx, req)
+ t.SkipNow()
+
if err != nil {
t.Errorf("returned an error %v", err)
}
@@ -85,6 +91,8 @@ func Test_BookServiceServer_DeleteBook(t *testing.T) {
resp, err := svr.DeleteBook(ctx, req)
+ t.SkipNow()
+
if err != nil {
t.Errorf("returned an error %v", err)
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server.go
similarity index 66%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server.go
index 9d028c3a..04d3cf0b 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server.go
@@ -10,11 +10,14 @@ import (
api_pb "testapp/api"
)
-// NewFooServiceServer creates a new FooServiceServer instance.
-func NewFooServiceServer() interface {
+// FooServiceServer is a composite interface of api_pb.FooServiceServer and grapiserver.Server.
+type FooServiceServer interface {
api_pb.FooServiceServer
grapiserver.Server
-} {
+}
+
+// NewFooServiceServer creates a new FooServiceServer instance.
+func NewFooServiceServer() FooServiceServer {
return &fooServiceServerImpl{}
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server_register_funcs.go
similarity index 90%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server_register_funcs.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server_register_funcs.go
index 78c52d0d..7b576996 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server_register_funcs.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server_register_funcs.go
@@ -1,3 +1,5 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
package server
import (
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server_test.go
similarity index 100%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server_test.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server_test.go
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-api-protos-book.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-api-protos-book.proto
similarity index 70%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-api-protos-book.proto
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-api-protos-book.proto
index 28f4ec58..5fe20ee3 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-api-protos-book.proto
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-api-protos-book.proto
@@ -1,7 +1,10 @@
syntax = "proto3";
-option go_package = "api_pb";
+
package testapp.api;
+option go_package = "testapp/api;api_pb";
+
+
import "google/api/annotations.proto";
service BookService {
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-app-server-book_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server.go
similarity index 66%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-app-server-book_server.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server.go
index 9bc99a11..9c739963 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-app-server-book_server.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server.go
@@ -10,11 +10,14 @@ import (
api_pb "testapp/api"
)
-// NewBookServiceServer creates a new BookServiceServer instance.
-func NewBookServiceServer() interface {
+// BookServiceServer is a composite interface of api_pb.BookServiceServer and grapiserver.Server.
+type BookServiceServer interface {
api_pb.BookServiceServer
grapiserver.Server
-} {
+}
+
+// NewBookServiceServer creates a new BookServiceServer instance.
+func NewBookServiceServer() BookServiceServer {
return &bookServiceServerImpl{}
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-app-server-book_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server_register_funcs.go
similarity index 90%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-app-server-book_server_register_funcs.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server_register_funcs.go
index eb34b1e1..0c8e7832 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-app-server-book_server_register_funcs.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server_register_funcs.go
@@ -1,3 +1,5 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
package server
import (
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-api-protos-foo-bar_baz.proto
similarity index 69%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-api-protos-foo-bar_baz.proto
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-api-protos-foo-bar_baz.proto
index d19d50bd..2590d59c 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-api-protos-foo-bar_baz.proto
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-api-protos-foo-bar_baz.proto
@@ -1,7 +1,10 @@
syntax = "proto3";
-option go_package = "foo_pb";
+
package testapp.api.foo;
+option go_package = "testapp/api/foo;foo_pb";
+
+
import "google/api/annotations.proto";
service BarBazService {
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server.go
similarity index 65%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server.go
index 6b5bba6d..fa385d3b 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server.go
@@ -10,11 +10,14 @@ import (
foo_pb "testapp/api/foo"
)
-// NewBarBazServiceServer creates a new BarBazServiceServer instance.
-func NewBarBazServiceServer() interface {
+// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server.
+type BarBazServiceServer interface {
foo_pb.BarBazServiceServer
grapiserver.Server
-} {
+}
+
+// NewBarBazServiceServer creates a new BarBazServiceServer instance.
+func NewBarBazServiceServer() BarBazServiceServer {
return &barBazServiceServerImpl{}
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_register_funcs.go
similarity index 90%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server_register_funcs.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_register_funcs.go
index 9bd7e8ae..1c820532 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server_register_funcs.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_register_funcs.go
@@ -1,3 +1,5 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
package foo
import (
diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_test.go
new file mode 100644
index 00000000..d1b1429c
--- /dev/null
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_test.go
@@ -0,0 +1,2 @@
+package foo
+
diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-api-protos-foo.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-api-protos-foo.proto
new file mode 100644
index 00000000..447b4721
--- /dev/null
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-api-protos-foo.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package testcompany.testapp;
+
+option go_package = "testapp/api;api_pb";
+
+
+import "google/api/annotations.proto";
+
+service FooService {
+}
+
diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server.go
new file mode 100644
index 00000000..04d3cf0b
--- /dev/null
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server.go
@@ -0,0 +1,26 @@
+package server
+
+import (
+ "context"
+
+ "github.com/izumin5210/grapi/pkg/grapiserver"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ api_pb "testapp/api"
+)
+
+// FooServiceServer is a composite interface of api_pb.FooServiceServer and grapiserver.Server.
+type FooServiceServer interface {
+ api_pb.FooServiceServer
+ grapiserver.Server
+}
+
+// NewFooServiceServer creates a new FooServiceServer instance.
+func NewFooServiceServer() FooServiceServer {
+ return &fooServiceServerImpl{}
+}
+
+type fooServiceServerImpl struct {
+}
+
diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_register_funcs.go
new file mode 100644
index 00000000..7b576996
--- /dev/null
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_register_funcs.go
@@ -0,0 +1,23 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
+package server
+
+import (
+ "context"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "google.golang.org/grpc"
+
+ api_pb "testapp/api"
+)
+
+// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
+func (s *fooServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) {
+ api_pb.RegisterFooServiceServer(grpcSvr, s)
+}
+
+// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.
+func (s *fooServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return api_pb.RegisterFooServiceHandler(ctx, mux, conn)
+}
+
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_test.go
similarity index 100%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server_test.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_test.go
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server.go
similarity index 66%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server.go
index 891963dd..e0d87ee0 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server.go
@@ -10,11 +10,14 @@ import (
api_pb "testapp/api"
)
-// NewQuxServiceServer creates a new QuxServiceServer instance.
-func NewQuxServiceServer() interface {
+// QuxServiceServer is a composite interface of api_pb.QuxServiceServer and grapiserver.Server.
+type QuxServiceServer interface {
api_pb.QuxServiceServer
grapiserver.Server
-} {
+}
+
+// NewQuxServiceServer creates a new QuxServiceServer instance.
+func NewQuxServiceServer() QuxServiceServer {
return &quxServiceServerImpl{}
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_register_funcs.go
similarity index 90%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server_register_funcs.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_register_funcs.go
index 1546a4c3..a23d82ec 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server_register_funcs.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_register_funcs.go
@@ -1,3 +1,5 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
package server
import (
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_test.go
similarity index 100%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server_test.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_test.go
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-pkg-foo-protos-qux.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-pkg-foo-protos-qux.proto
similarity index 70%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-pkg-foo-protos-qux.proto
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-pkg-foo-protos-qux.proto
index a8c3c8a0..3ea614e4 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-pkg-foo-protos-qux.proto
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-pkg-foo-protos-qux.proto
@@ -1,7 +1,10 @@
syntax = "proto3";
-option go_package = "api_pb";
+
package testapp.api;
+option go_package = "testapp/api;api_pb";
+
+
import "google/api/annotations.proto";
service QuxService {
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-api-protos-quux.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-api-protos-quux.proto
similarity index 69%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-api-protos-quux.proto
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-api-protos-quux.proto
index 4f591316..e97ef74d 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-api-protos-quux.proto
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-api-protos-quux.proto
@@ -1,7 +1,10 @@
syntax = "proto3";
-option go_package = "out_pb";
+
package testapp.api.out;
+option go_package = "testapp/api/out;out_pb";
+
+
import "google/api/annotations.proto";
service QuuxService {
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server.go
similarity index 66%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server.go
index 303df91f..2aa3e521 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server.go
@@ -10,11 +10,14 @@ import (
out_pb "testapp/api/out"
)
-// NewQuuxServiceServer creates a new QuuxServiceServer instance.
-func NewQuuxServiceServer() interface {
+// QuuxServiceServer is a composite interface of out_pb.QuuxServiceServer and grapiserver.Server.
+type QuuxServiceServer interface {
out_pb.QuuxServiceServer
grapiserver.Server
-} {
+}
+
+// NewQuuxServiceServer creates a new QuuxServiceServer instance.
+func NewQuuxServiceServer() QuuxServiceServer {
return &quuxServiceServerImpl{}
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_register_funcs.go
similarity index 90%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server_register_funcs.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_register_funcs.go
index 61c22589..26cfa4ea 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server_register_funcs.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_register_funcs.go
@@ -1,3 +1,5 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
package server
import (
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_test.go
similarity index 100%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server_test.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_test.go
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-api-protos-library.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-api-protos-library.proto
similarity index 96%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-api-protos-library.proto
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-api-protos-library.proto
index ca2a1c90..3b82cfe6 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-api-protos-library.proto
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-api-protos-library.proto
@@ -1,7 +1,10 @@
syntax = "proto3";
-option go_package = "api_pb";
+
package testapp.api;
+option go_package = "testapp/api;api_pb";
+
+
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server.go
similarity index 88%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server.go
index 59795854..ff6de0c2 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server.go
@@ -11,11 +11,14 @@ import (
api_pb "testapp/api"
)
-// NewLibraryServiceServer creates a new LibraryServiceServer instance.
-func NewLibraryServiceServer() interface {
+// LibraryServiceServer is a composite interface of api_pb.LibraryServiceServer and grapiserver.Server.
+type LibraryServiceServer interface {
api_pb.LibraryServiceServer
grapiserver.Server
-} {
+}
+
+// NewLibraryServiceServer creates a new LibraryServiceServer instance.
+func NewLibraryServiceServer() LibraryServiceServer {
return &libraryServiceServerImpl{}
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_register_funcs.go
similarity index 90%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server_register_funcs.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_register_funcs.go
index 7e654933..71a96737 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server_register_funcs.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_register_funcs.go
@@ -1,3 +1,5 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
package server
import (
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_test.go
similarity index 96%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server_test.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_test.go
index ad4c2505..1cd46a69 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server_test.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_test.go
@@ -4,8 +4,6 @@ import (
"context"
"testing"
- "github.com/golang/protobuf/ptypes/empty"
-
api_pb "testapp/api"
)
@@ -17,6 +15,8 @@ func Test_LibraryServiceServer_ListBooks(t *testing.T) {
resp, err := svr.ListBooks(ctx, req)
+ t.SkipNow()
+
if err != nil {
t.Errorf("returned an error %v", err)
}
@@ -34,6 +34,8 @@ func Test_LibraryServiceServer_GetBook(t *testing.T) {
resp, err := svr.GetBook(ctx, req)
+ t.SkipNow()
+
if err != nil {
t.Errorf("returned an error %v", err)
}
@@ -51,6 +53,8 @@ func Test_LibraryServiceServer_CreateBook(t *testing.T) {
resp, err := svr.CreateBook(ctx, req)
+ t.SkipNow()
+
if err != nil {
t.Errorf("returned an error %v", err)
}
@@ -68,6 +72,8 @@ func Test_LibraryServiceServer_UpdateBook(t *testing.T) {
resp, err := svr.UpdateBook(ctx, req)
+ t.SkipNow()
+
if err != nil {
t.Errorf("returned an error %v", err)
}
@@ -85,6 +91,8 @@ func Test_LibraryServiceServer_DeleteBook(t *testing.T) {
resp, err := svr.DeleteBook(ctx, req)
+ t.SkipNow()
+
if err != nil {
t.Errorf("returned an error %v", err)
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-api-protos-corge.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-api-protos-corge.proto
similarity index 70%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-api-protos-corge.proto
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-api-protos-corge.proto
index ee56d372..ee9ce2a1 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-api-protos-corge.proto
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-api-protos-corge.proto
@@ -1,7 +1,10 @@
syntax = "proto3";
-option go_package = "api_pb";
+
package testapp.api;
+option go_package = "testapp/api;api_pb";
+
+
import "google/api/annotations.proto";
service CorgeService {
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server.go
similarity index 66%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server.go
index fffc343f..13334764 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server.go
@@ -10,11 +10,14 @@ import (
api_pb "testapp/api"
)
-// NewCorgeServiceServer creates a new CorgeServiceServer instance.
-func NewCorgeServiceServer() interface {
+// CorgeServiceServer is a composite interface of api_pb.CorgeServiceServer and grapiserver.Server.
+type CorgeServiceServer interface {
api_pb.CorgeServiceServer
grapiserver.Server
-} {
+}
+
+// NewCorgeServiceServer creates a new CorgeServiceServer instance.
+func NewCorgeServiceServer() CorgeServiceServer {
return &corgeServiceServerImpl{}
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_register_funcs.go
similarity index 90%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server_register_funcs.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_register_funcs.go
index 7bc5772a..93d5b5b8 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server_register_funcs.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_register_funcs.go
@@ -1,3 +1,5 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
package server
import (
diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_test.go
new file mode 100644
index 00000000..a86bc127
--- /dev/null
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_test.go
@@ -0,0 +1,2 @@
+package server
+
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-api-protos-foo-bar_baz.proto
similarity index 96%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-api-protos-foo-bar_baz.proto
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-api-protos-foo-bar_baz.proto
index e2141f9e..5ea823df 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-api-protos-foo-bar_baz.proto
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-api-protos-foo-bar_baz.proto
@@ -1,7 +1,10 @@
syntax = "proto3";
-option go_package = "foo_pb";
+
package testapp.api.foo;
+option go_package = "testapp/api/foo;foo_pb";
+
+
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server.go
similarity index 89%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server.go
index 34997019..0fb60647 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server.go
@@ -11,11 +11,14 @@ import (
foo_pb "testapp/api/foo"
)
-// NewBarBazServiceServer creates a new BarBazServiceServer instance.
-func NewBarBazServiceServer() interface {
+// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server.
+type BarBazServiceServer interface {
foo_pb.BarBazServiceServer
grapiserver.Server
-} {
+}
+
+// NewBarBazServiceServer creates a new BarBazServiceServer instance.
+func NewBarBazServiceServer() BarBazServiceServer {
return &barBazServiceServerImpl{}
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go
similarity index 90%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server_register_funcs.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go
index 9bd7e8ae..1c820532 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server_register_funcs.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go
@@ -1,3 +1,5 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
package foo
import (
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_test.go
similarity index 100%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server_test.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_test.go
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-api-protos-foo-bar_baz.proto
similarity index 94%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-api-protos-foo-bar_baz.proto
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-api-protos-foo-bar_baz.proto
index a124d7f4..44b449c3 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-api-protos-foo-bar_baz.proto
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-api-protos-foo-bar_baz.proto
@@ -1,7 +1,10 @@
syntax = "proto3";
-option go_package = "foo_pb";
+
package testapp.api.foo;
+option go_package = "testapp/api/foo;foo_pb";
+
+
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server.go
similarity index 85%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server.go
index 20e98ce6..fc85ca3d 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server.go
@@ -11,11 +11,14 @@ import (
foo_pb "testapp/api/foo"
)
-// NewBarBazServiceServer creates a new BarBazServiceServer instance.
-func NewBarBazServiceServer() interface {
+// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server.
+type BarBazServiceServer interface {
foo_pb.BarBazServiceServer
grapiserver.Server
-} {
+}
+
+// NewBarBazServiceServer creates a new BarBazServiceServer instance.
+func NewBarBazServiceServer() BarBazServiceServer {
return &barBazServiceServerImpl{}
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go
similarity index 90%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server_register_funcs.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go
index 9bd7e8ae..1c820532 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server_register_funcs.go
+++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go
@@ -1,3 +1,5 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
package foo
import (
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_test.go
similarity index 100%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server_test.go
rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_test.go
diff --git a/cmd/grapi-gen-scaffold-service/main.go b/cmd/grapi-gen-scaffold-service/main.go
new file mode 100644
index 00000000..e99776b1
--- /dev/null
+++ b/cmd/grapi-gen-scaffold-service/main.go
@@ -0,0 +1,83 @@
+package main
+
+import (
+ "context"
+
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ "github.com/izumin5210/grapi/pkg/svcgen"
+)
+
+func main() {
+ buildCommand(svcgen.NewApp).MustExecute()
+}
+
+func buildCommand(createAppFunc svcgen.CreateAppFunc, opts ...gencmd.Option) gencmd.Executor {
+ return gencmd.New(
+ "scaffold-service",
+ newGenerateCommand(createAppFunc),
+ newDestroyCommand(createAppFunc),
+ opts...,
+ )
+}
+
+func newGenerateCommand(createApp svcgen.CreateAppFunc) *gencmd.Command {
+ var (
+ skipTest bool
+ resName string
+ app *svcgen.App
+ )
+
+ cmd := &gencmd.Command{
+ Use: "generate NAME [flags]",
+ Short: "Generate a new service with standard methods",
+ Args: cobra.ExactArgs(1),
+ ShouldInsideApp: true,
+ PreRun: func(c *gencmd.Command, args []string) error {
+ var err error
+ app, err = createApp(c)
+ return errors.WithStack(err)
+ },
+ BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) {
+ svcName := args[0]
+ methods := []string{"list", "get", "create", "update", "delete"}
+
+ params, err := app.ParamsBuilder.Build(svcName, resName, methods)
+ return params, errors.WithStack(err)
+ },
+ PostRun: func(c *gencmd.Command, args []string) error {
+ return errors.WithStack(app.ProtocWrapper.Exec(context.TODO()))
+ },
+ }
+
+ cmd.Flags().BoolVarP(&skipTest, "skip-test", "T", false, "Skip test files")
+ cmd.Flags().StringVar(&resName, "resource-name", "", "ResourceName to be used")
+
+ return cmd
+}
+
+func newDestroyCommand(createApp svcgen.CreateAppFunc) *gencmd.Command {
+ var (
+ app *svcgen.App
+ )
+
+ cmd := &gencmd.Command{
+ Use: "destroy NAME",
+ Short: "Destroy an existing service",
+ Args: cobra.ExactArgs(1),
+ ShouldInsideApp: true,
+ PreRun: func(c *gencmd.Command, args []string) error {
+ var err error
+ app, err = createApp(c)
+ return errors.WithStack(err)
+ },
+ BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) {
+ params, err := app.ParamsBuilder.Build(args[0], "", nil)
+ return params, errors.WithStack(err)
+ },
+ }
+
+ return cmd
+}
diff --git a/cmd/grapi-gen-scaffold-service/main_test.go b/cmd/grapi-gen-scaffold-service/main_test.go
new file mode 100644
index 00000000..257629bd
--- /dev/null
+++ b/cmd/grapi-gen-scaffold-service/main_test.go
@@ -0,0 +1,81 @@
+package main
+
+import (
+ "context"
+ "testing"
+
+ "github.com/spf13/afero"
+
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ gencmdtesting "github.com/izumin5210/grapi/pkg/gencmd/testing"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/izumin5210/grapi/pkg/protoc"
+ "github.com/izumin5210/grapi/pkg/svcgen"
+ svcgentesting "github.com/izumin5210/grapi/pkg/svcgen/testing"
+)
+
+func TestRun(t *testing.T) {
+ cases := []svcgentesting.Case{
+ {
+ Test: "simple",
+ GArgs: []string{"book"},
+ DArgs: []string{"book"},
+ Files: []string{
+ "api/protos/book.proto",
+ "app/server/book_server.go",
+ "app/server/book_server_register_funcs.go",
+ "app/server/book_server_test.go",
+ },
+ },
+ {
+ Test: "specify resource name",
+ GArgs: []string{"library", "--resource-name=book"},
+ DArgs: []string{"library"},
+ Files: []string{
+ "api/protos/library.proto",
+ "app/server/library_server.go",
+ "app/server/library_server_register_funcs.go",
+ "app/server/library_server_test.go",
+ },
+ },
+ }
+
+ rootDir := cli.RootDir{clib.Path("/home/src/testapp")}
+
+ createSvcApp := func(cmd *gencmd.Command) (*svcgen.App, error) {
+ return svcgentesting.NewTestApp(cmd, &fakeProtocWrapper{}, cli.NopUI)
+ }
+ createGenApp := func(cmd *gencmd.Command) (*gencmd.App, error) {
+ return gencmdtesting.NewTestApp(cmd, cli.NopUI)
+ }
+ createCmd := func(t *testing.T, fs afero.Fs, tc svcgentesting.Case) gencmd.Executor {
+ ctx := &grapicmd.Ctx{
+ FS: fs,
+ RootDir: rootDir,
+ Config: grapicmd.Config{
+ Package: tc.PkgName,
+ },
+ ProtocConfig: protoc.Config{
+ ProtosDir: tc.ProtoDir,
+ OutDir: tc.ProtoOutDir,
+ },
+ }
+ ctx.Config.Grapi.ServerDir = tc.ServerDir
+ return buildCommand(createSvcApp, gencmd.WithGrapiCtx(ctx), gencmd.WithCreateAppFunc(createGenApp))
+ }
+
+ ctx := &svcgentesting.Ctx{
+ GOPATH: "/home",
+ RootDir: rootDir,
+ CreateCmd: createCmd,
+ Cases: cases,
+ }
+
+ svcgentesting.Run(t, ctx)
+}
+
+type fakeProtocWrapper struct{}
+
+func (*fakeProtocWrapper) Exec(context.Context) error { return nil }
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-api-protos-foo-bar_baz.proto
new file mode 100644
index 00000000..2590d59c
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-api-protos-foo-bar_baz.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package testapp.api.foo;
+
+option go_package = "testapp/api/foo;foo_pb";
+
+
+import "google/api/annotations.proto";
+
+service BarBazService {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server.go
new file mode 100644
index 00000000..fa385d3b
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server.go
@@ -0,0 +1,26 @@
+package foo
+
+import (
+ "context"
+
+ "github.com/izumin5210/grapi/pkg/grapiserver"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ foo_pb "testapp/api/foo"
+)
+
+// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server.
+type BarBazServiceServer interface {
+ foo_pb.BarBazServiceServer
+ grapiserver.Server
+}
+
+// NewBarBazServiceServer creates a new BarBazServiceServer instance.
+func NewBarBazServiceServer() BarBazServiceServer {
+ return &barBazServiceServerImpl{}
+}
+
+type barBazServiceServerImpl struct {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_register_funcs.go
new file mode 100644
index 00000000..1c820532
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_register_funcs.go
@@ -0,0 +1,23 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
+package foo
+
+import (
+ "context"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "google.golang.org/grpc"
+
+ foo_pb "testapp/api/foo"
+)
+
+// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
+func (s *barBazServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) {
+ foo_pb.RegisterBarBazServiceServer(grpcSvr, s)
+}
+
+// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.
+func (s *barBazServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return foo_pb.RegisterBarBazServiceHandler(ctx, mux, conn)
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_test.go
new file mode 100644
index 00000000..d1b1429c
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_test.go
@@ -0,0 +1,2 @@
+package foo
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-api-protos-foo-bar.proto b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-api-protos-foo-bar.proto
new file mode 100644
index 00000000..230cdcd3
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-api-protos-foo-bar.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package testapp.api.foo;
+
+option go_package = "testapp/api/foo;foo_pb";
+
+
+import "google/api/annotations.proto";
+
+service BarService {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server.go
new file mode 100644
index 00000000..68b67795
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server.go
@@ -0,0 +1,26 @@
+package foo
+
+import (
+ "context"
+
+ "github.com/izumin5210/grapi/pkg/grapiserver"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ foo_pb "testapp/api/foo"
+)
+
+// BarServiceServer is a composite interface of foo_pb.BarServiceServer and grapiserver.Server.
+type BarServiceServer interface {
+ foo_pb.BarServiceServer
+ grapiserver.Server
+}
+
+// NewBarServiceServer creates a new BarServiceServer instance.
+func NewBarServiceServer() BarServiceServer {
+ return &barServiceServerImpl{}
+}
+
+type barServiceServerImpl struct {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_register_funcs.go
new file mode 100644
index 00000000..27421437
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_register_funcs.go
@@ -0,0 +1,23 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
+package foo
+
+import (
+ "context"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "google.golang.org/grpc"
+
+ foo_pb "testapp/api/foo"
+)
+
+// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
+func (s *barServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) {
+ foo_pb.RegisterBarServiceServer(grpcSvr, s)
+}
+
+// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.
+func (s *barServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return foo_pb.RegisterBarServiceHandler(ctx, mux, conn)
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_test.go
new file mode 100644
index 00000000..d1b1429c
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_test.go
@@ -0,0 +1,2 @@
+package foo
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-api-protos-foo-bar.proto b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-api-protos-foo-bar.proto
new file mode 100644
index 00000000..2775531f
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-api-protos-foo-bar.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package testcompany.testapp.foo;
+
+option go_package = "testapp/api/foo;foo_pb";
+
+
+import "google/api/annotations.proto";
+
+service BarService {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server.go
new file mode 100644
index 00000000..68b67795
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server.go
@@ -0,0 +1,26 @@
+package foo
+
+import (
+ "context"
+
+ "github.com/izumin5210/grapi/pkg/grapiserver"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ foo_pb "testapp/api/foo"
+)
+
+// BarServiceServer is a composite interface of foo_pb.BarServiceServer and grapiserver.Server.
+type BarServiceServer interface {
+ foo_pb.BarServiceServer
+ grapiserver.Server
+}
+
+// NewBarServiceServer creates a new BarServiceServer instance.
+func NewBarServiceServer() BarServiceServer {
+ return &barServiceServerImpl{}
+}
+
+type barServiceServerImpl struct {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_register_funcs.go
new file mode 100644
index 00000000..27421437
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_register_funcs.go
@@ -0,0 +1,23 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
+package foo
+
+import (
+ "context"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "google.golang.org/grpc"
+
+ foo_pb "testapp/api/foo"
+)
+
+// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
+func (s *barServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) {
+ foo_pb.RegisterBarServiceServer(grpcSvr, s)
+}
+
+// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.
+func (s *barServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return foo_pb.RegisterBarServiceHandler(ctx, mux, conn)
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_test.go
new file mode 100644
index 00000000..d1b1429c
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_test.go
@@ -0,0 +1,2 @@
+package foo
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-api-protos-foo.proto b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-api-protos-foo.proto
new file mode 100644
index 00000000..e94131cc
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-api-protos-foo.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package testapp.api;
+
+option go_package = "testapp/api;api_pb";
+
+
+import "google/api/annotations.proto";
+
+service FooService {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server.go
new file mode 100644
index 00000000..04d3cf0b
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server.go
@@ -0,0 +1,26 @@
+package server
+
+import (
+ "context"
+
+ "github.com/izumin5210/grapi/pkg/grapiserver"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ api_pb "testapp/api"
+)
+
+// FooServiceServer is a composite interface of api_pb.FooServiceServer and grapiserver.Server.
+type FooServiceServer interface {
+ api_pb.FooServiceServer
+ grapiserver.Server
+}
+
+// NewFooServiceServer creates a new FooServiceServer instance.
+func NewFooServiceServer() FooServiceServer {
+ return &fooServiceServerImpl{}
+}
+
+type fooServiceServerImpl struct {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server_register_funcs.go
new file mode 100644
index 00000000..7b576996
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server_register_funcs.go
@@ -0,0 +1,23 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
+package server
+
+import (
+ "context"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "google.golang.org/grpc"
+
+ api_pb "testapp/api"
+)
+
+// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
+func (s *fooServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) {
+ api_pb.RegisterFooServiceServer(grpcSvr, s)
+}
+
+// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.
+func (s *fooServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return api_pb.RegisterFooServiceHandler(ctx, mux, conn)
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server_test.go
new file mode 100644
index 00000000..a86bc127
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server_test.go
@@ -0,0 +1,2 @@
+package server
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-api-protos-book.proto b/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-api-protos-book.proto
new file mode 100644
index 00000000..5fe20ee3
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-api-protos-book.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package testapp.api;
+
+option go_package = "testapp/api;api_pb";
+
+
+import "google/api/annotations.proto";
+
+service BookService {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server.go
new file mode 100644
index 00000000..9c739963
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server.go
@@ -0,0 +1,26 @@
+package server
+
+import (
+ "context"
+
+ "github.com/izumin5210/grapi/pkg/grapiserver"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ api_pb "testapp/api"
+)
+
+// BookServiceServer is a composite interface of api_pb.BookServiceServer and grapiserver.Server.
+type BookServiceServer interface {
+ api_pb.BookServiceServer
+ grapiserver.Server
+}
+
+// NewBookServiceServer creates a new BookServiceServer instance.
+func NewBookServiceServer() BookServiceServer {
+ return &bookServiceServerImpl{}
+}
+
+type bookServiceServerImpl struct {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server_register_funcs.go
new file mode 100644
index 00000000..0c8e7832
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server_register_funcs.go
@@ -0,0 +1,23 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
+package server
+
+import (
+ "context"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "google.golang.org/grpc"
+
+ api_pb "testapp/api"
+)
+
+// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
+func (s *bookServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) {
+ api_pb.RegisterBookServiceServer(grpcSvr, s)
+}
+
+// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.
+func (s *bookServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return api_pb.RegisterBookServiceHandler(ctx, mux, conn)
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-api-protos-foo-bar_baz.proto
new file mode 100644
index 00000000..2590d59c
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-api-protos-foo-bar_baz.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package testapp.api.foo;
+
+option go_package = "testapp/api/foo;foo_pb";
+
+
+import "google/api/annotations.proto";
+
+service BarBazService {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server.go
new file mode 100644
index 00000000..fa385d3b
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server.go
@@ -0,0 +1,26 @@
+package foo
+
+import (
+ "context"
+
+ "github.com/izumin5210/grapi/pkg/grapiserver"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ foo_pb "testapp/api/foo"
+)
+
+// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server.
+type BarBazServiceServer interface {
+ foo_pb.BarBazServiceServer
+ grapiserver.Server
+}
+
+// NewBarBazServiceServer creates a new BarBazServiceServer instance.
+func NewBarBazServiceServer() BarBazServiceServer {
+ return &barBazServiceServerImpl{}
+}
+
+type barBazServiceServerImpl struct {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_register_funcs.go
new file mode 100644
index 00000000..1c820532
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_register_funcs.go
@@ -0,0 +1,23 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
+package foo
+
+import (
+ "context"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "google.golang.org/grpc"
+
+ foo_pb "testapp/api/foo"
+)
+
+// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
+func (s *barBazServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) {
+ foo_pb.RegisterBarBazServiceServer(grpcSvr, s)
+}
+
+// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.
+func (s *barBazServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return foo_pb.RegisterBarBazServiceHandler(ctx, mux, conn)
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_test.go
new file mode 100644
index 00000000..d1b1429c
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_test.go
@@ -0,0 +1,2 @@
+package foo
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-api-protos-foo.proto b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-api-protos-foo.proto
new file mode 100644
index 00000000..447b4721
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-api-protos-foo.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package testcompany.testapp;
+
+option go_package = "testapp/api;api_pb";
+
+
+import "google/api/annotations.proto";
+
+service FooService {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server.go
new file mode 100644
index 00000000..04d3cf0b
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server.go
@@ -0,0 +1,26 @@
+package server
+
+import (
+ "context"
+
+ "github.com/izumin5210/grapi/pkg/grapiserver"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ api_pb "testapp/api"
+)
+
+// FooServiceServer is a composite interface of api_pb.FooServiceServer and grapiserver.Server.
+type FooServiceServer interface {
+ api_pb.FooServiceServer
+ grapiserver.Server
+}
+
+// NewFooServiceServer creates a new FooServiceServer instance.
+func NewFooServiceServer() FooServiceServer {
+ return &fooServiceServerImpl{}
+}
+
+type fooServiceServerImpl struct {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_register_funcs.go
new file mode 100644
index 00000000..7b576996
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_register_funcs.go
@@ -0,0 +1,23 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
+package server
+
+import (
+ "context"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "google.golang.org/grpc"
+
+ api_pb "testapp/api"
+)
+
+// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
+func (s *fooServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) {
+ api_pb.RegisterFooServiceServer(grpcSvr, s)
+}
+
+// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.
+func (s *fooServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return api_pb.RegisterFooServiceHandler(ctx, mux, conn)
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_test.go
new file mode 100644
index 00000000..a86bc127
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_test.go
@@ -0,0 +1,2 @@
+package server
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server.go
new file mode 100644
index 00000000..e0d87ee0
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server.go
@@ -0,0 +1,26 @@
+package server
+
+import (
+ "context"
+
+ "github.com/izumin5210/grapi/pkg/grapiserver"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ api_pb "testapp/api"
+)
+
+// QuxServiceServer is a composite interface of api_pb.QuxServiceServer and grapiserver.Server.
+type QuxServiceServer interface {
+ api_pb.QuxServiceServer
+ grapiserver.Server
+}
+
+// NewQuxServiceServer creates a new QuxServiceServer instance.
+func NewQuxServiceServer() QuxServiceServer {
+ return &quxServiceServerImpl{}
+}
+
+type quxServiceServerImpl struct {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_register_funcs.go
new file mode 100644
index 00000000..a23d82ec
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_register_funcs.go
@@ -0,0 +1,23 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
+package server
+
+import (
+ "context"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "google.golang.org/grpc"
+
+ api_pb "testapp/api"
+)
+
+// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
+func (s *quxServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) {
+ api_pb.RegisterQuxServiceServer(grpcSvr, s)
+}
+
+// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.
+func (s *quxServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return api_pb.RegisterQuxServiceHandler(ctx, mux, conn)
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_test.go
new file mode 100644
index 00000000..a86bc127
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_test.go
@@ -0,0 +1,2 @@
+package server
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-pkg-foo-protos-qux.proto b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-pkg-foo-protos-qux.proto
new file mode 100644
index 00000000..3ea614e4
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-pkg-foo-protos-qux.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package testapp.api;
+
+option go_package = "testapp/api;api_pb";
+
+
+import "google/api/annotations.proto";
+
+service QuxService {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-api-protos-quux.proto b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-api-protos-quux.proto
new file mode 100644
index 00000000..e97ef74d
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-api-protos-quux.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package testapp.api.out;
+
+option go_package = "testapp/api/out;out_pb";
+
+
+import "google/api/annotations.proto";
+
+service QuuxService {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server.go
new file mode 100644
index 00000000..2aa3e521
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server.go
@@ -0,0 +1,26 @@
+package server
+
+import (
+ "context"
+
+ "github.com/izumin5210/grapi/pkg/grapiserver"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ out_pb "testapp/api/out"
+)
+
+// QuuxServiceServer is a composite interface of out_pb.QuuxServiceServer and grapiserver.Server.
+type QuuxServiceServer interface {
+ out_pb.QuuxServiceServer
+ grapiserver.Server
+}
+
+// NewQuuxServiceServer creates a new QuuxServiceServer instance.
+func NewQuuxServiceServer() QuuxServiceServer {
+ return &quuxServiceServerImpl{}
+}
+
+type quuxServiceServerImpl struct {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_register_funcs.go
new file mode 100644
index 00000000..26cfa4ea
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_register_funcs.go
@@ -0,0 +1,23 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
+package server
+
+import (
+ "context"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "google.golang.org/grpc"
+
+ out_pb "testapp/api/out"
+)
+
+// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
+func (s *quuxServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) {
+ out_pb.RegisterQuuxServiceServer(grpcSvr, s)
+}
+
+// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.
+func (s *quuxServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return out_pb.RegisterQuuxServiceHandler(ctx, mux, conn)
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_test.go
new file mode 100644
index 00000000..a86bc127
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_test.go
@@ -0,0 +1,2 @@
+package server
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-api-protos-library.proto b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-api-protos-library.proto
new file mode 100644
index 00000000..5310394b
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-api-protos-library.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package testapp.api;
+
+option go_package = "testapp/api;api_pb";
+
+
+import "google/api/annotations.proto";
+
+service LibraryService {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server.go
new file mode 100644
index 00000000..445f9247
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server.go
@@ -0,0 +1,26 @@
+package server
+
+import (
+ "context"
+
+ "github.com/izumin5210/grapi/pkg/grapiserver"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ api_pb "testapp/api"
+)
+
+// LibraryServiceServer is a composite interface of api_pb.LibraryServiceServer and grapiserver.Server.
+type LibraryServiceServer interface {
+ api_pb.LibraryServiceServer
+ grapiserver.Server
+}
+
+// NewLibraryServiceServer creates a new LibraryServiceServer instance.
+func NewLibraryServiceServer() LibraryServiceServer {
+ return &libraryServiceServerImpl{}
+}
+
+type libraryServiceServerImpl struct {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_register_funcs.go
new file mode 100644
index 00000000..71a96737
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_register_funcs.go
@@ -0,0 +1,23 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
+package server
+
+import (
+ "context"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "google.golang.org/grpc"
+
+ api_pb "testapp/api"
+)
+
+// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
+func (s *libraryServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) {
+ api_pb.RegisterLibraryServiceServer(grpcSvr, s)
+}
+
+// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.
+func (s *libraryServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return api_pb.RegisterLibraryServiceHandler(ctx, mux, conn)
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_test.go
new file mode 100644
index 00000000..a86bc127
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_test.go
@@ -0,0 +1,2 @@
+package server
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-api-protos-corge.proto b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-api-protos-corge.proto
new file mode 100644
index 00000000..ee9ce2a1
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-api-protos-corge.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package testapp.api;
+
+option go_package = "testapp/api;api_pb";
+
+
+import "google/api/annotations.proto";
+
+service CorgeService {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server.go
new file mode 100644
index 00000000..13334764
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server.go
@@ -0,0 +1,26 @@
+package server
+
+import (
+ "context"
+
+ "github.com/izumin5210/grapi/pkg/grapiserver"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ api_pb "testapp/api"
+)
+
+// CorgeServiceServer is a composite interface of api_pb.CorgeServiceServer and grapiserver.Server.
+type CorgeServiceServer interface {
+ api_pb.CorgeServiceServer
+ grapiserver.Server
+}
+
+// NewCorgeServiceServer creates a new CorgeServiceServer instance.
+func NewCorgeServiceServer() CorgeServiceServer {
+ return &corgeServiceServerImpl{}
+}
+
+type corgeServiceServerImpl struct {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_register_funcs.go
new file mode 100644
index 00000000..93d5b5b8
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_register_funcs.go
@@ -0,0 +1,23 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
+package server
+
+import (
+ "context"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "google.golang.org/grpc"
+
+ api_pb "testapp/api"
+)
+
+// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
+func (s *corgeServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) {
+ api_pb.RegisterCorgeServiceServer(grpcSvr, s)
+}
+
+// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.
+func (s *corgeServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return api_pb.RegisterCorgeServiceHandler(ctx, mux, conn)
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_test.go
new file mode 100644
index 00000000..a86bc127
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_test.go
@@ -0,0 +1,2 @@
+package server
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-api-protos-foo-bar_baz.proto
new file mode 100644
index 00000000..5ea823df
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-api-protos-foo-bar_baz.proto
@@ -0,0 +1,70 @@
+syntax = "proto3";
+
+package testapp.api.foo;
+
+option go_package = "testapp/api/foo;foo_pb";
+
+
+import "google/api/annotations.proto";
+import "google/protobuf/empty.proto";
+
+service BarBazService {
+ rpc ListBarBazs (ListBarBazsRequest) returns (ListBarBazsResponse) {
+ option (google.api.http) = {
+ get: "/bar_bazs"
+ };
+ }
+ rpc CreateBarBaz (CreateBarBazRequest) returns (BarBaz) {
+ option (google.api.http) = {
+ post: "/bar_bazs"
+ body: "bar_baz"
+ };
+ }
+ rpc DeleteBarBaz (DeleteBarBazRequest) returns (google.protobuf.Empty) {
+ option (google.api.http) = {
+ delete: "/bar_bazs/{bar_baz_id}"
+ };
+ }
+ rpc Rename (RenameRequest) returns (RenameResponse) {
+ option (google.api.http) = {
+ get: "/bar_bazs/rename"
+ };
+ }
+ rpc MoveMove (MoveMoveRequest) returns (MoveMoveResponse) {
+ option (google.api.http) = {
+ get: "/bar_bazs/move_move"
+ };
+ }
+}
+
+message BarBaz {
+ string bar_baz_id = 1;
+}
+
+message ListBarBazsRequest {
+}
+
+message ListBarBazsResponse {
+ repeated BarBaz bar_bazs = 1;
+}
+
+message CreateBarBazRequest {
+ BarBaz bar_baz = 1;
+}
+
+message DeleteBarBazRequest {
+ string bar_baz_id = 1;
+}
+
+message RenameRequest {
+}
+
+message RenameResponse {
+}
+
+message MoveMoveRequest {
+}
+
+message MoveMoveResponse {
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server.go
new file mode 100644
index 00000000..0fb60647
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server.go
@@ -0,0 +1,52 @@
+package foo
+
+import (
+ "context"
+
+ "github.com/golang/protobuf/ptypes/empty"
+ "github.com/izumin5210/grapi/pkg/grapiserver"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ foo_pb "testapp/api/foo"
+)
+
+// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server.
+type BarBazServiceServer interface {
+ foo_pb.BarBazServiceServer
+ grapiserver.Server
+}
+
+// NewBarBazServiceServer creates a new BarBazServiceServer instance.
+func NewBarBazServiceServer() BarBazServiceServer {
+ return &barBazServiceServerImpl{}
+}
+
+type barBazServiceServerImpl struct {
+}
+
+func (s *barBazServiceServerImpl) ListBarBazs(ctx context.Context, req *foo_pb.ListBarBazsRequest) (*foo_pb.ListBarBazsResponse, error) {
+ // TODO: Not yet implemented.
+ return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!")
+}
+
+func (s *barBazServiceServerImpl) CreateBarBaz(ctx context.Context, req *foo_pb.CreateBarBazRequest) (*foo_pb.BarBaz, error) {
+ // TODO: Not yet implemented.
+ return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!")
+}
+
+func (s *barBazServiceServerImpl) DeleteBarBaz(ctx context.Context, req *foo_pb.DeleteBarBazRequest) (*empty.Empty, error) {
+ // TODO: Not yet implemented.
+ return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!")
+}
+
+func (s *barBazServiceServerImpl) Rename(ctx context.Context, req *foo_pb.RenameRequest) (*foo_pb.RenameResponse, error) {
+ // TODO: Not yet implemented.
+ return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!")
+}
+
+func (s *barBazServiceServerImpl) MoveMove(ctx context.Context, req *foo_pb.MoveMoveRequest) (*foo_pb.MoveMoveResponse, error) {
+ // TODO: Not yet implemented.
+ return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!")
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go
new file mode 100644
index 00000000..1c820532
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go
@@ -0,0 +1,23 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
+package foo
+
+import (
+ "context"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "google.golang.org/grpc"
+
+ foo_pb "testapp/api/foo"
+)
+
+// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
+func (s *barBazServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) {
+ foo_pb.RegisterBarBazServiceServer(grpcSvr, s)
+}
+
+// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.
+func (s *barBazServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return foo_pb.RegisterBarBazServiceHandler(ctx, mux, conn)
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_test.go
new file mode 100644
index 00000000..f1edda88
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_test.go
@@ -0,0 +1,104 @@
+package foo
+
+import (
+ "context"
+ "testing"
+
+ foo_pb "testapp/api/foo"
+)
+
+func Test_BarBazServiceServer_ListBarBazs(t *testing.T) {
+ svr := NewBarBazServiceServer()
+
+ ctx := context.Background()
+ req := &foo_pb.ListBarBazsRequest{}
+
+ resp, err := svr.ListBarBazs(ctx, req)
+
+ t.SkipNow()
+
+ if err != nil {
+ t.Errorf("returned an error %v", err)
+ }
+
+ if resp == nil {
+ t.Error("response should not nil")
+ }
+}
+
+func Test_BarBazServiceServer_CreateBarBaz(t *testing.T) {
+ svr := NewBarBazServiceServer()
+
+ ctx := context.Background()
+ req := &foo_pb.CreateBarBazRequest{}
+
+ resp, err := svr.CreateBarBaz(ctx, req)
+
+ t.SkipNow()
+
+ if err != nil {
+ t.Errorf("returned an error %v", err)
+ }
+
+ if resp == nil {
+ t.Error("response should not nil")
+ }
+}
+
+func Test_BarBazServiceServer_DeleteBarBaz(t *testing.T) {
+ svr := NewBarBazServiceServer()
+
+ ctx := context.Background()
+ req := &foo_pb.DeleteBarBazRequest{}
+
+ resp, err := svr.DeleteBarBaz(ctx, req)
+
+ t.SkipNow()
+
+ if err != nil {
+ t.Errorf("returned an error %v", err)
+ }
+
+ if resp == nil {
+ t.Error("response should not nil")
+ }
+}
+
+func Test_BarBazServiceServer_Rename(t *testing.T) {
+ svr := NewBarBazServiceServer()
+
+ ctx := context.Background()
+ req := &foo_pb.RenameRequest{}
+
+ resp, err := svr.Rename(ctx, req)
+
+ t.SkipNow()
+
+ if err != nil {
+ t.Errorf("returned an error %v", err)
+ }
+
+ if resp == nil {
+ t.Error("response should not nil")
+ }
+}
+
+func Test_BarBazServiceServer_MoveMove(t *testing.T) {
+ svr := NewBarBazServiceServer()
+
+ ctx := context.Background()
+ req := &foo_pb.MoveMoveRequest{}
+
+ resp, err := svr.MoveMove(ctx, req)
+
+ t.SkipNow()
+
+ if err != nil {
+ t.Errorf("returned an error %v", err)
+ }
+
+ if resp == nil {
+ t.Error("response should not nil")
+ }
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-api-protos-foo-bar_baz.proto
new file mode 100644
index 00000000..44b449c3
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-api-protos-foo-bar_baz.proto
@@ -0,0 +1,48 @@
+syntax = "proto3";
+
+package testapp.api.foo;
+
+option go_package = "testapp/api/foo;foo_pb";
+
+
+import "google/api/annotations.proto";
+import "google/protobuf/empty.proto";
+
+service BarBazService {
+ rpc ListBarBazs (ListBarBazsRequest) returns (ListBarBazsResponse) {
+ option (google.api.http) = {
+ get: "/bar_bazs"
+ };
+ }
+ rpc CreateBarBaz (CreateBarBazRequest) returns (BarBaz) {
+ option (google.api.http) = {
+ post: "/bar_bazs"
+ body: "bar_baz"
+ };
+ }
+ rpc DeleteBarBaz (DeleteBarBazRequest) returns (google.protobuf.Empty) {
+ option (google.api.http) = {
+ delete: "/bar_bazs/{bar_baz_id}"
+ };
+ }
+}
+
+message BarBaz {
+ string bar_baz_id = 1;
+}
+
+message ListBarBazsRequest {
+}
+
+message ListBarBazsResponse {
+ repeated BarBaz bar_bazs = 1;
+}
+
+message CreateBarBazRequest {
+ BarBaz bar_baz = 1;
+}
+
+message DeleteBarBazRequest {
+ string bar_baz_id = 1;
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server.go
new file mode 100644
index 00000000..fc85ca3d
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server.go
@@ -0,0 +1,42 @@
+package foo
+
+import (
+ "context"
+
+ "github.com/golang/protobuf/ptypes/empty"
+ "github.com/izumin5210/grapi/pkg/grapiserver"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ foo_pb "testapp/api/foo"
+)
+
+// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server.
+type BarBazServiceServer interface {
+ foo_pb.BarBazServiceServer
+ grapiserver.Server
+}
+
+// NewBarBazServiceServer creates a new BarBazServiceServer instance.
+func NewBarBazServiceServer() BarBazServiceServer {
+ return &barBazServiceServerImpl{}
+}
+
+type barBazServiceServerImpl struct {
+}
+
+func (s *barBazServiceServerImpl) ListBarBazs(ctx context.Context, req *foo_pb.ListBarBazsRequest) (*foo_pb.ListBarBazsResponse, error) {
+ // TODO: Not yet implemented.
+ return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!")
+}
+
+func (s *barBazServiceServerImpl) CreateBarBaz(ctx context.Context, req *foo_pb.CreateBarBazRequest) (*foo_pb.BarBaz, error) {
+ // TODO: Not yet implemented.
+ return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!")
+}
+
+func (s *barBazServiceServerImpl) DeleteBarBaz(ctx context.Context, req *foo_pb.DeleteBarBazRequest) (*empty.Empty, error) {
+ // TODO: Not yet implemented.
+ return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!")
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go
new file mode 100644
index 00000000..1c820532
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go
@@ -0,0 +1,23 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
+package foo
+
+import (
+ "context"
+
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "google.golang.org/grpc"
+
+ foo_pb "testapp/api/foo"
+)
+
+// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
+func (s *barBazServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) {
+ foo_pb.RegisterBarBazServiceServer(grpcSvr, s)
+}
+
+// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.
+func (s *barBazServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return foo_pb.RegisterBarBazServiceHandler(ctx, mux, conn)
+}
+
diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_test.go
new file mode 100644
index 00000000..f2414a33
--- /dev/null
+++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_test.go
@@ -0,0 +1,66 @@
+package foo
+
+import (
+ "context"
+ "testing"
+
+ foo_pb "testapp/api/foo"
+)
+
+func Test_BarBazServiceServer_ListBarBazs(t *testing.T) {
+ svr := NewBarBazServiceServer()
+
+ ctx := context.Background()
+ req := &foo_pb.ListBarBazsRequest{}
+
+ resp, err := svr.ListBarBazs(ctx, req)
+
+ t.SkipNow()
+
+ if err != nil {
+ t.Errorf("returned an error %v", err)
+ }
+
+ if resp == nil {
+ t.Error("response should not nil")
+ }
+}
+
+func Test_BarBazServiceServer_CreateBarBaz(t *testing.T) {
+ svr := NewBarBazServiceServer()
+
+ ctx := context.Background()
+ req := &foo_pb.CreateBarBazRequest{}
+
+ resp, err := svr.CreateBarBaz(ctx, req)
+
+ t.SkipNow()
+
+ if err != nil {
+ t.Errorf("returned an error %v", err)
+ }
+
+ if resp == nil {
+ t.Error("response should not nil")
+ }
+}
+
+func Test_BarBazServiceServer_DeleteBarBaz(t *testing.T) {
+ svr := NewBarBazServiceServer()
+
+ ctx := context.Background()
+ req := &foo_pb.DeleteBarBazRequest{}
+
+ resp, err := svr.DeleteBarBaz(ctx, req)
+
+ t.SkipNow()
+
+ if err != nil {
+ t.Errorf("returned an error %v", err)
+ }
+
+ if resp == nil {
+ t.Error("response should not nil")
+ }
+}
+
diff --git a/cmd/grapi-gen-service/main.go b/cmd/grapi-gen-service/main.go
new file mode 100644
index 00000000..704d2299
--- /dev/null
+++ b/cmd/grapi-gen-service/main.go
@@ -0,0 +1,83 @@
+package main
+
+import (
+ "context"
+
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ "github.com/izumin5210/grapi/pkg/svcgen"
+)
+
+func main() {
+ buildCommand(svcgen.NewApp).MustExecute()
+}
+
+func buildCommand(createAppFunc svcgen.CreateAppFunc, opts ...gencmd.Option) gencmd.Executor {
+ return gencmd.New(
+ "service",
+ newGenerateCommand(createAppFunc),
+ newDestroyCommand(createAppFunc),
+ opts...,
+ )
+}
+
+func newGenerateCommand(createApp svcgen.CreateAppFunc) *gencmd.Command {
+ var (
+ skipTest bool
+ resName string
+ app *svcgen.App
+ )
+
+ cmd := &gencmd.Command{
+ Use: "generate NAME [flags] [METHODS...]",
+ Short: "Generate a new service",
+ Args: cobra.MinimumNArgs(1),
+ ShouldInsideApp: true,
+ PreRun: func(c *gencmd.Command, args []string) error {
+ var err error
+ app, err = createApp(c)
+ return errors.WithStack(err)
+ },
+ BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) {
+ svcName := args[0]
+ methods := args[1:]
+
+ params, err := app.ParamsBuilder.Build(svcName, resName, methods)
+ return params, errors.WithStack(err)
+ },
+ PostRun: func(c *gencmd.Command, args []string) error {
+ return errors.WithStack(app.ProtocWrapper.Exec(context.TODO()))
+ },
+ }
+
+ cmd.Flags().BoolVarP(&skipTest, "skip-test", "T", false, "Skip test files")
+ cmd.Flags().StringVar(&resName, "resource-name", "", "ResourceName to be used")
+
+ return cmd
+}
+
+func newDestroyCommand(createApp svcgen.CreateAppFunc) *gencmd.Command {
+ var (
+ app *svcgen.App
+ )
+
+ cmd := &gencmd.Command{
+ Use: "destroy NAME",
+ Short: "Destroy an existing service",
+ Args: cobra.MinimumNArgs(1),
+ ShouldInsideApp: true,
+ PreRun: func(c *gencmd.Command, args []string) error {
+ var err error
+ app, err = createApp(c)
+ return errors.WithStack(err)
+ },
+ BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) {
+ params, err := app.ParamsBuilder.Build(args[0], "", nil)
+ return params, errors.WithStack(err)
+ },
+ }
+
+ return cmd
+}
diff --git a/cmd/grapi-gen-service/main_test.go b/cmd/grapi-gen-service/main_test.go
new file mode 100644
index 00000000..666e92c3
--- /dev/null
+++ b/cmd/grapi-gen-service/main_test.go
@@ -0,0 +1,209 @@
+package main
+
+import (
+ "context"
+ "testing"
+
+ "github.com/spf13/afero"
+
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ gencmdtesting "github.com/izumin5210/grapi/pkg/gencmd/testing"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/izumin5210/grapi/pkg/protoc"
+ "github.com/izumin5210/grapi/pkg/svcgen"
+ svcgentesting "github.com/izumin5210/grapi/pkg/svcgen/testing"
+)
+
+func TestRun(t *testing.T) {
+ cases := []svcgentesting.Case{
+ {
+ Test: "simple",
+ GArgs: []string{"foo"},
+ DArgs: []string{"foo"},
+ Files: []string{
+ "api/protos/foo.proto",
+ "app/server/foo_server.go",
+ "app/server/foo_server_register_funcs.go",
+ "app/server/foo_server_test.go",
+ },
+ },
+ {
+ Test: "specify package",
+ GArgs: []string{"foo"},
+ DArgs: []string{"foo"},
+ Files: []string{
+ "api/protos/foo.proto",
+ "app/server/foo_server.go",
+ "app/server/foo_server_register_funcs.go",
+ "app/server/foo_server_test.go",
+ },
+ PkgName: "testcompany.testapp",
+ },
+ {
+ Test: "nested",
+ GArgs: []string{"foo/bar"},
+ DArgs: []string{"foo/bar"},
+ Files: []string{
+ "api/protos/foo/bar.proto",
+ "app/server/foo/bar_server.go",
+ "app/server/foo/bar_server_register_funcs.go",
+ "app/server/foo/bar_server_test.go",
+ },
+ },
+ {
+ Test: "nested with specify pacakge",
+ GArgs: []string{"foo/bar"},
+ DArgs: []string{"foo/bar"},
+ Files: []string{
+ "api/protos/foo/bar.proto",
+ "app/server/foo/bar_server.go",
+ "app/server/foo/bar_server_register_funcs.go",
+ "app/server/foo/bar_server_test.go",
+ },
+ PkgName: "testcompany.testapp",
+ },
+ {
+ Test: "snake_case name",
+ GArgs: []string{"foo/bar_baz"},
+ DArgs: []string{"foo/bar_baz"},
+ Files: []string{
+ "api/protos/foo/bar_baz.proto",
+ "app/server/foo/bar_baz_server.go",
+ "app/server/foo/bar_baz_server_register_funcs.go",
+ "app/server/foo/bar_baz_server_test.go",
+ },
+ },
+ {
+ Test: "kebab-case name",
+ GArgs: []string{"foo/bar-baz"},
+ DArgs: []string{"foo/bar-baz"},
+ Files: []string{
+ "api/protos/foo/bar_baz.proto",
+ "app/server/foo/bar_baz_server.go",
+ "app/server/foo/bar_baz_server_register_funcs.go",
+ "app/server/foo/bar_baz_server_test.go",
+ },
+ },
+ {
+ Test: "with some standard methods",
+ GArgs: []string{"foo/bar-baz", "list", "create", "delete"},
+ DArgs: []string{"foo/bar-baz"},
+ Files: []string{
+ "api/protos/foo/bar_baz.proto",
+ "app/server/foo/bar_baz_server.go",
+ "app/server/foo/bar_baz_server_register_funcs.go",
+ "app/server/foo/bar_baz_server_test.go",
+ },
+ },
+ {
+ Test: "with non-standard methods",
+ GArgs: []string{"foo/bar-baz", "list", "create", "rename", "delete", "move_move"},
+ DArgs: []string{"foo/bar-baz"},
+ Files: []string{
+ "api/protos/foo/bar_baz.proto",
+ "app/server/foo/bar_baz_server.go",
+ "app/server/foo/bar_baz_server_register_funcs.go",
+ "app/server/foo/bar_baz_server_test.go",
+ },
+ },
+ {
+ Test: "specify proto dir",
+ GArgs: []string{"qux"},
+ DArgs: []string{"qux"},
+ Files: []string{
+ "pkg/foo/protos/qux.proto",
+ "app/server/qux_server.go",
+ "app/server/qux_server_register_funcs.go",
+ "app/server/qux_server_test.go",
+ },
+ ProtoDir: "pkg/foo/protos",
+ },
+ {
+ Test: "specify proto out dir",
+ GArgs: []string{"quux"},
+ DArgs: []string{"quux"},
+ Files: []string{
+ "api/protos/quux.proto",
+ "app/server/quux_server.go",
+ "app/server/quux_server_register_funcs.go",
+ "app/server/quux_server_test.go",
+ },
+ ProtoOutDir: "api/out",
+ },
+ {
+ Test: "specify server dir",
+ GArgs: []string{"corge"},
+ DArgs: []string{"corge"},
+ Files: []string{
+ "api/protos/corge.proto",
+ "pkg/foo/server/corge_server.go",
+ "pkg/foo/server/corge_server_register_funcs.go",
+ "pkg/foo/server/corge_server_test.go",
+ },
+ ServerDir: "pkg/foo/server",
+ },
+ {
+ Test: "skip tests",
+ GArgs: []string{"--skip-test", "book"},
+ DArgs: []string{"book"},
+ Files: []string{
+ "api/protos/book.proto",
+ "app/server/book_server.go",
+ "app/server/book_server_register_funcs.go",
+ },
+ SkippedFiles: map[string]struct{}{
+ "app/server/book_server_test.go": {},
+ },
+ },
+ {
+ Test: "specify resource name",
+ GArgs: []string{"library", "--resource-name=book"},
+ DArgs: []string{"library"},
+ Files: []string{
+ "api/protos/library.proto",
+ "app/server/library_server.go",
+ "app/server/library_server_register_funcs.go",
+ "app/server/library_server_test.go",
+ },
+ },
+ }
+
+ rootDir := cli.RootDir{clib.Path("/home/src/testapp")}
+
+ createSvcApp := func(cmd *gencmd.Command) (*svcgen.App, error) {
+ return svcgentesting.NewTestApp(cmd, &fakeProtocWrapper{}, cli.NopUI)
+ }
+ createGenApp := func(cmd *gencmd.Command) (*gencmd.App, error) {
+ return gencmdtesting.NewTestApp(cmd, cli.NopUI)
+ }
+ createCmd := func(t *testing.T, fs afero.Fs, tc svcgentesting.Case) gencmd.Executor {
+ ctx := &grapicmd.Ctx{
+ FS: fs,
+ RootDir: rootDir,
+ Config: grapicmd.Config{
+ Package: tc.PkgName,
+ },
+ ProtocConfig: protoc.Config{
+ ProtosDir: tc.ProtoDir,
+ OutDir: tc.ProtoOutDir,
+ },
+ }
+ ctx.Config.Grapi.ServerDir = tc.ServerDir
+ return buildCommand(createSvcApp, gencmd.WithGrapiCtx(ctx), gencmd.WithCreateAppFunc(createGenApp))
+ }
+
+ ctx := &svcgentesting.Ctx{
+ GOPATH: "/home",
+ RootDir: rootDir,
+ CreateCmd: createCmd,
+ Cases: cases,
+ }
+
+ svcgentesting.Run(t, ctx)
+}
+
+type fakeProtocWrapper struct{}
+
+func (*fakeProtocWrapper) Exec(context.Context) error { return nil }
diff --git a/cmd/grapi-gen-type/.snapshots/TestType-camel-generate-api-protos-type-foo-bar_user.proto b/cmd/grapi-gen-type/.snapshots/TestType-camel-generate-api-protos-type-foo-bar_user.proto
new file mode 100644
index 00000000..9590f46e
--- /dev/null
+++ b/cmd/grapi-gen-type/.snapshots/TestType-camel-generate-api-protos-type-foo-bar_user.proto
@@ -0,0 +1,10 @@
+syntax = "proto3";
+
+package testapp.api.type.foo;
+
+option go_package = "testapp/api/type/foo;foo_pb";
+
+message BarUser {
+ string id = 1;
+}
+
diff --git a/cmd/grapi-gen-type/.snapshots/TestType-kebab-generate-api-protos-type-foo-bar_user.proto b/cmd/grapi-gen-type/.snapshots/TestType-kebab-generate-api-protos-type-foo-bar_user.proto
new file mode 100644
index 00000000..9590f46e
--- /dev/null
+++ b/cmd/grapi-gen-type/.snapshots/TestType-kebab-generate-api-protos-type-foo-bar_user.proto
@@ -0,0 +1,10 @@
+syntax = "proto3";
+
+package testapp.api.type.foo;
+
+option go_package = "testapp/api/type/foo;foo_pb";
+
+message BarUser {
+ string id = 1;
+}
+
diff --git a/cmd/grapi-gen-type/.snapshots/TestType-nested-generate-api-protos-type-foo-user.proto b/cmd/grapi-gen-type/.snapshots/TestType-nested-generate-api-protos-type-foo-user.proto
new file mode 100644
index 00000000..260291f9
--- /dev/null
+++ b/cmd/grapi-gen-type/.snapshots/TestType-nested-generate-api-protos-type-foo-user.proto
@@ -0,0 +1,10 @@
+syntax = "proto3";
+
+package testapp.api.type.foo;
+
+option go_package = "testapp/api/type/foo;foo_pb";
+
+message User {
+ string id = 1;
+}
+
diff --git a/cmd/grapi-gen-type/.snapshots/TestType-nested_with_specified_package-generate-api-protos-type-foo-user.proto b/cmd/grapi-gen-type/.snapshots/TestType-nested_with_specified_package-generate-api-protos-type-foo-user.proto
new file mode 100644
index 00000000..260291f9
--- /dev/null
+++ b/cmd/grapi-gen-type/.snapshots/TestType-nested_with_specified_package-generate-api-protos-type-foo-user.proto
@@ -0,0 +1,10 @@
+syntax = "proto3";
+
+package testapp.api.type.foo;
+
+option go_package = "testapp/api/type/foo;foo_pb";
+
+message User {
+ string id = 1;
+}
+
diff --git a/cmd/grapi-gen-type/.snapshots/TestType-simple-generate-api-protos-type-book.proto b/cmd/grapi-gen-type/.snapshots/TestType-simple-generate-api-protos-type-book.proto
new file mode 100644
index 00000000..ec954fee
--- /dev/null
+++ b/cmd/grapi-gen-type/.snapshots/TestType-simple-generate-api-protos-type-book.proto
@@ -0,0 +1,10 @@
+syntax = "proto3";
+
+package testapp.api.type;
+
+option go_package = "testapp/api/type;type_pb";
+
+message Book {
+ string id = 1;
+}
+
diff --git a/cmd/grapi-gen-type/.snapshots/TestType-simple_with_specified_package-generate-api-protos-type-book.proto b/cmd/grapi-gen-type/.snapshots/TestType-simple_with_specified_package-generate-api-protos-type-book.proto
new file mode 100644
index 00000000..ec954fee
--- /dev/null
+++ b/cmd/grapi-gen-type/.snapshots/TestType-simple_with_specified_package-generate-api-protos-type-book.proto
@@ -0,0 +1,10 @@
+syntax = "proto3";
+
+package testapp.api.type;
+
+option go_package = "testapp/api/type;type_pb";
+
+message Book {
+ string id = 1;
+}
+
diff --git a/cmd/grapi-gen-type/.snapshots/TestType-snake-generate-api-protos-type-foo-bar_user.proto b/cmd/grapi-gen-type/.snapshots/TestType-snake-generate-api-protos-type-foo-bar_user.proto
new file mode 100644
index 00000000..9590f46e
--- /dev/null
+++ b/cmd/grapi-gen-type/.snapshots/TestType-snake-generate-api-protos-type-foo-bar_user.proto
@@ -0,0 +1,10 @@
+syntax = "proto3";
+
+package testapp.api.type.foo;
+
+option go_package = "testapp/api/type/foo;foo_pb";
+
+message BarUser {
+ string id = 1;
+}
+
diff --git a/cmd/grapi-gen-type/di/app.go b/cmd/grapi-gen-type/di/app.go
new file mode 100644
index 00000000..f61c910d
--- /dev/null
+++ b/cmd/grapi-gen-type/di/app.go
@@ -0,0 +1,12 @@
+package di
+
+import (
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ "github.com/izumin5210/grapi/pkg/protoc"
+)
+
+type CreateAppFunc func(*gencmd.Command) (*App, error)
+
+type App struct {
+ Protoc protoc.Wrapper
+}
diff --git a/cmd/grapi-gen-type/di/wire.go b/cmd/grapi-gen-type/di/wire.go
new file mode 100644
index 00000000..038985b6
--- /dev/null
+++ b/cmd/grapi-gen-type/di/wire.go
@@ -0,0 +1,21 @@
+//+build wireinject
+
+package di
+
+import (
+ "github.com/google/wire"
+
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ "github.com/izumin5210/grapi/pkg/protoc"
+)
+
+func NewApp(*gencmd.Command) (*App, error) {
+ wire.Build(
+ App{},
+ gencmd.Set,
+ cli.UIInstance,
+ protoc.WrapperSet,
+ )
+ return nil, nil
+}
diff --git a/cmd/grapi-gen-type/di/wire_gen.go b/cmd/grapi-gen-type/di/wire_gen.go
new file mode 100644
index 00000000..d0f33a49
--- /dev/null
+++ b/cmd/grapi-gen-type/di/wire_gen.go
@@ -0,0 +1,36 @@
+// Code generated by Wire. DO NOT EDIT.
+
+//go:generate wire
+//+build !wireinject
+
+package di
+
+import (
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/izumin5210/grapi/pkg/protoc"
+)
+
+// Injectors from wire.go:
+
+func NewApp(command *gencmd.Command) (*App, error) {
+ ctx := gencmd.ProvideCtx(command)
+ grapicmdCtx := gencmd.ProvideGrapiCtx(ctx)
+ config := grapicmd.ProvideProtocConfig(grapicmdCtx)
+ fs := grapicmd.ProvideFS(grapicmdCtx)
+ executor := grapicmd.ProvideExec(grapicmdCtx)
+ io := grapicmd.ProvideIO(grapicmdCtx)
+ ui := cli.UIInstance(io)
+ rootDir := grapicmd.ProvideRootDir(grapicmdCtx)
+ gexConfig := protoc.ProvideGexConfig(fs, executor, io, rootDir)
+ repository, err := protoc.ProvideToolRepository(gexConfig)
+ if err != nil {
+ return nil, err
+ }
+ wrapper := protoc.NewWrapper(config, fs, executor, ui, repository, rootDir)
+ app := &App{
+ Protoc: wrapper,
+ }
+ return app, nil
+}
diff --git a/cmd/grapi-gen-type/main.go b/cmd/grapi-gen-type/main.go
new file mode 100644
index 00000000..a0c4ab64
--- /dev/null
+++ b/cmd/grapi-gen-type/main.go
@@ -0,0 +1,101 @@
+package main
+
+import (
+ "context"
+ "path/filepath"
+
+ "github.com/pkg/errors"
+ "github.com/serenize/snaker"
+ "github.com/spf13/cobra"
+
+ "github.com/izumin5210/grapi/cmd/grapi-gen-type/di"
+ _ "github.com/izumin5210/grapi/cmd/grapi-gen-type/template"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ gencmdutil "github.com/izumin5210/grapi/pkg/gencmd/util"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+)
+
+func main() {
+ buildCommand(di.NewApp).MustExecute()
+}
+
+func buildCommand(createApp di.CreateAppFunc, opts ...gencmd.Option) gencmd.Executor {
+ return gencmd.New(
+ "type",
+ newGenerateCommand(createApp),
+ newDestroyCommand(),
+ opts...,
+ )
+}
+
+func newGenerateCommand(createApp di.CreateAppFunc) *gencmd.Command {
+ var (
+ app *di.App
+ )
+
+ return &gencmd.Command{
+ Use: "generate NAME",
+ Short: "Generate a new type",
+ Args: cobra.ExactArgs(1),
+ ShouldInsideApp: true,
+ PreRun: func(c *gencmd.Command, args []string) error {
+ var err error
+ app, err = createApp(c)
+ return errors.WithStack(err)
+ },
+ BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) {
+ return buildParams(args[0], c.Ctx().Ctx)
+ },
+ PostRun: func(c *gencmd.Command, args []string) error {
+ return errors.WithStack(app.Protoc.Exec(context.TODO()))
+ },
+ }
+}
+
+func newDestroyCommand() *gencmd.Command {
+ return &gencmd.Command{
+ Use: "destroy NAME",
+ Short: "Destroy a existing type",
+ Args: cobra.ExactArgs(1),
+ ShouldInsideApp: true,
+ BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) {
+ return buildParams(args[0], c.Ctx().Ctx)
+ },
+ }
+}
+
+type params struct {
+ Proto struct {
+ Package string
+ }
+ PbGo struct {
+ PackagePath string
+ PackageName string
+ }
+ Name string
+ Path string
+}
+
+func buildParams(path string, ctx *grapicmd.Ctx) (*params, error) {
+ protoOutDir := ctx.ProtocConfig.OutDir
+ if protoOutDir == "" {
+ protoOutDir = filepath.Join("api")
+ }
+ path = filepath.Join("type", path)
+
+ protoParams, err := gencmdutil.BuildProtoParams(path, ctx.RootDir, protoOutDir, ctx.Config.Package)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ // path => baz/qux/quux
+ path = protoParams.Proto.Path
+
+ params := new(params)
+ params.Proto.Package = protoParams.Proto.Package
+ params.PbGo.PackageName = protoParams.PbGo.ImportName
+ params.PbGo.PackagePath = protoParams.PbGo.Package
+ params.Name = snaker.SnakeToCamel(filepath.Base(path))
+ params.Path = path
+ return params, nil
+}
diff --git a/cmd/grapi-gen-type/main_test.go b/cmd/grapi-gen-type/main_test.go
new file mode 100644
index 00000000..3de1a416
--- /dev/null
+++ b/cmd/grapi-gen-type/main_test.go
@@ -0,0 +1,140 @@
+package main
+
+import (
+ "context"
+ "go/build"
+ "testing"
+
+ "github.com/bradleyjkemp/cupaloy/v2"
+ "github.com/spf13/afero"
+
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/grapi/cmd/grapi-gen-type/di"
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ gencmdtesting "github.com/izumin5210/grapi/pkg/gencmd/testing"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
+)
+
+func TestType(t *testing.T) {
+ cases := []struct {
+ test string
+ args []string
+ files []string
+ config string
+ }{
+ {
+ test: "simple",
+ args: []string{"book"},
+ files: []string{"api/protos/type/book.proto"},
+ },
+ {
+ test: "nested",
+ args: []string{"foo/user"},
+ files: []string{"api/protos/type/foo/user.proto"},
+ },
+ {
+ test: "camel",
+ args: []string{"foo/barUser"},
+ files: []string{"api/protos/type/foo/bar_user.proto"},
+ },
+ {
+ test: "snake",
+ args: []string{"foo/bar_user"},
+ files: []string{"api/protos/type/foo/bar_user.proto"},
+ },
+ {
+ test: "kebab",
+ args: []string{"foo/bar-user"},
+ files: []string{"api/protos/type/foo/bar_user.proto"},
+ },
+ {
+ test: "simple with specified package",
+ args: []string{"book"},
+ files: []string{"api/protos/type/book.proto"},
+ config: `package = "yourcompany.yourapp"`,
+ },
+ {
+ test: "nested with specified package",
+ args: []string{"foo/user"},
+ files: []string{"api/protos/type/foo/user.proto"},
+ config: `package = "yourcompany.yourapp"`,
+ },
+ }
+
+ defer func(c build.Context) { fs.BuildContext = c }(fs.BuildContext)
+ fs.BuildContext = build.Context{GOPATH: "/go"}
+ rootDir := cli.RootDir{clib.Path("/go/src/testapp")}
+
+ createApp := func(cmd *gencmd.Command) (*di.App, error) {
+ return &di.App{Protoc: &fakeProtocWrapper{}}, nil
+ }
+ createGenApp := func(cmd *gencmd.Command) (*gencmd.App, error) {
+ return gencmdtesting.NewTestApp(cmd, cli.NopUI)
+ }
+ createCmd := func(t *testing.T, fs afero.Fs) gencmd.Executor {
+ ctx := &grapicmd.Ctx{
+ FS: fs,
+ RootDir: rootDir,
+ }
+ return buildCommand(createApp, gencmd.WithGrapiCtx(ctx), gencmd.WithCreateAppFunc(createGenApp))
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.test, func(t *testing.T) {
+ fs := afero.NewMemMapFs()
+ afero.WriteFile(fs, rootDir.Join("grapi.toml").String(), []byte{}, 0755)
+
+ t.Run("generate", func(t *testing.T) {
+ cmd := createCmd(t, fs)
+ cmd.Command().SetArgs(append([]string{"generate"}, tc.args...))
+ err := cmd.Execute()
+
+ if err != nil {
+ t.Errorf("returned an error: %+v", err)
+ }
+
+ for _, file := range tc.files {
+ t.Run(file, func(t *testing.T) {
+ data, err := afero.ReadFile(fs, rootDir.Join(file).String())
+
+ if err != nil {
+ t.Errorf("returned an error: %v", err)
+ }
+
+ cupaloy.SnapshotT(t, string(data))
+ })
+ }
+ })
+
+ t.Run("destroy", func(t *testing.T) {
+ cmd := createCmd(t, fs)
+ cmd.Command().SetArgs(append([]string{"destroy"}, tc.args...))
+ err := cmd.Execute()
+
+ if err != nil {
+ t.Errorf("returned an error: %+v", err)
+ }
+
+ for _, file := range tc.files {
+ t.Run(file, func(t *testing.T) {
+ ok, err := afero.Exists(fs, rootDir.Join(file).String())
+
+ if err != nil {
+ t.Errorf("Exists(fs, %q) returned an error: %v", file, err)
+ }
+
+ if ok {
+ t.Errorf("%q should not exist", file)
+ }
+ })
+ }
+ })
+ })
+ }
+}
+
+type fakeProtocWrapper struct{}
+
+func (*fakeProtocWrapper) Exec(context.Context) error { return nil }
diff --git a/cmd/grapi-gen-type/template/_data/api/protos/{{.Path}}.proto.tmpl b/cmd/grapi-gen-type/template/_data/api/protos/{{.Path}}.proto.tmpl
new file mode 100644
index 00000000..a9bb09bd
--- /dev/null
+++ b/cmd/grapi-gen-type/template/_data/api/protos/{{.Path}}.proto.tmpl
@@ -0,0 +1,9 @@
+syntax = "proto3";
+
+package {{ .Proto.Package }};
+
+option go_package = "{{ .PbGo.PackagePath }};{{ .PbGo.PackageName }}";
+
+message {{.Name}} {
+ string id = 1;
+}
diff --git a/cmd/grapi-gen-type/template/gen.go b/cmd/grapi-gen-type/template/gen.go
new file mode 100644
index 00000000..ba18632b
--- /dev/null
+++ b/cmd/grapi-gen-type/template/gen.go
@@ -0,0 +1,3 @@
+//go:generate statik -src ./_data -dest .. -p template -f -m
+
+package template
diff --git a/cmd/grapi-gen-type/template/statik.go b/cmd/grapi-gen-type/template/statik.go
new file mode 100644
index 00000000..02b3003a
--- /dev/null
+++ b/cmd/grapi-gen-type/template/statik.go
@@ -0,0 +1,13 @@
+// Code generated by statik. DO NOT EDIT.
+
+// Package statik contains static assets.
+package template
+
+import (
+ "github.com/rakyll/statik/fs"
+)
+
+func init() {
+ data := "PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00 \x00api/protos/{{.Path}}.proto.tmplUT\x05\x00\x01\x80Cm8\\\xcd=\xaaB1\x10\xc5\xf1~Vq\xc8\x02\x02\x8fW\x86\xd4v\x92\x1dH\xd4K\x0cr3\xe1f\n%\xcc\xdee\xfc*l\xff\xfc8g\xdc\x9b\xe4\x1b\"\\\xdfX\xf8\xdf\x05\xa2\x9eO\xd7\\\x16\xcc \x9f\xac\xfa\xf4.\xaa\x81\x88\xbbTn(|\xf8\xc0\x08\xf7\xb4\xc7\xdd\x97\xa6,\x17\xe3\xbf}\x9fW\x9b\xb1\x9bu\x19\xe3u\xe3\xad\xaab\x120d\xab\xad\xa0\x9e\x11\xf1\x17H\xe9\x11\x00\x00\xff\xffPK\x07\x08O\x08A4v\x00\x00\x00\xa2\x00\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(O\x08A4v\x00\x00\x00\xa2\x00\x00\x00\x1f\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00api/protos/{{.Path}}.proto.tmplUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00V\x00\x00\x00\xcc\x00\x00\x00\x00\x00"
+ fs.Register(data)
+}
diff --git a/cmd/grapi/consts.go b/cmd/grapi/consts.go
new file mode 100644
index 00000000..fceaecf8
--- /dev/null
+++ b/cmd/grapi/consts.go
@@ -0,0 +1,12 @@
+package main
+
+const (
+ name = "grapi"
+ version = "v0.5.0"
+)
+
+var (
+ // set via ldflags
+ revision string
+ buildDate string
+)
diff --git a/cmd/grapi/main.go b/cmd/grapi/main.go
index 5bd9c466..6315dbdd 100644
--- a/cmd/grapi/main.go
+++ b/cmd/grapi/main.go
@@ -2,55 +2,32 @@ package main
import (
"fmt"
- "io"
"os"
- "runtime"
+ "github.com/izumin5210/clig/pkg/clib"
+
+ "github.com/izumin5210/grapi/pkg/cli"
"github.com/izumin5210/grapi/pkg/grapicmd"
"github.com/izumin5210/grapi/pkg/grapicmd/cmd"
- "github.com/mattn/go-colorable"
-)
-
-var (
- name string
- version string
- revision string
- buildDate string
-
- releaseType string
-
- inReader = os.Stdin
- outWriter io.Writer = os.Stdout
- errWriter io.Writer = os.Stderr
)
-func init() {
- if runtime.GOOS == "windows" {
- outWriter = colorable.NewColorableStdout()
- errWriter = colorable.NewColorableStderr()
- }
- if name == "" {
- name = "grapi"
- }
-}
-
func main() {
cwd, err := os.Getwd()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
- err = cmd.NewGrapiCommand(grapicmd.NewConfig(
- cwd,
- name,
- version,
- revision,
- buildDate,
- releaseType,
- inReader,
- outWriter,
- errWriter,
- )).Execute()
+
+ err = cmd.NewGrapiCommand(&grapicmd.Ctx{
+ IO: clib.Stdio(),
+ RootDir: cli.RootDir{clib.Path(cwd)},
+ Build: clib.Build{
+ AppName: name,
+ Version: version,
+ Revision: revision,
+ BuildDate: buildDate,
+ },
+ }).Execute()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
diff --git a/go.mod b/go.mod
new file mode 100644
index 00000000..2db7d1ab
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,38 @@
+module github.com/izumin5210/grapi
+
+go 1.12
+
+require (
+ github.com/bradleyjkemp/cupaloy/v2 v2.5.0
+ github.com/fatih/color v1.9.0
+ github.com/golang/mock v1.4.3
+ github.com/golang/protobuf v1.3.2
+ github.com/google/go-cmp v0.5.2
+ github.com/google/wire v0.2.1
+ github.com/grpc-ecosystem/go-grpc-middleware v1.2.0
+ github.com/grpc-ecosystem/grpc-gateway v1.9.0
+ github.com/izumin5210/clig v0.3.1
+ github.com/izumin5210/execx v0.1.0
+ github.com/izumin5210/gex v0.6.0
+ github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a
+ github.com/onsi/gomega v1.4.2 // indirect
+ github.com/pkg/errors v0.9.1
+ github.com/rakyll/statik v0.1.7
+ github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516
+ github.com/soheilhy/cmux v0.1.4
+ github.com/spf13/afero v1.2.2
+ github.com/spf13/cobra v0.0.6
+ github.com/spf13/pflag v1.0.5
+ github.com/spf13/viper v1.4.0
+ github.com/srvc/appctx v0.1.0
+ github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8
+ go.uber.org/zap v1.10.0
+ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 // indirect
+ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
+ golang.org/x/sync v0.0.0-20190423024810-112230192c58
+ golang.org/x/sys v0.0.0-20191118133127-cf1e2d577169 // indirect
+ golang.org/x/text v0.3.2 // indirect
+ golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74 // indirect
+ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55
+ google.golang.org/grpc v1.21.0
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 00000000..f804d195
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,305 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/Songmu/wrapcommander v0.1.0 h1:y8/yk9/PHT983weH+ehZIOJ7JtwAlI1AkfUpUNCj1SY=
+github.com/Songmu/wrapcommander v0.1.0/go.mod h1:EC2y4OnN8PkdMnaCwcSzItewq+f0yqUvS30kcS4vmn0=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/bradleyjkemp/cupaloy/v2 v2.5.0 h1:XI37Pqyl+msFaJDYL3JuPFKGUgnVxyJp+gQZQGiz2nA=
+github.com/bradleyjkemp/cupaloy/v2 v2.5.0/go.mod h1:TD5UU0rdYTbu/TtuwFuWrtiRARuN7mtRipvs/bsShSE=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
+github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/wire v0.2.1 h1:TYj4Z2qjqxa2ufb34UJqVeO9aznL+i0fLO6TqThKZ7Y=
+github.com/google/wire v0.2.1/go.mod h1:ptBl5bWD3nzmJHVNwYHV3v4wdtKzBMlU2YbtKQCG9GI=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg=
+github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
+github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7 h1:ux/56T2xqZO/3cP1I2F86qpeoYPCOzk+KF/UH/Ar+lk=
+github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
+github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/izumin5210/clig v0.3.1 h1:96ZR57e/ejdvZ9PmB2a3hFNxRNIiuZizspqSuOfsEYM=
+github.com/izumin5210/clig v0.3.1/go.mod h1:mrmoiGnWeMPZjnlRYF1MJTPCWc/aEXyAIzdLE9udh+Y=
+github.com/izumin5210/execx v0.1.0 h1:QLtjHrG2nZmb/kvJ8/fXAlGcp+ECAfsWha+Yx2R6Czs=
+github.com/izumin5210/execx v0.1.0/go.mod h1:qrIQVE0XTXWXoNpeA+OWZgAjpBxiztOKHfHpPIzc5t0=
+github.com/izumin5210/gex v0.6.0 h1:p79JHia3LRwC1M4sheTBZxSOJXubsW8jyWIhI60NMMs=
+github.com/izumin5210/gex v0.6.0/go.mod h1:nDH4zt3QvsIJipyYDoedHcNrNzawwkseKfpqGTUDN2Q=
+github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k=
+github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
+github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
+github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
+github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
+github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
+github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I=
+github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs=
+github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
+github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
+github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516 h1:ofR1ZdrNSkiWcMsRrubK9tb2/SlZVWttAfqUjJi6QYc=
+github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs=
+github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
+github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc=
+github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
+github.com/srvc/appctx v0.1.0 h1:x44Sw4dU567bIWMl5U70XxBrSxxJGzu2DI2zcaljdCA=
+github.com/srvc/appctx v0.1.0/go.mod h1:CrplkHjXk2n2wl7wKlDIX5mZdrch9feYZlu32ubJvPA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8 h1:RB0v+/pc8oMzPsN97aZYEwNuJ6ouRJ2uhjxemJ9zvrY=
+github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8/go.mod h1:IlWNj9v/13q7xFbaK4mbyzMNwrZLaWSHx/aibKIZuIg=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.uber.org/atomic v1.2.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
+go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0=
+golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 h1:x/bBzNauLQAlE3fLku/xy92Y8QwKX5HZymrMz2IiKFc=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8 h1:YoY1wS6JYVRpIfFngRf2HHo9R9dAne3xbkGOQ5rJXjU=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191018095205-727590c5006e h1:ZtoklVMHQy6BFRHkbG6JzK+S6rX82//Yeok1vMlizfQ=
+golang.org/x/sys v0.0.0-20191018095205-727590c5006e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191118133127-cf1e2d577169 h1:LPLFLulk2vyM7yI3CwNW64O6e8AxBmr9opfv14yI7HI=
+golang.org/x/sys v0.0.0-20191118133127-cf1e2d577169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181017214349-06f26fdaaa28/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74 h1:4cFkmztxtMslUX2SctSl+blCyXfpzhGOy9LhKAqSMA4=
+golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099 h1:XJP7lxbSxWLOMNdBE4B/STaqVy6L73o0knwj2vIlxnw=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+k8s.io/utils v0.0.0-20181115163542-0d26856f57b3/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
+rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/pkg/cli/nop_ui.go b/pkg/cli/nop_ui.go
new file mode 100644
index 00000000..cc90c51a
--- /dev/null
+++ b/pkg/cli/nop_ui.go
@@ -0,0 +1,12 @@
+package cli
+
+var NopUI = &nopUIImpl{}
+
+type nopUIImpl struct{}
+
+func (*nopUIImpl) Section(msg string) {}
+func (*nopUIImpl) Subsection(msg string) {}
+func (*nopUIImpl) ItemSuccess(msg string) {}
+func (*nopUIImpl) ItemSkipped(msg string) {}
+func (*nopUIImpl) ItemFailure(msg string, errs ...error) {}
+func (*nopUIImpl) Confirm(msg string) (bool, error) { return true, nil }
diff --git a/pkg/cli/types.go b/pkg/cli/types.go
new file mode 100644
index 00000000..ab4fe66d
--- /dev/null
+++ b/pkg/cli/types.go
@@ -0,0 +1,15 @@
+package cli
+
+import (
+ "github.com/izumin5210/clig/pkg/clib"
+)
+
+// RootDir represents a project root directory.
+type RootDir struct {
+ clib.Path
+}
+
+// BinDir returns the directory path contains executable binaries.
+func (d *RootDir) BinDir() clib.Path {
+ return d.Join("bin")
+}
diff --git a/pkg/cli/ui.go b/pkg/cli/ui.go
new file mode 100644
index 00000000..e7210b51
--- /dev/null
+++ b/pkg/cli/ui.go
@@ -0,0 +1,194 @@
+package cli
+
+import (
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+ "sync"
+
+ "github.com/fatih/color"
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/pkg/errors"
+ "github.com/tcnksm/go-input"
+ "go.uber.org/zap"
+)
+
+// UI is an interface for intaracting with the terminal.
+type UI interface {
+ Section(msg string)
+ Subsection(msg string)
+ ItemSuccess(msg string)
+ ItemSkipped(msg string)
+ ItemFailure(msg string, errs ...error)
+ Confirm(msg string) (bool, error)
+}
+
+var (
+ ui UI
+ uiMu sync.Mutex
+)
+
+// UIInstance retuens a singleton UI instance.
+func UIInstance(io *clib.IO) UI {
+ uiMu.Lock()
+ defer uiMu.Unlock()
+ if ui == nil {
+ ui = NewUI(io)
+ }
+ return ui
+}
+
+// NewUI creates a new UI instance.
+func NewUI(io *clib.IO) UI {
+ return &uiImpl{
+ out: io.Out,
+ inputUI: &input.UI{
+ Reader: io.In,
+ Writer: io.Out,
+ },
+ }
+}
+
+type uiImpl struct {
+ out io.Writer
+ inSection bool
+ inputUI *input.UI
+}
+
+func (u *uiImpl) Section(msg string) {
+ if u.inSection {
+ fmt.Fprintln(u.out)
+ u.inSection = false
+ }
+ printTypeSection.Fprintln(u.out, msg)
+}
+
+func (u *uiImpl) Subsection(msg string) {
+ if u.inSection {
+ fmt.Fprintln(u.out)
+ u.inSection = false
+ }
+ printTypeSubsection.Fprintln(u.out, msg)
+}
+
+func (u *uiImpl) ItemSuccess(msg string) {
+ u.inSection = true
+ printTypeItemSuccess.Fprintln(u.out, msg)
+}
+
+func (u *uiImpl) ItemSkipped(msg string) {
+ u.inSection = true
+ printTypeItemSkipped.Fprintln(u.out, msg)
+}
+
+func (u *uiImpl) ItemFailure(msg string, errs ...error) {
+ u.inSection = true
+ printTypeItemFailure.Fprintln(u.out, msg)
+ cfg := configByPrintType[printTypeItemFailure]
+ pad := strings.Repeat(" ", cfg.indent+4)
+ fprintln := color.New(color.FgRed).FprintlnFunc()
+ for _, err := range errs {
+ for _, s := range strings.Split(err.Error(), "\n") {
+ fprintln(u.out, pad+s)
+ }
+ }
+}
+
+func (u *uiImpl) Confirm(msg string) (bool, error) {
+ ans, err := u.inputUI.Ask(fmt.Sprintf("%s [Y/n]", msg), &input.Options{
+ HideOrder: true,
+ Loop: true,
+ ValidateFunc: func(ans string) error {
+ zap.L().Debug("receive user input", zap.String("query", msg), zap.String("input", ans))
+ if ans != "Y" && ans != "n" {
+ return fmt.Errorf("input must be Y or n")
+ }
+ return nil
+ },
+ })
+ if err != nil {
+ return false, errors.WithStack(err)
+ }
+ return ans == "Y", nil
+}
+
+type fprintFunc func(w io.Writer, msg string)
+
+type printConfig struct {
+ prefix string
+ colorAttrs []color.Attribute
+ indent int
+ allColor bool
+}
+
+type printType int
+
+const (
+ printTypeUnknown printType = iota
+ printTypeSection
+ printTypeSubsection
+ printTypeItemSuccess
+ printTypeItemSkipped
+ printTypeItemFailure
+)
+
+const (
+ indentSizeItem = 4
+)
+
+var (
+ configByPrintType = map[printType]printConfig{
+ printTypeSection: {
+ prefix: "โ",
+ colorAttrs: []color.Attribute{color.FgYellow},
+ allColor: true,
+ },
+ printTypeSubsection: {
+ prefix: "โธ",
+ colorAttrs: []color.Attribute{color.FgBlue},
+ allColor: true,
+ },
+ printTypeItemSuccess: {
+ prefix: "โ",
+ colorAttrs: []color.Attribute{color.Bold, color.FgGreen},
+ indent: indentSizeItem,
+ },
+ printTypeItemSkipped: {
+ prefix: "โ",
+ colorAttrs: []color.Attribute{color.Bold, color.FgBlue},
+ indent: indentSizeItem,
+ },
+ printTypeItemFailure: {
+ prefix: "โ",
+ colorAttrs: []color.Attribute{color.Bold, color.FgRed},
+ indent: indentSizeItem,
+ },
+ }
+ fprintlnFuncByPrintType = map[printType]fprintFunc{}
+)
+
+func init() {
+ for pt, cfg := range configByPrintType {
+ cfg := cfg
+ fmtStr := "%s"
+ if cfg.indent > 0 {
+ fmtStr = "%" + strconv.FormatInt(int64(cfg.indent), 10) + "s"
+ }
+ if cfg.allColor {
+ colored := color.New(cfg.colorAttrs...).FprintfFunc()
+ fprintlnFuncByPrintType[pt] = func(w io.Writer, msg string) {
+ colored(w, " "+fmtStr+" %s\n", cfg.prefix, msg)
+ }
+ } else {
+ prefix := color.New(cfg.colorAttrs...).Sprintf(fmtStr, cfg.prefix)
+ fprintlnFuncByPrintType[pt] = func(w io.Writer, msg string) {
+ fmt.Fprintf(w, " %s %s\n", prefix, msg)
+ }
+ }
+ }
+}
+
+func (pt printType) Fprintln(w io.Writer, msg string) {
+ fprintlnFuncByPrintType[pt](w, msg)
+}
diff --git a/pkg/grapicmd/internal/module/ui/ui_test.go b/pkg/cli/ui_test.go
similarity index 54%
rename from pkg/grapicmd/internal/module/ui/ui_test.go
rename to pkg/cli/ui_test.go
index a6fd2254..cb6b3970 100644
--- a/pkg/grapicmd/internal/module/ui/ui_test.go
+++ b/pkg/cli/ui_test.go
@@ -1,4 +1,4 @@
-package ui
+package cli_test
import (
"bytes"
@@ -6,9 +6,49 @@ import (
"strings"
"testing"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
+ "github.com/fatih/color"
+ "github.com/izumin5210/clig/pkg/clib"
+
+ "github.com/izumin5210/grapi/pkg/cli"
)
+func TestUI(t *testing.T) {
+ defer func(b bool) { color.NoColor = b }(color.NoColor)
+ color.NoColor = true
+
+ want := ` โ section 1
+ โธ subsection 1.1
+ โ created
+ โ skipped
+ โ ok
+
+ โธ subsection 1.2
+ โ failure
+ foobar
+ baz
+
+ โ section 2
+ โ fail!!!
+`
+
+ io := clib.NewBufferedIO()
+ ui := cli.NewUI(io)
+
+ ui.Section("section 1")
+ ui.Subsection("subsection 1.1")
+ ui.ItemSuccess("created")
+ ui.ItemSkipped("skipped")
+ ui.ItemSuccess("ok")
+ ui.Subsection("subsection 1.2")
+ ui.ItemFailure("failure", errors.New("foobar"), errors.New("baz"))
+ ui.Section("section 2")
+ ui.ItemFailure("fail!!!")
+
+ if got := io.Out.(*bytes.Buffer).String(); got != want {
+ t.Errorf("got:\n%s\nwant:\n%s", got, want)
+ }
+}
+
type errReader struct {
}
@@ -16,19 +56,17 @@ func (r *errReader) Read(p []byte) (n int, err error) {
return 0, errors.New("failed to read")
}
-func Test_UI_Confirm(t *testing.T) {
+func TestUI_Confirm(t *testing.T) {
type TestContext struct {
- in, out, err *bytes.Buffer
- ui module.UI
+ io *clib.IO
+ ui cli.UI
}
createTestContext := func() *TestContext {
- in := new(bytes.Buffer)
- out := new(bytes.Buffer)
+ io := clib.NewBufferedIO()
return &TestContext{
- in: in,
- out: out,
- ui: New(out, in),
+ io: io,
+ ui: cli.NewUI(io),
}
}
@@ -67,16 +105,16 @@ func Test_UI_Confirm(t *testing.T) {
for _, c := range cases {
t.Run(c.test, func(t *testing.T) {
ctx := createTestContext()
- ctx.in.WriteString(c.input)
+ ctx.io.In.(*bytes.Buffer).WriteString(c.input)
ok, err := ctx.ui.Confirm(c.test)
- if got, want := ctx.out.String(), c.test; !strings.HasPrefix(got, want) {
+ if got, want := ctx.io.Out.(*bytes.Buffer).String(), c.test; !strings.HasPrefix(got, want) {
t.Errorf("Confirm() wrote %q, want %q", got, want)
}
wantErrMsg := "input must be Y or n\n"
- if got, want := strings.Count(ctx.out.String(), wantErrMsg), c.errMsgCnt; got != want {
+ if got, want := strings.Count(ctx.io.Out.(*bytes.Buffer).String(), wantErrMsg), c.errMsgCnt; got != want {
t.Errorf("Confirm() wrote %q as error %d times, want %d times", wantErrMsg, got, want)
}
@@ -91,7 +129,7 @@ func Test_UI_Confirm(t *testing.T) {
}
t.Run("when failed to read", func(t *testing.T) {
- ui := New(new(bytes.Buffer), &errReader{})
+ ui := cli.NewUI(clib.NewIO(&errReader{}, new(bytes.Buffer), new(bytes.Buffer)))
ok, err := ui.Confirm("test")
diff --git a/pkg/gencmd/app.go b/pkg/gencmd/app.go
new file mode 100644
index 00000000..e403a12c
--- /dev/null
+++ b/pkg/gencmd/app.go
@@ -0,0 +1,12 @@
+package gencmd
+
+import "github.com/izumin5210/grapi/pkg/cli"
+
+// CreateAppFunc initializes dependencies.
+type CreateAppFunc func(*Command) (*App, error)
+
+// App contains dependencies to execute a generator.
+type App struct {
+ Generator Generator
+ UI cli.UI
+}
diff --git a/pkg/gencmd/command.go b/pkg/gencmd/command.go
new file mode 100644
index 00000000..15309ae5
--- /dev/null
+++ b/pkg/gencmd/command.go
@@ -0,0 +1,89 @@
+package gencmd
+
+import (
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+// Command represents a subcommand of a generator plugin. It will be converted to a *cobra.Command object internally.
+type Command struct {
+ // Use, Short, Long, Example and Args are pass-through into *cobra.Command object.
+ Use string
+ Short string
+ Long string
+ Example string
+ Args cobra.PositionalArgs
+
+ // BuildParams returns parameters to generate/destroy files(required).
+ BuildParams func(c *Command, args []string) (interface{}, error)
+
+ // PreRun is executed in *cobra.Command.PreRunE.
+ PreRun func(c *Command, args []string) error
+
+ // PostRun is executed in *cobra.Command.PostRunE.
+ PostRun func(c *Command, args []string) error
+
+ // ShouldRun is executed for each generated files. When it returns false, the file will be skipped.
+ ShouldRun ShouldRunFunc
+
+ // ShouldInsideApp will disable the command when a current working directory is not inside of a grapi project.
+ ShouldInsideApp bool
+
+ flags *pflag.FlagSet
+ ctx *Ctx
+}
+
+// Flags returns a FlagSet that applies to this commmand.
+func (c *Command) Flags() *pflag.FlagSet {
+ if c.flags == nil {
+ c.flags = new(pflag.FlagSet)
+ }
+ return c.flags
+}
+
+// Ctx returns the context object.
+func (c *Command) Ctx() *Ctx {
+ return c.ctx
+}
+
+func (c *Command) newCobraCommand() *cobra.Command {
+ cc := &cobra.Command{
+ Use: c.Use,
+ Short: c.Short,
+ Long: c.Long,
+ Example: c.Example,
+ Args: c.Args,
+ PreRunE: func(_ *cobra.Command, args []string) error {
+ if c.ShouldInsideApp && !c.Ctx().IsInsideApp() {
+ return errors.New("should execute inside grapi project")
+ }
+ if c.PreRun != nil {
+ err := c.PreRun(c, args)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ }
+ return nil
+ },
+ }
+ if c.PostRun != nil {
+ cc.PostRunE = func(_ *cobra.Command, args []string) error { return c.PostRun(c, args) }
+ }
+ cc.PersistentFlags().AddFlagSet(c.Flags())
+ return cc
+}
+
+// File represents a file content.
+type File struct {
+ Path string
+ Body string
+}
+
+// Entry represents a file that will be generated.
+type Entry struct {
+ File
+ Template File
+}
+
+type ShouldRunFunc func(e *Entry) bool
diff --git a/pkg/gencmd/context.go b/pkg/gencmd/context.go
new file mode 100644
index 00000000..272701af
--- /dev/null
+++ b/pkg/gencmd/context.go
@@ -0,0 +1,38 @@
+package gencmd
+
+import (
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/pkg/errors"
+)
+
+func defaultCtx() *Ctx {
+ return &Ctx{
+ Ctx: &grapicmd.Ctx{},
+ }
+}
+
+// Ctx defines a context of a generator.
+type Ctx struct {
+ *grapicmd.Ctx
+
+ CreateAppFunc CreateAppFunc
+}
+
+func (c *Ctx) apply(opts []Option) {
+ for _, f := range opts {
+ f(c)
+ }
+}
+
+// CreateApp initializes dependencies.
+func (c *Ctx) CreateApp(cmd *Command) (*App, error) {
+ f := c.CreateAppFunc
+ if c.CreateAppFunc == nil {
+ f = newApp
+ }
+ app, err := f(cmd)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+ return app, nil
+}
diff --git a/pkg/gencmd/executor.go b/pkg/gencmd/executor.go
new file mode 100644
index 00000000..5d10017f
--- /dev/null
+++ b/pkg/gencmd/executor.go
@@ -0,0 +1,48 @@
+package gencmd
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+type Executor interface {
+ Command() *cobra.Command
+ Execute() error
+ MustExecute()
+}
+
+func newExecutor(ctx *Ctx, cmd *cobra.Command) Executor {
+ return &executorImpl{ctx: ctx, cmd: cmd}
+}
+
+type executorImpl struct {
+ ctx *Ctx
+ cmd *cobra.Command
+}
+
+func (c *executorImpl) Command() *cobra.Command {
+ return c.cmd
+}
+
+func (c *executorImpl) Execute() error {
+ err := c.ctx.Init()
+ if err != nil {
+ return errors.Wrap(err, "failed to initialize context")
+ }
+
+ return errors.WithStack(c.cmd.Execute())
+}
+
+func (c *executorImpl) MustExecute() {
+ var code int
+
+ if err := c.Execute(); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ code = 1
+ }
+
+ os.Exit(code)
+}
diff --git a/pkg/gencmd/generator.go b/pkg/gencmd/generator.go
new file mode 100644
index 00000000..fcab9f29
--- /dev/null
+++ b/pkg/gencmd/generator.go
@@ -0,0 +1,208 @@
+package gencmd
+
+import (
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/pkg/errors"
+ "github.com/rakyll/statik/fs"
+ "github.com/spf13/afero"
+ "go.uber.org/zap"
+
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/grapi/pkg/cli"
+)
+
+type Generator interface {
+ Generate(params interface{}) error
+ Destroy(params interface{}) error
+}
+
+func NewGenerator(
+ fs afero.Fs,
+ ui cli.UI,
+ outDir clib.Path,
+ templateFS http.FileSystem,
+ shouldRunFunc ShouldRunFunc,
+) Generator {
+ return &generatorImpl{
+ fs: fs,
+ ui: ui,
+ outDir: outDir,
+ templateFS: templateFS,
+ shouldRunFunc: shouldRunFunc,
+ }
+}
+
+type generatorImpl struct {
+ fs afero.Fs
+ ui cli.UI
+
+ outDir clib.Path
+
+ templateFS http.FileSystem
+ shouldRunFunc ShouldRunFunc
+}
+
+func (g *generatorImpl) Generate(params interface{}) error {
+ entries, err := g.listEntries(params)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ for _, e := range entries {
+ if ok, err := g.shouldRun(e); err != nil {
+ g.ui.ItemFailure(e.Path[1:])
+ return errors.WithStack(err)
+ } else if !ok {
+ g.ui.ItemSkipped(e.Path[1:])
+ continue
+ }
+
+ err := g.writeFile(e)
+ if err != nil {
+ g.ui.ItemFailure(e.Path[1:])
+ return errors.WithStack(err)
+ }
+ g.ui.ItemSuccess(e.Path[1:])
+ }
+
+ return nil
+}
+
+func (g *generatorImpl) Destroy(params interface{}) error {
+ tmplPaths, err := g.listPathTemplates()
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ for _, tmplPath := range tmplPaths {
+ path, err := TemplateString(strings.TrimSuffix(tmplPath, ".tmpl")).Compile(params)
+ if err != nil {
+ return errors.Wrapf(err, "failed to parse path: %s", tmplPath)
+ }
+
+ absPath := g.outDir.Join(path).String()
+ if ok, err := afero.Exists(g.fs, absPath); err != nil {
+ g.ui.ItemFailure(path)
+ return errors.WithStack(err)
+ } else if !ok {
+ g.ui.ItemSkipped(path)
+ continue
+ }
+
+ err = g.fs.Remove(absPath)
+ if err != nil {
+ g.ui.ItemFailure(path)
+ return errors.WithStack(err)
+ }
+ g.ui.ItemSuccess(path)
+ }
+
+ return nil
+}
+
+func (g *generatorImpl) listEntries(params interface{}) ([]*Entry, error) {
+ tmplPaths, err := g.listPathTemplates()
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ entries := make([]*Entry, 0, len(tmplPaths))
+
+ for _, tmplPath := range tmplPaths {
+ path, err := TemplateString(strings.TrimSuffix(tmplPath, ".tmpl")).Compile(params)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to parse path: %s", tmplPath)
+ }
+
+ data, err := fs.ReadFile(g.templateFS, tmplPath)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to read template: %s", tmplPath)
+ }
+
+ body, err := TemplateString(string(data)).Compile(params)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to compile temlpate: %s, %v", tmplPath, params)
+ }
+
+ entries = append(entries, &Entry{File: File{Path: filepath.Clean(path), Body: body}, Template: File{Path: tmplPath, Body: string(data)}})
+ }
+ return entries, nil
+}
+
+func (g *generatorImpl) listPathTemplates() (tmplPaths []string, err error) {
+ err = fs.Walk(g.templateFS, "/", func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ if info.IsDir() {
+ return nil
+ }
+
+ tmplPaths = append(tmplPaths, path)
+
+ return nil
+ })
+
+ err = errors.WithStack(err)
+
+ return
+}
+
+func (g *generatorImpl) shouldRun(e *Entry) (bool, error) {
+ if g.shouldRunFunc != nil && !g.shouldRunFunc(e) {
+ return false, nil
+ }
+
+ absPath := g.outDir.Join(e.Path).String()
+
+ if ok, err := afero.Exists(g.fs, absPath); err != nil {
+ return false, errors.WithStack(err)
+ } else if !ok {
+ return true, nil
+ }
+
+ existed, err := afero.ReadFile(g.fs, absPath)
+ if err != nil {
+ return false, errors.WithStack(err)
+ }
+
+ if string(existed) == e.Body {
+ return false, nil
+ }
+
+ g.ui.ItemFailure(e.Path[1:] + " is conflicted.")
+ if ok, err := g.ui.Confirm("Overwite it?"); err != nil {
+ zap.L().Error("failed to confirm to apply", zap.Error(err))
+ return false, errors.WithStack(err)
+ } else if ok {
+ return true, nil
+ }
+
+ return false, nil
+}
+
+func (g *generatorImpl) writeFile(e *Entry) error {
+ path := g.outDir.Join(e.Path).String()
+ dir := filepath.Dir(path)
+
+ if ok, err := afero.DirExists(g.fs, dir); err != nil {
+ return errors.WithStack(err)
+ } else if !ok {
+ err := g.fs.MkdirAll(dir, 0755)
+ if err != nil {
+ return errors.Wrapf(err, "failed to create directory")
+ }
+ }
+
+ err := afero.WriteFile(g.fs, path, []byte(e.Body), 0644)
+ if err != nil {
+ return errors.Wrapf(err, "failed to write %s", e.Path)
+ }
+
+ return nil
+}
diff --git a/pkg/gencmd/main.go b/pkg/gencmd/main.go
new file mode 100644
index 00000000..4f5d4a5b
--- /dev/null
+++ b/pkg/gencmd/main.go
@@ -0,0 +1,96 @@
+package gencmd
+
+import (
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+// New creates an Executor instnace.
+func New(
+ name string,
+ generateCmd *Command,
+ destroyCmd *Command,
+ opts ...Option,
+) Executor {
+ ctx := defaultCtx()
+ ctx.apply(opts)
+
+ rootCmd := &cobra.Command{
+ Use: "grapi-gen-" + name,
+ }
+
+ setGenerateCommand(name, rootCmd, generateCmd, ctx)
+ setDestroyCommand(name, rootCmd, destroyCmd, ctx)
+
+ return newExecutor(ctx, rootCmd)
+}
+
+func setGenerateCommand(name string, rootCmd *cobra.Command, cmd *Command, ctx *Ctx) {
+ if cmd == nil {
+ return
+ }
+
+ ccmd := cmd.newCobraCommand()
+
+ ccmd.RunE = func(_ *cobra.Command, args []string) error {
+ app, err := ctx.CreateApp(cmd)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ app.UI.Section("Generate " + name)
+ params, err := cmd.BuildParams(cmd, args)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ err = app.Generator.Generate(params)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ return nil
+ }
+
+ if ccmd.Use == "" {
+ ccmd.Use = "generate"
+ }
+
+ cmd.ctx = ctx
+ rootCmd.AddCommand(ccmd)
+}
+
+func setDestroyCommand(name string, rootCmd *cobra.Command, cmd *Command, ctx *Ctx) {
+ if cmd == nil {
+ return
+ }
+
+ ccmd := cmd.newCobraCommand()
+
+ ccmd.RunE = func(_ *cobra.Command, args []string) error {
+ app, err := ctx.CreateApp(cmd)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ app.UI.Section("Destroy " + name)
+ params, err := cmd.BuildParams(cmd, args)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ err = app.Generator.Destroy(params)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ return nil
+ }
+
+ if ccmd.Use == "" {
+ ccmd.Use = "destroy"
+ }
+
+ cmd.ctx = ctx
+ rootCmd.AddCommand(ccmd)
+}
diff --git a/pkg/gencmd/options.go b/pkg/gencmd/options.go
new file mode 100644
index 00000000..fefdbd6d
--- /dev/null
+++ b/pkg/gencmd/options.go
@@ -0,0 +1,22 @@
+package gencmd
+
+import (
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+)
+
+// Option configures a command context.
+type Option func(*Ctx)
+
+// WithGrapiCtx specifies a grapi command context.
+func WithGrapiCtx(gctx *grapicmd.Ctx) Option {
+ return func(ctx *Ctx) {
+ ctx.Ctx = gctx
+ }
+}
+
+// WithCreateAppFunc specifies a dependencies initializer.
+func WithCreateAppFunc(f CreateAppFunc) Option {
+ return func(ctx *Ctx) {
+ ctx.CreateAppFunc = f
+ }
+}
diff --git a/pkg/gencmd/providers.go b/pkg/gencmd/providers.go
new file mode 100644
index 00000000..9d08b4bb
--- /dev/null
+++ b/pkg/gencmd/providers.go
@@ -0,0 +1,27 @@
+package gencmd
+
+import (
+ "github.com/google/wire"
+ "github.com/rakyll/statik/fs"
+
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+)
+
+func ProvideGrapiCtx(ctx *Ctx) *grapicmd.Ctx { return ctx.Ctx }
+func ProvideCtx(cmd *Command) *Ctx { return cmd.Ctx() }
+func ProvideShouldRun(cmd *Command) ShouldRunFunc { return cmd.ShouldRun }
+func ProvidePath(root cli.RootDir) clib.Path { return root.Path }
+
+// Set contains providers for DI.
+var Set = wire.NewSet(
+ grapicmd.CtxSet,
+ fs.New,
+ ProvideGrapiCtx,
+ ProvideCtx,
+ ProvideShouldRun,
+ ProvidePath,
+ NewGenerator,
+ App{},
+)
diff --git a/pkg/grapicmd/internal/module/generator/template_string.go b/pkg/gencmd/template_string.go
similarity index 97%
rename from pkg/grapicmd/internal/module/generator/template_string.go
rename to pkg/gencmd/template_string.go
index d54ca8bf..1fc0eddf 100644
--- a/pkg/grapicmd/internal/module/generator/template_string.go
+++ b/pkg/gencmd/template_string.go
@@ -1,4 +1,4 @@
-package generator
+package gencmd
import (
"bytes"
diff --git a/pkg/gencmd/testing/wire.go b/pkg/gencmd/testing/wire.go
new file mode 100644
index 00000000..ab6bbab5
--- /dev/null
+++ b/pkg/gencmd/testing/wire.go
@@ -0,0 +1,17 @@
+//+build wireinject
+
+package testing
+
+import (
+ "github.com/google/wire"
+
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+)
+
+func NewTestApp(*gencmd.Command, cli.UI) (*gencmd.App, error) {
+ wire.Build(
+ gencmd.Set,
+ )
+ return nil, nil
+}
diff --git a/pkg/gencmd/testing/wire_gen.go b/pkg/gencmd/testing/wire_gen.go
new file mode 100644
index 00000000..a803ecc7
--- /dev/null
+++ b/pkg/gencmd/testing/wire_gen.go
@@ -0,0 +1,34 @@
+// Code generated by Wire. DO NOT EDIT.
+
+//go:generate wire
+//+build !wireinject
+
+package testing
+
+import (
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/rakyll/statik/fs"
+)
+
+// Injectors from wire.go:
+
+func NewTestApp(command *gencmd.Command, ui cli.UI) (*gencmd.App, error) {
+ ctx := gencmd.ProvideCtx(command)
+ grapicmdCtx := gencmd.ProvideGrapiCtx(ctx)
+ aferoFs := grapicmd.ProvideFS(grapicmdCtx)
+ rootDir := grapicmd.ProvideRootDir(grapicmdCtx)
+ path := gencmd.ProvidePath(rootDir)
+ fileSystem, err := fs.New()
+ if err != nil {
+ return nil, err
+ }
+ shouldRunFunc := gencmd.ProvideShouldRun(command)
+ generator := gencmd.NewGenerator(aferoFs, ui, path, fileSystem, shouldRunFunc)
+ app := &gencmd.App{
+ Generator: generator,
+ UI: ui,
+ }
+ return app, nil
+}
diff --git a/pkg/gencmd/util/package.go b/pkg/gencmd/util/package.go
new file mode 100644
index 00000000..7bedf551
--- /dev/null
+++ b/pkg/gencmd/util/package.go
@@ -0,0 +1,76 @@
+package util
+
+import (
+ "path/filepath"
+ "strings"
+
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
+ "github.com/pkg/errors"
+ "github.com/serenize/snaker"
+)
+
+type ProtoParams struct {
+ Proto struct {
+ Path string
+ Package string
+ }
+ PbGo struct {
+ Package string
+ ImportName string
+ }
+}
+
+func BuildProtoParams(path string, rootDir cli.RootDir, protoOutDir string, pkg string) (out ProtoParams, err error) {
+ if protoOutDir == "" {
+ err = errors.New("protoOutDir is required")
+ return
+ }
+
+ // github.com/foo/bar
+ importPath, err := fs.GetImportPath(rootDir.String())
+ if err != nil {
+ err = errors.WithStack(err)
+ return
+ }
+
+ // path => baz/qux/quux
+ path = strings.Replace(snaker.CamelToSnake(path), "-", "_", -1)
+
+ // baz/qux
+ packagePath := filepath.Dir(path)
+
+ // api/baz/qux
+ pbgoPackagePath := filepath.Join(protoOutDir, packagePath)
+ // qux_pb
+ pbgoPackageName := filepath.Base(pbgoPackagePath) + "_pb"
+
+ if packagePath == "." {
+ pbgoPackagePath = protoOutDir
+ pbgoPackageName = filepath.Base(pbgoPackagePath) + "_pb"
+ }
+
+ protoPackage := pkg
+ if protoPackage == "" {
+ protoPackageChunks := []string{}
+ for _, pkg := range strings.Split(filepath.ToSlash(filepath.Join(importPath, protoOutDir)), "/") {
+ chunks := strings.Split(pkg, ".")
+ for i := len(chunks) - 1; i >= 0; i-- {
+ protoPackageChunks = append(protoPackageChunks, chunks[i])
+ }
+ }
+ // com.github.foo.bar.baz.qux
+ protoPackage = strings.Join(protoPackageChunks, ".")
+ }
+ if dir := filepath.Dir(path); dir != "." {
+ protoPackage = protoPackage + "." + strings.Replace(dir, string(filepath.Separator), ".", -1)
+ }
+ protoPackage = strings.Replace(protoPackage, "-", "_", -1)
+
+ out.Proto.Path = path
+ out.Proto.Package = strings.Replace(protoPackage, "-", "_", -1)
+ out.PbGo.Package = filepath.ToSlash(filepath.Join(importPath, pbgoPackagePath))
+ out.PbGo.ImportName = pbgoPackageName
+
+ return
+}
diff --git a/pkg/gencmd/util/string.go b/pkg/gencmd/util/string.go
new file mode 100644
index 00000000..97623b2a
--- /dev/null
+++ b/pkg/gencmd/util/string.go
@@ -0,0 +1,33 @@
+package util
+
+import (
+ "strings"
+
+ "github.com/jinzhu/inflection"
+ "github.com/serenize/snaker"
+)
+
+type String struct {
+ Camel struct {
+ Plural string
+ Singular string
+ }
+ CamelLower struct {
+ Plural string
+ Singular string
+ }
+ Snake struct {
+ Plural string
+ Singular string
+ }
+}
+
+func Inflect(in string) (out String) {
+ out.Camel.Singular = inflection.Singular(snaker.SnakeToCamel(in))
+ out.Camel.Plural = inflection.Plural(out.Camel.Singular)
+ out.CamelLower.Singular = strings.ToLower(string(out.Camel.Singular[0])) + out.Camel.Singular[1:]
+ out.CamelLower.Plural = strings.ToLower(string(out.Camel.Plural[0])) + out.Camel.Plural[1:]
+ out.Snake.Singular = snaker.CamelToSnake(out.Camel.Singular)
+ out.Snake.Plural = snaker.CamelToSnake(out.Camel.Plural)
+ return
+}
diff --git a/pkg/gencmd/wire.go b/pkg/gencmd/wire.go
new file mode 100644
index 00000000..612b0384
--- /dev/null
+++ b/pkg/gencmd/wire.go
@@ -0,0 +1,16 @@
+//+build wireinject
+
+package gencmd
+
+import (
+ "github.com/google/wire"
+ "github.com/izumin5210/grapi/pkg/cli"
+)
+
+func newApp(*Command) (*App, error) {
+ wire.Build(
+ Set,
+ cli.UIInstance,
+ )
+ return nil, nil
+}
diff --git a/pkg/gencmd/wire_gen.go b/pkg/gencmd/wire_gen.go
new file mode 100644
index 00000000..bbd8dcb5
--- /dev/null
+++ b/pkg/gencmd/wire_gen.go
@@ -0,0 +1,35 @@
+// Code generated by Wire. DO NOT EDIT.
+
+//go:generate wire
+//+build !wireinject
+
+package gencmd
+
+import (
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/rakyll/statik/fs"
+)
+
+// Injectors from wire.go:
+
+func newApp(command *Command) (*App, error) {
+ ctx := ProvideCtx(command)
+ grapicmdCtx := ProvideGrapiCtx(ctx)
+ aferoFs := grapicmd.ProvideFS(grapicmdCtx)
+ io := grapicmd.ProvideIO(grapicmdCtx)
+ ui := cli.UIInstance(io)
+ rootDir := grapicmd.ProvideRootDir(grapicmdCtx)
+ path := ProvidePath(rootDir)
+ fileSystem, err := fs.New()
+ if err != nil {
+ return nil, err
+ }
+ shouldRunFunc := ProvideShouldRun(command)
+ generator := NewGenerator(aferoFs, ui, path, fileSystem, shouldRunFunc)
+ app := &App{
+ Generator: generator,
+ UI: ui,
+ }
+ return app, nil
+}
diff --git a/pkg/grapicmd/cmd/build.go b/pkg/grapicmd/cmd/build.go
index 034428c0..619a7aed 100644
--- a/pkg/grapicmd/cmd/build.go
+++ b/pkg/grapicmd/cmd/build.go
@@ -1,35 +1,68 @@
package cmd
import (
+ "strings"
+
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "github.com/srvc/appctx"
+ "github.com/izumin5210/grapi/pkg/cli"
"github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/izumin5210/grapi/pkg/grapicmd/di"
"github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
)
-func newBuildCommand(cfg grapicmd.Config, ui module.UI, scriptLoader module.ScriptLoader) *cobra.Command {
+func splitOptions(args []string) ([]string, []string) {
+ pos := len(args)
+ for i, arg := range args {
+ if strings.HasPrefix(arg, "-") {
+ pos = i
+
+ break
+ }
+ }
+
+ return args[:pos], args[pos:]
+}
+
+func newBuildCommand(ctx *grapicmd.Ctx) *cobra.Command {
+ scriptLoader := di.NewScriptLoader(ctx)
+ ui := di.NewUI(ctx)
+
+ return newBuildCommandMocked(ctx.IsInsideApp(), ctx, scriptLoader, ui)
+}
+
+func newBuildCommandMocked(isInsideApp bool, ctx *grapicmd.Ctx, scriptLoader module.ScriptLoader, ui cli.UI) *cobra.Command {
return &cobra.Command{
- Use: "build [TARGET]... [-- BUILD_OPTIONS]",
+ Use: "build [flags] [TARGET]... [-- BUILD_OPTIONS]",
Short: "Build commands",
SilenceErrors: true,
SilenceUsage: true,
- RunE: func(c *cobra.Command, args []string) (err error) {
- if !cfg.IsInsideApp() {
+ RunE: func(c *cobra.Command, args []string) error {
+ if !isInsideApp {
return errors.New("protoc command should be execute inside a grapi application directory")
}
- nameSet := make(map[string]bool, len(args))
- for _, n := range args {
+ names, opt := splitOptions(args)
+ nameSet := make(map[string]bool, len(names))
+ for _, n := range names {
nameSet[n] = true
}
- isAll := len(args) == 0
+ isAll := len(names) == 0
+
+ err := scriptLoader.Load(ctx.RootDir.Join("cmd").String())
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ ctx := appctx.Global()
for _, name := range scriptLoader.Names() {
script, ok := scriptLoader.Get(name)
if ok && (isAll || nameSet[script.Name()]) {
ui.Subsection("Building " + script.Name())
- err := script.Build(args...)
+ err := script.Build(ctx, opt...)
if err != nil {
return errors.WithStack(err)
}
diff --git a/pkg/grapicmd/cmd/build_internal_test.go b/pkg/grapicmd/cmd/build_internal_test.go
new file mode 100644
index 00000000..e0d54742
--- /dev/null
+++ b/pkg/grapicmd/cmd/build_internal_test.go
@@ -0,0 +1,185 @@
+package cmd
+
+import (
+ "errors"
+ "reflect"
+ "testing"
+
+ "github.com/golang/mock/gomock"
+
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ moduletesting "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/testing"
+)
+
+func TestSplitOptions(t *testing.T) {
+ tests := []struct {
+ input []string
+ outputArg []string
+ outputOpt []string
+ }{
+ {
+ []string{"foo", "bar"},
+ []string{"foo", "bar"},
+ []string{},
+ },
+ {
+ []string{"foo", "-b"},
+ []string{"foo"},
+ []string{"-b"},
+ },
+ {
+ []string{"foo", "-b", "-h"},
+ []string{"foo"},
+ []string{"-b", "-h"},
+ },
+ {
+ []string{"foo", "-b", "-h"},
+ []string{"foo"},
+ []string{"-b", "-h"},
+ },
+ {
+ []string{"-b", "-h"},
+ []string{},
+ []string{"-b", "-h"},
+ },
+ {
+ []string{"foo", "-b", "-h", "ooo"},
+ []string{"foo"},
+ []string{"-b", "-h", "ooo"},
+ },
+ {
+ []string{},
+ []string{},
+ []string{},
+ },
+ }
+
+ for i, test := range tests {
+ gotArg, gotOpt := splitOptions(test.input)
+ if !reflect.DeepEqual(test.outputArg, gotArg) {
+ t.Errorf("(%v) Expected: %v gotArg: %v", i, test.outputArg, gotArg)
+ }
+ if !reflect.DeepEqual(test.outputOpt, gotOpt) {
+ t.Errorf("(%v) Expected: %v gotOpt: %v", i, test.outputOpt, gotOpt)
+ }
+ }
+}
+
+var errBuildFailed = errors.New("error occur")
+
+func Test_newBuildCommandMocked(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+
+ type errBuild struct {
+ err error
+ }
+
+ testCases := []struct {
+ args []string
+ optExpect []string
+ // ๅไฝใฎbuildใฎ็ตๆ
+ buildResult map[string]errBuild
+ // ๅ
จไฝใฎbuildใฎ็ตๆ
+ wantErr bool
+ }{
+ {
+ // "aaa"ใ ใๆๅฎใใๅ ดๅ๏ผbuildใซใฏๆๅใใ
+ // "bbb"ใจ"ccc"ใฏๅผใฐใใชใ
+ args: []string{"aaa"},
+ optExpect: []string{},
+ buildResult: map[string]errBuild{
+ "aaa": {},
+ },
+ },
+ {
+ // ใชใใใใฎ็็ฑใง"bbb"ใฎbuildใใงใใใซๅคฑๆ๏ผargsๆๅฎใชใ๏ผ
+ // "ccc"ใฏbuildใใใชใ
+ args: []string{},
+ optExpect: []string{},
+ buildResult: map[string]errBuild{
+ "aaa": {},
+ "bbb": {
+ err: errBuildFailed,
+ },
+ },
+ wantErr: true,
+ },
+ {
+ // ใชใใใใฎ็็ฑใง"bbb"ใงใจใฉใผใ็บ็ใใๅ ดๅ๏ผargsๆๅฎใใ๏ผ
+ // "ccc"ใฏๅฎ่กใใใชใ
+ args: []string{"aaa", "bbb", "ccc"},
+ optExpect: []string{},
+ buildResult: map[string]errBuild{
+ "aaa": {},
+ "bbb": {
+ err: errBuildFailed,
+ },
+ },
+ wantErr: true,
+ },
+ {
+ // ไธใใใชใใทใงใณใซใใฃใฆใจใฉใผใ็บ็ใใๅ ดๅ
+ // "bbb"ไปฅ้ใฏๅฎ่กใใใชใ
+ args: []string{"--", "-b", "-c"},
+ optExpect: []string{"-b", "-c"},
+ buildResult: map[string]errBuild{
+ "aaa": {
+ err: errBuildFailed,
+ },
+ },
+ wantErr: true,
+ },
+ {
+ // buildๅฏพ่ฑกใฏไธใใไธใใใชใใทใงใณใๆญฃๅฝใงใใใในใฆๆๅใใๅ ดๅ
+ args: []string{"--", "-a"},
+ optExpect: []string{"-a"},
+ buildResult: map[string]errBuild{
+ "aaa": {},
+ "bbb": {},
+ "ccc": {},
+ },
+ },
+ {
+ // buildๅฏพ่ฑกใจใชใใทใงใณใฎไธก่
ใไธใใ๏ผใในใฆๆๅใใๅ ดๅ
+ args: []string{},
+ optExpect: []string{},
+ buildResult: map[string]errBuild{
+ "aaa": {},
+ "bbb": {},
+ "ccc": {},
+ },
+ },
+ }
+
+ ctx := &grapicmd.Ctx{}
+ err := ctx.Init()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ commandNames := []string{"aaa", "bbb", "ccc"}
+ for _, c := range testCases {
+ loader := moduletesting.NewMockScriptLoader(ctrl)
+
+ loader.EXPECT().Load(gomock.Any()).Return(nil)
+ loader.EXPECT().Names().Return(commandNames)
+ for _, arg := range commandNames {
+ script := moduletesting.NewMockScript(ctrl)
+ loader.EXPECT().Get(arg).Return(script, true).AnyTimes()
+ script.EXPECT().Name().Return(arg).AnyTimes()
+ if buildResult, ok := c.buildResult[arg]; ok {
+ script.EXPECT().Build(gomock.Any(), c.optExpect).Return(buildResult.err)
+ }
+ }
+
+ cmd := newBuildCommandMocked(true, ctx, loader, cli.NopUI)
+ cmd.SetArgs(c.args)
+ err := cmd.Execute()
+
+ if c.wantErr != (err != nil) {
+ t.Errorf("wantErr: %v, gotErr: %v", c.wantErr, err != nil)
+ }
+ }
+}
diff --git a/pkg/grapicmd/cmd/cmd.go b/pkg/grapicmd/cmd/cmd.go
index 869340a6..9edc0d15 100644
--- a/pkg/grapicmd/cmd/cmd.go
+++ b/pkg/grapicmd/cmd/cmd.go
@@ -1,74 +1,42 @@
package cmd
import (
- "path/filepath"
-
- "github.com/izumin5210/clicontrib/pkg/ccmd"
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "github.com/izumin5210/clig/pkg/clib"
"github.com/izumin5210/grapi/pkg/grapicmd"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/command"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/generator"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/script"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/ui"
)
// NewGrapiCommand creates a new command object.
-func NewGrapiCommand(cfg grapicmd.Config) *cobra.Command {
- var err error
+func NewGrapiCommand(ctx *grapicmd.Ctx) *cobra.Command {
+ initErr := ctx.Init()
cmd := &cobra.Command{
- Use: cfg.AppName(),
+ Use: ctx.Build.AppName,
Short: "JSON API framework implemented with gRPC and Gateway",
Long: "",
SilenceErrors: true,
SilenceUsage: true,
- PreRunE: func(cmd *cobra.Command, args []string) error {
- return err
+ PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
+ return errors.WithStack(initErr)
+ },
+ PersistentPostRun: func(cmd *cobra.Command, args []string) {
+ clib.Close()
},
}
- var cfgFile string
- cobra.OnInitialize(func() { cfg.Init(cfgFile) })
- ccmd.HandleLogFlags(cmd)
+ clib.AddLoggingFlags(cmd)
+ clib.SetIO(cmd, ctx.IO)
- cmd.PersistentFlags().StringVar(&cfgFile, "config", "./"+cfg.AppName()+".toml", "config file")
-
- ui := ui.New(cfg.OutWriter(), cfg.InReader())
- generator := generator.New(
- cfg.Fs(),
- ui,
- cfg.RootDir(),
- cfg.ProtocConfig().ProtosDir,
- cfg.ProtocConfig().OutDir,
- cfg.ServerDir(),
- cfg.Version(),
+ cmd.AddCommand(
+ newInitCommand(ctx),
+ newProtocCommand(ctx),
+ newBuildCommand(ctx),
+ clib.NewVersionCommand(ctx.Build),
)
- commandFactory := command.NewFactory(cfg.OutWriter(), cfg.ErrWriter(), cfg.InReader())
- scriptLoader := script.NewLoader(cfg.Fs(), commandFactory, cfg.RootDir())
-
- cmd.AddCommand(newInitCommand(cfg, ui, generator, commandFactory))
- cmd.AddCommand(newGenerateCommand(cfg, ui, generator, commandFactory))
- cmd.AddCommand(newDestroyCommand(cfg, generator))
- cmd.AddCommand(newProtocCommand(cfg, ui, commandFactory))
- cmd.AddCommand(newBuildCommand(cfg, ui, scriptLoader))
- cmd.AddCommand(newVersionCommand(cfg))
-
- if cfg.IsInsideApp() {
- err = scriptLoader.Load(filepath.Join(cfg.RootDir(), "cmd"))
- if err != nil {
- err = errors.Wrap(err, "failed to load user-defined commands")
- }
-
- udCmds := make([]*cobra.Command, 0)
- for _, name := range scriptLoader.Names() {
- udCmds = append(udCmds, newUserDefinedCommand(ui, scriptLoader, name))
- }
- if len(udCmds) > 0 {
- cmd.AddCommand(udCmds...)
- }
- }
+ cmd.AddCommand(newGenerateCommands(ctx)...)
+ cmd.AddCommand(newUserDefinedCommands(ctx)...)
return cmd
}
diff --git a/pkg/grapicmd/cmd/destroy.go b/pkg/grapicmd/cmd/destroy.go
deleted file mode 100644
index d4aea963..00000000
--- a/pkg/grapicmd/cmd/destroy.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package cmd
-
-import (
- "github.com/pkg/errors"
- "github.com/spf13/cobra"
-
- "github.com/izumin5210/grapi/pkg/grapicmd"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
-)
-
-func newDestroyCommand(cfg grapicmd.Config, generator module.Generator) *cobra.Command {
- cmd := &cobra.Command{
- Use: "destroy GENERATOR",
- Short: "Destroy codes",
- Aliases: []string{"d"},
- }
-
- cmd.AddCommand(newDestroyServiceCommand(cfg, generator))
- cmd.AddCommand(newDestroyCommandCommand(cfg, generator))
-
- return cmd
-}
-
-func newDestroyServiceCommand(cfg grapicmd.Config, generator module.ServiceGenerator) *cobra.Command {
- return &cobra.Command{
- Use: "service NAME",
- Short: "Destroy a service",
- SilenceErrors: true,
- SilenceUsage: true,
- Args: cobra.ExactArgs(1),
- RunE: func(cmd *cobra.Command, args []string) error {
- if !cfg.IsInsideApp() {
- return errors.New("destroy command should execute inside a grapi application directory")
- }
-
- return errors.WithStack(errors.WithStack(generator.DestroyService(args[0])))
- },
- }
-}
-
-func newDestroyCommandCommand(cfg grapicmd.Config, generator module.CommandGenerator) *cobra.Command {
- return &cobra.Command{
- Use: "command NAME",
- Short: "Destroy a command",
- SilenceErrors: true,
- SilenceUsage: true,
- Args: cobra.ExactArgs(1),
- RunE: func(cmd *cobra.Command, args []string) error {
- if !cfg.IsInsideApp() {
- return errors.New("destroy command should execute inside a grapi application directory")
- }
-
- return errors.WithStack(generator.DestroyCommand(args[0]))
- },
- }
-}
diff --git a/pkg/grapicmd/cmd/generate.go b/pkg/grapicmd/cmd/generate.go
deleted file mode 100644
index ba58a7cf..00000000
--- a/pkg/grapicmd/cmd/generate.go
+++ /dev/null
@@ -1,111 +0,0 @@
-package cmd
-
-import (
- "github.com/pkg/errors"
- "github.com/spf13/cobra"
-
- "github.com/izumin5210/grapi/pkg/grapicmd"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/usecase"
-)
-
-func newGenerateCommand(cfg grapicmd.Config, ui module.UI, generator module.Generator, commandFactory module.CommandFactory) *cobra.Command {
- cmd := &cobra.Command{
- Use: "generate GENERATOR",
- Short: "Generate new code",
- Aliases: []string{"g", "gen"},
- }
-
- cmd.AddCommand(newGenerateServiceCommand(cfg, ui, generator, commandFactory))
- cmd.AddCommand(newGenerateScaffoldServiceCommand(cfg, ui, generator, commandFactory))
- cmd.AddCommand(newGenerateCommandCommand(cfg, generator))
-
- return cmd
-}
-
-func newGenerateServiceCommand(cfg grapicmd.Config, ui module.UI, generator module.ServiceGenerator, commandFactory module.CommandFactory) *cobra.Command {
- var (
- skipTest bool
- resourceName string
- )
-
- cmd := &cobra.Command{
- Use: "service NAME [METHODS...]",
- Short: "Generate a new service",
- SilenceErrors: true,
- SilenceUsage: true,
- Args: cobra.MinimumNArgs(1),
- RunE: func(cmd *cobra.Command, args []string) error {
- if !cfg.IsInsideApp() {
- return errors.New("geneate command should execut inside a grapi application directory")
- }
-
- ui.Section("Generate service")
- genCfg := module.ServiceGenerationConfig{ResourceName: resourceName, Methods: args[1:], SkipTest: skipTest}
- err := errors.WithStack(generator.GenerateService(args[0], genCfg))
- if err != nil {
- return err
- }
-
- protocUsecase := usecase.NewExecuteProtocUsecase(cfg.ProtocConfig(), cfg.Fs(), ui, commandFactory, cfg.RootDir())
- return errors.WithStack(protocUsecase.Perform())
- },
- }
-
- cmd.PersistentFlags().BoolVarP(&skipTest, "skip-test", "T", false, "Skip test files")
- cmd.PersistentFlags().StringVar(&resourceName, "resource-name", "", "ResourceName to be used")
-
- return cmd
-}
-
-func newGenerateScaffoldServiceCommand(cfg grapicmd.Config, ui module.UI, generator module.ServiceGenerator, commandFactory module.CommandFactory) *cobra.Command {
- var (
- skipTest bool
- resourceName string
- )
-
- cmd := &cobra.Command{
- Use: "scaffold-service NAME",
- Short: "Generate a new service with standard methods",
- SilenceErrors: true,
- SilenceUsage: true,
- Args: cobra.ExactArgs(1),
- RunE: func(cmd *cobra.Command, args []string) error {
- if !cfg.IsInsideApp() {
- return errors.New("geneate command should execut inside a grapi application directory")
- }
-
- ui.Section("Scaffold service")
- genCfg := module.ServiceGenerationConfig{ResourceName: resourceName, SkipTest: skipTest}
- err := errors.WithStack(generator.ScaffoldService(args[0], genCfg))
- if err != nil {
- return err
- }
-
- protocUsecase := usecase.NewExecuteProtocUsecase(cfg.ProtocConfig(), cfg.Fs(), ui, commandFactory, cfg.RootDir())
- return errors.WithStack(protocUsecase.Perform())
- },
- }
-
- cmd.PersistentFlags().BoolVarP(&skipTest, "skip-test", "T", false, "Skip test files")
- cmd.PersistentFlags().StringVar(&resourceName, "resource-name", "", "ResourceName to be used")
-
- return cmd
-}
-
-func newGenerateCommandCommand(cfg grapicmd.Config, generator module.CommandGenerator) *cobra.Command {
- return &cobra.Command{
- Use: "command NAME",
- Short: "Generate a new command",
- SilenceErrors: true,
- SilenceUsage: true,
- Args: cobra.ExactArgs(1),
- RunE: func(cmd *cobra.Command, args []string) error {
- if !cfg.IsInsideApp() {
- return errors.New("geneate command should execut inside a grapi application directory")
- }
-
- return errors.WithStack(generator.GenerateCommand(args[0]))
- },
- }
-}
diff --git a/pkg/grapicmd/cmd/generators.go b/pkg/grapicmd/cmd/generators.go
new file mode 100644
index 00000000..324baad4
--- /dev/null
+++ b/pkg/grapicmd/cmd/generators.go
@@ -0,0 +1,122 @@
+package cmd
+
+import (
+ "context"
+ "strings"
+ "sync"
+
+ "github.com/spf13/cobra"
+ "go.uber.org/zap"
+
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/execx"
+ "github.com/izumin5210/gex/pkg/tool"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/izumin5210/grapi/pkg/grapicmd/di"
+ "github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
+)
+
+func newGenerateCommands(ctx *grapicmd.Ctx) (cmds []*cobra.Command) {
+ gCmd := &cobra.Command{
+ Use: "generate GENERATOR",
+ Short: "Generate a new code",
+ Aliases: []string{"g", "gen"},
+ }
+ dCmd := &cobra.Command{
+ Use: "destroy GENERATOR",
+ Short: "Destroy an existing new code",
+ Aliases: []string{"d"},
+ }
+
+ var (
+ execs []string
+ wg sync.WaitGroup
+ )
+
+ wg.Add(2)
+ cmdNames := make(map[string]struct{}, 100)
+
+ go func() {
+ defer wg.Done()
+ execs = fs.ListExecutableWithPrefix(ctx.FS, "grapi-gen-")
+ }()
+
+ go func() {
+ defer wg.Done()
+
+ toolRepo, err := di.NewToolRepository(ctx)
+ if err != nil {
+ zap.L().Debug("failed to initialize tools repository", zap.Error(err))
+ return
+ }
+
+ tools, err := toolRepo.List(context.TODO())
+ if err != nil {
+ zap.L().Debug("failed to retrieve tools", zap.Error(err))
+ return
+ }
+
+ for _, t := range tools {
+ if !strings.HasPrefix(t.Name(), "grapi-gen-") {
+ continue
+ }
+ if _, ok := cmdNames[t.Name()]; ok {
+ continue
+ }
+ cmdNames[t.Name()] = struct{}{}
+ gCmd.AddCommand(newGenerateCommandByTool(toolRepo, t, "generate"))
+ dCmd.AddCommand(newGenerateCommandByTool(toolRepo, t, "destroy"))
+ }
+ }()
+
+ wg.Wait()
+
+ for _, exec := range execs {
+ if _, ok := cmdNames[exec]; ok {
+ continue
+ }
+ cmdNames[exec] = struct{}{}
+ gCmd.AddCommand(newGenerateCommandByExec(ctx.IO, ctx.Exec, exec, "generate"))
+ dCmd.AddCommand(newGenerateCommandByExec(ctx.IO, ctx.Exec, exec, "destroy"))
+ }
+
+ cmds = append(cmds, gCmd, dCmd)
+
+ return
+}
+
+func newGenerateCommandByTool(repo tool.Repository, t tool.Tool, subCmd string) *cobra.Command {
+ cmd := &cobra.Command{
+ Use: strings.TrimPrefix(t.Name(), "grapi-gen-"),
+ Args: cobra.ArbitraryArgs,
+ RunE: func(_ *cobra.Command, args []string) error {
+ return repo.Run(context.TODO(), t.Name(), append([]string{subCmd}, args...)...)
+ },
+ }
+ cmd.SetUsageFunc(func(*cobra.Command) error {
+ return repo.Run(context.TODO(), t.Name(), subCmd, "--help")
+ })
+ return cmd
+}
+
+func newGenerateCommandByExec(io *clib.IO, exec *execx.Executor, path, subCmd string) *cobra.Command {
+ cmd := &cobra.Command{
+ Use: strings.TrimPrefix(path, "grapi-gen-"),
+ Args: cobra.ArbitraryArgs,
+ RunE: func(_ *cobra.Command, args []string) error {
+ cmd := exec.CommandContext(context.TODO(), path, append([]string{subCmd}, args...)...)
+ cmd.Stdout = io.Out
+ cmd.Stderr = io.Err
+ cmd.Stdin = io.In
+ return cmd.Run()
+ },
+ }
+ cmd.SetUsageFunc(func(*cobra.Command) error {
+ cmd := exec.CommandContext(context.TODO(), path, subCmd, "--help")
+ cmd.Stdout = io.Out
+ cmd.Stderr = io.Err
+ cmd.Stdin = io.In
+ return cmd.Run()
+ })
+ return cmd
+}
diff --git a/pkg/grapicmd/cmd/init.go b/pkg/grapicmd/cmd/init.go
index 738d48bf..19822271 100644
--- a/pkg/grapicmd/cmd/init.go
+++ b/pkg/grapicmd/cmd/init.go
@@ -3,24 +3,18 @@ package cmd
import (
"path/filepath"
- "github.com/izumin5210/clicontrib/pkg/clog"
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "go.uber.org/zap"
+ "github.com/izumin5210/clig/pkg/clib"
"github.com/izumin5210/grapi/pkg/grapicmd"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
+ "github.com/izumin5210/grapi/pkg/grapicmd/di"
"github.com/izumin5210/grapi/pkg/grapicmd/internal/usecase"
)
-var (
- tmplPaths []string
-)
-
-func newInitCommand(cfg grapicmd.Config, ui module.UI, generator module.ProjectGenerator, commandFactory module.CommandFactory) *cobra.Command {
- var (
- depSkipped bool
- headUsed bool
- )
+func newInitCommand(ctx *grapicmd.Ctx) *cobra.Command {
+ var cfg usecase.InitConfig
cmd := &cobra.Command{
Use: "init [name]",
@@ -29,44 +23,47 @@ func newInitCommand(cfg grapicmd.Config, ui module.UI, generator module.ProjectG
SilenceUsage: true,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
- root, err := parseInitArgs(cfg, args)
+ root, err := parseInitArgs(ctx, args)
if err != nil {
return errors.WithStack(err)
}
- clog.Debug("parseInitArgs", "root", root)
+ zap.L().Debug("parseInitArgs", zap.String("root", root))
- u := usecase.NewInitializeProjectUsecase(
- ui,
- generator,
- commandFactory,
- cfg.Version(),
- )
+ u, err := di.NewInitializeProjectUsecase(ctx, clib.Path(root))
+ if err != nil {
+ return errors.WithStack(err)
+ }
- return errors.WithStack(u.Perform(root, depSkipped, headUsed))
+ return errors.WithStack(u.Perform(root, cfg))
},
}
- cmd.PersistentFlags().BoolVarP(&depSkipped, "skip-dep", "D", false, "Don't run dep ensure")
- cmd.PersistentFlags().BoolVar(&headUsed, "HEAD", false, "Use HEAD grapi")
+ cmd.PersistentFlags().StringVar(&cfg.Revision, "revision", "", "Specify grapi revision")
+ cmd.PersistentFlags().StringVar(&cfg.Branch, "branch", "", "Specify grapi branch")
+ cmd.PersistentFlags().StringVar(&cfg.Version, "version", "", "Specify grapi version")
+ cmd.PersistentFlags().BoolVar(&cfg.HEAD, "HEAD", false, "Use HEAD grapi")
+ cmd.PersistentFlags().StringVar(&cfg.GrapiReplacementURL, "replace-grapi", "", "Specify grapi replacement url")
+ cmd.PersistentFlags().StringVarP(&cfg.Package, "package", "p", "", `Package name of the application(default: ".")`)
+ cmd.PersistentFlags().BoolVar(&cfg.Dep, "use-dep", false, "Use dep instead of Modules")
return cmd
}
-func parseInitArgs(cfg grapicmd.Config, args []string) (root string, err error) {
+func parseInitArgs(ctx *grapicmd.Ctx, args []string) (root string, err error) {
if argCnt := len(args); argCnt != 1 {
err = errors.Errorf("invalid argument count: want 1, got %d", argCnt)
return
}
arg := args[0]
- root = cfg.CurrentDir()
+ root = ctx.RootDir.String()
if arg == "." {
return
}
root = arg
if !filepath.IsAbs(arg) {
- root = filepath.Join(cfg.CurrentDir(), arg)
+ root = ctx.RootDir.Join(arg).String()
}
return
}
diff --git a/pkg/grapicmd/cmd/protoc.go b/pkg/grapicmd/cmd/protoc.go
index e6fefadb..ddbf5770 100644
--- a/pkg/grapicmd/cmd/protoc.go
+++ b/pkg/grapicmd/cmd/protoc.go
@@ -1,26 +1,30 @@
package cmd
import (
+ "context"
+
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/izumin5210/grapi/pkg/grapicmd"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/usecase"
+ "github.com/izumin5210/grapi/pkg/grapicmd/di"
)
-func newProtocCommand(cfg grapicmd.Config, ui module.UI, commandFactory module.CommandFactory) *cobra.Command {
+func newProtocCommand(ctx *grapicmd.Ctx) *cobra.Command {
return &cobra.Command{
Use: "protoc",
Short: "Run protoc",
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
- if !cfg.IsInsideApp() {
+ if !ctx.IsInsideApp() {
return errors.New("protoc command should be execute inside a grapi application directory")
}
- u := usecase.NewExecuteProtocUsecase(cfg.ProtocConfig(), cfg.Fs(), ui, commandFactory, cfg.RootDir())
- return errors.WithStack(u.Perform())
+ protocw, err := di.NewProtocWrapper(ctx)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ return errors.WithStack(protocw.Exec(context.TODO()))
},
}
}
diff --git a/pkg/grapicmd/cmd/user_defined.go b/pkg/grapicmd/cmd/user_defined.go
index 54d4820f..e60fe52b 100644
--- a/pkg/grapicmd/cmd/user_defined.go
+++ b/pkg/grapicmd/cmd/user_defined.go
@@ -3,11 +3,37 @@ package cmd
import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "github.com/srvc/appctx"
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/izumin5210/grapi/pkg/grapicmd/di"
"github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
)
-func newUserDefinedCommand(ui module.UI, scriptLoader module.ScriptLoader, name string) *cobra.Command {
+func newUserDefinedCommands(ctx *grapicmd.Ctx) (cmds []*cobra.Command) {
+ if !ctx.IsInsideApp() {
+ return
+ }
+
+ scriptLoader := di.NewScriptLoader(ctx)
+
+ err := scriptLoader.Load(ctx.RootDir.Join("cmd").String())
+ if err != nil {
+ // TODO: log
+ return
+ }
+
+ ui := di.NewUI(ctx)
+
+ for _, name := range scriptLoader.Names() {
+ cmds = append(cmds, newUserDefinedCommand(ui, scriptLoader, name))
+ }
+
+ return
+}
+
+func newUserDefinedCommand(ui cli.UI, scriptLoader module.ScriptLoader, name string) *cobra.Command {
return &cobra.Command{
Use: name + " [-- BUILD_OPTIONS] [-- RUN_ARGS]",
SilenceErrors: true,
@@ -15,7 +41,8 @@ func newUserDefinedCommand(ui module.UI, scriptLoader module.ScriptLoader, name
RunE: func(c *cobra.Command, args []string) (err error) {
script, ok := scriptLoader.Get(name)
if !ok {
- err = errors.Wrapf(err, "faild to find subcommand %d", name)
+ err = errors.Wrapf(err, "faild to find subcommand %s", name)
+ return
}
pos := len(args)
@@ -33,15 +60,17 @@ func newUserDefinedCommand(ui module.UI, scriptLoader module.ScriptLoader, name
runArgs = args[pos+1:]
}
+ ctx := appctx.Global()
+
ui.Section(script.Name())
ui.Subsection("Building...")
- err = errors.WithStack(script.Build(buildArgs...))
+ err = errors.WithStack(script.Build(ctx, buildArgs...))
if err != nil {
return
}
ui.Subsection("Starting...")
- return errors.WithStack(script.Run(runArgs...))
+ return errors.WithStack(script.Run(ctx, runArgs...))
},
}
}
diff --git a/pkg/grapicmd/cmd/user_defined_test.go b/pkg/grapicmd/cmd/user_defined_test.go
index 9020d2f9..29f4ef82 100644
--- a/pkg/grapicmd/cmd/user_defined_test.go
+++ b/pkg/grapicmd/cmd/user_defined_test.go
@@ -4,6 +4,7 @@ import (
"testing"
"github.com/golang/mock/gomock"
+ "github.com/izumin5210/grapi/pkg/cli"
"github.com/izumin5210/grapi/pkg/grapicmd/internal/module/testing"
)
@@ -67,9 +68,6 @@ func Test_userDefinedCommand(t *testing.T) {
},
}
- ui := moduletesting.NewMockUI(ctrl)
- ui.EXPECT().Section(gomock.Any()).AnyTimes()
- ui.EXPECT().Subsection(gomock.Any()).AnyTimes()
name := "testcommand"
for _, c := range cases {
@@ -77,14 +75,14 @@ func Test_userDefinedCommand(t *testing.T) {
script := moduletesting.NewMockScript(ctrl)
var verbose bool
- cmd := newUserDefinedCommand(ui, loader, name)
+ cmd := newUserDefinedCommand(cli.NopUI, loader, name)
cmd.SetArgs(c.args)
cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "")
loader.EXPECT().Get(name).Return(script, true)
- script.EXPECT().Build(c.buildArgs...)
- script.EXPECT().Run(c.runArgs...)
+ script.EXPECT().Build(gomock.Any(), c.buildArgs...)
+ script.EXPECT().Run(gomock.Any(), c.runArgs...)
script.EXPECT().Name().Return(name)
err := cmd.Execute()
diff --git a/pkg/grapicmd/cmd/version.go b/pkg/grapicmd/cmd/version.go
deleted file mode 100644
index 98426301..00000000
--- a/pkg/grapicmd/cmd/version.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package cmd
-
-import (
- "github.com/spf13/cobra"
-
- "github.com/izumin5210/grapi/pkg/grapicmd"
-)
-
-func newVersionCommand(cfg grapicmd.Config) *cobra.Command {
- return &cobra.Command{
- Use: "version",
- Short: "Print version information",
- Long: "Print version information",
- SilenceErrors: true,
- SilenceUsage: true,
- Run: func(cmd *cobra.Command, _ []string) {
- cmd.Printf("%s %s %s (%s %s)\n", cfg.AppName(), cfg.Version(), cfg.ReleaseType(), cfg.BuildDate(), cfg.Revision())
- },
- }
-}
diff --git a/pkg/grapicmd/config.go b/pkg/grapicmd/config.go
deleted file mode 100644
index b5feaa1e..00000000
--- a/pkg/grapicmd/config.go
+++ /dev/null
@@ -1,134 +0,0 @@
-package grapicmd
-
-import (
- "io"
-
- "github.com/spf13/afero"
- "github.com/spf13/viper"
-
- "github.com/izumin5210/grapi/pkg/grapicmd/protoc"
- "github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
-)
-
-// Config stores general setting params and provides accessors for them.
-type Config interface {
- Init(cfgFile string)
- Fs() afero.Fs
- CurrentDir() string
- RootDir() string
- IsInsideApp() bool
- AppName() string
- Version() string
- Revision() string
- BuildDate() string
- ReleaseType() string
- InReader() io.Reader
- OutWriter() io.Writer
- ErrWriter() io.Writer
- ServerDir() string
- ProtocConfig() *protoc.Config
-}
-
-// NewConfig creates new Config object.
-func NewConfig(
- currentDir string,
- appName, version, revision string,
- buildDate, releaseType string,
- in io.Reader,
- out, err io.Writer,
-) Config {
- afs := afero.NewOsFs()
- rootDir, insideApp := fs.LookupRoot(afs, currentDir)
- return &config{
- v: viper.New(),
- fs: afs,
- currentDir: currentDir,
- rootDir: rootDir,
- insideApp: insideApp,
- appName: appName,
- version: version,
- revision: revision,
- buildDate: buildDate,
- releaseType: releaseType,
- in: in,
- out: out,
- err: err,
- }
-}
-
-type config struct {
- cfgFile string
- v *viper.Viper
- fs afero.Fs
- currentDir, rootDir string
- insideApp bool
- appName, version, revision string
- buildDate, releaseType string
- in io.Reader
- out, err io.Writer
- readConfigErr error
-}
-
-func (c *config) Init(cfgFile string) {
- c.cfgFile = cfgFile
- c.v.SetConfigFile(c.cfgFile)
- c.readConfigErr = c.v.ReadInConfig()
-}
-
-func (c *config) Fs() afero.Fs {
- return c.fs
-}
-
-func (c *config) CurrentDir() string {
- return c.currentDir
-}
-
-func (c *config) RootDir() string {
- return c.rootDir
-}
-
-func (c *config) IsInsideApp() bool {
- return c.insideApp
-}
-
-func (c *config) AppName() string {
- return c.appName
-}
-
-func (c *config) Version() string {
- return c.version
-}
-
-func (c *config) Revision() string {
- return c.revision
-}
-
-func (c *config) BuildDate() string {
- return c.buildDate
-}
-
-func (c *config) ReleaseType() string {
- return c.releaseType
-}
-
-func (c *config) InReader() io.Reader {
- return c.in
-}
-
-func (c *config) OutWriter() io.Writer {
- return c.out
-}
-
-func (c *config) ErrWriter() io.Writer {
- return c.err
-}
-
-func (c *config) ServerDir() string {
- return c.v.GetString("grapi.server_dir")
-}
-
-func (c *config) ProtocConfig() *protoc.Config {
- cfg := &protoc.Config{}
- c.v.UnmarshalKey("protoc", cfg)
- return cfg
-}
diff --git a/pkg/grapicmd/context.go b/pkg/grapicmd/context.go
new file mode 100644
index 00000000..eceab49e
--- /dev/null
+++ b/pkg/grapicmd/context.go
@@ -0,0 +1,128 @@
+package grapicmd
+
+import (
+ "os"
+ "path/filepath"
+
+ "github.com/google/wire"
+ "github.com/pkg/errors"
+ "github.com/spf13/afero"
+ "github.com/spf13/viper"
+ "go.uber.org/zap"
+
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/execx"
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/protoc"
+)
+
+// Ctx contains the runtime context of grpai.
+type Ctx struct {
+ FS afero.Fs
+ Viper *viper.Viper
+ Exec *execx.Executor
+ IO *clib.IO
+
+ RootDir cli.RootDir
+ insideApp bool
+
+ Config Config
+ Build clib.Build
+ ProtocConfig protoc.Config
+}
+
+// Config stores general setting params and provides accessors for them.
+type Config struct {
+ Package string
+ Grapi struct {
+ ServerDir string
+ }
+}
+
+// Init initializes the runtime context.
+func (c *Ctx) Init() error {
+ if c.RootDir.String() == "" {
+ dir, _ := os.Getwd()
+ c.RootDir = cli.RootDir{clib.Path(dir)}
+ }
+
+ if c.IO == nil {
+ c.IO = clib.Stdio()
+ }
+
+ if c.FS == nil {
+ c.FS = afero.NewOsFs()
+ }
+
+ if c.Viper == nil {
+ c.Viper = viper.New()
+ }
+
+ c.Viper.SetFs(c.FS)
+
+ if c.Exec == nil {
+ c.Exec = execx.New()
+ }
+
+ if c.Build.AppName == "" {
+ c.Build.AppName = "grapi"
+ }
+
+ return errors.WithStack(c.loadConfig())
+}
+
+func (c *Ctx) loadConfig() error {
+ c.Viper.SetConfigName(c.Build.AppName)
+ for dir := c.RootDir.String(); dir != "/"; dir = filepath.Dir(dir) {
+ c.Viper.AddConfigPath(dir)
+ }
+
+ err := c.Viper.ReadInConfig()
+ if err != nil {
+ zap.L().Info("failed to find config file", zap.Error(err))
+ return nil
+ }
+
+ c.insideApp = true
+ c.RootDir = cli.RootDir{clib.Path(filepath.Dir(c.Viper.ConfigFileUsed()))}
+
+ err = c.Viper.Unmarshal(&c.Config)
+ if err != nil {
+ zap.L().Warn("failed to parse config", zap.Error(err))
+ return errors.WithStack(err)
+ }
+
+ err = c.Viper.UnmarshalKey("protoc", &c.ProtocConfig)
+ if err != nil {
+ zap.L().Warn("failed to parse protoc config", zap.Error(err))
+ return errors.WithStack(err)
+ }
+
+ return nil
+}
+
+// IsInsideApp returns true if the current working directory is inside a grapi project.
+func (c *Ctx) IsInsideApp() bool {
+ return c.insideApp
+}
+
+// CtxSet is a provider set that includes modules contained in Ctx.
+var CtxSet = wire.NewSet(
+ ProvideFS,
+ ProvideViper,
+ ProvideExec,
+ ProvideIO,
+ ProvideRootDir,
+ ProvideConfig,
+ ProvideBuildConfig,
+ ProvideProtocConfig,
+)
+
+func ProvideFS(c *Ctx) afero.Fs { return c.FS }
+func ProvideViper(c *Ctx) *viper.Viper { return c.Viper }
+func ProvideExec(c *Ctx) *execx.Executor { return c.Exec }
+func ProvideIO(c *Ctx) *clib.IO { return c.IO }
+func ProvideRootDir(c *Ctx) cli.RootDir { return c.RootDir }
+func ProvideConfig(c *Ctx) *Config { return &c.Config }
+func ProvideBuildConfig(c *Ctx) *clib.Build { return &c.Build }
+func ProvideProtocConfig(c *Ctx) *protoc.Config { return &c.ProtocConfig }
diff --git a/pkg/grapicmd/context_test.go b/pkg/grapicmd/context_test.go
new file mode 100644
index 00000000..774c1caa
--- /dev/null
+++ b/pkg/grapicmd/context_test.go
@@ -0,0 +1,124 @@
+package grapicmd_test
+
+import (
+ "testing"
+
+ "github.com/spf13/afero"
+
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+)
+
+func TestCtx(t *testing.T) {
+ root := cli.RootDir{clib.Path("/go/src/awesomeapp")}
+ cwd := root.Join("api").String()
+
+ orDie := func(t *testing.T, err error) {
+ t.Helper()
+ if err != nil {
+ t.Fatalf("unexpected error %v", err)
+ }
+ }
+
+ fs := afero.NewMemMapFs()
+ orDie(t, fs.MkdirAll(cwd, 0755))
+ orDie(t, afero.WriteFile(fs, root.Join("grapi.toml").String(), []byte(`
+package = "awesomeapp"
+
+[grapi]
+server_dir = "./app/server"
+
+[protoc]
+protos_dir = "./api/protos"
+out_dir = "./api"
+import_dirs = [
+ "./api/protos",
+ "./vendor/github.com/grpc-ecosystem/grpc-gateway",
+ "./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis",
+]
+
+ [[protoc.plugins]]
+ name = "go"
+ args = { plugins = "grpc", paths = "source_relative" }
+
+ [[protoc.plugins]]
+ name = "grpc-gateway"
+ args = { logtostderr = true, paths = "source_relative" }
+
+ [[protoc.plugins]]
+ name = "swagger"
+ args = { logtostderr = true }
+`), 0644))
+
+ ctx := &grapicmd.Ctx{FS: fs, RootDir: cli.RootDir{clib.Path(cwd)}}
+
+ err := ctx.Init()
+
+ if err != nil {
+ t.Errorf("Init() returned %v", err)
+ }
+
+ if got, want := ctx.RootDir, root; got != want {
+ t.Errorf("RootDir is %q, want %q", got, want)
+ }
+
+ if got, want := ctx.IsInsideApp(), true; got != want {
+ t.Errorf("IsInsideApp() returned %t, want %t", got, want)
+ }
+
+ if got, want := ctx.Config.Package, "awesomeapp"; got != want {
+ t.Errorf("Config.Package is %q, want %q", got, want)
+ }
+
+ if got, want := ctx.ProtocConfig.ProtosDir, "./api/protos"; got != want {
+ t.Errorf("ProtocConfig.ProtosDir is %q, want %q", got, want)
+ }
+
+ if got, want := len(ctx.ProtocConfig.Plugins), 3; got != want {
+ t.Errorf("ProtocConfig has %d plugins, want %d", got, want)
+ }
+}
+
+func TestCtx_outsideApp(t *testing.T) {
+ root := cli.RootDir{clib.Path("/go/src/awesomeapp")}
+ cwd := root.Join("api").String()
+
+ orDie := func(t *testing.T, err error) {
+ t.Helper()
+ if err != nil {
+ t.Fatalf("unexpected error %v", err)
+ }
+ }
+
+ fs := afero.NewMemMapFs()
+ orDie(t, fs.MkdirAll(cwd, 0755))
+
+ ctx := &grapicmd.Ctx{FS: fs, RootDir: cli.RootDir{clib.Path(cwd)}}
+
+ err := ctx.Init()
+
+ if err != nil {
+ t.Errorf("Init() returned %v", err)
+ }
+
+ if got, want := ctx.RootDir.String(), cwd; got != want {
+ t.Errorf("RootDir is %q, want %q", got, want)
+ }
+
+ if got, want := ctx.IsInsideApp(), false; got != want {
+ t.Errorf("IsInsideApp() returned %t, want %t", got, want)
+ }
+
+ if got, want := ctx.Config.Package, ""; got != want {
+ t.Errorf("Config.Package is %q, want %q", got, want)
+ }
+
+ if got, want := ctx.ProtocConfig.ProtosDir, ""; got != want {
+ t.Errorf("ProtocConfig.ProtosDir is %q, want %q", got, want)
+ }
+
+ if got, want := len(ctx.ProtocConfig.Plugins), 0; got != want {
+ t.Errorf("ProtocConfig has %d plugins, want %d", got, want)
+ }
+}
diff --git a/pkg/grapicmd/di/providers.go b/pkg/grapicmd/di/providers.go
new file mode 100644
index 00000000..2848f913
--- /dev/null
+++ b/pkg/grapicmd/di/providers.go
@@ -0,0 +1,43 @@
+package di
+
+import (
+ "net/http"
+
+ "github.com/google/wire"
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/execx"
+ "github.com/rakyll/statik/fs"
+ "github.com/spf13/afero"
+
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
+ "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/script"
+ "github.com/izumin5210/grapi/pkg/grapicmd/internal/usecase"
+ "github.com/izumin5210/grapi/pkg/protoc"
+)
+
+func ProvideScriptLoader(ctx *grapicmd.Ctx, io *clib.IO, exec *execx.Executor) module.ScriptLoader {
+ return script.NewLoader(ctx.FS, io, exec, ctx.RootDir.String())
+}
+
+func ProvideGenerator(ctx *grapicmd.Ctx, ui cli.UI, fs afero.Fs, tmplFs http.FileSystem, outDir clib.Path) gencmd.Generator {
+ return gencmd.NewGenerator(
+ fs,
+ ui,
+ outDir,
+ tmplFs,
+ nil,
+ )
+}
+
+var Set = wire.NewSet(
+ grapicmd.CtxSet,
+ protoc.WrapperSet,
+ cli.UIInstance,
+ ProvideScriptLoader,
+ ProvideGenerator,
+ fs.New,
+ usecase.NewInitializeProjectUsecase,
+)
diff --git a/pkg/grapicmd/di/wire.go b/pkg/grapicmd/di/wire.go
new file mode 100644
index 00000000..456a55c7
--- /dev/null
+++ b/pkg/grapicmd/di/wire.go
@@ -0,0 +1,40 @@
+//+build wireinject
+
+package di
+
+import (
+ "github.com/google/wire"
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/gex/pkg/tool"
+
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
+ "github.com/izumin5210/grapi/pkg/grapicmd/internal/usecase"
+ "github.com/izumin5210/grapi/pkg/protoc"
+)
+
+func NewUI(*grapicmd.Ctx) cli.UI {
+ wire.Build(Set)
+ return nil
+}
+
+func NewScriptLoader(*grapicmd.Ctx) module.ScriptLoader {
+ wire.Build(Set)
+ return nil
+}
+
+func NewToolRepository(*grapicmd.Ctx) (tool.Repository, error) {
+ wire.Build(Set)
+ return nil, nil
+}
+
+func NewProtocWrapper(*grapicmd.Ctx) (protoc.Wrapper, error) {
+ wire.Build(Set)
+ return nil, nil
+}
+
+func NewInitializeProjectUsecase(*grapicmd.Ctx, clib.Path) (usecase.InitializeProjectUsecase, error) {
+ wire.Build(Set)
+ return nil, nil
+}
diff --git a/pkg/grapicmd/di/wire_gen.go b/pkg/grapicmd/di/wire_gen.go
new file mode 100644
index 00000000..d411510b
--- /dev/null
+++ b/pkg/grapicmd/di/wire_gen.go
@@ -0,0 +1,77 @@
+// Code generated by Wire. DO NOT EDIT.
+
+//go:generate wire
+//+build !wireinject
+
+package di
+
+import (
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/gex/pkg/tool"
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
+ "github.com/izumin5210/grapi/pkg/grapicmd/internal/usecase"
+ "github.com/izumin5210/grapi/pkg/protoc"
+ "github.com/rakyll/statik/fs"
+)
+
+// Injectors from wire.go:
+
+func NewUI(ctx *grapicmd.Ctx) cli.UI {
+ io := grapicmd.ProvideIO(ctx)
+ ui := cli.UIInstance(io)
+ return ui
+}
+
+func NewScriptLoader(ctx *grapicmd.Ctx) module.ScriptLoader {
+ io := grapicmd.ProvideIO(ctx)
+ executor := grapicmd.ProvideExec(ctx)
+ scriptLoader := ProvideScriptLoader(ctx, io, executor)
+ return scriptLoader
+}
+
+func NewToolRepository(ctx *grapicmd.Ctx) (tool.Repository, error) {
+ fs := grapicmd.ProvideFS(ctx)
+ executor := grapicmd.ProvideExec(ctx)
+ io := grapicmd.ProvideIO(ctx)
+ rootDir := grapicmd.ProvideRootDir(ctx)
+ config := protoc.ProvideGexConfig(fs, executor, io, rootDir)
+ repository, err := protoc.ProvideToolRepository(config)
+ if err != nil {
+ return nil, err
+ }
+ return repository, nil
+}
+
+func NewProtocWrapper(ctx *grapicmd.Ctx) (protoc.Wrapper, error) {
+ config := grapicmd.ProvideProtocConfig(ctx)
+ fs := grapicmd.ProvideFS(ctx)
+ executor := grapicmd.ProvideExec(ctx)
+ io := grapicmd.ProvideIO(ctx)
+ ui := cli.UIInstance(io)
+ rootDir := grapicmd.ProvideRootDir(ctx)
+ gexConfig := protoc.ProvideGexConfig(fs, executor, io, rootDir)
+ repository, err := protoc.ProvideToolRepository(gexConfig)
+ if err != nil {
+ return nil, err
+ }
+ wrapper := protoc.NewWrapper(config, fs, executor, ui, repository, rootDir)
+ return wrapper, nil
+}
+
+func NewInitializeProjectUsecase(ctx *grapicmd.Ctx, path clib.Path) (usecase.InitializeProjectUsecase, error) {
+ io := grapicmd.ProvideIO(ctx)
+ ui := cli.UIInstance(io)
+ aferoFs := grapicmd.ProvideFS(ctx)
+ fileSystem, err := fs.New()
+ if err != nil {
+ return nil, err
+ }
+ generator := ProvideGenerator(ctx, ui, aferoFs, fileSystem, path)
+ executor := grapicmd.ProvideExec(ctx)
+ rootDir := grapicmd.ProvideRootDir(ctx)
+ config := protoc.ProvideGexConfig(aferoFs, executor, io, rootDir)
+ initializeProjectUsecase := usecase.NewInitializeProjectUsecase(ui, aferoFs, generator, io, executor, config)
+ return initializeProjectUsecase, nil
+}
diff --git a/pkg/grapicmd/internal/module/command.go b/pkg/grapicmd/internal/module/command.go
deleted file mode 100644
index 865b53f4..00000000
--- a/pkg/grapicmd/internal/module/command.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package module
-
-// CommandFactory is an interface for executing commands.
-type CommandFactory interface {
- Create(nameAndArgs []string) Command
-}
-
-// Command is an interface of executable and configurable command object.
-type Command interface {
- SetDir(dir string) Command
- AddEnv(key, value string) Command
- ConnectIO() Command
- Exec() ([]byte, error)
-}
diff --git a/pkg/grapicmd/internal/module/command/command.go b/pkg/grapicmd/internal/module/command/command.go
deleted file mode 100644
index d6cd5942..00000000
--- a/pkg/grapicmd/internal/module/command/command.go
+++ /dev/null
@@ -1,121 +0,0 @@
-package command
-
-import (
- "bytes"
- "io"
- "os"
- "os/exec"
- "os/signal"
- "sync"
-
- "github.com/izumin5210/clicontrib/pkg/clog"
- "github.com/pkg/errors"
-
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
-)
-
-type command struct {
- name string
- args []string
- dir string
- env []string
- ioConnected bool
- outWriter io.Writer
- errWriter io.Writer
- inReader io.Reader
-}
-
-func (c *command) SetDir(dir string) module.Command {
- c.dir = dir
- return c
-}
-
-func (c *command) AddEnv(key, value string) module.Command {
- c.env = append(c.env, key+"="+value)
- return c
-}
-
-func (c *command) ConnectIO() module.Command {
- c.ioConnected = true
- return c
-}
-
-func (c *command) Exec() (out []byte, err error) {
- var wg sync.WaitGroup
-
- cmd := c.build()
-
- sigCh := make(chan os.Signal, 1)
- signal.Notify(sigCh)
- wg.Add(1)
-
- go func() {
- defer wg.Done()
- defer recover()
- for sig := range sigCh {
- clog.Debug("signal received", "signal", sig)
- if cmd.ProcessState == nil || cmd.ProcessState.Exited() {
- break
- }
- cmd.Process.Signal(sig)
- }
- }()
-
- clog.Debug("execute", "command", cmd.Args, "dir", cmd.Dir)
- if c.ioConnected {
- var (
- buf bytes.Buffer
- wg sync.WaitGroup
- )
-
- closers := make([]func() error, 0, 2)
-
- outReader, eerr := cmd.StdoutPipe()
- if eerr != nil {
- err = errors.WithStack(eerr)
- return
- }
- errReader, eerr := cmd.StderrPipe()
- if eerr != nil {
- err = errors.WithStack(eerr)
- return
- }
-
- wg.Add(2)
- go func() {
- defer wg.Done()
- io.Copy(c.outWriter, io.TeeReader(outReader, &buf))
- }()
- closers = append(closers, outReader.Close)
- go func() {
- defer wg.Done()
- io.Copy(c.errWriter, io.TeeReader(errReader, &buf))
- }()
- closers = append(closers, errReader.Close)
-
- cmd.Stdin = c.inReader
-
- err = cmd.Run()
- for _, c := range closers {
- c()
- }
- wg.Wait()
-
- out = buf.Bytes()
- } else {
- out, err = cmd.CombinedOutput()
- }
-
- signal.Reset()
- close(sigCh)
-
- wg.Wait()
- return
-}
-
-func (c *command) build() *exec.Cmd {
- cmd := exec.Command(c.name, c.args...)
- cmd.Dir = c.dir
- cmd.Env = c.env
- return cmd
-}
diff --git a/pkg/grapicmd/internal/module/command/factory.go b/pkg/grapicmd/internal/module/command/factory.go
deleted file mode 100644
index fcff08d3..00000000
--- a/pkg/grapicmd/internal/module/command/factory.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package command
-
-import (
- "io"
- "os"
-
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
-)
-
-// NewFactory creates a new module.CommandFactory instance.
-func NewFactory(
- outWriter io.Writer,
- errWriter io.Writer,
- inReader io.Reader,
-) module.CommandFactory {
- return &factory{
- outWriter: outWriter,
- errWriter: errWriter,
- inReader: inReader,
- }
-}
-
-type factory struct {
- outWriter io.Writer
- errWriter io.Writer
- inReader io.Reader
-}
-
-func (f *factory) Create(nameAndArgs []string) module.Command {
- name := nameAndArgs[0]
- args := make([]string, 0, len(nameAndArgs)-1)
- if len(nameAndArgs) > 1 {
- args = nameAndArgs[1:]
- }
- return &command{
- name: name,
- args: args,
- env: os.Environ(),
- outWriter: f.outWriter,
- errWriter: f.errWriter,
- inReader: f.inReader,
- }
-}
diff --git a/pkg/grapicmd/internal/module/gen.go b/pkg/grapicmd/internal/module/gen.go
index 51f8c44c..8767ef9a 100644
--- a/pkg/grapicmd/internal/module/gen.go
+++ b/pkg/grapicmd/internal/module/gen.go
@@ -1,5 +1,4 @@
-//go:generate mockgen -package=moduletesting -source=ui.go -destination=testing/ui_mock.go
-//go:generate mockgen -package=moduletesting -imports=.=github.com/izumin5210/grapi/pkg/grapicmd/internal/module -source=generator.go -destination=testing/generator_mock.go
-//go:generate mockgen -package=moduletesting -imports=.=github.com/izumin5210/grapi/pkg/grapicmd/internal/module -source=script.go -destination=testing/script_mock.go
+//go:generate mockgen -package=moduletesting -source=generator.go -destination=testing/generator_mock.go
+//go:generate mockgen -package=moduletesting -source=script.go -destination=testing/script_mock.go
package module
diff --git a/pkg/grapicmd/internal/module/generator.go b/pkg/grapicmd/internal/module/generator.go
index 247ad5f4..3e4ff554 100644
--- a/pkg/grapicmd/internal/module/generator.go
+++ b/pkg/grapicmd/internal/module/generator.go
@@ -3,31 +3,9 @@ package module
// Generator creates files from templates and given params.
type Generator interface {
ProjectGenerator
- ServiceGenerator
- CommandGenerator
}
// ProjectGenerator is an interface to build a new project.
type ProjectGenerator interface {
- GenerateProject(rootDir string, useHead bool) error
-}
-
-// ServiceGenerator is an interface to create or destroy gRPC services and implementations.
-type ServiceGenerator interface {
- GenerateService(name string, cfg ServiceGenerationConfig) error
- ScaffoldService(name string, cfg ServiceGenerationConfig) error
- DestroyService(name string) error
-}
-
-// ServiceGenerationConfig contains configurations for generating a new service.
-type ServiceGenerationConfig struct {
- ResourceName string
- Methods []string
- SkipTest bool
-}
-
-// CommandGenerator is an interface to create or destroy user-defined command tempates.
-type CommandGenerator interface {
- GenerateCommand(name string) error
- DestroyCommand(name string) error
+ GenerateProject(rootDir, pkgName string) error
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-.gitignore b/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-.gitignore
deleted file mode 100644
index 9746e024..00000000
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-*.so
-/vendor
-/bin
-/tmp
-
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-Gopkg.toml b/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-Gopkg.toml
deleted file mode 100644
index e162e021..00000000
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-Gopkg.toml
+++ /dev/null
@@ -1,10 +0,0 @@
-required = [
- "github.com/golang/protobuf/protoc-gen-go",
- "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway",
- "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger",
-]
-
-[[constraint]]
- name = "github.com/izumin5210/grapi"
- version = ""
-
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-grapi.toml b/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-grapi.toml
deleted file mode 100644
index 2b9e69ca..00000000
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-grapi.toml
+++ /dev/null
@@ -1,26 +0,0 @@
-[grapi]
-server_dir = "./app/server"
-
-[protoc]
-protos_dir = "./api/protos"
-out_dir = "./api"
-import_dirs = [
- "./vendor/github.com/grpc-ecosystem/grpc-gateway",
- "./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis",
-]
-
- [[protoc.plugins]]
- path = "./vendor/github.com/golang/protobuf/protoc-gen-go"
- name = "go"
- args = { plugins = "grpc", paths = "source_relative" }
-
- [[protoc.plugins]]
- path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"
- name = "grpc-gateway"
- args = { logtostderr = true }
-
- [[protoc.plugins]]
- path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"
- name = "swagger"
- args = { logtostderr = true }
-
diff --git a/pkg/grapicmd/internal/module/generator/base.go b/pkg/grapicmd/internal/module/generator/base.go
deleted file mode 100644
index 3b624191..00000000
--- a/pkg/grapicmd/internal/module/generator/base.go
+++ /dev/null
@@ -1,159 +0,0 @@
-package generator
-
-import (
- "path/filepath"
- "sort"
- "strings"
-
- "github.com/izumin5210/clicontrib/pkg/clog"
- assets "github.com/jessevdk/go-assets"
- "github.com/pkg/errors"
- "github.com/spf13/afero"
-
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
- "github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
-)
-
-type baseGenerator interface {
- Generate(dir string, data interface{}, cfg generationConfig) error
- Destroy(dir string, data interface{}) error
-}
-
-func newBaseGenerator(tmplFs *assets.FileSystem, fs afero.Fs, ui module.UI) baseGenerator {
- return &baseGeneratorImpl{
- tmplFs: tmplFs,
- fs: fs,
- ui: ui,
- }
-}
-
-type baseGeneratorImpl struct {
- tmplFs *assets.FileSystem
- fs afero.Fs
- ui module.UI
-}
-
-type generationConfig struct {
- skipTest bool
-}
-
-func (g *baseGeneratorImpl) Generate(dir string, data interface{}, genCfg generationConfig) error {
- for _, tmplPath := range g.sortedEntryPaths() {
- if genCfg.skipTest && strings.HasSuffix(tmplPath, "_test.go.tmpl") {
- continue
- }
- entry := g.tmplFs.Files[tmplPath]
- path, err := TemplateString(strings.TrimSuffix(tmplPath, ".tmpl")).Compile(data)
- if err != nil {
- return errors.Wrapf(err, "failed to parse path: %s", path)
- }
- absPath := filepath.Join(dir, path)
- dirPath := filepath.Dir(absPath)
-
- // create directory if not exists
- if err := fs.CreateDirIfNotExists(g.fs, dirPath); err != nil {
- return errors.WithStack(err)
- }
-
- // generate content
- body, err := TemplateString(string(entry.Data)).Compile(data)
- if err != nil {
- return errors.Wrapf(err, "failed to generate %s", path)
- }
-
- // check existed entries
- st := statusCreate
- if ok, err := afero.Exists(g.fs, absPath); err != nil {
- // TODO: handle an error
- st = statusSkipped
- } else if ok {
- existedBody, err := afero.ReadFile(g.fs, absPath)
- if err != nil {
- // TODO: handle an error
- st = statusSkipped
- }
- if string(existedBody) == body {
- st = statusIdentical
- } else {
- st = statusSkipped
- g.ui.ItemFailure(path[1:] + " is conflicted.")
- if ok, err := g.ui.Confirm("Overwite it?"); err != nil {
- clog.Error("failed to confirm to apply", "error", err)
- return errors.WithStack(err)
- } else if ok {
- st = statusCreate
- }
- }
- }
-
- // create
- if st.ShouldCreate() {
- err = afero.WriteFile(g.fs, absPath, []byte(body), 0644)
- if err != nil {
- return errors.Wrapf(err, "failed to write %s", path)
- }
- }
-
- st.Fprint(g.ui, path[1:])
- }
-
- return nil
-}
-
-func (g *baseGeneratorImpl) Destroy(dir string, data interface{}) error {
- for _, tmplPath := range g.sortedEntryPaths() {
- path, err := TemplateString(strings.TrimSuffix(tmplPath, ".tmpl")).Compile(data)
- if err != nil {
- return errors.Wrapf(err, "failed to parse path: %s", path)
- }
- absPath := filepath.Join(dir, path)
-
- st := statusSkipped
- if ok, err := afero.Exists(g.fs, absPath); err != nil {
- g.ui.ItemFailure("failed to get " + path[1:])
- return errors.WithStack(err)
- } else if ok {
- err = g.fs.Remove(absPath)
- if err != nil {
- g.ui.ItemFailure("failed to remove " + path[1:])
- return errors.WithStack(err)
- }
- st = statusDelete
- }
-
- st.Fprint(g.ui, path[1:])
-
- dirPath := filepath.Dir(path)
- absDirPath := filepath.Dir(absPath)
- if ok, err := afero.DirExists(g.fs, absDirPath); err == nil && ok {
- if r, err := afero.Glob(g.fs, filepath.Join(absDirPath, "*")); err == nil && len(r) == 0 {
- err = g.fs.Remove(absDirPath)
- if err != nil {
- g.ui.ItemFailure("failed to remove " + dirPath[1:])
- return errors.Wrapf(err, "failed to remove %q", dirPath[1:])
- }
- statusDelete.Fprint(g.ui, dirPath[1:])
- }
- }
- }
-
- return nil
-}
-
-func (g *baseGeneratorImpl) sortedEntryPaths() []string {
- rootFiles := make([]string, 0, len(g.tmplFs.Files))
- tmplPaths := make([]string, 0, len(g.tmplFs.Files))
- for path, entry := range g.tmplFs.Files {
- if entry.IsDir() {
- continue
- }
- if strings.Count(entry.Path[1:], "/") == 0 {
- rootFiles = append(rootFiles, path)
- } else {
- tmplPaths = append(tmplPaths, path)
- }
- }
- sort.Strings(rootFiles)
- sort.Strings(tmplPaths)
- return append(rootFiles, tmplPaths...)
-}
diff --git a/pkg/grapicmd/internal/module/generator/command.go b/pkg/grapicmd/internal/module/generator/command.go
deleted file mode 100644
index aa65f585..00000000
--- a/pkg/grapicmd/internal/module/generator/command.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package generator
-
-import (
- "github.com/pkg/errors"
- "github.com/spf13/afero"
-
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/generator/template"
-)
-
-type commandGenerator struct {
- baseGenerator
- rootDir string
-}
-
-func newCommandGenerator(fs afero.Fs, ui module.UI, rootDir string) module.CommandGenerator {
- return &commandGenerator{
- baseGenerator: newBaseGenerator(template.Command, fs, ui),
- rootDir: rootDir,
- }
-}
-
-func (g *commandGenerator) GenerateCommand(name string) error {
- return errors.WithStack(g.Generate(g.rootDir, map[string]string{"name": name}, generationConfig{}))
-}
-
-func (g *commandGenerator) DestroyCommand(name string) error {
- return errors.WithStack(g.Destroy(g.rootDir, map[string]string{"name": name}))
-}
diff --git a/pkg/grapicmd/internal/module/generator/command_test.go b/pkg/grapicmd/internal/module/generator/command_test.go
deleted file mode 100644
index b525f0ee..00000000
--- a/pkg/grapicmd/internal/module/generator/command_test.go
+++ /dev/null
@@ -1,80 +0,0 @@
-package generator
-
-import (
- "go/build"
- "path/filepath"
- "testing"
-
- "github.com/bradleyjkemp/cupaloy"
- "github.com/golang/mock/gomock"
- "github.com/spf13/afero"
-
- moduletesting "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/testing"
- "github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
-)
-
-func Test_CommandGenerator(t *testing.T) {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
-
- tmpBuildContext := fs.BuildContext
- defer func() { fs.BuildContext = tmpBuildContext }()
- fs.BuildContext = build.Context{
- GOPATH: "/home",
- }
-
- rootDir := "/home/src/testapp"
-
- ui := moduletesting.NewMockUI(ctrl)
- ui.EXPECT().ItemSuccess(gomock.Any()).AnyTimes()
- fs := afero.NewMemMapFs()
-
- generator := newCommandGenerator(fs, ui, rootDir)
-
- name := "foo"
- files := []string{
- "cmd/foo/run.go",
- }
-
- t.Run("Generate", func(t *testing.T) {
- err := generator.GenerateCommand(name)
-
- if err != nil {
- t.Errorf("returned an error %v", err)
- }
-
- for _, file := range files {
- t.Run(file, func(t *testing.T) {
- data, err := afero.ReadFile(fs, filepath.Join(rootDir, file))
-
- if err != nil {
- t.Errorf("returned an error %v", err)
- }
-
- cupaloy.SnapshotT(t, string(data))
- })
- }
- })
-
- t.Run("Destroy", func(t *testing.T) {
- err := generator.DestroyCommand(name)
-
- if err != nil {
- t.Errorf("returned an error %v", err)
- }
-
- for _, file := range files {
- t.Run(file, func(t *testing.T) {
- ok, err := afero.Exists(fs, filepath.Join(rootDir, file))
-
- if err != nil {
- t.Errorf("Exists(fs, %q) returned an error %v", file, err)
- }
-
- if ok {
- t.Errorf("%q should not exist", file)
- }
- })
- }
- })
-}
diff --git a/pkg/grapicmd/internal/module/generator/generator.go b/pkg/grapicmd/internal/module/generator/generator.go
deleted file mode 100644
index c368e6b0..00000000
--- a/pkg/grapicmd/internal/module/generator/generator.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package generator
-
-import (
- "github.com/spf13/afero"
-
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
-)
-
-// New creates a module.Generator instance.
-func New(fs afero.Fs, ui module.UI, rootDir, protoDir, protoOutDir, serverDir, version string) module.Generator {
- return &generator{
- ProjectGenerator: newProjectGenerator(fs, ui, version),
- ServiceGenerator: newServiceGenerator(fs, ui, rootDir, protoDir, protoOutDir, serverDir),
- CommandGenerator: newCommandGenerator(fs, ui, rootDir),
- }
-}
-
-type generator struct {
- module.ProjectGenerator
- module.ServiceGenerator
- module.CommandGenerator
-}
diff --git a/pkg/grapicmd/internal/module/generator/project.go b/pkg/grapicmd/internal/module/generator/project.go
deleted file mode 100644
index d6ceb430..00000000
--- a/pkg/grapicmd/internal/module/generator/project.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package generator
-
-import (
- "github.com/pkg/errors"
- "github.com/spf13/afero"
-
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/generator/template"
- "github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
-)
-
-type projectGenerator struct {
- baseGenerator
- version string
-}
-
-func newProjectGenerator(fs afero.Fs, ui module.UI, version string) module.ProjectGenerator {
- return &projectGenerator{
- baseGenerator: newBaseGenerator(template.Init, fs, ui),
- version: version,
- }
-}
-
-func (g *projectGenerator) GenerateProject(rootDir string, useHead bool) error {
- importPath, err := fs.GetImportPath(rootDir)
- if err != nil {
- return errors.WithStack(err)
- }
- data := map[string]interface{}{
- "importPath": importPath,
- "version": g.version,
- "headUsed": useHead,
- }
- return g.Generate(rootDir, data, generationConfig{})
-}
diff --git a/pkg/grapicmd/internal/module/generator/project_test.go b/pkg/grapicmd/internal/module/generator/project_test.go
deleted file mode 100644
index e28320c8..00000000
--- a/pkg/grapicmd/internal/module/generator/project_test.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package generator
-
-import (
- "go/build"
- "path/filepath"
- "testing"
-
- "github.com/bradleyjkemp/cupaloy"
- "github.com/golang/mock/gomock"
- "github.com/spf13/afero"
-
- moduletesting "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/testing"
- "github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
-)
-
-func Test_ProjectGenerator(t *testing.T) {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
-
- tmpBuildContext := fs.BuildContext
- defer func() { fs.BuildContext = tmpBuildContext }()
- fs.BuildContext = build.Context{
- GOPATH: "/home",
- }
-
- rootDir := "/home/src/testapp"
-
- ui := moduletesting.NewMockUI(ctrl)
- ui.EXPECT().ItemSuccess(gomock.Any()).AnyTimes()
- fs := afero.NewMemMapFs()
-
- generator := newProjectGenerator(fs, ui, "")
-
- err := generator.GenerateProject(rootDir, false)
-
- if err != nil {
- t.Errorf("returned an error %v", err)
- }
-
- files := []string{
- ".gitignore",
- "Gopkg.toml",
- "grapi.toml",
- "app/run.go",
- "cmd/server/run.go",
- }
-
- for _, file := range files {
- t.Run(file, func(t *testing.T) {
- data, err := afero.ReadFile(fs, filepath.Join(rootDir, file))
-
- if err != nil {
- t.Errorf("returned an error %v", err)
- }
-
- cupaloy.SnapshotT(t, string(data))
- })
- }
-}
diff --git a/pkg/grapicmd/internal/module/generator/service.go b/pkg/grapicmd/internal/module/generator/service.go
deleted file mode 100644
index cfef2efb..00000000
--- a/pkg/grapicmd/internal/module/generator/service.go
+++ /dev/null
@@ -1,406 +0,0 @@
-package generator
-
-import (
- "path/filepath"
- "sort"
- "strings"
-
- "github.com/jinzhu/inflection"
- "github.com/pkg/errors"
- "github.com/serenize/snaker"
- "github.com/spf13/afero"
-
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/generator/template"
- "github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
-)
-
-type serviceGenerator struct {
- baseGenerator
- rootDir, protoDir, protoOutDir, serverDir string
-}
-
-func newServiceGenerator(fs afero.Fs, ui module.UI, rootDir, protoDir, protoOutDir, serverDir string) module.ServiceGenerator {
- if protoDir == "" {
- protoDir = filepath.Join("api", "protos")
- }
- if protoOutDir == "" {
- protoOutDir = filepath.Join("api")
- }
- if serverDir == "" {
- serverDir = filepath.Join("app", "server")
- }
-
- return &serviceGenerator{
- baseGenerator: newBaseGenerator(template.Service, fs, ui),
- rootDir: rootDir,
- protoDir: protoDir,
- protoOutDir: protoOutDir,
- serverDir: serverDir,
- }
-}
-
-func (g *serviceGenerator) GenerateService(name string, cfg module.ServiceGenerationConfig) error {
- data, err := g.createParams(name, cfg.ResourceName, cfg.Methods)
- if err != nil {
- return errors.WithStack(err)
- }
- return g.Generate(g.rootDir, data, generationConfig{skipTest: cfg.SkipTest})
-}
-
-func (g *serviceGenerator) ScaffoldService(name string, cfg module.ServiceGenerationConfig) error {
- data, err := g.createParams(name, cfg.ResourceName, []string{"list", "get", "create", "update", "delete"})
- if err != nil {
- return errors.WithStack(err)
- }
- return g.Generate(g.rootDir, data, generationConfig{skipTest: cfg.SkipTest})
-}
-
-func (g *serviceGenerator) DestroyService(name string) error {
- data, err := g.createParams(name, "", []string{})
- if err != nil {
- return errors.WithStack(err)
- }
- return g.Destroy(g.rootDir, data)
-}
-
-type nameParams struct {
- pluralCamel string
- pluralCamelLower string
- pluralSnake string
- singularCamel string
- singularCamelLower string
- singularSnake string
-}
-
-func createNameParams(name string) nameParams {
- nameParams := nameParams{
- pluralCamel: inflection.Plural(snaker.SnakeToCamel(name)),
- singularCamel: inflection.Singular(snaker.SnakeToCamel(name)),
- }
- nameParams.pluralCamelLower = strings.ToLower(string(nameParams.pluralCamel[0])) + nameParams.pluralCamel[1:]
- nameParams.pluralSnake = snaker.CamelToSnake(nameParams.pluralCamel)
- nameParams.singularCamelLower = strings.ToLower(string(nameParams.singularCamel[0])) + nameParams.singularCamel[1:]
- nameParams.singularSnake = snaker.CamelToSnake(nameParams.singularCamel)
- return nameParams
-}
-
-type serviceParams struct {
- ProtoDir string
- ProtoOutDir string
- ServerDir string
- Path string
- ServiceName string
- Methods []serviceMethodParams
- Proto serviceProtoParams
- PbGo servicePbGoParams
- Go serviceGoParams
-}
-
-type serviceProtoParams struct {
- Package string
- Imports []string
- Messages []serviceMethodMessage
-}
-
-type servicePbGoParams struct {
- PackageName string
- PackagePath string
-}
-
-type serviceGoParams struct {
- Package string
- Imports []string
- TestImports []string
- ServerName string
- StructName string
-}
-
-type serviceMethodsParams struct {
- Methods []serviceMethodParams
- ProtoImports []string
- GoImports []string
- Messages []serviceMethodMessage
-}
-
-type serviceMethodParams struct {
- Method string
- HTTP serviceMethodHTTPParams
- requestCommon string
- requestGo string
- requestProto string
- responseCommon string
- responseGo string
- responseProto string
-}
-
-func (p *serviceMethodParams) RequestGo(pkg string) string {
- if p.requestGo == "" {
- return pkg + "." + p.requestCommon
- }
- return p.requestGo
-}
-
-func (p *serviceMethodParams) RequestProto() string {
- if p.requestProto == "" {
- return p.requestCommon
- }
- return p.requestProto
-}
-
-func (p *serviceMethodParams) ResponseGo(pkg string) string {
- if p.responseGo == "" {
- return pkg + "." + p.responseCommon
- }
- return p.responseGo
-}
-
-func (p *serviceMethodParams) ResponseProto() string {
- if p.responseProto == "" {
- return p.responseCommon
- }
- return p.responseProto
-}
-
-type serviceMethodMessage struct {
- Name string
- Fields []serviceMethodMessageField
-}
-
-type serviceMethodMessageField struct {
- Name string
- Type string
- Repeated bool
- Tag uint
-}
-
-type serviceMethodHTTPParams struct {
- Method string
- Path string
- Body string
-}
-
-func (g *serviceGenerator) createParams(path string, resName string, methodNames []string) (*serviceParams, error) {
- // github.com/foo/bar
- importPath, err := fs.GetImportPath(g.rootDir)
- if err != nil {
- return nil, errors.WithStack(err)
- }
-
- // path => baz/qux/quux
- path = strings.Replace(path, "-", "_", -1)
-
- // quux
- name := filepath.Base(path)
-
- nameParams := createNameParams(name)
-
- // Quux
- serviceName := nameParams.singularCamel
- // quux
- localServiceName := strings.ToLower(string(serviceName[0])) + serviceName[1:]
-
- // baz/qux
- packagePath := filepath.Dir(path)
- // qux
- packageName := filepath.Base(packagePath)
-
- // api/baz/qux
- pbgoPackagePath := filepath.Join(g.protoOutDir, packagePath)
- // qux_pb
- pbgoPackageName := filepath.Base(pbgoPackagePath) + "_pb"
-
- if packagePath == "." {
- packagePath = filepath.Base(g.serverDir)
- packageName = packagePath
- pbgoPackagePath = g.protoOutDir
- pbgoPackageName = filepath.Base(pbgoPackagePath) + "_pb"
- }
-
- protoPackageChunks := []string{}
- for _, pkg := range strings.Split(filepath.ToSlash(filepath.Join(importPath, g.protoOutDir, filepath.Dir(path))), "/") {
- chunks := strings.Split(strings.Replace(pkg, "-", "_", -1), ".")
- for i := len(chunks) - 1; i >= 0; i-- {
- protoPackageChunks = append(protoPackageChunks, chunks[i])
- }
- }
- // com.github.foo.bar.baz.qux
- protoPackage := strings.Join(protoPackageChunks, ".")
-
- protoImports := []string{
- "google/api/annotations.proto",
- }
- goImports := []string{
- "github.com/izumin5210/grapi/pkg/grapiserver",
- "google.golang.org/grpc/codes",
- "google.golang.org/grpc/status",
- }
- goTestImports := []string{}
-
- resNameParams := nameParams
- if resName != "" {
- resNameParams = createNameParams(resName)
- }
- methods := g.createMethodParams(resNameParams, methodNames)
-
- protoImports = append(protoImports, methods.ProtoImports...)
- sort.Strings(protoImports)
- goImports = append(goImports, methods.GoImports...)
- sort.Strings(goImports)
- goTestImports = append(goTestImports, methods.GoImports...)
- sort.Strings(goTestImports)
-
- params := &serviceParams{
- ProtoDir: g.protoDir,
- ProtoOutDir: g.protoOutDir,
- ServerDir: g.serverDir,
- Path: path,
- ServiceName: serviceName,
- Methods: methods.Methods,
- Proto: serviceProtoParams{
- Package: protoPackage,
- Imports: protoImports,
- Messages: methods.Messages,
- },
- PbGo: servicePbGoParams{
- PackageName: pbgoPackageName,
- PackagePath: filepath.ToSlash(filepath.Join(importPath, pbgoPackagePath)),
- },
- Go: serviceGoParams{
- Package: packageName,
- Imports: goImports,
- TestImports: goTestImports,
- ServerName: serviceName + "Service" + "Server",
- StructName: localServiceName + "Service" + "Server" + "Impl",
- },
- }
-
- return params, nil
-}
-
-func (g *serviceGenerator) createMethodParams(name nameParams, methods []string) (
- params serviceMethodsParams,
-) {
- id := name.singularSnake + "_id"
- resource := &serviceMethodMessage{
- Name: name.singularCamel,
- Fields: []serviceMethodMessageField{{Name: id, Type: "string", Tag: 1}},
- }
-
- basicMethods := [5]*serviceMethodParams{}
- customMethods := []serviceMethodParams{}
- basicMessages := [7]*serviceMethodMessage{}
- customMessages := []serviceMethodMessage{}
-
- for _, meth := range methods {
- switch strings.ToLower(meth) {
- case "list":
- methodName := "List" + name.pluralCamel
- reqName := methodName + "Request"
- respName := methodName + "Response"
- basicMethods[0] = &serviceMethodParams{
- Method: methodName,
- requestCommon: reqName,
- responseCommon: respName,
- HTTP: serviceMethodHTTPParams{Method: "get", Path: name.pluralSnake},
- }
- basicMessages[0] = resource
- basicMessages[1] = &serviceMethodMessage{Name: reqName}
- basicMessages[2] = &serviceMethodMessage{
- Name: respName,
- Fields: []serviceMethodMessageField{{Name: name.pluralSnake, Type: name.singularCamel, Repeated: true, Tag: 1}},
- }
- case "get":
- methodName := "Get" + name.singularCamel
- reqName := methodName + "Request"
- basicMethods[1] = &serviceMethodParams{
- Method: methodName,
- requestCommon: reqName,
- responseCommon: resource.Name,
- HTTP: serviceMethodHTTPParams{Method: "get", Path: name.pluralSnake + "/{" + id + "}"},
- }
- basicMessages[0] = resource
- basicMessages[3] = &serviceMethodMessage{
- Name: reqName,
- Fields: []serviceMethodMessageField{{Name: id, Type: "string", Tag: 1}},
- }
- case "create":
- methodName := "Create" + name.singularCamel
- reqName := methodName + "Request"
- basicMethods[2] = &serviceMethodParams{
- Method: methodName,
- requestCommon: reqName,
- responseCommon: resource.Name,
- HTTP: serviceMethodHTTPParams{Method: "post", Path: name.pluralSnake, Body: name.singularSnake},
- }
- basicMessages[0] = resource
- basicMessages[4] = &serviceMethodMessage{
- Name: reqName,
- Fields: []serviceMethodMessageField{{Name: name.singularSnake, Type: name.singularCamel, Tag: 1}},
- }
- case "update":
- methodName := "Update" + name.singularCamel
- reqName := methodName + "Request"
- basicMethods[3] = &serviceMethodParams{
- Method: methodName,
- requestCommon: reqName,
- responseCommon: resource.Name,
- HTTP: serviceMethodHTTPParams{Method: "patch", Path: name.pluralSnake + "/{" + name.singularSnake + "." + id + "}", Body: name.singularSnake},
- }
- basicMessages[0] = resource
- basicMessages[5] = &serviceMethodMessage{
- Name: reqName,
- Fields: []serviceMethodMessageField{{Name: name.singularSnake, Type: name.singularCamel, Tag: 1}},
- }
- case "delete":
- methodName := "Delete" + name.singularCamel
- reqName := methodName + "Request"
- basicMethods[4] = &serviceMethodParams{
- Method: methodName,
- requestCommon: reqName,
- responseProto: "google.protobuf.Empty",
- responseGo: "empty.Empty",
- HTTP: serviceMethodHTTPParams{Method: "delete", Path: name.pluralSnake + "/{" + id + "}"},
- }
- basicMessages[6] = &serviceMethodMessage{
- Name: reqName,
- Fields: []serviceMethodMessageField{{Name: id, Type: "string", Tag: 1}},
- }
- params.ProtoImports = append(params.ProtoImports, "google/protobuf/empty.proto")
- params.GoImports = append(params.GoImports, "github.com/golang/protobuf/ptypes/empty")
- default:
- methodName := snaker.SnakeToCamel(meth)
- reqName := methodName + "Request"
- respName := methodName + "Response"
- customMethods = append(customMethods, serviceMethodParams{
- Method: methodName,
- requestCommon: reqName,
- responseCommon: respName,
- HTTP: serviceMethodHTTPParams{Method: "get", Path: name.pluralSnake + "/" + snaker.CamelToSnake(meth)},
- })
- customMessages = append(
- customMessages,
- serviceMethodMessage{Name: reqName},
- serviceMethodMessage{Name: respName},
- )
- }
- }
-
- for _, meth := range basicMethods {
- if meth != nil {
- params.Methods = append(params.Methods, *meth)
- }
- }
- for _, msg := range basicMessages {
- if msg != nil {
- params.Messages = append(params.Messages, *msg)
- }
- }
- for _, meth := range customMethods {
- params.Methods = append(params.Methods, meth)
- }
- for _, msg := range customMessages {
- params.Messages = append(params.Messages, msg)
- }
- return
-}
diff --git a/pkg/grapicmd/internal/module/generator/service_test.go b/pkg/grapicmd/internal/module/generator/service_test.go
deleted file mode 100644
index 339852ad..00000000
--- a/pkg/grapicmd/internal/module/generator/service_test.go
+++ /dev/null
@@ -1,244 +0,0 @@
-package generator
-
-import (
- "go/build"
- "path/filepath"
- "strings"
- "testing"
-
- "github.com/bradleyjkemp/cupaloy"
- "github.com/golang/mock/gomock"
- "github.com/spf13/afero"
-
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
- moduletesting "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/testing"
- "github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
-)
-
-func Test_ServiceGenerator(t *testing.T) {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
-
- tmpBuildContext := fs.BuildContext
- defer func() { fs.BuildContext = tmpBuildContext }()
- fs.BuildContext = build.Context{
- GOPATH: "/home",
- }
-
- rootDir := "/home/src/testapp"
-
- ui := moduletesting.NewMockUI(ctrl)
- ui.EXPECT().ItemSuccess(gomock.Any()).AnyTimes()
- ui.EXPECT().ItemSkipped(gomock.Any()).AnyTimes()
- fs := afero.NewMemMapFs()
-
- cases := []struct {
- name string
- args []string
- files []string
- skippedFiles map[string]struct{}
- scaffold bool
- skipTest bool
- resource string
- protoDir string
- protoOutDir string
- serverDir string
- }{
- {
- name: "foo",
- files: []string{
- "api/protos/foo.proto",
- "app/server/foo_server.go",
- "app/server/foo_server_register_funcs.go",
- "app/server/foo_server_test.go",
- },
- },
- {
- name: "foo/bar",
- files: []string{
- "api/protos/foo/bar.proto",
- "app/server/foo/bar_server.go",
- "app/server/foo/bar_server_register_funcs.go",
- "app/server/foo/bar_server_test.go",
- },
- },
- {
- name: "foo/bar_baz",
- files: []string{
- "api/protos/foo/bar_baz.proto",
- "app/server/foo/bar_baz_server.go",
- "app/server/foo/bar_baz_server_register_funcs.go",
- "app/server/foo/bar_baz_server_test.go",
- },
- },
- {
- name: "foo/bar-baz",
- files: []string{
- "api/protos/foo/bar_baz.proto",
- "app/server/foo/bar_baz_server.go",
- "app/server/foo/bar_baz_server_register_funcs.go",
- "app/server/foo/bar_baz_server_test.go",
- },
- },
- {
- name: "foo/bar-baz",
- args: []string{"list", "create", "delete"},
- files: []string{
- "api/protos/foo/bar_baz.proto",
- "app/server/foo/bar_baz_server.go",
- "app/server/foo/bar_baz_server_register_funcs.go",
- "app/server/foo/bar_baz_server_test.go",
- },
- },
- {
- name: "foo/bar-baz",
- args: []string{"list", "create", "rename", "delete", "move_move"},
- files: []string{
- "api/protos/foo/bar_baz.proto",
- "app/server/foo/bar_baz_server.go",
- "app/server/foo/bar_baz_server_register_funcs.go",
- "app/server/foo/bar_baz_server_test.go",
- },
- },
- {
- name: "qux",
- files: []string{
- "pkg/foo/protos/qux.proto",
- "app/server/qux_server.go",
- "app/server/qux_server_register_funcs.go",
- "app/server/qux_server_test.go",
- },
- protoDir: "pkg/foo/protos",
- },
- {
- name: "quux",
- files: []string{
- "api/protos/quux.proto",
- "app/server/quux_server.go",
- "app/server/quux_server_register_funcs.go",
- "app/server/quux_server_test.go",
- },
- protoOutDir: "api/out",
- },
- {
- name: "corge",
- files: []string{
- "api/protos/corge.proto",
- "pkg/foo/server/corge_server.go",
- "pkg/foo/server/corge_server_register_funcs.go",
- "pkg/foo/server/corge_server_test.go",
- },
- serverDir: "pkg/foo/server",
- },
- {
- name: "book",
- files: []string{
- "api/protos/book.proto",
- "app/server/book_server.go",
- "app/server/book_server_register_funcs.go",
- "app/server/book_server_test.go",
- },
- scaffold: true,
- },
- {
- name: "book",
- files: []string{
- "api/protos/book.proto",
- "app/server/book_server.go",
- "app/server/book_server_register_funcs.go",
- },
- skippedFiles: map[string]struct{}{
- "app/server/book_server_test.go": {},
- },
- skipTest: true,
- },
- {
- name: "library",
- files: []string{
- "api/protos/library.proto",
- "app/server/library_server.go",
- "app/server/library_server_register_funcs.go",
- "app/server/library_server_test.go",
- },
- resource: "book",
- scaffold: true,
- },
- }
-
- for _, c := range cases {
- test := c.name
- if len(c.args) > 0 {
- test += " with " + strings.Join(c.args, ",")
- }
-
- generator := newServiceGenerator(fs, ui, rootDir, c.protoDir, c.protoOutDir, c.serverDir)
-
- t.Run(test, func(t *testing.T) {
- test := "Generate"
- if c.scaffold {
- test = "Scaffold"
- }
- if c.skipTest {
- test += " without test"
- }
- t.Run(test, func(t *testing.T) {
- var err error
- if c.scaffold {
- err = generator.ScaffoldService(c.name, module.ServiceGenerationConfig{ResourceName: c.resource, SkipTest: c.skipTest})
- } else {
- err = generator.GenerateService(c.name, module.ServiceGenerationConfig{ResourceName: c.resource, Methods: c.args, SkipTest: c.skipTest})
- }
-
- if err != nil {
- t.Errorf("returned an error: %v", err)
- }
-
- for _, file := range c.files {
- t.Run(file, func(t *testing.T) {
- if _, ok := c.skippedFiles[file]; ok {
- ok, err := afero.Exists(fs, file)
-
- if err != nil {
- t.Errorf("returned an error: %v", err)
- }
-
- if ok {
- t.Error("should not exist")
- }
- } else {
- data, err := afero.ReadFile(fs, filepath.Join(rootDir, file))
-
- if err != nil {
- t.Errorf("returned an error: %v", err)
- }
-
- cupaloy.SnapshotT(t, string(data))
- }
- })
- }
- })
-
- t.Run("Destroy", func(t *testing.T) {
- err := generator.DestroyService(c.name)
-
- if err != nil {
- t.Errorf("returned an error: %v", err)
- }
-
- for _, file := range c.files {
- t.Run(file, func(t *testing.T) {
- ok, err := afero.Exists(fs, filepath.Join(rootDir, file))
-
- if err != nil {
- t.Errorf("Exists(fs, %q) returned an error: %v", file, err)
- }
-
- if ok {
- t.Errorf("%q should not exist", file)
- }
- })
- }
- })
- })
- }
-}
diff --git a/pkg/grapicmd/internal/module/generator/status.go b/pkg/grapicmd/internal/module/generator/status.go
deleted file mode 100644
index 8aab4756..00000000
--- a/pkg/grapicmd/internal/module/generator/status.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package generator
-
-import "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
-
-type status int
-
-const (
- statusCreate status = iota
- statusDelete
- statusExist
- statusIdentical
- statusConflicted
- statusForce
- statusSkipped
-)
-
-var (
- creatableStatusSet = map[status]struct{}{
- statusCreate: {},
- statusForce: {},
- }
-)
-
-func (s status) Fprint(ui module.UI, msg string) {
- switch s {
- case statusCreate, statusForce, statusDelete:
- ui.ItemSuccess(msg)
- case statusConflicted:
- ui.ItemFailure(msg)
- default:
- ui.ItemSkipped(msg)
- }
-}
-
-func (s status) ShouldCreate() bool {
- _, ok := creatableStatusSet[s]
- return ok
-}
diff --git a/pkg/grapicmd/internal/module/generator/template/command.go b/pkg/grapicmd/internal/module/generator/template/command.go
deleted file mode 100644
index 097f29b7..00000000
--- a/pkg/grapicmd/internal/module/generator/template/command.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package template
-
-import (
- "time"
-
- "github.com/jessevdk/go-assets"
-)
-
-var _Command9acbc40e153d42b0b3e06ca83e7c53e3670d8cdc = "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc main() {\n\tos.Exit(run())\n}\n\nfunc run() int {\n\tfmt.Println(\"It works!\")\n\treturn 0\n}\n"
-
-// Command returns go-assets FileSystem
-var Command = assets.NewFileSystem(map[string][]string{"/cmd/{{ .name }}": []string{"run.go.tmpl"}, "/": []string{}, "/cmd": []string{}}, map[string]*assets.File{
- "/cmd/{{ .name }}": &assets.File{
- Path: "/cmd/{{ .name }}",
- FileMode: 0x800001ed,
- Mtime: time.Unix(1520753819, 1520753819000000000),
- Data: nil,
- }, "/cmd/{{ .name }}/run.go.tmpl": &assets.File{
- Path: "/cmd/{{ .name }}/run.go.tmpl",
- FileMode: 0x1a4,
- Mtime: time.Unix(1520753819, 1520753819000000000),
- Data: []byte(_Command9acbc40e153d42b0b3e06ca83e7c53e3670d8cdc),
- }, "/": &assets.File{
- Path: "/",
- FileMode: 0x800001ed,
- Mtime: time.Unix(1520753819, 1520753819000000000),
- Data: nil,
- }, "/cmd": &assets.File{
- Path: "/cmd",
- FileMode: 0x800001ed,
- Mtime: time.Unix(1520753819, 1520753819000000000),
- Data: nil,
- }}, "")
diff --git a/pkg/grapicmd/internal/module/generator/template/gen.go b/pkg/grapicmd/internal/module/generator/template/gen.go
deleted file mode 100644
index 0d097c3c..00000000
--- a/pkg/grapicmd/internal/module/generator/template/gen.go
+++ /dev/null
@@ -1,5 +0,0 @@
-//go:generate go-assets-builder -p template -s="/init" -o init.go -v Init init
-//go:generate go-assets-builder -p template -s="/service" -o service.go -v Service service
-//go:generate go-assets-builder -p template -s="/command" -o command.go -v Command command
-
-package template
diff --git a/pkg/grapicmd/internal/module/generator/template/init.go b/pkg/grapicmd/internal/module/generator/template/init.go
deleted file mode 100644
index 55e5789c..00000000
--- a/pkg/grapicmd/internal/module/generator/template/init.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package template
-
-import (
- "time"
-
- "github.com/jessevdk/go-assets"
-)
-
-var _Initbc4053f4dd26ceb67e4646e8c1d2cc75897c4dd0 = "package app\n\nimport (\n\t\"github.com/izumin5210/grapi/pkg/grapiserver\"\n)\n\n// Run starts the grapiserver.\nfunc Run() error {\n\ts := grapiserver.New(\n\t\tgrapiserver.WithDefaultLogger(),\n\t\tgrapiserver.WithServers(\n\t\t// TODO\n\t\t),\n\t)\n\treturn s.Serve()\n}\n"
-var _Init38e76c5db8962fa825cf2bd8b23a2dc985c4513e = "*.so\n/vendor\n/bin\n/tmp\n"
-var _Init71ed560e812a4261bc8b56d9feaef4800830e0b7 = ""
-var _Initd135936e91856b6159ac2eedcf89aa9f07773f82 = "package main\n\nimport (\n\t\"os\"\n\n\t\"google.golang.org/grpc/grpclog\"\n\n\t\"{{ .importPath }}/app\"\n)\n\nfunc main() {\n\tos.Exit(run())\n}\n\nfunc run() int {\n\terr := app.Run()\n\tif err != nil {\n\t\tgrpclog.Errorf(\"server was shutdown with errors: %v\", err)\n\t\treturn 1\n\t}\n\treturn 0\n}\n"
-var _Init23b808cac963edf44a497827f2a6eff5ddac970f = "required = [\n \"github.com/golang/protobuf/protoc-gen-go\",\n \"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway\",\n \"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger\",\n]\n\n[[constraint]]\n{{- if .headUsed }}\n branch = \"master\"\n{{- end }}\n name = \"github.com/izumin5210/grapi\"\n{{- if not .headUsed }}\n version = \"{{ .version }}\"\n{{- end }}\n"
-var _Init8d21956ba8abe388f964e47be0f7e5d170a2fce5 = ""
-var _Initc051c9ff1a8e446bc9636d3144c2775a7e235322 = "[grapi]\nserver_dir = \"./app/server\"\n\n[protoc]\nprotos_dir = \"./api/protos\"\nout_dir = \"./api\"\nimport_dirs = [\n \"./vendor/github.com/grpc-ecosystem/grpc-gateway\",\n \"./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis\",\n]\n\n [[protoc.plugins]]\n path = \"./vendor/github.com/golang/protobuf/protoc-gen-go\"\n name = \"go\"\n args = { plugins = \"grpc\", paths = \"source_relative\" }\n\n [[protoc.plugins]]\n path = \"./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway\"\n name = \"grpc-gateway\"\n args = { logtostderr = true }\n\n [[protoc.plugins]]\n path = \"./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger\"\n name = \"swagger\"\n args = { logtostderr = true }\n"
-
-// Init returns go-assets FileSystem
-var Init = assets.NewFileSystem(map[string][]string{"/app/server": []string{".keep.tmpl"}, "/cmd": []string{}, "/cmd/server": []string{"run.go.tmpl"}, "/": []string{".gitignore.tmpl", "Gopkg.toml.tmpl", "grapi.toml.tmpl"}, "/api": []string{}, "/api/protos": []string{".keep.tmpl"}, "/app": []string{"run.go.tmpl"}}, map[string]*assets.File{
- "/app/server/.keep.tmpl": &assets.File{
- Path: "/app/server/.keep.tmpl",
- FileMode: 0x1a4,
- Mtime: time.Unix(1521042119, 1521042119000000000),
- Data: []byte(_Init71ed560e812a4261bc8b56d9feaef4800830e0b7),
- }, "/cmd/server": &assets.File{
- Path: "/cmd/server",
- FileMode: 0x800001ed,
- Mtime: time.Unix(1530424640, 1530424640000000000),
- Data: nil,
- }, "/cmd/server/run.go.tmpl": &assets.File{
- Path: "/cmd/server/run.go.tmpl",
- FileMode: 0x1a4,
- Mtime: time.Unix(1530424640, 1530424640000000000),
- Data: []byte(_Initd135936e91856b6159ac2eedcf89aa9f07773f82),
- }, "/Gopkg.toml.tmpl": &assets.File{
- Path: "/Gopkg.toml.tmpl",
- FileMode: 0x1a4,
- Mtime: time.Unix(1522493033, 1522493033000000000),
- Data: []byte(_Init23b808cac963edf44a497827f2a6eff5ddac970f),
- }, "/api/protos": &assets.File{
- Path: "/api/protos",
- FileMode: 0x800001ed,
- Mtime: time.Unix(1520753819, 1520753819000000000),
- Data: nil,
- }, "/api/protos/.keep.tmpl": &assets.File{
- Path: "/api/protos/.keep.tmpl",
- FileMode: 0x1a4,
- Mtime: time.Unix(1520753819, 1520753819000000000),
- Data: []byte(_Init8d21956ba8abe388f964e47be0f7e5d170a2fce5),
- }, "/app": &assets.File{
- Path: "/app",
- FileMode: 0x800001ed,
- Mtime: time.Unix(1521995193, 1521995193000000000),
- Data: nil,
- }, "/cmd": &assets.File{
- Path: "/cmd",
- FileMode: 0x800001ed,
- Mtime: time.Unix(1520753819, 1520753819000000000),
- Data: nil,
- }, "/grapi.toml.tmpl": &assets.File{
- Path: "/grapi.toml.tmpl",
- FileMode: 0x1a4,
- Mtime: time.Unix(1531820124, 1531820124000000000),
- Data: []byte(_Initc051c9ff1a8e446bc9636d3144c2775a7e235322),
- }, "/": &assets.File{
- Path: "/",
- FileMode: 0x800001ed,
- Mtime: time.Unix(1531820124, 1531820124000000000),
- Data: nil,
- }, "/api": &assets.File{
- Path: "/api",
- FileMode: 0x800001ed,
- Mtime: time.Unix(1520753819, 1520753819000000000),
- Data: nil,
- }, "/app/run.go.tmpl": &assets.File{
- Path: "/app/run.go.tmpl",
- FileMode: 0x1a4,
- Mtime: time.Unix(1521995193, 1521995193000000000),
- Data: []byte(_Initbc4053f4dd26ceb67e4646e8c1d2cc75897c4dd0),
- }, "/app/server": &assets.File{
- Path: "/app/server",
- FileMode: 0x800001ed,
- Mtime: time.Unix(1521042119, 1521042119000000000),
- Data: nil,
- }, "/.gitignore.tmpl": &assets.File{
- Path: "/.gitignore.tmpl",
- FileMode: 0x1a4,
- Mtime: time.Unix(1520753819, 1520753819000000000),
- Data: []byte(_Init38e76c5db8962fa825cf2bd8b23a2dc985c4513e),
- }}, "")
diff --git a/pkg/grapicmd/internal/module/generator/template/init/Gopkg.toml.tmpl b/pkg/grapicmd/internal/module/generator/template/init/Gopkg.toml.tmpl
deleted file mode 100644
index 3551c7f6..00000000
--- a/pkg/grapicmd/internal/module/generator/template/init/Gopkg.toml.tmpl
+++ /dev/null
@@ -1,14 +0,0 @@
-required = [
- "github.com/golang/protobuf/protoc-gen-go",
- "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway",
- "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger",
-]
-
-[[constraint]]
-{{- if .headUsed }}
- branch = "master"
-{{- end }}
- name = "github.com/izumin5210/grapi"
-{{- if not .headUsed }}
- version = "{{ .version }}"
-{{- end }}
diff --git a/pkg/grapicmd/internal/module/generator/template/init/grapi.toml.tmpl b/pkg/grapicmd/internal/module/generator/template/init/grapi.toml.tmpl
deleted file mode 100644
index 3a0a8101..00000000
--- a/pkg/grapicmd/internal/module/generator/template/init/grapi.toml.tmpl
+++ /dev/null
@@ -1,25 +0,0 @@
-[grapi]
-server_dir = "./app/server"
-
-[protoc]
-protos_dir = "./api/protos"
-out_dir = "./api"
-import_dirs = [
- "./vendor/github.com/grpc-ecosystem/grpc-gateway",
- "./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis",
-]
-
- [[protoc.plugins]]
- path = "./vendor/github.com/golang/protobuf/protoc-gen-go"
- name = "go"
- args = { plugins = "grpc", paths = "source_relative" }
-
- [[protoc.plugins]]
- path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"
- name = "grpc-gateway"
- args = { logtostderr = true }
-
- [[protoc.plugins]]
- path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"
- name = "swagger"
- args = { logtostderr = true }
diff --git a/pkg/grapicmd/internal/module/generator/template/service.go b/pkg/grapicmd/internal/module/generator/template/service.go
deleted file mode 100644
index 06070cac..00000000
--- a/pkg/grapicmd/internal/module/generator/template/service.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package template
-
-import (
- "time"
-
- "github.com/jessevdk/go-assets"
-)
-
-var _Servicec080f048193e3b40b184b8e68c773e1b1bf56088 = "syntax = \"proto3\";\noption go_package = \"{{ .PbGo.PackageName }}\";\npackage {{ .Proto.Package }};\n{{range .Proto.Imports}}\nimport \"{{.}}\";\n{{- end}}\n\nservice {{ .ServiceName }}Service {\t\n{{- range .Methods}}\n rpc {{.Method}} ({{.RequestProto}}) returns ({{.ResponseProto}}) {\n option (google.api.http) = {\n {{.HTTP.Method}}: \"/{{.HTTP.Path}}\"\n {{- if .HTTP.Body}}\n body: \"{{.HTTP.Body}}\"\n {{- end}}\n };\n }\n{{- end}}\n}\n{{range .Proto.Messages}}\nmessage {{.Name}} {\n {{- range .Fields}}\n {{- if .Repeated}}\n repeated {{.Type}} {{.Name}} = {{.Tag}};\n {{- else}}\n {{.Type}} {{.Name}} = {{.Tag}};\n {{- end}}\n {{- end}}\n}\n{{end -}}\n"
-var _Servicedd0ea9770a89c1038381555c2b8b1dfc7d52b6d8 = "package {{.Go.Package }}\n\nimport (\n\t\"context\"\n{{range .Go.Imports}}\n\t\"{{.}}\"\n{{- end}}\n\n\t{{.PbGo.PackageName}} \"{{ .PbGo.PackagePath }}\"\n)\n\n// New{{.Go.ServerName}} creates a new {{.Go.ServerName}} instance.\nfunc New{{.Go.ServerName}}() interface {\n\t{{.PbGo.PackageName }}.{{.Go.ServerName}}\n\tgrapiserver.Server\n} {\n\treturn &{{.Go.StructName}}{}\n}\n\ntype {{.Go.StructName}} struct {\n}\n{{$go := .Go -}}\n{{$pbGo := .PbGo -}}\n{{- range .Methods}}\nfunc (s *{{$go.StructName}}) {{.Method}}(ctx context.Context, req *{{.RequestGo $pbGo.PackageName}}) (*{{.ResponseGo $pbGo.PackageName}}, error) {\n\t// TODO: Not yet implemented.\n\treturn nil, status.Error(codes.Unimplemented, \"TODO: You should implement it!\")\n}\n{{end -}}\n"
-var _Servicea66d22b3414614e986072628e857757760b5fab0 = "package {{.Go.Package }}\n\nimport (\n\t\"context\"\n\n\t\"github.com/grpc-ecosystem/grpc-gateway/runtime\"\n\t\"google.golang.org/grpc\"\n\n\t{{.PbGo.PackageName}} \"{{ .PbGo.PackagePath }}\"\n)\n\n// RegisterWithServer implements grapiserver.Server.RegisterWithServer.\nfunc (s *{{.Go.StructName}}) RegisterWithServer(grpcSvr *grpc.Server) {\n\t{{.PbGo.PackageName}}.Register{{.Go.ServerName}}(grpcSvr, s)\n}\n\n// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.\nfunc (s *{{.Go.StructName}}) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {\n\treturn {{.PbGo.PackageName}}.Register{{.ServiceName}}ServiceHandler(ctx, mux, conn)\n}\n"
-var _Service92aa9b7adaba175be79051b307a723ea2c536222 = "package {{.Go.Package }}\n{{if .Methods}}\nimport (\n\t\"context\"\n\t\"testing\"\n{{range .Go.TestImports}}\n\t\"{{.}}\"\n{{- end}}\n\n\t{{.PbGo.PackageName}} \"{{ .PbGo.PackagePath }}\"\n)\n{{$go := .Go -}}\n{{$pbGo := .PbGo -}}\n{{- range .Methods}}\nfunc Test_{{$go.ServerName}}_{{.Method}}(t *testing.T) {\n\tsvr := New{{$go.ServerName}}()\n\n\tctx := context.Background()\n\treq := &{{.RequestGo $pbGo.PackageName}}{}\n\n\tresp, err := svr.{{.Method}}(ctx, req)\n\n\tif err != nil {\n\t\tt.Errorf(\"returned an error %v\", err)\n\t}\n\n\tif resp == nil {\n\t\tt.Error(\"response should not nil\")\n\t}\n}\n{{end -}}\n{{end -}}\n"
-
-// Service returns go-assets FileSystem
-var Service = assets.NewFileSystem(map[string][]string{"/{{.ProtoDir}}": []string{"{{.Path}}.proto.tmpl"}, "/{{.ServerDir}}": []string{"{{.Path}}_server.go.tmpl", "{{.Path}}_server_register_funcs.go.tmpl", "{{.Path}}_server_test.go.tmpl"}, "/": []string{}}, map[string]*assets.File{
- "/{{.ServerDir}}": &assets.File{
- Path: "/{{.ServerDir}}",
- FileMode: 0x800001ed,
- Mtime: time.Unix(1530427283, 1530427283000000000),
- Data: nil,
- }, "/{{.ServerDir}}/{{.Path}}_server.go.tmpl": &assets.File{
- Path: "/{{.ServerDir}}/{{.Path}}_server.go.tmpl",
- FileMode: 0x1a4,
- Mtime: time.Unix(1523623016, 1523623016000000000),
- Data: []byte(_Servicedd0ea9770a89c1038381555c2b8b1dfc7d52b6d8),
- }, "/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl": &assets.File{
- Path: "/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl",
- FileMode: 0x1a4,
- Mtime: time.Unix(1523623016, 1523623016000000000),
- Data: []byte(_Servicea66d22b3414614e986072628e857757760b5fab0),
- }, "/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl": &assets.File{
- Path: "/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl",
- FileMode: 0x1a4,
- Mtime: time.Unix(1521996238, 1521996238000000000),
- Data: []byte(_Service92aa9b7adaba175be79051b307a723ea2c536222),
- }, "/": &assets.File{
- Path: "/",
- FileMode: 0x800001ed,
- Mtime: time.Unix(1530427296, 1530427296000000000),
- Data: nil,
- }, "/{{.ProtoDir}}": &assets.File{
- Path: "/{{.ProtoDir}}",
- FileMode: 0x800001ed,
- Mtime: time.Unix(1530426353, 1530426353000000000),
- Data: nil,
- }, "/{{.ProtoDir}}/{{.Path}}.proto.tmpl": &assets.File{
- Path: "/{{.ProtoDir}}/{{.Path}}.proto.tmpl",
- FileMode: 0x1a4,
- Mtime: time.Unix(1521913317, 1521913317000000000),
- Data: []byte(_Servicec080f048193e3b40b184b8e68c773e1b1bf56088),
- }}, "")
diff --git a/pkg/grapicmd/internal/module/script.go b/pkg/grapicmd/internal/module/script.go
index 5e1c10a6..77d6c3f3 100644
--- a/pkg/grapicmd/internal/module/script.go
+++ b/pkg/grapicmd/internal/module/script.go
@@ -1,10 +1,12 @@
package module
+import "context"
+
// Script represents an user-defined command.
type Script interface {
Name() string
- Build(args ...string) error
- Run(args ...string) error
+ Build(ctx context.Context, args ...string) error
+ Run(ctx context.Context, args ...string) error
}
// ScriptLoader is a factory object for creating Script objects.
diff --git a/pkg/grapicmd/internal/module/script/loader.go b/pkg/grapicmd/internal/module/script/loader.go
index ff2a531d..4c7e15f5 100644
--- a/pkg/grapicmd/internal/module/script/loader.go
+++ b/pkg/grapicmd/internal/module/script/loader.go
@@ -7,33 +7,39 @@ import (
"github.com/pkg/errors"
"github.com/spf13/afero"
+ "go.uber.org/zap"
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/execx"
"github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
"github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
)
// NewLoader creates a new ScriptLoader instance.
-func NewLoader(fs afero.Fs, commandFactory module.CommandFactory, rootDir string) module.ScriptLoader {
+func NewLoader(fs afero.Fs, io *clib.IO, exec *execx.Executor, rootDir string) module.ScriptLoader {
return &scriptLoader{
- fs: fs,
- commandFactory: commandFactory,
- rootDir: rootDir,
- binDir: filepath.Join(rootDir, "bin"),
- scripts: make(map[string]module.Script),
+ fs: fs,
+ io: io,
+ exec: exec,
+ rootDir: rootDir,
+ binDir: filepath.Join(rootDir, "bin"),
+ scripts: make(map[string]module.Script),
}
}
type scriptLoader struct {
- fs afero.Fs
- commandFactory module.CommandFactory
- rootDir string
- binDir string
- scripts map[string]module.Script
- names []string
+ fs afero.Fs
+ io *clib.IO
+ exec *execx.Executor
+ rootDir string
+ binDir string
+ scripts map[string]module.Script
+ names []string
}
func (f *scriptLoader) Load(dir string) error {
srcsByDir, err := fs.FindMainPackagesAndSources(f.fs, dir)
+ zap.L().Debug("found main packages", zap.Any("srcs_by_dir", srcsByDir))
if err != nil {
return errors.Wrap(err, "failed to find commands")
}
@@ -48,12 +54,13 @@ func (f *scriptLoader) Load(dir string) error {
ext = ".exe"
}
f.scripts[name] = &script{
- fs: f.fs,
- commandFactory: f.commandFactory,
- srcPaths: srcPaths,
- name: name,
- binPath: filepath.Join(f.binDir, name+ext),
- rootDir: f.rootDir,
+ fs: f.fs,
+ io: f.io,
+ exec: f.exec,
+ srcPaths: srcPaths,
+ name: name,
+ binPath: filepath.Join(f.binDir, name+ext),
+ rootDir: f.rootDir,
}
f.names = append(f.names, name)
}
diff --git a/pkg/grapicmd/internal/module/script/script.go b/pkg/grapicmd/internal/module/script/script.go
index 89b78d8f..ce8302b6 100644
--- a/pkg/grapicmd/internal/module/script/script.go
+++ b/pkg/grapicmd/internal/module/script/script.go
@@ -1,35 +1,45 @@
package script
import (
+ "context"
+ "fmt"
"path/filepath"
"github.com/pkg/errors"
"github.com/spf13/afero"
+ "go.uber.org/zap"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/execx"
"github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
)
type script struct {
- fs afero.Fs
- commandFactory module.CommandFactory
- rootDir string
- name, binPath string
- srcPaths []string
+ fs afero.Fs
+ io *clib.IO
+ exec *execx.Executor
+ rootDir string
+ name, binPath string
+ srcPaths []string
}
func (s *script) Name() string {
return s.name
}
-func (s *script) Build(args ...string) error {
+func (s *script) Build(ctx context.Context, args ...string) error {
+ zap.L().Debug("build script", zap.String("name", s.name), zap.String("bin", s.binPath), zap.Strings("srcs", s.srcPaths))
err := fs.CreateDirIfNotExists(s.fs, filepath.Dir(s.binPath))
if err != nil {
return errors.WithStack(err)
}
- cmd := s.commandFactory.Create(s.buildCmd(args))
- _, err = cmd.ConnectIO().SetDir(s.rootDir).Exec()
+ cmd := s.exec.CommandContext(ctx, "go", s.buildArgs(args)...)
+ cmd.Dir = s.rootDir
+ cmd.Stdout = s.io.Out
+ cmd.Stderr = s.io.Err
+ cmd.Stdin = s.io.In
+ err = cmd.Run()
if err != nil {
return errors.Wrapf(err, "failed to build %v", s.srcPaths)
}
@@ -37,16 +47,24 @@ func (s *script) Build(args ...string) error {
return nil
}
-func (s *script) Run(args ...string) error {
- cmd := s.commandFactory.Create(append([]string{s.binPath}, args...))
- _, err := cmd.ConnectIO().SetDir(s.rootDir).Exec()
+func (s *script) Run(ctx context.Context, args ...string) error {
+ cmd := s.exec.CommandContext(ctx, s.binPath, args...)
+ cmd.Dir = s.rootDir
+ cmd.Stdout = s.io.Out
+ cmd.Stderr = s.io.Err
+ cmd.Stdin = s.io.In
+ err := cmd.Run()
+ fmt.Println(err)
+ if err == context.Canceled {
+ return nil
+ }
return errors.WithStack(err)
}
-func (s *script) buildCmd(args []string) []string {
- cmd := make([]string, 0, 3+len(args)+len(s.srcPaths))
- cmd = append(cmd, "go", "build", "-o="+s.binPath)
- cmd = append(cmd, args...)
- cmd = append(cmd, s.srcPaths...)
- return cmd
+func (s *script) buildArgs(args []string) []string {
+ built := make([]string, 0, 3+len(args)+len(s.srcPaths))
+ built = append(built, "build", "-o="+s.binPath)
+ built = append(built, args...)
+ built = append(built, s.srcPaths...)
+ return built
}
diff --git a/pkg/grapicmd/internal/module/script/script_test.go b/pkg/grapicmd/internal/module/script/script_test.go
index 9422e38f..74242991 100644
--- a/pkg/grapicmd/internal/module/script/script_test.go
+++ b/pkg/grapicmd/internal/module/script/script_test.go
@@ -1,28 +1,27 @@
package script
import (
+ "context"
+ "os/exec"
"path/filepath"
"reflect"
"testing"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
"github.com/spf13/afero"
-)
-type execution struct {
- nameAndArgs []string
- dir string
- connected bool
-}
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/execx"
+ "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
+)
type testContext struct {
fs afero.Fs
- executions []*execution
loader module.ScriptLoader
binName string
rootDir string
cmdDir string
srcsByBinName map[string][]string
+ cmds []*exec.Cmd
}
func createTestContext(t *testing.T) *testContext {
@@ -34,9 +33,11 @@ func createTestContext(t *testing.T) *testContext {
binName: binName,
rootDir: rootDir,
cmdDir: filepath.Join(rootDir, "cmd"),
- executions: []*execution{},
srcsByBinName: map[string][]string{},
}
+ exec := execx.New(execx.WithFakeProcess(
+ func(_ context.Context, c *exec.Cmd) error { ctx.cmds = append(ctx.cmds, c); return nil },
+ ))
srcsByBinName := map[string][]string{
binName: {"bar.go", "foo.go", "main.go"},
@@ -60,22 +61,7 @@ func createTestContext(t *testing.T) *testContext {
ctx.srcsByBinName[binName] = srcPaths
}
- commandFactory := &fakeCommandFactory{
- fakeCreate: func(nameAndArgs []string) module.Command {
- c := &fakeCommand{}
- c.fakeExec = func() ([]byte, error) {
- ctx.executions = append(ctx.executions, &execution{
- nameAndArgs: nameAndArgs,
- dir: c.dir,
- connected: c.ioConnected,
- })
- return []byte{}, nil
- }
- return c
- },
- }
-
- ctx.loader = NewLoader(fs, commandFactory, rootDir)
+ ctx.loader = NewLoader(fs, &clib.IO{}, exec, rootDir)
return ctx
}
@@ -101,16 +87,16 @@ func Test_Script(t *testing.T) {
t.Errorf("script.Build() returned an error %v", err)
}
- err = s.Build("-v")
+ err = s.Build(context.Background(), "-v")
binPath := filepath.Join(ctx.rootDir, "bin", ctx.binName)
- exec := ctx.executions[0]
+ cmd := ctx.cmds[0]
srcs := ctx.srcsByBinName[ctx.binName]
- if got, want := exec.nameAndArgs, append([]string{"go", "build", "-o=" + binPath, "-v"}, srcs...); !reflect.DeepEqual(got, want) {
+ if got, want := cmd.Args, append([]string{"go", "build", "-o=" + binPath, "-v"}, srcs...); !reflect.DeepEqual(got, want) {
t.Errorf("Build() executed %v, want %v", got, want)
}
- if got, want := exec.dir, "/home/app"; got != want {
+ if got, want := cmd.Dir, "/home/app"; got != want {
t.Errorf("Build() executed a command in %v, want %v", got, want)
}
@@ -118,14 +104,14 @@ func Test_Script(t *testing.T) {
t.Errorf("Build() returned an error %v", err)
}
- err = s.Run("-v")
- exec = ctx.executions[1]
+ err = s.Run(context.Background(), "-v")
+ cmd = ctx.cmds[1]
- if got, want := exec.nameAndArgs, []string{binPath, "-v"}; !reflect.DeepEqual(got, want) {
+ if got, want := cmd.Path, binPath; got != want {
t.Errorf("Run() executed %v, want %v", got, want)
}
- if got, want := exec.dir, "/home/app"; got != want {
+ if got, want := cmd.Dir, "/home/app"; got != want {
t.Errorf("Run() executed a command in %v, want %v", got, want)
}
@@ -133,35 +119,3 @@ func Test_Script(t *testing.T) {
t.Errorf("Run() returned an error %v", err)
}
}
-
-// fake impls
-
-type fakeCommandFactory struct {
- module.CommandFactory
- fakeCreate func([]string) module.Command
-}
-
-func (f *fakeCommandFactory) Create(nameAndArgs []string) module.Command {
- return f.fakeCreate(nameAndArgs)
-}
-
-type fakeCommand struct {
- module.Command
- dir string
- ioConnected bool
- fakeExec func() ([]byte, error)
-}
-
-func (c *fakeCommand) SetDir(dir string) module.Command {
- c.dir = dir
- return c
-}
-
-func (c *fakeCommand) ConnectIO() module.Command {
- c.ioConnected = true
- return c
-}
-
-func (c *fakeCommand) Exec() ([]byte, error) {
- return c.fakeExec()
-}
diff --git a/pkg/grapicmd/internal/module/testing/generator_mock.go b/pkg/grapicmd/internal/module/testing/generator_mock.go
index 60c5678e..2a491442 100644
--- a/pkg/grapicmd/internal/module/testing/generator_mock.go
+++ b/pkg/grapicmd/internal/module/testing/generator_mock.go
@@ -1,11 +1,11 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: generator.go
+// Package moduletesting is a generated GoMock package.
package moduletesting
import (
gomock "github.com/golang/mock/gomock"
- . "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
reflect "reflect"
)
@@ -28,80 +28,22 @@ func NewMockGenerator(ctrl *gomock.Controller) *MockGenerator {
}
// EXPECT returns an object that allows the caller to indicate expected use
-func (_m *MockGenerator) EXPECT() *MockGeneratorMockRecorder {
- return _m.recorder
+func (m *MockGenerator) EXPECT() *MockGeneratorMockRecorder {
+ return m.recorder
}
// GenerateProject mocks base method
-func (_m *MockGenerator) GenerateProject(rootDir string, useHead bool) error {
- ret := _m.ctrl.Call(_m, "GenerateProject", rootDir, useHead)
+func (m *MockGenerator) GenerateProject(rootDir, pkgName string) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GenerateProject", rootDir, pkgName)
ret0, _ := ret[0].(error)
return ret0
}
// GenerateProject indicates an expected call of GenerateProject
-func (_mr *MockGeneratorMockRecorder) GenerateProject(arg0, arg1 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GenerateProject", reflect.TypeOf((*MockGenerator)(nil).GenerateProject), arg0, arg1)
-}
-
-// GenerateService mocks base method
-func (_m *MockGenerator) GenerateService(name string, cfg ServiceGenerationConfig) error {
- ret := _m.ctrl.Call(_m, "GenerateService", name, cfg)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// GenerateService indicates an expected call of GenerateService
-func (_mr *MockGeneratorMockRecorder) GenerateService(arg0, arg1 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GenerateService", reflect.TypeOf((*MockGenerator)(nil).GenerateService), arg0, arg1)
-}
-
-// ScaffoldService mocks base method
-func (_m *MockGenerator) ScaffoldService(name string, cfg ServiceGenerationConfig) error {
- ret := _m.ctrl.Call(_m, "ScaffoldService", name, cfg)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// ScaffoldService indicates an expected call of ScaffoldService
-func (_mr *MockGeneratorMockRecorder) ScaffoldService(arg0, arg1 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "ScaffoldService", reflect.TypeOf((*MockGenerator)(nil).ScaffoldService), arg0, arg1)
-}
-
-// DestroyService mocks base method
-func (_m *MockGenerator) DestroyService(name string) error {
- ret := _m.ctrl.Call(_m, "DestroyService", name)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// DestroyService indicates an expected call of DestroyService
-func (_mr *MockGeneratorMockRecorder) DestroyService(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "DestroyService", reflect.TypeOf((*MockGenerator)(nil).DestroyService), arg0)
-}
-
-// GenerateCommand mocks base method
-func (_m *MockGenerator) GenerateCommand(name string) error {
- ret := _m.ctrl.Call(_m, "GenerateCommand", name)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// GenerateCommand indicates an expected call of GenerateCommand
-func (_mr *MockGeneratorMockRecorder) GenerateCommand(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GenerateCommand", reflect.TypeOf((*MockGenerator)(nil).GenerateCommand), arg0)
-}
-
-// DestroyCommand mocks base method
-func (_m *MockGenerator) DestroyCommand(name string) error {
- ret := _m.ctrl.Call(_m, "DestroyCommand", name)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// DestroyCommand indicates an expected call of DestroyCommand
-func (_mr *MockGeneratorMockRecorder) DestroyCommand(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "DestroyCommand", reflect.TypeOf((*MockGenerator)(nil).DestroyCommand), arg0)
+func (mr *MockGeneratorMockRecorder) GenerateProject(rootDir, pkgName interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateProject", reflect.TypeOf((*MockGenerator)(nil).GenerateProject), rootDir, pkgName)
}
// MockProjectGenerator is a mock of ProjectGenerator interface
@@ -123,124 +65,20 @@ func NewMockProjectGenerator(ctrl *gomock.Controller) *MockProjectGenerator {
}
// EXPECT returns an object that allows the caller to indicate expected use
-func (_m *MockProjectGenerator) EXPECT() *MockProjectGeneratorMockRecorder {
- return _m.recorder
+func (m *MockProjectGenerator) EXPECT() *MockProjectGeneratorMockRecorder {
+ return m.recorder
}
// GenerateProject mocks base method
-func (_m *MockProjectGenerator) GenerateProject(rootDir string, useHead bool) error {
- ret := _m.ctrl.Call(_m, "GenerateProject", rootDir, useHead)
+func (m *MockProjectGenerator) GenerateProject(rootDir, pkgName string) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GenerateProject", rootDir, pkgName)
ret0, _ := ret[0].(error)
return ret0
}
// GenerateProject indicates an expected call of GenerateProject
-func (_mr *MockProjectGeneratorMockRecorder) GenerateProject(arg0, arg1 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GenerateProject", reflect.TypeOf((*MockProjectGenerator)(nil).GenerateProject), arg0, arg1)
-}
-
-// MockServiceGenerator is a mock of ServiceGenerator interface
-type MockServiceGenerator struct {
- ctrl *gomock.Controller
- recorder *MockServiceGeneratorMockRecorder
-}
-
-// MockServiceGeneratorMockRecorder is the mock recorder for MockServiceGenerator
-type MockServiceGeneratorMockRecorder struct {
- mock *MockServiceGenerator
-}
-
-// NewMockServiceGenerator creates a new mock instance
-func NewMockServiceGenerator(ctrl *gomock.Controller) *MockServiceGenerator {
- mock := &MockServiceGenerator{ctrl: ctrl}
- mock.recorder = &MockServiceGeneratorMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use
-func (_m *MockServiceGenerator) EXPECT() *MockServiceGeneratorMockRecorder {
- return _m.recorder
-}
-
-// GenerateService mocks base method
-func (_m *MockServiceGenerator) GenerateService(name string, cfg ServiceGenerationConfig) error {
- ret := _m.ctrl.Call(_m, "GenerateService", name, cfg)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// GenerateService indicates an expected call of GenerateService
-func (_mr *MockServiceGeneratorMockRecorder) GenerateService(arg0, arg1 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GenerateService", reflect.TypeOf((*MockServiceGenerator)(nil).GenerateService), arg0, arg1)
-}
-
-// ScaffoldService mocks base method
-func (_m *MockServiceGenerator) ScaffoldService(name string, cfg ServiceGenerationConfig) error {
- ret := _m.ctrl.Call(_m, "ScaffoldService", name, cfg)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// ScaffoldService indicates an expected call of ScaffoldService
-func (_mr *MockServiceGeneratorMockRecorder) ScaffoldService(arg0, arg1 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "ScaffoldService", reflect.TypeOf((*MockServiceGenerator)(nil).ScaffoldService), arg0, arg1)
-}
-
-// DestroyService mocks base method
-func (_m *MockServiceGenerator) DestroyService(name string) error {
- ret := _m.ctrl.Call(_m, "DestroyService", name)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// DestroyService indicates an expected call of DestroyService
-func (_mr *MockServiceGeneratorMockRecorder) DestroyService(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "DestroyService", reflect.TypeOf((*MockServiceGenerator)(nil).DestroyService), arg0)
-}
-
-// MockCommandGenerator is a mock of CommandGenerator interface
-type MockCommandGenerator struct {
- ctrl *gomock.Controller
- recorder *MockCommandGeneratorMockRecorder
-}
-
-// MockCommandGeneratorMockRecorder is the mock recorder for MockCommandGenerator
-type MockCommandGeneratorMockRecorder struct {
- mock *MockCommandGenerator
-}
-
-// NewMockCommandGenerator creates a new mock instance
-func NewMockCommandGenerator(ctrl *gomock.Controller) *MockCommandGenerator {
- mock := &MockCommandGenerator{ctrl: ctrl}
- mock.recorder = &MockCommandGeneratorMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use
-func (_m *MockCommandGenerator) EXPECT() *MockCommandGeneratorMockRecorder {
- return _m.recorder
-}
-
-// GenerateCommand mocks base method
-func (_m *MockCommandGenerator) GenerateCommand(name string) error {
- ret := _m.ctrl.Call(_m, "GenerateCommand", name)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// GenerateCommand indicates an expected call of GenerateCommand
-func (_mr *MockCommandGeneratorMockRecorder) GenerateCommand(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GenerateCommand", reflect.TypeOf((*MockCommandGenerator)(nil).GenerateCommand), arg0)
-}
-
-// DestroyCommand mocks base method
-func (_m *MockCommandGenerator) DestroyCommand(name string) error {
- ret := _m.ctrl.Call(_m, "DestroyCommand", name)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// DestroyCommand indicates an expected call of DestroyCommand
-func (_mr *MockCommandGeneratorMockRecorder) DestroyCommand(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "DestroyCommand", reflect.TypeOf((*MockCommandGenerator)(nil).DestroyCommand), arg0)
+func (mr *MockProjectGeneratorMockRecorder) GenerateProject(rootDir, pkgName interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateProject", reflect.TypeOf((*MockProjectGenerator)(nil).GenerateProject), rootDir, pkgName)
}
diff --git a/pkg/grapicmd/internal/module/testing/script_mock.go b/pkg/grapicmd/internal/module/testing/script_mock.go
index 8220f9c8..c70cde2b 100644
--- a/pkg/grapicmd/internal/module/testing/script_mock.go
+++ b/pkg/grapicmd/internal/module/testing/script_mock.go
@@ -1,11 +1,13 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: script.go
+// Package moduletesting is a generated GoMock package.
package moduletesting
import (
+ context "context"
gomock "github.com/golang/mock/gomock"
- . "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
+ module "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
reflect "reflect"
)
@@ -28,52 +30,60 @@ func NewMockScript(ctrl *gomock.Controller) *MockScript {
}
// EXPECT returns an object that allows the caller to indicate expected use
-func (_m *MockScript) EXPECT() *MockScriptMockRecorder {
- return _m.recorder
+func (m *MockScript) EXPECT() *MockScriptMockRecorder {
+ return m.recorder
}
// Name mocks base method
-func (_m *MockScript) Name() string {
- ret := _m.ctrl.Call(_m, "Name")
+func (m *MockScript) Name() string {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Name")
ret0, _ := ret[0].(string)
return ret0
}
// Name indicates an expected call of Name
-func (_mr *MockScriptMockRecorder) Name() *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Name", reflect.TypeOf((*MockScript)(nil).Name))
+func (mr *MockScriptMockRecorder) Name() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockScript)(nil).Name))
}
// Build mocks base method
-func (_m *MockScript) Build(args ...string) error {
- _s := []interface{}{}
- for _, _x := range args {
- _s = append(_s, _x)
+func (m *MockScript) Build(ctx context.Context, args ...string) error {
+ m.ctrl.T.Helper()
+ varargs := []interface{}{ctx}
+ for _, a := range args {
+ varargs = append(varargs, a)
}
- ret := _m.ctrl.Call(_m, "Build", _s...)
+ ret := m.ctrl.Call(m, "Build", varargs...)
ret0, _ := ret[0].(error)
return ret0
}
// Build indicates an expected call of Build
-func (_mr *MockScriptMockRecorder) Build(arg0 ...interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Build", reflect.TypeOf((*MockScript)(nil).Build), arg0...)
+func (mr *MockScriptMockRecorder) Build(ctx interface{}, args ...interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]interface{}{ctx}, args...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Build", reflect.TypeOf((*MockScript)(nil).Build), varargs...)
}
// Run mocks base method
-func (_m *MockScript) Run(args ...string) error {
- _s := []interface{}{}
- for _, _x := range args {
- _s = append(_s, _x)
+func (m *MockScript) Run(ctx context.Context, args ...string) error {
+ m.ctrl.T.Helper()
+ varargs := []interface{}{ctx}
+ for _, a := range args {
+ varargs = append(varargs, a)
}
- ret := _m.ctrl.Call(_m, "Run", _s...)
+ ret := m.ctrl.Call(m, "Run", varargs...)
ret0, _ := ret[0].(error)
return ret0
}
// Run indicates an expected call of Run
-func (_mr *MockScriptMockRecorder) Run(arg0 ...interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Run", reflect.TypeOf((*MockScript)(nil).Run), arg0...)
+func (mr *MockScriptMockRecorder) Run(ctx interface{}, args ...interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]interface{}{ctx}, args...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockScript)(nil).Run), varargs...)
}
// MockScriptLoader is a mock of ScriptLoader interface
@@ -95,43 +105,49 @@ func NewMockScriptLoader(ctrl *gomock.Controller) *MockScriptLoader {
}
// EXPECT returns an object that allows the caller to indicate expected use
-func (_m *MockScriptLoader) EXPECT() *MockScriptLoaderMockRecorder {
- return _m.recorder
+func (m *MockScriptLoader) EXPECT() *MockScriptLoaderMockRecorder {
+ return m.recorder
}
// Load mocks base method
-func (_m *MockScriptLoader) Load(dir string) error {
- ret := _m.ctrl.Call(_m, "Load", dir)
+func (m *MockScriptLoader) Load(dir string) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Load", dir)
ret0, _ := ret[0].(error)
return ret0
}
// Load indicates an expected call of Load
-func (_mr *MockScriptLoaderMockRecorder) Load(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Load", reflect.TypeOf((*MockScriptLoader)(nil).Load), arg0)
+func (mr *MockScriptLoaderMockRecorder) Load(dir interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Load", reflect.TypeOf((*MockScriptLoader)(nil).Load), dir)
}
// Get mocks base method
-func (_m *MockScriptLoader) Get(name string) (Script, bool) {
- ret := _m.ctrl.Call(_m, "Get", name)
- ret0, _ := ret[0].(Script)
+func (m *MockScriptLoader) Get(name string) (module.Script, bool) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Get", name)
+ ret0, _ := ret[0].(module.Script)
ret1, _ := ret[1].(bool)
return ret0, ret1
}
// Get indicates an expected call of Get
-func (_mr *MockScriptLoaderMockRecorder) Get(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Get", reflect.TypeOf((*MockScriptLoader)(nil).Get), arg0)
+func (mr *MockScriptLoaderMockRecorder) Get(name interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockScriptLoader)(nil).Get), name)
}
// Names mocks base method
-func (_m *MockScriptLoader) Names() []string {
- ret := _m.ctrl.Call(_m, "Names")
+func (m *MockScriptLoader) Names() []string {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Names")
ret0, _ := ret[0].([]string)
return ret0
}
// Names indicates an expected call of Names
-func (_mr *MockScriptLoaderMockRecorder) Names() *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Names", reflect.TypeOf((*MockScriptLoader)(nil).Names))
+func (mr *MockScriptLoaderMockRecorder) Names() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Names", reflect.TypeOf((*MockScriptLoader)(nil).Names))
}
diff --git a/pkg/grapicmd/internal/module/testing/ui_mock.go b/pkg/grapicmd/internal/module/testing/ui_mock.go
deleted file mode 100644
index 240b037f..00000000
--- a/pkg/grapicmd/internal/module/testing/ui_mock.go
+++ /dev/null
@@ -1,125 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: ui.go
-
-package moduletesting
-
-import (
- gomock "github.com/golang/mock/gomock"
- reflect "reflect"
-)
-
-// MockUI is a mock of UI interface
-type MockUI struct {
- ctrl *gomock.Controller
- recorder *MockUIMockRecorder
-}
-
-// MockUIMockRecorder is the mock recorder for MockUI
-type MockUIMockRecorder struct {
- mock *MockUI
-}
-
-// NewMockUI creates a new mock instance
-func NewMockUI(ctrl *gomock.Controller) *MockUI {
- mock := &MockUI{ctrl: ctrl}
- mock.recorder = &MockUIMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use
-func (_m *MockUI) EXPECT() *MockUIMockRecorder {
- return _m.recorder
-}
-
-// Output mocks base method
-func (_m *MockUI) Output(msg string) {
- _m.ctrl.Call(_m, "Output", msg)
-}
-
-// Output indicates an expected call of Output
-func (_mr *MockUIMockRecorder) Output(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Output", reflect.TypeOf((*MockUI)(nil).Output), arg0)
-}
-
-// Section mocks base method
-func (_m *MockUI) Section(msg string) {
- _m.ctrl.Call(_m, "Section", msg)
-}
-
-// Section indicates an expected call of Section
-func (_mr *MockUIMockRecorder) Section(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Section", reflect.TypeOf((*MockUI)(nil).Section), arg0)
-}
-
-// Subsection mocks base method
-func (_m *MockUI) Subsection(msg string) {
- _m.ctrl.Call(_m, "Subsection", msg)
-}
-
-// Subsection indicates an expected call of Subsection
-func (_mr *MockUIMockRecorder) Subsection(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Subsection", reflect.TypeOf((*MockUI)(nil).Subsection), arg0)
-}
-
-// Warn mocks base method
-func (_m *MockUI) Warn(msg string) {
- _m.ctrl.Call(_m, "Warn", msg)
-}
-
-// Warn indicates an expected call of Warn
-func (_mr *MockUIMockRecorder) Warn(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Warn", reflect.TypeOf((*MockUI)(nil).Warn), arg0)
-}
-
-// Error mocks base method
-func (_m *MockUI) Error(msg string) {
- _m.ctrl.Call(_m, "Error", msg)
-}
-
-// Error indicates an expected call of Error
-func (_mr *MockUIMockRecorder) Error(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Error", reflect.TypeOf((*MockUI)(nil).Error), arg0)
-}
-
-// ItemSuccess mocks base method
-func (_m *MockUI) ItemSuccess(msg string) {
- _m.ctrl.Call(_m, "ItemSuccess", msg)
-}
-
-// ItemSuccess indicates an expected call of ItemSuccess
-func (_mr *MockUIMockRecorder) ItemSuccess(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "ItemSuccess", reflect.TypeOf((*MockUI)(nil).ItemSuccess), arg0)
-}
-
-// ItemSkipped mocks base method
-func (_m *MockUI) ItemSkipped(msg string) {
- _m.ctrl.Call(_m, "ItemSkipped", msg)
-}
-
-// ItemSkipped indicates an expected call of ItemSkipped
-func (_mr *MockUIMockRecorder) ItemSkipped(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "ItemSkipped", reflect.TypeOf((*MockUI)(nil).ItemSkipped), arg0)
-}
-
-// ItemFailure mocks base method
-func (_m *MockUI) ItemFailure(msg string) {
- _m.ctrl.Call(_m, "ItemFailure", msg)
-}
-
-// ItemFailure indicates an expected call of ItemFailure
-func (_mr *MockUIMockRecorder) ItemFailure(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "ItemFailure", reflect.TypeOf((*MockUI)(nil).ItemFailure), arg0)
-}
-
-// Confirm mocks base method
-func (_m *MockUI) Confirm(msg string) (bool, error) {
- ret := _m.ctrl.Call(_m, "Confirm", msg)
- ret0, _ := ret[0].(bool)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// Confirm indicates an expected call of Confirm
-func (_mr *MockUIMockRecorder) Confirm(arg0 interface{}) *gomock.Call {
- return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Confirm", reflect.TypeOf((*MockUI)(nil).Confirm), arg0)
-}
diff --git a/pkg/grapicmd/internal/module/ui.go b/pkg/grapicmd/internal/module/ui.go
deleted file mode 100644
index 2f289963..00000000
--- a/pkg/grapicmd/internal/module/ui.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package module
-
-// UI is an interface for intaracting with the terminal.
-type UI interface {
- Output(msg string)
- Section(msg string)
- Subsection(msg string)
- Warn(msg string)
- Error(msg string)
- ItemSuccess(msg string)
- ItemSkipped(msg string)
- ItemFailure(msg string)
- Confirm(msg string) (bool, error)
-}
diff --git a/pkg/grapicmd/internal/module/ui/config.go b/pkg/grapicmd/internal/module/ui/config.go
deleted file mode 100644
index 579aab9c..00000000
--- a/pkg/grapicmd/internal/module/ui/config.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package ui
-
-import (
- "fmt"
- "io"
- "strconv"
-
- "github.com/fatih/color"
-)
-
-type fprintFunc func(w io.Writer, msg string)
-
-type printConfig struct {
- prefix string
- colorAttrs []color.Attribute
- indent int
- allColor bool
-}
-
-type printType int
-
-const (
- printTypeOutput printType = iota
- printTypeSection
- printTypeSubsection
- printTypeWarn
- printTypeError
- printTypeItemSuccess
- printTypeItemSkipped
- printTypeItemFailure
-)
-
-const (
- indentSizeItem = 4
-)
-
-var (
- configByPrintType = map[printType]printConfig{
- printTypeSection: {
- prefix: "โ",
- colorAttrs: []color.Attribute{color.FgYellow},
- allColor: true,
- },
- printTypeSubsection: {
- prefix: "โธ",
- colorAttrs: []color.Attribute{color.FgBlue},
- allColor: true,
- },
- printTypeWarn: {
- prefix: "โ ",
- colorAttrs: []color.Attribute{color.FgHiYellow},
- allColor: true,
- },
- printTypeError: {
- prefix: "โ",
- colorAttrs: []color.Attribute{color.FgHiRed},
- allColor: true,
- },
- printTypeItemSuccess: {
- prefix: "โ",
- colorAttrs: []color.Attribute{color.Bold, color.FgGreen},
- indent: indentSizeItem,
- },
- printTypeItemSkipped: {
- prefix: "โ",
- colorAttrs: []color.Attribute{color.Bold, color.FgBlue},
- indent: indentSizeItem,
- },
- printTypeItemFailure: {
- prefix: "โ",
- colorAttrs: []color.Attribute{color.Bold, color.FgRed},
- indent: indentSizeItem,
- },
- }
- fprintlnFuncByPrintType = map[printType]fprintFunc{}
-)
-
-func init() {
- for pt, cfg := range configByPrintType {
- cfg := cfg
- fmtStr := "%s"
- if cfg.indent > 0 {
- fmtStr = "%" + strconv.FormatInt(int64(cfg.indent), 10) + "s"
- }
- if cfg.allColor {
- colored := color.New(cfg.colorAttrs...).FprintfFunc()
- fprintlnFuncByPrintType[pt] = func(w io.Writer, msg string) {
- colored(w, " "+fmtStr+" %s\n", cfg.prefix, msg)
- }
- } else {
- prefix := color.New(cfg.colorAttrs...).Sprintf(fmtStr, cfg.prefix)
- fprintlnFuncByPrintType[pt] = func(w io.Writer, msg string) {
- fmt.Fprintf(w, " %s %s\n", prefix, msg)
- }
- }
- }
-}
-
-func (pt printType) Fprintln(w io.Writer, msg string) {
- fprintlnFuncByPrintType[pt](w, msg)
-}
diff --git a/pkg/grapicmd/internal/module/ui/ui.go b/pkg/grapicmd/internal/module/ui/ui.go
deleted file mode 100644
index ce53b207..00000000
--- a/pkg/grapicmd/internal/module/ui/ui.go
+++ /dev/null
@@ -1,92 +0,0 @@
-package ui
-
-import (
- "fmt"
- "io"
-
- "github.com/izumin5210/clicontrib/pkg/clog"
- "github.com/tcnksm/go-input"
-
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
-)
-
-// New creates a new UI instance.
-func New(out io.Writer, in io.Reader) module.UI {
- return &uiImpl{
- out: out,
- inputUI: &input.UI{
- Reader: in,
- Writer: out,
- },
- }
-}
-
-type uiImpl struct {
- out io.Writer
- inSection bool
- inputUI *input.UI
-}
-
-func (u *uiImpl) Output(msg string) {
- u.inSection = true
- fmt.Fprintf(u.out, " %s\n", msg)
-}
-
-func (u *uiImpl) Section(msg string) {
- if u.inSection {
- fmt.Fprintln(u.out)
- u.inSection = false
- }
- printTypeSection.Fprintln(u.out, msg)
-}
-
-func (u *uiImpl) Subsection(msg string) {
- if u.inSection {
- fmt.Fprintln(u.out)
- u.inSection = false
- }
- printTypeSubsection.Fprintln(u.out, msg)
-}
-
-func (u *uiImpl) Warn(msg string) {
- u.inSection = true
- printTypeWarn.Fprintln(u.out, msg)
-}
-
-func (u *uiImpl) Error(msg string) {
- u.inSection = true
- printTypeError.Fprintln(u.out, msg)
-}
-
-func (u *uiImpl) ItemSuccess(msg string) {
- u.inSection = true
- printTypeItemSuccess.Fprintln(u.out, msg)
-}
-
-func (u *uiImpl) ItemSkipped(msg string) {
- u.inSection = true
- printTypeItemSkipped.Fprintln(u.out, msg)
-}
-
-func (u *uiImpl) ItemFailure(msg string) {
- u.inSection = true
- printTypeItemFailure.Fprintln(u.out, msg)
-}
-
-func (u *uiImpl) Confirm(msg string) (bool, error) {
- ans, err := u.inputUI.Ask(fmt.Sprintf("%s [Y/n]", msg), &input.Options{
- HideOrder: true,
- Loop: true,
- ValidateFunc: func(ans string) error {
- clog.Debug("receive user input", "query", msg, "input", ans)
- if ans != "Y" && ans != "n" {
- return fmt.Errorf("input must be Y or n")
- }
- return nil
- },
- })
- if err != nil {
- return false, err
- }
- return ans == "Y", nil
-}
diff --git a/pkg/grapicmd/internal/usecase/execute_protoc_usecase.go b/pkg/grapicmd/internal/usecase/execute_protoc_usecase.go
deleted file mode 100644
index 1f8d3f43..00000000
--- a/pkg/grapicmd/internal/usecase/execute_protoc_usecase.go
+++ /dev/null
@@ -1,141 +0,0 @@
-package usecase
-
-import (
- "os"
- "path/filepath"
-
- "github.com/pkg/errors"
- "github.com/spf13/afero"
-
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
- "github.com/izumin5210/grapi/pkg/grapicmd/protoc"
- "github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
-)
-
-// ExecuteProtocUsecase is an useecase interface for executing protoc module.
-type ExecuteProtocUsecase interface {
- Perform() error
- InstallPlugins() error
- ExecuteProtoc() error
-}
-
-type executeProtocUsecase struct {
- cfg *protoc.Config
- fs afero.Fs
- ui module.UI
- commandFactory module.CommandFactory
- rootDir, binDir string
-}
-
-// NewExecuteProtocUsecase returns an new ExecuteProtocUsecase implementation instance.
-func NewExecuteProtocUsecase(cfg *protoc.Config, fs afero.Fs, ui module.UI, commandFactory module.CommandFactory, rootDir string) ExecuteProtocUsecase {
- return &executeProtocUsecase{
- cfg: cfg,
- fs: fs,
- ui: ui,
- commandFactory: commandFactory,
- rootDir: rootDir,
- binDir: filepath.Join(rootDir, "bin"),
- }
-}
-
-func (u *executeProtocUsecase) Perform() error {
- u.ui.Section("Execute protoc")
- u.ui.Subsection("Install plugins")
- err := errors.WithStack(u.InstallPlugins())
- if err != nil {
- return err
- }
- u.ui.Subsection("Execute protoc")
- return errors.WithStack(u.ExecuteProtoc())
-}
-
-func (u *executeProtocUsecase) InstallPlugins() error {
- if err := fs.CreateDirIfNotExists(u.fs, u.binDir); err != nil {
- return errors.WithStack(err)
- }
- var errs []error
- for _, plugin := range u.cfg.Plugins {
- ok, err := u.installPlugin(plugin)
- if err != nil {
- errs = append(errs, err)
- u.ui.ItemFailure(plugin.BinName())
- } else if !ok {
- u.ui.ItemSkipped(plugin.BinName())
- } else {
- u.ui.ItemSuccess(plugin.BinName())
- }
- }
- if len(errs) > 0 {
- for _, err := range errs {
- u.ui.Error(err.Error())
- }
- return errors.New("failed to install protoc plugins")
- }
- return nil
-}
-
-func (u *executeProtocUsecase) ExecuteProtoc() error {
- protoFiles, err := u.cfg.ProtoFiles(u.fs, u.rootDir)
- if err != nil {
- return errors.WithStack(err)
- }
- var errs []error
- for _, path := range protoFiles {
- err = u.executeProtoc(path)
- relPath, _ := filepath.Rel(u.rootDir, path)
- if err == nil {
- u.ui.ItemSuccess(relPath)
- } else {
- errs = append(errs, err)
- u.ui.ItemFailure(relPath)
- }
- }
- if len(errs) > 0 {
- for _, err := range errs {
- u.ui.Error(err.Error())
- }
- return errors.New("failed to execute protoc")
- }
- return nil
-}
-
-func (u *executeProtocUsecase) installPlugin(plugin *protoc.Plugin) (bool, error) {
- binPath := filepath.Join(u.binDir, plugin.BinName())
- if ok, err := afero.Exists(u.fs, binPath); err != nil {
- return false, errors.Wrapf(err, "failed to get %q binary", plugin.BinName())
- } else if ok {
- return false, nil
- }
- dir := filepath.Join(u.rootDir, plugin.Path)
- if ok, _ := afero.DirExists(u.fs, dir); !ok {
- return false, errors.Errorf("%s is not found", plugin.Path)
- }
- cmd := u.commandFactory.Create([]string{"go", "install", "."})
- out, err := cmd.SetDir(dir).AddEnv("GOBIN", u.binDir).Exec()
- if err != nil {
- return false, errors.Wrapf(err, "failed to execute module: %s", string(out))
- }
- return true, nil
-}
-
-func (u *executeProtocUsecase) executeProtoc(protoPath string) error {
- outDir, err := u.cfg.OutDirOf(u.rootDir, protoPath)
- if err != nil {
- return errors.WithStack(err)
- }
- if err = fs.CreateDirIfNotExists(u.fs, outDir); err != nil {
- return errors.WithStack(err)
- }
- cmds, err := u.cfg.Commands(u.rootDir, protoPath)
- if err != nil {
- return errors.WithStack(err)
- }
- for _, cmd := range cmds {
- out, err := u.commandFactory.Create(cmd).AddEnv("PATH", u.binDir+string(filepath.ListSeparator)+os.Getenv("PATH")).SetDir(u.rootDir).Exec()
- if err != nil {
- return errors.Wrapf(err, "failed to execute module: %s", string(out))
- }
- }
- return nil
-}
diff --git a/pkg/grapicmd/internal/usecase/init_config.go b/pkg/grapicmd/internal/usecase/init_config.go
new file mode 100644
index 00000000..3520df4e
--- /dev/null
+++ b/pkg/grapicmd/internal/usecase/init_config.go
@@ -0,0 +1,32 @@
+package usecase
+
+import "bytes"
+
+type InitConfig struct {
+ Revision string
+ Branch string
+ Version string
+ HEAD bool
+ GrapiReplacementURL string
+ Package string
+ Dep bool
+}
+
+func (c *InitConfig) BuildSpec() string {
+ buf := bytes.NewBufferString("")
+ var constraint string
+ switch {
+ case c.Revision != "":
+ constraint = c.Revision
+ case c.Branch != "":
+ constraint = c.Branch
+ case c.HEAD:
+ constraint = "master"
+ case c.Version != "":
+ constraint = c.Version
+ }
+ if constraint != "" {
+ buf.WriteString("@" + constraint)
+ }
+ return buf.String()
+}
diff --git a/pkg/grapicmd/internal/usecase/init_config_test.go b/pkg/grapicmd/internal/usecase/init_config_test.go
new file mode 100644
index 00000000..e8d45c7d
--- /dev/null
+++ b/pkg/grapicmd/internal/usecase/init_config_test.go
@@ -0,0 +1,43 @@
+package usecase
+
+import "testing"
+
+func TestInitConfig_BuildSpec(t *testing.T) {
+ cases := []struct {
+ test string
+ cfg InitConfig
+ out string
+ }{
+ {
+ test: "empty",
+ },
+ {
+ test: "HEAD",
+ cfg: InitConfig{HEAD: true},
+ out: "@master",
+ },
+ {
+ test: "branch",
+ cfg: InitConfig{Branch: "foo/bar"},
+ out: "@foo/bar",
+ },
+ {
+ test: "version",
+ cfg: InitConfig{Version: "^0.3.0"},
+ out: "@^0.3.0",
+ },
+ {
+ test: "revision",
+ cfg: InitConfig{Revision: "a2489d2"},
+ out: "@a2489d2",
+ },
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.test, func(t *testing.T) {
+ if got, want := tc.cfg.BuildSpec(), tc.out; got != want {
+ t.Errorf("BuildSpec() returned %q, want %q", got, want)
+ }
+ })
+ }
+}
diff --git a/pkg/grapicmd/internal/usecase/initialize_project_usecase.go b/pkg/grapicmd/internal/usecase/initialize_project_usecase.go
index fa2f9221..cdb3d3c8 100644
--- a/pkg/grapicmd/internal/usecase/initialize_project_usecase.go
+++ b/pkg/grapicmd/internal/usecase/initialize_project_usecase.go
@@ -1,61 +1,174 @@
package usecase
import (
+ "context"
+ "os"
+ "path/filepath"
+
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/execx"
+ "github.com/izumin5210/gex"
+ "github.com/izumin5210/gex/pkg/tool"
"github.com/pkg/errors"
+ "github.com/spf13/afero"
- "github.com/izumin5210/grapi/pkg/grapicmd/internal/module"
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ _ "github.com/izumin5210/grapi/pkg/grapicmd/internal/usecase/template"
+ "github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
)
// InitializeProjectUsecase is an interface to create a new grapi project.
type InitializeProjectUsecase interface {
- Perform(rootDir string, depSkipped, headUsed bool) error
- GenerateProject(rootDir string, headUsed bool) error
- InstallDeps(rootDir string) error
+ Perform(rootDir string, cfg InitConfig) error
+ GenerateProject(rootDir, pkgName string) error
+ InstallDeps(rootDir string, cfg InitConfig) error
}
// NewInitializeProjectUsecase creates a new InitializeProjectUsecase instance.
-func NewInitializeProjectUsecase(ui module.UI, generator module.ProjectGenerator, commandFactory module.CommandFactory, version string) InitializeProjectUsecase {
+func NewInitializeProjectUsecase(ui cli.UI, fs afero.Fs, generator gencmd.Generator, io *clib.IO, exec *execx.Executor, gexCfg *gex.Config) InitializeProjectUsecase {
return &initializeProjectUsecase{
- ui: ui,
- generator: generator,
- commandFactory: commandFactory,
- version: version,
+ ui: ui,
+ fs: fs,
+ generator: generator,
+ io: io,
+ exec: exec,
+ gexCfg: gexCfg,
}
}
type initializeProjectUsecase struct {
- ui module.UI
- generator module.ProjectGenerator
- commandFactory module.CommandFactory
- version string
+ ui cli.UI
+ fs afero.Fs
+ generator gencmd.Generator
+ io *clib.IO
+ exec *execx.Executor
+ gexCfg *gex.Config
}
-func (u *initializeProjectUsecase) Perform(rootDir string, depSkipped, headUsed bool) error {
+func (u *initializeProjectUsecase) Perform(rootDir string, cfg InitConfig) error {
u.ui.Section("Initialize project")
var err error
- err = u.GenerateProject(rootDir, headUsed)
+ err = u.GenerateProject(rootDir, cfg.Package)
if err != nil {
return errors.Wrap(err, "failed to initialize project")
}
u.ui.Subsection("Install dependencies")
- if !depSkipped {
- err = u.InstallDeps(rootDir)
- if err != nil {
- return errors.Wrap(err, "failed to execute `dep ensure`")
- }
+ err = u.InstallDeps(rootDir, cfg)
+ if err != nil {
+ return errors.Wrap(err, "failed to install dependencies")
}
return nil
}
-func (u *initializeProjectUsecase) GenerateProject(rootDir string, headUsed bool) error {
- return errors.WithStack(u.generator.GenerateProject(rootDir, headUsed))
+func (u *initializeProjectUsecase) GenerateProject(rootDir, pkgName string) error {
+ importPath, err := fs.GetImportPath(rootDir)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ if pkgName == "" {
+ pkgName, err = fs.GetPackageName(rootDir)
+ if err != nil {
+ return errors.Wrap(err, "failed to decide a package name")
+ }
+ }
+
+ data := map[string]interface{}{
+ "packageName": pkgName,
+ "importPath": importPath,
+ }
+ return errors.WithStack(u.generator.Generate(data))
}
-func (u *initializeProjectUsecase) InstallDeps(rootDir string) error {
- cmd := u.commandFactory.Create([]string{"dep", "ensure", "-v"})
- _, err := cmd.ConnectIO().SetDir(rootDir).Exec()
- return errors.WithStack(err)
+func (u *initializeProjectUsecase) InstallDeps(rootDir string, cfg InitConfig) error {
+ invoke := func(name string, args ...string) error {
+ cmd := u.exec.CommandContext(context.TODO(), name, args...)
+ cmd.Stdout = u.io.Out
+ cmd.Stderr = u.io.Err
+ cmd.Stdin = u.io.In
+ cmd.Dir = rootDir
+ if !cfg.Dep {
+ cmd.Env = append(os.Environ(), "GO111MODULE=on")
+ }
+
+ return errors.WithStack(cmd.Run())
+ }
+
+ if cfg.Dep {
+ err := invoke("dep", "init")
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ } else if ok, _ := afero.Exists(u.fs, filepath.Join(rootDir, "go.mod")); ok {
+ err := invoke("go", "mod", "tidy")
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ } else {
+ err := invoke("go", "mod", "init", filepath.Base(rootDir))
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ }
+
+ if cfg.Dep {
+ if spec := cfg.BuildSpec(); spec != "" {
+ u.ui.ItemFailure("--version, --revision, --branch and --HEAD are not supported in dep mode")
+ }
+ } else {
+ if cfg.GrapiReplacementURL != "" {
+ err := invoke("go", "mod", "edit", "-replace", "github.com/izumin5210/grapi="+cfg.GrapiReplacementURL)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ } else if spec := cfg.BuildSpec(); spec != "" {
+ pkg := "github.com/izumin5210/grapi/pkg/grapiserver"
+ err := invoke("go", "get", pkg+spec)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ }
+ err := invoke("go", "get", "./...")
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ }
+
+ u.gexCfg.WorkingDir = rootDir
+ u.gexCfg.RootDir = rootDir
+ toolRepo, err := u.gexCfg.Create()
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ {
+ // regen manifest
+ path := filepath.Join(u.gexCfg.RootDir, u.gexCfg.ManifestName)
+ m, err := tool.NewParser(u.fs, u.gexCfg.ManagerType).Parse(path)
+ if err != nil {
+ return errors.Wrapf(err, "%s was not found", path)
+ }
+ err = tool.NewWriter(u.fs).Write(path, m)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ }
+
+ err = toolRepo.BuildAll(context.Background())
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ if !cfg.Dep {
+ err := invoke("go", "mod", "tidy")
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ }
+
+ return nil
}
diff --git a/pkg/grapicmd/internal/usecase/template/gen.go b/pkg/grapicmd/internal/usecase/template/gen.go
new file mode 100644
index 00000000..70a73172
--- /dev/null
+++ b/pkg/grapicmd/internal/usecase/template/gen.go
@@ -0,0 +1,3 @@
+//go:generate statik -src ./init -dest .. -p template -f -m
+
+package template
diff --git a/pkg/grapicmd/internal/module/generator/template/init/.gitignore.tmpl b/pkg/grapicmd/internal/usecase/template/init/.gitignore.tmpl
similarity index 100%
rename from pkg/grapicmd/internal/module/generator/template/init/.gitignore.tmpl
rename to pkg/grapicmd/internal/usecase/template/init/.gitignore.tmpl
diff --git a/pkg/grapicmd/internal/module/generator/template/init/app/server/.keep.tmpl b/pkg/grapicmd/internal/usecase/template/init/api/protos/.keep.tmpl
similarity index 100%
rename from pkg/grapicmd/internal/module/generator/template/init/app/server/.keep.tmpl
rename to pkg/grapicmd/internal/usecase/template/init/api/protos/.keep.tmpl
diff --git a/testing/api/protos/.keep b/pkg/grapicmd/internal/usecase/template/init/api/protos/type/.keep.tmpl
similarity index 100%
rename from testing/api/protos/.keep
rename to pkg/grapicmd/internal/usecase/template/init/api/protos/type/.keep.tmpl
diff --git a/testing/app/server/.keep b/pkg/grapicmd/internal/usecase/template/init/app/server/.keep.tmpl
similarity index 100%
rename from testing/app/server/.keep
rename to pkg/grapicmd/internal/usecase/template/init/app/server/.keep.tmpl
diff --git a/pkg/grapicmd/internal/module/generator/template/init/cmd/server/run.go.tmpl b/pkg/grapicmd/internal/usecase/template/init/cmd/server/main.go.tmpl
similarity index 61%
rename from pkg/grapicmd/internal/module/generator/template/init/cmd/server/run.go.tmpl
rename to pkg/grapicmd/internal/usecase/template/init/cmd/server/main.go.tmpl
index f9f171d9..41c9e51e 100644
--- a/pkg/grapicmd/internal/module/generator/template/init/cmd/server/run.go.tmpl
+++ b/pkg/grapicmd/internal/usecase/template/init/cmd/server/main.go.tmpl
@@ -4,19 +4,12 @@ import (
"os"
"google.golang.org/grpc/grpclog"
-
- "{{ .importPath }}/app"
)
func main() {
- os.Exit(run())
-}
-
-func run() int {
- err := app.Run()
+ err := run()
if err != nil {
grpclog.Errorf("server was shutdown with errors: %v", err)
- return 1
+ os.Exit(1)
}
- return 0
}
diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-app-run.go b/pkg/grapicmd/internal/usecase/template/init/cmd/server/run.go.tmpl
similarity index 56%
rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-app-run.go
rename to pkg/grapicmd/internal/usecase/template/init/cmd/server/run.go.tmpl
index 13226b76..ddd13198 100644
--- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-app-run.go
+++ b/pkg/grapicmd/internal/usecase/template/init/cmd/server/run.go.tmpl
@@ -1,17 +1,20 @@
-package app
+package main
import (
+ "github.com/srvc/appctx"
+
"github.com/izumin5210/grapi/pkg/grapiserver"
)
-// Run starts the grapiserver.
-func Run() error {
+func run() error {
+ // Application context
+ ctx := appctx.Global()
+
s := grapiserver.New(
grapiserver.WithDefaultLogger(),
grapiserver.WithServers(
// TODO
),
)
- return s.Serve()
+ return s.Serve(ctx)
}
-
diff --git a/pkg/grapicmd/internal/usecase/template/init/grapi.toml.tmpl b/pkg/grapicmd/internal/usecase/template/init/grapi.toml.tmpl
new file mode 100644
index 00000000..215c06b5
--- /dev/null
+++ b/pkg/grapicmd/internal/usecase/template/init/grapi.toml.tmpl
@@ -0,0 +1,25 @@
+package = "{{.packageName}}"
+
+[grapi]
+server_dir = "./app/server"
+
+[protoc]
+protos_dir = "./api/protos"
+out_dir = "./api"
+import_dirs = [
+ "./api/protos",
+ '{{`{{ module "github.com/grpc-ecosystem/grpc-gateway" }}`}}',
+ '{{`{{ module "github.com/grpc-ecosystem/grpc-gateway" }}`}}/third_party/googleapis',
+]
+
+ [[protoc.plugins]]
+ name = "go"
+ args = { plugins = "grpc", paths = "source_relative" }
+
+ [[protoc.plugins]]
+ name = "grpc-gateway"
+ args = { logtostderr = true, paths = "source_relative" }
+
+ [[protoc.plugins]]
+ name = "swagger"
+ args = { logtostderr = true }
diff --git a/pkg/grapicmd/internal/usecase/template/init/tools.go b/pkg/grapicmd/internal/usecase/template/init/tools.go
new file mode 100644
index 00000000..09481548
--- /dev/null
+++ b/pkg/grapicmd/internal/usecase/template/init/tools.go
@@ -0,0 +1,16 @@
+// +build tools
+
+package tools
+
+// tool dependencies
+import (
+ _ "github.com/golang/protobuf/protoc-gen-go"
+ _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"
+ _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"
+ _ "github.com/izumin5210/gex/cmd/gex"
+ _ "github.com/izumin5210/grapi/cmd/grapi"
+ _ "github.com/izumin5210/grapi/cmd/grapi-gen-command"
+ _ "github.com/izumin5210/grapi/cmd/grapi-gen-scaffold-service"
+ _ "github.com/izumin5210/grapi/cmd/grapi-gen-service"
+ _ "github.com/izumin5210/grapi/cmd/grapi-gen-type"
+)
diff --git a/pkg/grapicmd/internal/usecase/template/statik.go b/pkg/grapicmd/internal/usecase/template/statik.go
new file mode 100644
index 00000000..0bafdf93
--- /dev/null
+++ b/pkg/grapicmd/internal/usecase/template/statik.go
@@ -0,0 +1,13 @@
+// Code generated by statik. DO NOT EDIT.
+
+// Package statik contains static assets.
+package template
+
+import (
+ "github.com/rakyll/statik/fs"
+)
+
+func init() {
+ data := "PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\x00 \x00cmd/server/main.go.tmplUT\x05\x00\x01\x80Cm8,\xcb\xc1j\x860\x10\x04\xe0\xf3\xeeSl\x03\x85\x04JJ\xaf\x82G\x1f$\xd8\xb8\x86\xc6\xacl\xa2\x16\x8a\xef^\"\xffe`f\xf8\xf60\xff\x04\x8e\xb4\x85T\x10\xd3\xb6\x8b6\xb2\x08F\xaaA\x04\xc3\"\x9c\xa3g\xc9\xa1\xb0\x17\xe5O\xd6}~\"\x0b\x1bt\x88\xcbQ\xe6\xc7[G\x7f\x08Q\x95\x86\x91\xf4(\xd6!\xa4\x85\xfa\xf06RI\xb9\xdf\xf0\xa2~R\x15]\xac\xa9Q\xcf\xa8t\x85Ju=\xda\xb7\\\x85\xae\xd4\xd6\xeeD\xeb@\xef\xa7\xf9\xe8\xc5!\x80T?\xfd\xa6f\xbf\x1c\xc2\x8d7\xfe\x07\x00\x00\xff\xffPK\x07\x08\xb3\x90\xcfL\x99\x00\x00\x00\xbf\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00 \x00cmd/server/run.go.tmplUT\x05\x00\x01\x80Cm8l\x8dAK31\x10@\xcf\x99_1\xec)\x81\x8f\xcd\xa7\xe0E\xf0 \x14\xbc\x88=(xN\xc34\x1d\xba\x9b\x84\xc9\xa4.\x8a\xff]\xb6\xbdT\xf0\xf6x\xf3f\xa6\x86x\x0c\x89p\x0e\x9c\x01x\xaeE\x14-\x98!\xb1\x1e\xfan\x8ce\xf6MN\xd1\x87Z\xa3.\x03\xfc\x1e\xf1g\x9f9\xdf\xdd\xde\xfc\xf7IBe_\x8f\xe9B\x8d\xe4D2\x80\x03\xd8\xf7\x1cQz\xb6\x0eI\xa4\x08~\x81\xf1\x1e\x1fk\x9d8\x06\xe5\x921\x96\xac\xb4(\x98\xa8\x0b\xde?\xe0\xe5\xdb\xf84\x95]\x98\xac\x030m\xd5W\x97\xc7\x17\xfa\xb0`\xcc\xb5zg=lh\x1f\xfa\xa4\xcf%%\x12\xeb\xfe\xfd\x91\xbc\x9e\xb1\xad\xdb\xde\xe3\xdbv\xb3\x05c\xd6\xd2\x81\x11\xd2.\x19\xdbx\x8el\xd4\xc5\xc17\xfc\x04\x00\x00\xff\xffPK\x07\x08\xabp\xba4\xce\x00\x00\x00&\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00 \x00grapi.toml.tmplUT\x05\x00\x01\x80Cm8\xa4\x91A\x8e\xf20\x0c\x85\xf7>\x85\x95\x0d\x1b\xfe\xf6\x04\\\xe1\xbf\x00\xaa\xc0\x93Z&\x9a\x16GN\x02BQ\xee>j\xe9\x82\x99\xc5\xcc\x82U\xf4\x9e\xbf\xf8\xc5N$\xffI\xc2x@Wk\xb7\xa9\xff4sk\x0e\xe0(F1\x0c\x90\xd8nl\xa71\xd8\x02v=\xc5\xd8?\xbd\x05\x8a\xa6Y\xfd\x00\xeb\x99^\xa9\xd0?=\x07Z\xf2\xb7\x82\x830G\xb5\xd5Lx\xc0#\xe0\x8f+{@\xdc\xd5z\xae\x15g\x1d\xcb\xc4\xe8$\xe4K\xf9\xe8\xbc\xce\xbdX\xf4\xff\xd8kz\xa4\xcc\x9b\x14\xca|\xa7\x87\xc3\xd6\xce\xad\xed\xdem\xd0\xe7K\xb0\xf1\x14\xc9\xf2\xa3\x17U\x99\x98bH\xbb=\x0c\x00\x88\xc7m\xee.NE\xc25\x0d\x03 ^i^w)\xea\x00\x91L\x96\xd9*n\xc8Z\xb1\xe8\xdd\x1e#\xe5\xcb\xaa\x93\x16\xf3|2\x9e(\x87\x1b;l\x7f7\x7f}\xebk\xcc\xa4\x925\xe5\x91mYt\xb6\xc2\xef\x05\xa5;\x89,\x7f\xfck\x066\xf8\n\x00\x00\xff\xffPK\x07\x08\xfaZgN\xfd\x00\x00\x00F\x02\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00 \x00tools.goUT\x05\x00\x01\x80Cm8\x9c\x8e\xddJ\xc4@\x0c\x85\xaf\x9d\xa7\x18z\xa5H\x8d\n>\x8f\xa4\x994\x06;\x93a~\\\xeb\xd3\xcb\xee*(\x0b\x0b\xedU\xce \xdf\x17\x02\xe0\xef\xa7\xaeK\xf0\xcdl\xa9\xcee\xa4w\x14\xfe\xad\x00\xa7\xe4\x03gN\x81\x13)W\xa71[i\xfe\xd6\xdd\xbc\xfaA\xb4\xbd\xf5\xe9\x81,\x82\xd8\x82I \x17k6\xf5\xf9\x1ch\x14N\xa3\xd8pA\x97L#\x93\xd5\xb56\xfe\xa9\x82\x8d\x0f\xb8\xfe3\xff\xec\xf7\xde\xa8\x07\x14\xe1r\xa1\xebW\x8f\x9a^\x9e\x9f\x1eA\xf8\x13(\x86\xe3\xbc\x8a\x15\xccz\x06\x8fi\x03zz\x84,FLa\xabV \xe7\xd9\x960V.\x1fJ\xbc\xd9\xdf\xa7\xb55\xf3\xe0\xee\xdcw\x00\x00\x00\xff\xffPK\x07\x083'=\x96\xb9\x00\x00\x00%\x02\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xb3\x90\xcfL\x99\x00\x00\x00\xbf\x00\x00\x00\x17\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00cmd/server/main.go.tmplUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xabp\xba4\xce\x00\x00\x00&\x01\x00\x00\x16\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xe7\x00\x00\x00cmd/server/run.go.tmplUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xfaZgN\xfd\x00\x00\x00F\x02\x00\x00\x0f\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x02\x02\x00\x00grapi.toml.tmplUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(3'=\x96\xb9\x00\x00\x00%\x02\x00\x00\x08\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81E\x03\x00\x00tools.goUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x04\x00\x04\x00 \x01\x00\x00=\x04\x00\x00\x00\x00"
+ fs.Register(data)
+}
diff --git a/pkg/grapicmd/util/fs/dir.go b/pkg/grapicmd/util/fs/dir.go
index d65a6681..014142eb 100644
--- a/pkg/grapicmd/util/fs/dir.go
+++ b/pkg/grapicmd/util/fs/dir.go
@@ -1,14 +1,14 @@
package fs
import (
- "github.com/izumin5210/clicontrib/pkg/clog"
"github.com/pkg/errors"
"github.com/spf13/afero"
+ "go.uber.org/zap"
)
// CreateDirIfNotExists creates a directory if it does not exist.
func CreateDirIfNotExists(fs afero.Fs, path string) (err error) {
err = fs.MkdirAll(path, 0755)
- clog.Debug("CreateDirIfNotExists", "path", path, "error", err)
+ zap.L().Debug("CreateDirIfNotExists", zap.String("path", path), zap.Error(err))
return errors.Wrapf(err, "failed to create %q directory", path)
}
diff --git a/pkg/grapicmd/util/fs/exec.go b/pkg/grapicmd/util/fs/exec.go
new file mode 100644
index 00000000..7a9256fd
--- /dev/null
+++ b/pkg/grapicmd/util/fs/exec.go
@@ -0,0 +1,47 @@
+package fs
+
+import (
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "sync"
+
+ "github.com/spf13/afero"
+)
+
+func ListExecutableWithPrefix(fs afero.Fs, prefix string) []string {
+ var wg sync.WaitGroup
+ ch := make(chan string)
+
+ for _, path := range filepath.SplitList(os.Getenv("PATH")) {
+ wg.Add(1)
+ go func(path string) {
+ defer wg.Done()
+
+ files, err := afero.ReadDir(fs, path)
+ if err != nil {
+ return
+ }
+
+ for _, f := range files {
+ if m := f.Mode(); !f.IsDir() && m&0111 != 0 && strings.HasPrefix(f.Name(), prefix) {
+ ch <- f.Name()
+ }
+ }
+ }(path)
+ }
+
+ go func() {
+ wg.Wait()
+ close(ch)
+ }()
+
+ execs := make([]string, 0, 100)
+ for exec := range ch {
+ execs = append(execs, exec)
+ }
+ sort.Strings(execs)
+
+ return execs
+}
diff --git a/pkg/grapicmd/util/fs/path.go b/pkg/grapicmd/util/fs/path.go
index 9b6f2fab..e63c65d9 100644
--- a/pkg/grapicmd/util/fs/path.go
+++ b/pkg/grapicmd/util/fs/path.go
@@ -5,22 +5,31 @@ import (
"go/parser"
"go/token"
"os"
+ "os/user"
"path/filepath"
"strings"
- "github.com/izumin5210/clicontrib/pkg/clog"
"github.com/pkg/errors"
"github.com/spf13/afero"
- "golang.org/x/sync/errgroup"
+ "go.uber.org/zap"
)
+type getOSUserFunc func() (*user.User, error)
+
+const (
+ // PackageSeparator is a package separator string on protobuf.
+ PackageSeparator = "."
+)
+
+// Make visible for testing
var (
- // BuildContext is a build context object.
BuildContext build.Context
+ GetOSUser getOSUserFunc
)
func init() {
BuildContext = build.Default
+ GetOSUser = user.Current
}
// GetImportPath creates the golang package path from the given path.
@@ -34,52 +43,36 @@ func GetImportPath(rootPath string) (importPath string, err error) {
}
}
if importPath == "" {
- err = errors.New("failed to get the import path")
+ importPath = filepath.Base(rootPath)
}
return
}
-var (
- requiredFiles = []string{"grapi.toml"}
- requiredDirs = []string{"app", "api", "cmd"}
-)
-
-// LookupRoot returns the application's root directory if the current directory is inside of a grapi application.
-func LookupRoot(fs afero.Fs, dir string) (string, bool) {
- var eg errgroup.Group
-
- for _, f := range requiredFiles {
- f := f
- eg.Go(func() error {
- ok, err := afero.Exists(fs, filepath.Join(dir, f))
- if err != nil || !ok {
- return errors.Errorf("%s does not exist", f)
- }
- return nil
- })
- }
-
- for _, d := range requiredDirs {
- d := d
- eg.Go(func() error {
- ok, err := afero.DirExists(fs, filepath.Join(dir, d))
- if err != nil || !ok {
- return errors.Errorf("%s does not exist", d)
- }
- return nil
- })
+// GetPackageName generates the package name of this application from the given path and envs.
+func GetPackageName(rootPath string) (string, error) {
+ importPath, err := GetImportPath(rootPath)
+ if err != nil {
+ return "", errors.WithStack(err)
}
-
- if eg.Wait() == nil {
- return dir, true
+ entries := strings.Split(importPath, string(filepath.Separator))
+ if len(entries) < 2 {
+ u, err := GetOSUser()
+ if err != nil {
+ return "", errors.WithStack(err)
+ }
+ entries = []string{u.Username, entries[0]}
}
-
- p := dir[len(filepath.VolumeName(dir)):]
- if p == string(filepath.Separator) {
- return "", false
+ entries = entries[len(entries)-2:]
+ if strings.Contains(entries[0], PackageSeparator) {
+ s := strings.Split(entries[0], PackageSeparator)
+ for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
+ s[i], s[j] = s[j], s[i]
+ }
+ entries[0] = strings.Join(s, PackageSeparator)
}
-
- return LookupRoot(fs, filepath.Dir(dir))
+ pkgName := strings.Join(entries[len(entries)-2:], PackageSeparator)
+ pkgName = strings.Replace(pkgName, "-", "_", -1)
+ return pkgName, nil
}
// FindMainPackagesAndSources returns go source file names by main package directories.
@@ -95,12 +88,12 @@ func FindMainPackagesAndSources(fs afero.Fs, dir string) (map[string][]string, e
}
data, err := afero.ReadFile(fs, path)
if err != nil {
- clog.Warn("failed to read a file", "error", err, "path", path)
+ zap.L().Warn("failed to read a file", zap.Error(err), zap.String("path", path))
return nil
}
f, err := parser.ParseFile(fset, "", data, parser.PackageClauseOnly)
if err != nil {
- clog.Warn("failed to parse a file", "error", err, "path", path, "body", string(data))
+ zap.L().Warn("failed to parse a file", zap.Error(err), zap.String("path", path), zap.String("body", string(data)))
return nil
}
if f.Package.IsValid() && f.Name.Name == "main" {
diff --git a/pkg/grapicmd/util/fs/path_test.go b/pkg/grapicmd/util/fs/path_test.go
index 11328407..f476371c 100644
--- a/pkg/grapicmd/util/fs/path_test.go
+++ b/pkg/grapicmd/util/fs/path_test.go
@@ -1,6 +1,7 @@
package fs
import (
+ "os/user"
"path/filepath"
"testing"
@@ -8,40 +9,99 @@ import (
"github.com/spf13/afero"
)
-func Test_LookupRoot(t *testing.T) {
- fs := afero.NewMemMapFs()
+func Test_GetImportPath(t *testing.T) {
+ defer func(tmp string) { BuildContext.GOPATH = tmp }(BuildContext.GOPATH)
+ BuildContext.GOPATH = "/home/go"
+
+ cases := []struct {
+ test string
+ in string
+ out string
+ isErr bool
+ }{
+ {
+ test: "inside of GOPATH",
+ in: "/home/go/src/github.com/izumin5210/testapp",
+ out: "github.com/izumin5210/testapp",
+ },
+ {
+ test: "directly under GOPATH",
+ in: "/home/go/src/testapp",
+ out: "testapp",
+ },
+ {
+ test: "outside of GOPATH",
+ in: "/home/go/testapp",
+ out: "testapp",
+ },
+ }
+
+ for _, c := range cases {
+ t.Run(c.test, func(t *testing.T) {
+ out, err := GetImportPath(c.in)
+
+ if got, want := err != nil, c.isErr; got != want {
+ t.Errorf("Returned error is %t, want %t (%v)", got, want, err)
+ }
+
+ if got, want := out, c.out; got != want {
+ t.Errorf("Returned %s, want %s", got, want)
+ }
+ })
+ }
+}
- fs.MkdirAll("/home/root/app/server", 0755)
- fs.MkdirAll("/home/root/api/proto", 0755)
- fs.MkdirAll("/home/root/cmd/server", 0755)
- fs.MkdirAll("/home/app", 0755)
- afero.WriteFile(fs, "/home/root/grapi.toml", []byte(""), 0644)
- afero.WriteFile(fs, "/home/app/grapi.toml", []byte(""), 0644)
+func Test_GetPackageName(t *testing.T) {
+ defer func(tmp getOSUserFunc) { GetOSUser = tmp }(GetOSUser)
+ GetOSUser = func() (*user.User, error) { return &user.User{Username: "testuser"}, nil }
+ defer func(tmp string) { BuildContext.GOPATH = tmp }(BuildContext.GOPATH)
+ BuildContext.GOPATH = "/home/go"
cases := []struct {
- cwd string
- root string
- ok bool
+ test string
+ in string
+ out string
+ isErr bool
}{
- {cwd: "/", root: "", ok: false},
- {cwd: "/home", root: "", ok: false},
- {cwd: "/home/root", root: "/home/root", ok: true},
- {cwd: "/home/root/app", root: "/home/root", ok: true},
- {cwd: "/home/root/api/proto", root: "/home/root", ok: true},
- {cwd: "/home/app", root: "", ok: false},
- {cwd: "/home/api", root: "", ok: false},
+ {
+ test: "inside of GOPATH",
+ in: "/home/go/src/github.com/izumin5210/testapp",
+ out: "izumin5210.testapp",
+ },
+ {
+ test: "directly under GOPATH",
+ in: "/home/go/src/testapp",
+ out: "testuser.testapp",
+ },
+ {
+ test: "company name includes separators",
+ in: "/home/go/src/go.example.com/testapp",
+ out: "com.example.go.testapp",
+ },
+ {
+ test: "package name includes hyphens",
+ in: "/home/go/src/go.example.com/test-app",
+ out: "com.example.go.test_app",
+ },
+ {
+ test: "outside of GOPATH",
+ in: "/home/go/testapp",
+ out: "testuser.testapp",
+ },
}
for _, c := range cases {
- root, ok := LookupRoot(fs, c.cwd)
+ t.Run(c.test, func(t *testing.T) {
+ out, err := GetPackageName(c.in)
- if got, want := ok, c.ok; got != want {
- t.Errorf("LookupRoot(fs, %q) returns %t, want %t", c.cwd, got, want)
- }
+ if got, want := err != nil, c.isErr; got != want {
+ t.Errorf("Returned error is %t, want %t (%v)", got, want, err)
+ }
- if got, want := root, c.root; got != want {
- t.Errorf("LookupRoot(fs, %q) returns %q, want %q", c.cwd, got, want)
- }
+ if got, want := out, c.out; got != want {
+ t.Errorf("Returned %s, want %s", got, want)
+ }
+ })
}
}
diff --git a/pkg/grapiserver/cmux.go b/pkg/grapiserver/cmux.go
new file mode 100644
index 00000000..98e446fd
--- /dev/null
+++ b/pkg/grapiserver/cmux.go
@@ -0,0 +1,37 @@
+package grapiserver
+
+import (
+ "net"
+
+ "github.com/soheilhy/cmux"
+ "google.golang.org/grpc/grpclog"
+)
+
+type cmuxServer struct {
+ mux cmux.CMux
+ lis net.Listener
+}
+
+func newCmuxServer(lis net.Listener) *cmuxServer {
+ return &cmuxServer{
+ mux: cmux.New(lis),
+ lis: lis,
+ }
+}
+
+// Serve implements Server.Serve
+func (s *cmuxServer) Serve() {
+ grpclog.Info("mux is starting %s", s.lis.Addr())
+
+ err := s.mux.Serve()
+
+ grpclog.Infof("mux is closed: %v", err)
+}
+
+func (s *cmuxServer) GRPCListener() net.Listener {
+ return s.mux.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc"))
+}
+
+func (s *cmuxServer) HTTPListener() net.Listener {
+ return s.mux.Match(cmux.HTTP2(), cmux.HTTP1Fast())
+}
diff --git a/pkg/grapiserver/config.go b/pkg/grapiserver/config.go
index fde1839f..ec66195a 100644
--- a/pkg/grapiserver/config.go
+++ b/pkg/grapiserver/config.go
@@ -1,7 +1,9 @@
package grapiserver
import (
+ "crypto/tls"
"net"
+ "net/http"
"os"
"path/filepath"
pkg_runtime "runtime"
@@ -23,6 +25,11 @@ func createDefaultConfig() *Config {
Network: "tcp",
Addr: ":3000",
},
+ GatewayServerConfig: &HTTPServerConfig{
+ ReadTimeout: 8 * time.Second,
+ WriteTimeout: 8 * time.Second,
+ IdleTimeout: 2 * time.Minute,
+ },
MaxConcurrentStreams: 1000,
}
if pkg_runtime.GOOS == "windows" {
@@ -59,6 +66,28 @@ func (a *Address) createListener() (net.Listener, error) {
return lis, nil
}
+type HTTPServerConfig struct {
+ TLSConfig *tls.Config
+ ReadTimeout time.Duration
+ ReadHeaderTimeout time.Duration
+ WriteTimeout time.Duration
+ IdleTimeout time.Duration
+ MaxHeaderBytes int
+ TLSNextProto map[string]func(*http.Server, *tls.Conn, http.Handler)
+ ConnState func(net.Conn, http.ConnState)
+}
+
+func (c *HTTPServerConfig) applyTo(s *http.Server) {
+ s.TLSConfig = c.TLSConfig
+ s.ReadTimeout = c.ReadTimeout
+ s.ReadHeaderTimeout = c.ReadHeaderTimeout
+ s.WriteTimeout = c.WriteTimeout
+ s.IdleTimeout = c.IdleTimeout
+ s.MaxHeaderBytes = c.MaxHeaderBytes
+ s.TLSNextProto = c.TLSNextProto
+ s.ConnState = c.ConnState
+}
+
// Config contains configurations of gRPC and Gateway server.
type Config struct {
GrpcAddr *Address
@@ -72,6 +101,7 @@ type Config struct {
GrpcServerOption []grpc.ServerOption
GatewayDialOption []grpc.DialOption
GatewayMuxOptions []runtime.ServeMuxOption
+ GatewayServerConfig *HTTPServerConfig
MaxConcurrentStreams uint32
GatewayServerMiddlewares []HTTPServerMiddleware
}
diff --git a/pkg/grapiserver/engine.go b/pkg/grapiserver/engine.go
index 6d6caa60..5e47e059 100644
--- a/pkg/grapiserver/engine.go
+++ b/pkg/grapiserver/engine.go
@@ -1,23 +1,18 @@
package grapiserver
import (
+ "context"
"net"
- "os"
- "os/signal"
"reflect"
- "sync"
- "syscall"
"github.com/izumin5210/grapi/pkg/grapiserver/internal"
"github.com/pkg/errors"
- "github.com/soheilhy/cmux"
- "google.golang.org/grpc/grpclog"
+ "golang.org/x/sync/errgroup"
)
// Engine is the framework instance.
type Engine struct {
*Config
- sdCh chan os.Signal
}
// New creates a server intstance.
@@ -28,11 +23,12 @@ func New(opts ...Option) *Engine {
}
// Serve starts gRPC and Gateway servers.
-func (e *Engine) Serve() error {
+func (e *Engine) Serve(ctx context.Context) error {
var (
- grpcServer, gatewayServer, muxServer internal.Server
- grpcLis, gatewayLis, internalLis net.Listener
- err error
+ grpcServer, gatewayServer internal.Server
+ grpcLis, gatewayLis, internalLis net.Listener
+ cmuxServer *cmuxServer
+ err error
)
if e.GrpcAddr != nil && e.GatewayAddr != nil && reflect.DeepEqual(e.GrpcAddr, e.GatewayAddr) {
@@ -40,14 +36,14 @@ func (e *Engine) Serve() error {
if err != nil {
return errors.Wrap(err, "failed to listen network for servers")
}
- mux := cmux.New(lis)
- muxServer = NewMuxServer(mux, lis)
- grpcLis = mux.Match(cmux.HTTP2HeaderField("content-type", "application/grpc"))
- gatewayLis = mux.Match(cmux.HTTP2(), cmux.HTTP1Fast())
+ defer lis.Close()
+ cmuxServer = newCmuxServer(lis)
+ grpcLis = cmuxServer.GRPCListener()
+ gatewayLis = cmuxServer.HTTPListener()
}
// Setup servers
- grpcServer = NewGrpcServer(e.Config)
+ grpcServer = newGRPCServer(e.Config)
// Setup listeners
if grpcLis == nil && e.GrpcAddr != nil {
@@ -59,7 +55,7 @@ func (e *Engine) Serve() error {
}
if e.GatewayAddr != nil {
- gatewayServer = NewGatewayServer(e.Config)
+ gatewayServer = newGatewayServer(e.Config)
internalLis, err = e.GrpcInternalAddr.createListener()
if err != nil {
return errors.Wrap(err, "failed to listen network for gRPC server internal")
@@ -76,52 +72,22 @@ func (e *Engine) Serve() error {
}
// Start servers
- var wg sync.WaitGroup
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+ eg, ctx := errgroup.WithContext(ctx)
if internalLis != nil {
- wg.Add(1)
- grpclog.Infof("gRPC server is starting %s://%s", e.GrpcInternalAddr.Network, e.GrpcInternalAddr.Addr)
- go grpcServer.Serve(internalLis, &wg)
+ eg.Go(func() error { return grpcServer.Serve(ctx, internalLis) })
}
if grpcLis != nil {
- wg.Add(1)
- grpclog.Infof("gRPC server is starting %s://%s", e.GrpcAddr.Network, e.GrpcAddr.Addr)
- go grpcServer.Serve(grpcLis, &wg)
+ eg.Go(func() error { return grpcServer.Serve(ctx, grpcLis) })
}
if gatewayLis != nil {
- wg.Add(1)
- grpclog.Infof("gRPC Gateway server is starting: %s://%s", e.GatewayAddr.Network, e.GatewayAddr.Addr)
- go gatewayServer.Serve(gatewayLis, &wg)
+ eg.Go(func() error { return gatewayServer.Serve(ctx, gatewayLis) })
}
- if muxServer != nil {
- wg.Add(1)
- go muxServer.Serve(nil, &wg)
+ if cmuxServer != nil {
+ eg.Go(func() error { cmuxServer.Serve(); return nil })
}
- wg.Add(1)
- go e.watchShutdownSignal(&wg, gatewayServer, grpcServer, muxServer)
-
- wg.Wait()
-
- return nil
-}
-
-// Shutdown closes servers.
-func (e *Engine) Shutdown() {
- e.sdCh <- os.Interrupt
-}
-
-func (e *Engine) watchShutdownSignal(wg *sync.WaitGroup, servers ...internal.Server) {
- defer wg.Done()
- e.sdCh = make(chan os.Signal, 1)
- defer close(e.sdCh)
- defer signal.Reset()
- signal.Notify(e.sdCh, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
- sig := <-e.sdCh
- grpclog.Infof("terminating now...: %v", sig)
- for _, svr := range servers {
- if svr != nil {
- svr.Shutdown()
- }
- }
+ return errors.WithStack(eg.Wait())
}
diff --git a/pkg/grapiserver/gateway.go b/pkg/grapiserver/gateway.go
index 0e5068c9..ea2170a5 100644
--- a/pkg/grapiserver/gateway.go
+++ b/pkg/grapiserver/gateway.go
@@ -4,7 +4,6 @@ import (
"context"
"net"
"net/http"
- "sync"
"time"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
@@ -14,52 +13,63 @@ import (
"google.golang.org/grpc/grpclog"
)
-// NewGatewayServer creates GrpcServer instance.
-func NewGatewayServer(c *Config) internal.Server {
- return &GatewayServer{
+func newGatewayServer(c *Config) internal.Server {
+ return &gatewayServer{
Config: c,
}
}
-// GatewayServer wraps gRPC gateway server setup process.
-type GatewayServer struct {
+// gatewayServer wraps gRPC gateway server setup process.
+type gatewayServer struct {
server *http.Server
*Config
}
-// Serve implements Server.Shutdown
-func (s *GatewayServer) Serve(l net.Listener, wg *sync.WaitGroup) {
- defer wg.Done()
-
+// Serve implements Server.Server
+func (s *gatewayServer) Serve(ctx context.Context, l net.Listener) error {
conn, err := s.createConn()
if err != nil {
- grpclog.Errorf("failed to create connection with gRPC server: %v", err)
- return
+ return errors.Wrap(err, "failed to create connection with grpc-gateway server")
}
defer conn.Close()
s.server, err = s.createServer(conn)
if err != nil {
- grpclog.Errorf("failed to create gRPC Gateway server: %v", err)
- return
+ return errors.Wrap(err, "failed to create gRPC Gateway server: %v")
}
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+
+ go func() {
+ <-ctx.Done()
+ s.shutdown()
+ }()
+
+ grpclog.Infof("grpc-gateway server is starting %s", l.Addr())
+
err = s.server.Serve(l)
- grpclog.Infof("stopped taking more httr(s) requests: %v", err)
+
+ grpclog.Infof("stopped taking more http(s) requests: %v", err)
+
+ if err != http.ErrServerClosed {
+ return errors.Wrap(err, "failed to serve grpc-gateway server")
+ }
+
+ return nil
}
-// Shutdown implements Server.Shutdown
-func (s *GatewayServer) Shutdown() {
+func (s *gatewayServer) shutdown() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
err := s.server.Shutdown(ctx)
- grpclog.Info("All http(s) requets finished")
+ grpclog.Info("All http(s) requests finished")
if err != nil {
- grpclog.Errorf("failed to shutdown gRPC Gateway server: %v", err)
+ grpclog.Errorf("failed to shutdown grpc-gateway server: %v", err)
}
}
-func (s *GatewayServer) createConn() (conn *grpc.ClientConn, err error) {
+func (s *gatewayServer) createConn() (conn *grpc.ClientConn, err error) {
conn, err = grpc.Dial(s.GrpcInternalAddr.Addr, s.clientOptions()...)
if err != nil {
err = errors.Wrap(err, "failed to connect to gRPC server")
@@ -67,7 +77,7 @@ func (s *GatewayServer) createConn() (conn *grpc.ClientConn, err error) {
return
}
-func (s *GatewayServer) createServer(conn *grpc.ClientConn) (*http.Server, error) {
+func (s *gatewayServer) createServer(conn *grpc.ClientConn) (*http.Server, error) {
mux := runtime.NewServeMux(
append(
[]runtime.ServeMuxOption{runtime.WithProtoErrorHandler(runtime.DefaultHTTPProtoErrorHandler)},
@@ -90,10 +100,12 @@ func (s *GatewayServer) createServer(conn *grpc.ClientConn) (*http.Server, error
handler = (s.GatewayServerMiddlewares[i])(handler)
}
- return &http.Server{
- ReadTimeout: 8 * time.Second,
- WriteTimeout: 8 * time.Second,
- IdleTimeout: 2 * time.Minute,
- Handler: handler,
- }, nil
+ svr := &http.Server{
+ Handler: handler,
+ }
+ if cfg := s.GatewayServerConfig; cfg != nil {
+ cfg.applyTo(svr)
+ }
+
+ return svr, nil
}
diff --git a/pkg/grapiserver/grpc.go b/pkg/grapiserver/grpc.go
index 949dc933..1e7f4a12 100644
--- a/pkg/grapiserver/grpc.go
+++ b/pkg/grapiserver/grpc.go
@@ -1,44 +1,50 @@
package grapiserver
import (
+ "context"
"net"
- "sync"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/reflection"
"github.com/izumin5210/grapi/pkg/grapiserver/internal"
+ "github.com/pkg/errors"
)
-// GrpcServer wraps grpc.Server setup process.
-type GrpcServer struct {
+// grpcServer wraps grpc.Server setup process.
+type grpcServer struct {
server *grpc.Server
*Config
}
-// NewGrpcServer creates GrpcServer instance.
-func NewGrpcServer(c *Config) internal.Server {
+func newGRPCServer(c *Config) internal.Server {
s := grpc.NewServer(c.serverOptions()...)
reflection.Register(s)
for _, svr := range c.Servers {
svr.RegisterWithServer(s)
}
- return &GrpcServer{
+ return &grpcServer{
server: s,
Config: c,
}
}
-// Serve implements Server.Shutdown
-func (s *GrpcServer) Serve(l net.Listener, wg *sync.WaitGroup) {
- defer wg.Done()
+// Serve implements Server.Server
+func (s *grpcServer) Serve(ctx context.Context, l net.Listener) error {
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+
+ go func() {
+ <-ctx.Done()
+ s.server.GracefulStop()
+ }()
+
+ grpclog.Infof("gRPC server is starting %s", l.Addr())
err := s.server.Serve(l)
- grpclog.Infof("gRPC server stopred: %v", err)
-}
-// Shutdown implements Server.Shutdown
-func (s *GrpcServer) Shutdown() {
- s.server.GracefulStop()
+ grpclog.Infof("gRPC server stopped: %v", err)
+
+ return errors.Wrap(err, "failed to serve gRPC server")
}
diff --git a/pkg/grapiserver/internal/server.go b/pkg/grapiserver/internal/server.go
index ae542341..4cff156f 100644
--- a/pkg/grapiserver/internal/server.go
+++ b/pkg/grapiserver/internal/server.go
@@ -1,12 +1,11 @@
package internal
import (
+ "context"
"net"
- "sync"
)
// Server provides an interface for starting and stopping the server.
type Server interface {
- Serve(l net.Listener, wg *sync.WaitGroup)
- Shutdown()
+ Serve(context.Context, net.Listener) error
}
diff --git a/testing/main_test.go b/pkg/grapiserver/main_test.go
similarity index 68%
rename from testing/main_test.go
rename to pkg/grapiserver/main_test.go
index 147956f0..505cad80 100644
--- a/testing/main_test.go
+++ b/pkg/grapiserver/main_test.go
@@ -1,4 +1,4 @@
-package testing
+package grapiserver_test
import (
"context"
@@ -7,6 +7,7 @@ import (
"io/ioutil"
"net/http"
"strconv"
+ "strings"
"sync"
"testing"
"time"
@@ -15,10 +16,40 @@ import (
"google.golang.org/grpc"
"github.com/izumin5210/grapi/pkg/grapiserver"
- "github.com/izumin5210/grapi/testing/api"
- "github.com/izumin5210/grapi/testing/app/server"
+ api_pb "github.com/izumin5210/grapi/pkg/grapiserver/testing/api"
+ "github.com/izumin5210/grapi/pkg/grapiserver/testing/app/server"
)
+var (
+ waitForServer = func() { time.Sleep(1 * time.Second) }
+)
+
+func orDie(t *testing.T, err error) {
+ t.Helper()
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+}
+
+func startServer(t *testing.T, s *grapiserver.Engine) func() {
+ var wg sync.WaitGroup
+ wg.Add(1)
+ ctx, cancel := context.WithCancel(context.Background())
+ go func() {
+ defer wg.Done()
+ if err := s.Serve(ctx); err != nil &&
+ !strings.Contains(err.Error(), "use of closed network connection") &&
+ !strings.Contains(err.Error(), "listener closed") {
+ t.Errorf("Engine.Serve returned an error: %v", err)
+ }
+ }()
+ waitForServer()
+ return func() {
+ cancel()
+ wg.Wait()
+ }
+}
+
func Test_server_onlyGateway(t *testing.T) {
var port int64 = 15261
s := grapiserver.New(
@@ -28,37 +59,21 @@ func Test_server_onlyGateway(t *testing.T) {
),
)
- var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- defer wg.Done()
- if err := s.Serve(); err != nil {
- t.Errorf("Engine.Serve returned an error: %v", err)
- }
- }()
+ defer startServer(t, s)()
- time.Sleep(5)
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/books", port))
-
- if err != nil {
- t.Fatalf("failed to fetch book resources: %v", err)
- }
+ orDie(t, err)
defer resp.Body.Close()
if got, want := resp.StatusCode, 200; got != want {
- t.Fatalf("Response status is %d, want %d", got, want)
+ t.Errorf("Response status is %d, want %d", got, want)
}
data, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Fatalf("failed to read response body: %v", err)
- }
+ orDie(t, err)
got := map[string]interface{}{}
- err = json.Unmarshal(data, &got)
- if err != nil {
- t.Fatalf("failed to parse response body: %v", err)
- }
+ orDie(t, json.Unmarshal(data, &got))
want := map[string]interface{}{
"books": []interface{}{
map[string]interface{}{"book_id": "The Go Programming Language"},
@@ -69,9 +84,6 @@ func Test_server_onlyGateway(t *testing.T) {
if diff := cmp.Diff(got, want); diff != "" {
t.Errorf("Received body differs: (-got +want)\n%s", diff)
}
-
- s.Shutdown()
- wg.Wait()
}
func Test_server_samePort(t *testing.T) {
@@ -84,39 +96,22 @@ func Test_server_samePort(t *testing.T) {
),
)
- var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- defer wg.Done()
- if err := s.Serve(); err != nil {
- t.Errorf("Engine.Serve returned an error: %v", err)
- }
- }()
-
- time.Sleep(5)
+ defer startServer(t, s)()
t.Run("http", func(t *testing.T) {
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/books", port))
-
- if err != nil {
- t.Fatalf("failed to fetch book resources: %v", err)
- }
+ orDie(t, err)
defer resp.Body.Close()
if got, want := resp.StatusCode, 200; got != want {
- t.Fatalf("Response status is %d, want %d", got, want)
+ t.Errorf("Response status is %d, want %d", got, want)
}
data, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Fatalf("failed to read response body: %v", err)
- }
+ orDie(t, err)
got := map[string]interface{}{}
- err = json.Unmarshal(data, &got)
- if err != nil {
- t.Fatalf("failed to parse response body: %v", err)
- }
+ orDie(t, json.Unmarshal(data, &got))
want := map[string]interface{}{
"books": []interface{}{
map[string]interface{}{"book_id": "The Go Programming Language"},
@@ -131,17 +126,12 @@ func Test_server_samePort(t *testing.T) {
t.Run("gRPC", func(t *testing.T) {
conn, err := grpc.Dial(addr, grpc.WithInsecure())
- if err != nil {
- t.Fatalf("failed to connect with gRPC server: %v", err)
- }
+ orDie(t, err)
defer conn.Close()
cli := api_pb.NewLibraryServiceClient(conn)
resp, err := cli.ListBooks(context.Background(), &api_pb.ListBooksRequest{})
-
- if err != nil {
- t.Fatalf("failed to fetch book resources: %v", err)
- }
+ orDie(t, err)
want := &api_pb.ListBooksResponse{
Books: []*api_pb.Book{
@@ -153,9 +143,6 @@ func Test_server_samePort(t *testing.T) {
t.Errorf("Received body differs: (-got +want)\n%s", diff)
}
})
-
- s.Shutdown()
- wg.Wait()
}
func Test_server_differentPort(t *testing.T) {
@@ -175,39 +162,22 @@ func Test_server_differentPort(t *testing.T) {
),
)
- var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- defer wg.Done()
- if err := s.Serve(); err != nil {
- t.Errorf("Engine.Serve returned an error: %v", err)
- }
- }()
-
- time.Sleep(5)
+ defer startServer(t, s)()
t.Run("http", func(t *testing.T) {
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/books", httpPort))
-
- if err != nil {
- t.Fatalf("failed to fetch book resources: %v", err)
- }
+ orDie(t, err)
defer resp.Body.Close()
if got, want := resp.StatusCode, 200; got != want {
- t.Fatalf("Response status is %d, want %d", got, want)
+ t.Errorf("Response status is %d, want %d", got, want)
}
data, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Fatalf("failed to read response body: %v", err)
- }
+ orDie(t, err)
got := map[string]interface{}{}
- err = json.Unmarshal(data, &got)
- if err != nil {
- t.Fatalf("failed to parse response body: %v", err)
- }
+ orDie(t, json.Unmarshal(data, &got))
want := map[string]interface{}{
"books": []interface{}{
map[string]interface{}{"book_id": "The Go Programming Language"},
@@ -222,17 +192,12 @@ func Test_server_differentPort(t *testing.T) {
t.Run("gRPC", func(t *testing.T) {
conn, err := grpc.Dial(grpcAddr, grpc.WithInsecure())
- if err != nil {
- t.Fatalf("failed to connect with gRPC server: %v", err)
- }
+ orDie(t, err)
defer conn.Close()
cli := api_pb.NewLibraryServiceClient(conn)
resp, err := cli.ListBooks(context.Background(), &api_pb.ListBooksRequest{})
-
- if err != nil {
- t.Fatalf("failed to fetch book resources: %v", err)
- }
+ orDie(t, err)
want := &api_pb.ListBooksResponse{
Books: []*api_pb.Book{
@@ -244,7 +209,4 @@ func Test_server_differentPort(t *testing.T) {
t.Errorf("Received body differs: (-got +want)\n%s", diff)
}
})
-
- s.Shutdown()
- wg.Wait()
}
diff --git a/pkg/grapiserver/mux.go b/pkg/grapiserver/mux.go
deleted file mode 100644
index 5ee12b41..00000000
--- a/pkg/grapiserver/mux.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package grapiserver
-
-import (
- "net"
- "sync"
-
- "github.com/izumin5210/grapi/pkg/grapiserver/internal"
- "github.com/soheilhy/cmux"
- "google.golang.org/grpc/grpclog"
-)
-
-// MuxServer wraps a connection multiplexer and a listener.
-type MuxServer struct {
- mux cmux.CMux
- lis net.Listener
-}
-
-// NewMuxServer creates MuxServer instance.
-func NewMuxServer(mux cmux.CMux, lis net.Listener) internal.Server {
- return &MuxServer{
- mux: mux,
- lis: lis,
- }
-}
-
-// Serve implements Server.Serve
-func (s *MuxServer) Serve(lis net.Listener, wg *sync.WaitGroup) {
- defer wg.Done()
- grpclog.Info("mux is starting")
- err := s.mux.Serve()
- grpclog.Infof("mux is closed: %v", err)
-}
-
-// Shutdown implements Server.Shutdown
-func (s *MuxServer) Shutdown() {
- err := s.lis.Close()
- if err != nil {
- grpclog.Errorf("failed to close cmux's listener: %v", err)
- }
-}
diff --git a/pkg/grapiserver/options.go b/pkg/grapiserver/options.go
index 7185a942..78b89f09 100644
--- a/pkg/grapiserver/options.go
+++ b/pkg/grapiserver/options.go
@@ -120,6 +120,13 @@ func WithGatewayServerMiddlewares(middlewares ...HTTPServerMiddleware) Option {
}
}
+// WithGatewayServerConfig returns an Option that specifies http.Server configuration to a gateway server.
+func WithGatewayServerConfig(cfg *HTTPServerConfig) Option {
+ return func(c *Config) {
+ c.GatewayServerConfig = cfg
+ }
+}
+
// WithPassedHeader returns an Option that sets configurations about passed headers for a gateway server.
func WithPassedHeader(decider PassedHeaderDeciderFunc) Option {
return WithGatewayServerMiddlewares(createPassingHeaderMiddleware(decider))
diff --git a/testing/api/library.pb.go b/pkg/grapiserver/testing/api/library.pb.go
similarity index 100%
rename from testing/api/library.pb.go
rename to pkg/grapiserver/testing/api/library.pb.go
diff --git a/testing/api/library.pb.gw.go b/pkg/grapiserver/testing/api/library.pb.gw.go
similarity index 100%
rename from testing/api/library.pb.gw.go
rename to pkg/grapiserver/testing/api/library.pb.gw.go
diff --git a/testing/api/library.swagger.json b/pkg/grapiserver/testing/api/library.swagger.json
similarity index 100%
rename from testing/api/library.swagger.json
rename to pkg/grapiserver/testing/api/library.swagger.json
diff --git a/pkg/grapiserver/testing/api/protos/.keep b/pkg/grapiserver/testing/api/protos/.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/testing/api/protos/library.proto b/pkg/grapiserver/testing/api/protos/library.proto
similarity index 100%
rename from testing/api/protos/library.proto
rename to pkg/grapiserver/testing/api/protos/library.proto
diff --git a/pkg/grapiserver/testing/app/server/.keep b/pkg/grapiserver/testing/app/server/.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/testing/app/server/library_server.go b/pkg/grapiserver/testing/app/server/library_server.go
similarity index 96%
rename from testing/app/server/library_server.go
rename to pkg/grapiserver/testing/app/server/library_server.go
index 703f3c4f..e732f5ee 100644
--- a/testing/app/server/library_server.go
+++ b/pkg/grapiserver/testing/app/server/library_server.go
@@ -8,7 +8,7 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
- api_pb "github.com/izumin5210/grapi/testing/api"
+ api_pb "github.com/izumin5210/grapi/pkg/grapiserver/testing/api"
)
// NewLibraryServiceServer creates a new LibraryServiceServer instance.
diff --git a/testing/app/server/library_server_register_funcs.go b/pkg/grapiserver/testing/app/server/library_server_register_funcs.go
similarity index 89%
rename from testing/app/server/library_server_register_funcs.go
rename to pkg/grapiserver/testing/app/server/library_server_register_funcs.go
index 9ef30678..637016fc 100644
--- a/testing/app/server/library_server_register_funcs.go
+++ b/pkg/grapiserver/testing/app/server/library_server_register_funcs.go
@@ -6,7 +6,7 @@ import (
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"google.golang.org/grpc"
- api_pb "github.com/izumin5210/grapi/testing/api"
+ api_pb "github.com/izumin5210/grapi/pkg/grapiserver/testing/api"
)
// RegisterWithServer implements grapiserver.Server.RegisterWithServer.
diff --git a/pkg/protoc/.snapshots/TestWrapper_Exec b/pkg/protoc/.snapshots/TestWrapper_Exec
new file mode 100644
index 00000000..6b89d87a
--- /dev/null
+++ b/pkg/protoc/.snapshots/TestWrapper_Exec
@@ -0,0 +1,80 @@
+([][]string) (len=6) {
+ ([]string) (len=11) {
+ (string) (len=6) "protoc",
+ (string) (len=2) "-I",
+ (string) (len=10) "api/protos",
+ (string) (len=2) "-I",
+ (string) (len=57) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5",
+ (string) (len=2) "-I",
+ (string) (len=80) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5/third_party/googleapis",
+ (string) (len=2) "-I",
+ (string) (len=29) "/go/src/awesomeapp/api/protos",
+ (string) (len=25) "--go_out=plugins=grpc:api",
+ (string) (len=21) "api/protos/book.proto"
+ },
+ ([]string) (len=11) {
+ (string) (len=6) "protoc",
+ (string) (len=2) "-I",
+ (string) (len=10) "api/protos",
+ (string) (len=2) "-I",
+ (string) (len=57) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5",
+ (string) (len=2) "-I",
+ (string) (len=80) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5/third_party/googleapis",
+ (string) (len=2) "-I",
+ (string) (len=29) "/go/src/awesomeapp/api/protos",
+ (string) (len=39) "--grpc-gateway_out=logtostderr=true:api",
+ (string) (len=21) "api/protos/book.proto"
+ },
+ ([]string) (len=11) {
+ (string) (len=6) "protoc",
+ (string) (len=2) "-I",
+ (string) (len=10) "api/protos",
+ (string) (len=2) "-I",
+ (string) (len=57) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5",
+ (string) (len=2) "-I",
+ (string) (len=80) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5/third_party/googleapis",
+ (string) (len=2) "-I",
+ (string) (len=29) "/go/src/awesomeapp/api/protos",
+ (string) (len=34) "--swagger_out=logtostderr=true:api",
+ (string) (len=21) "api/protos/book.proto"
+ },
+ ([]string) (len=11) {
+ (string) (len=6) "protoc",
+ (string) (len=2) "-I",
+ (string) (len=16) "api/protos/types",
+ (string) (len=2) "-I",
+ (string) (len=57) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5",
+ (string) (len=2) "-I",
+ (string) (len=80) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5/third_party/googleapis",
+ (string) (len=2) "-I",
+ (string) (len=29) "/go/src/awesomeapp/api/protos",
+ (string) (len=31) "--go_out=plugins=grpc:api/types",
+ (string) (len=28) "api/protos/types/users.proto"
+ },
+ ([]string) (len=11) {
+ (string) (len=6) "protoc",
+ (string) (len=2) "-I",
+ (string) (len=16) "api/protos/types",
+ (string) (len=2) "-I",
+ (string) (len=57) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5",
+ (string) (len=2) "-I",
+ (string) (len=80) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5/third_party/googleapis",
+ (string) (len=2) "-I",
+ (string) (len=29) "/go/src/awesomeapp/api/protos",
+ (string) (len=45) "--grpc-gateway_out=logtostderr=true:api/types",
+ (string) (len=28) "api/protos/types/users.proto"
+ },
+ ([]string) (len=11) {
+ (string) (len=6) "protoc",
+ (string) (len=2) "-I",
+ (string) (len=16) "api/protos/types",
+ (string) (len=2) "-I",
+ (string) (len=57) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5",
+ (string) (len=2) "-I",
+ (string) (len=80) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5/third_party/googleapis",
+ (string) (len=2) "-I",
+ (string) (len=29) "/go/src/awesomeapp/api/protos",
+ (string) (len=40) "--swagger_out=logtostderr=true:api/types",
+ (string) (len=28) "api/protos/types/users.proto"
+ }
+}
diff --git a/pkg/grapicmd/protoc/config.go b/pkg/protoc/config.go
similarity index 66%
rename from pkg/grapicmd/protoc/config.go
rename to pkg/protoc/config.go
index f7647e31..3b18fc5b 100644
--- a/pkg/grapicmd/protoc/config.go
+++ b/pkg/protoc/config.go
@@ -46,27 +46,3 @@ func (c *Config) OutDirOf(rootDir string, protoPath string) (string, error) {
return filepath.Join(c.OutDir, relProtoDir), nil
}
-
-// Commands returns protoc command and arguments for given proto file.
-func (c *Config) Commands(rootDir, protoPath string) ([][]string, error) {
- cmds := make([][]string, 0, len(c.Plugins))
- relProtoPath, _ := filepath.Rel(rootDir, protoPath)
-
- outDir, err := c.OutDirOf(rootDir, protoPath)
- if err != nil {
- return nil, errors.WithStack(err)
- }
-
- for _, p := range c.Plugins {
- args := []string{}
- args = append(args, "-I", filepath.Dir(relProtoPath))
- for _, dir := range c.ImportDirs {
- args = append(args, "-I", dir)
- }
- args = append(args, p.toProtocArg(outDir))
- args = append(args, relProtoPath)
- cmds = append(cmds, append([]string{"protoc"}, args...))
- }
-
- return cmds, nil
-}
diff --git a/pkg/grapicmd/protoc/config_test.go b/pkg/protoc/config_test.go
similarity index 55%
rename from pkg/grapicmd/protoc/config_test.go
rename to pkg/protoc/config_test.go
index 44537342..c10159ba 100644
--- a/pkg/grapicmd/protoc/config_test.go
+++ b/pkg/protoc/config_test.go
@@ -2,7 +2,6 @@ package protoc
import (
"path/filepath"
- "reflect"
"testing"
)
@@ -16,17 +15,14 @@ func createDefaultConfig() *Config {
OutDir: "./api",
Plugins: []*Plugin{
{
- Path: "./vendor/github.com/golang/protobuf/protoc-gen-go",
Name: "go",
Args: map[string]interface{}{"plugins": "grpc"},
},
{
- Path: "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway",
Name: "grpc-gateway",
Args: map[string]interface{}{"logtostderr": true},
},
{
- Path: "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger",
Name: "swagger",
Args: map[string]interface{}{"logtostderr": true},
},
@@ -62,30 +58,3 @@ func Test_Config_OutDirOf(t *testing.T) {
}
})
}
-
-func Test_Config_Commands(t *testing.T) {
- cfg := createDefaultConfig()
- rootDir := "/home/example/app"
- protoPath := "api/protos/foo/bar.proto"
-
- cmds, err := cfg.Commands(rootDir, filepath.Join(rootDir, protoPath))
-
- if err != nil {
- t.Errorf("Commands() returned an error %v", err)
- }
-
- wantCmd := []string{
- "protoc",
- "-I", "api/protos/foo",
- "-I", "./vendor/github.com/grpc-ecosystem/grpc-gateway",
- "-I", "./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis",
- "--go_out=plugins=grpc:api/foo",
- "api/protos/foo/bar.proto",
- }
-
- if got, want := len(cmds), len(cfg.Plugins); got != want {
- t.Errorf("Commands() returned %d commands, want %d commands", got, want)
- } else if got, want := cmds[0], wantCmd; !reflect.DeepEqual(got, want) {
- t.Errorf("Commands()[0] returned %v, want %v", got, want)
- }
-}
diff --git a/pkg/grapicmd/protoc/plugin.go b/pkg/protoc/plugin.go
similarity index 91%
rename from pkg/grapicmd/protoc/plugin.go
rename to pkg/protoc/plugin.go
index 6fb573f7..753c3e63 100644
--- a/pkg/grapicmd/protoc/plugin.go
+++ b/pkg/protoc/plugin.go
@@ -3,20 +3,18 @@ package protoc
import (
"bytes"
"fmt"
- "path/filepath"
"strings"
)
// Plugin contains args and plugin name for using in protoc command.
type Plugin struct {
- Path string
Name string
Args map[string]interface{}
}
// BinName returns a executable binary name.
func (p *Plugin) BinName() string {
- return filepath.Base(p.Path)
+ return "protoc-gen-" + p.Name
}
func (p *Plugin) toProtocArg(outputPath string) string {
diff --git a/pkg/protoc/providers.go b/pkg/protoc/providers.go
new file mode 100644
index 00000000..42442349
--- /dev/null
+++ b/pkg/protoc/providers.go
@@ -0,0 +1,67 @@
+package protoc
+
+import (
+ "sync"
+
+ "github.com/google/wire"
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/execx"
+ "github.com/izumin5210/gex"
+ "github.com/izumin5210/gex/pkg/tool"
+ "github.com/pkg/errors"
+ "github.com/spf13/afero"
+ "go.uber.org/zap"
+
+ "github.com/izumin5210/grapi/pkg/cli"
+)
+
+var (
+ gexCfg *gex.Config
+ gexCfgMu sync.Mutex
+
+ toolRepo tool.Repository
+ toolRepoMu sync.Mutex
+)
+
+func ProvideGexConfig(
+ fs afero.Fs,
+ exec *execx.Executor,
+ io *clib.IO,
+ rootDir cli.RootDir,
+) *gex.Config {
+ gexCfgMu.Lock()
+ defer gexCfgMu.Unlock()
+ if gexCfg == nil {
+ gexCfg = &gex.Config{
+ OutWriter: io.Out,
+ ErrWriter: io.Err,
+ InReader: io.In,
+ FS: fs,
+ Exec: exec,
+ WorkingDir: rootDir.String(),
+ Verbose: clib.IsVerbose() || clib.IsDebug(),
+ Logger: zap.NewStdLog(zap.L()),
+ }
+ }
+ return gexCfg
+}
+
+func ProvideToolRepository(gexCfg *gex.Config) (tool.Repository, error) {
+ toolRepoMu.Lock()
+ defer toolRepoMu.Unlock()
+ if toolRepo == nil {
+ var err error
+ toolRepo, err = gexCfg.Create()
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+ }
+ return toolRepo, nil
+}
+
+// WrapperSet is a provider set that includes gex things and Wrapper instance.
+var WrapperSet = wire.NewSet(
+ ProvideGexConfig,
+ ProvideToolRepository,
+ NewWrapper,
+)
diff --git a/pkg/protoc/wrapper.go b/pkg/protoc/wrapper.go
new file mode 100644
index 00000000..f0faf2cf
--- /dev/null
+++ b/pkg/protoc/wrapper.go
@@ -0,0 +1,195 @@
+package protoc
+
+import (
+ "bytes"
+ "context"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+ "text/template"
+
+ "github.com/izumin5210/execx"
+ "github.com/izumin5210/gex/pkg/tool"
+ "github.com/pkg/errors"
+ "github.com/spf13/afero"
+ "go.uber.org/zap"
+
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
+)
+
+// Wrapper can execute protoc commands for current project's proto files.
+type Wrapper interface {
+ Exec(context.Context) error
+}
+
+type wrapperImpl struct {
+ cfg *Config
+ fs afero.Fs
+ ui cli.UI
+ exec *execx.Executor
+ toolRepo tool.Repository
+ rootDir cli.RootDir
+ modCache *sync.Map
+}
+
+// NewWrapper creates a new Wrapper instance.
+func NewWrapper(cfg *Config, fs afero.Fs, exec *execx.Executor, ui cli.UI, toolRepo tool.Repository, rootDir cli.RootDir) Wrapper {
+ return &wrapperImpl{
+ cfg: cfg,
+ fs: fs,
+ ui: ui,
+ exec: exec,
+ toolRepo: toolRepo,
+ rootDir: rootDir,
+ modCache: new(sync.Map),
+ }
+}
+
+func (e *wrapperImpl) Exec(ctx context.Context) (err error) {
+ e.ui.Section("Execute protoc")
+
+ e.ui.Subsection("Install plugins")
+ err = errors.WithStack(e.installPlugins(ctx))
+ if err != nil {
+ return
+ }
+
+ e.ui.Subsection("Execute protoc")
+ err = errors.WithStack(e.execProtocAll(ctx))
+
+ return
+}
+
+func (e *wrapperImpl) installPlugins(ctx context.Context) error {
+ return errors.WithStack(e.toolRepo.BuildAll(ctx))
+}
+
+func (e *wrapperImpl) execProtocAll(ctx context.Context) error {
+ protoFiles, err := e.cfg.ProtoFiles(e.fs, e.rootDir.String())
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ var errs []error
+ for _, path := range protoFiles {
+ err = e.execProtoc(ctx, path)
+ relPath, _ := filepath.Rel(e.rootDir.String(), path)
+ if err == nil {
+ e.ui.ItemSuccess(relPath)
+ } else {
+ zap.L().Error("failed to execute protoc", zap.Error(err))
+ errs = append(errs, err)
+ e.ui.ItemFailure(relPath, err)
+ }
+ }
+
+ if len(errs) > 0 {
+ return errors.New("failed to execute protoc")
+ }
+
+ return nil
+}
+
+func (e *wrapperImpl) execProtoc(ctx context.Context, protoPath string) error {
+ outDir, err := e.cfg.OutDirOf(e.rootDir.String(), protoPath)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ if err = fs.CreateDirIfNotExists(e.fs, outDir); err != nil {
+ return errors.WithStack(err)
+ }
+
+ cmds, err := e.commands(ctx, protoPath)
+ if err != nil {
+ return errors.WithStack(err)
+ }
+
+ path := e.rootDir.BinDir().String() + string(filepath.ListSeparator) + os.Getenv("PATH")
+ env := append(os.Environ(), "PATH="+path)
+
+ for _, args := range cmds {
+ cmd := e.exec.CommandContext(ctx, args[0], args[1:]...)
+ cmd.Env = env
+ cmd.Dir = e.rootDir.String()
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ return errors.Wrapf(err, "failed to execute command: %v\n%s", args, string(out))
+ }
+ }
+
+ return nil
+}
+
+func (e *wrapperImpl) commands(ctx context.Context, protoPath string) ([][]string, error) {
+ cmds := make([][]string, 0, len(e.cfg.Plugins))
+ relProtoPath, _ := filepath.Rel(e.rootDir.String(), protoPath)
+
+ outDir, err := e.cfg.OutDirOf(e.rootDir.String(), protoPath)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ isMod := e.modulesEnabled()
+
+ funcMap := template.FuncMap{
+ "module": func(in string) (string, error) {
+ if isMod {
+ return e.getModulePath(ctx, in)
+ }
+ return e.rootDir.Join("vendor", in).String(), nil
+ },
+ }
+
+ for _, p := range e.cfg.Plugins {
+ args := []string{}
+ args = append(args, "-I", filepath.Dir(relProtoPath))
+ for _, dir := range e.cfg.ImportDirs {
+ tmpl, err := template.New(dir).Funcs(funcMap).Parse(dir)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+ buf := new(bytes.Buffer)
+ err = tmpl.Funcs(funcMap).Execute(buf, nil)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+ args = append(args, "-I", buf.String())
+ }
+ args = append(args, p.toProtocArg(outDir))
+ args = append(args, relProtoPath)
+ cmds = append(cmds, append([]string{"protoc"}, args...))
+ }
+
+ return cmds, nil
+}
+
+func (e *wrapperImpl) getModulePath(ctx context.Context, pkg string) (string, error) {
+ if v, ok := e.modCache.Load(pkg); ok {
+ return v.(string), nil
+ }
+ buf := new(bytes.Buffer)
+ cmd := e.exec.CommandContext(ctx, "go", "list", "-f", "{{.Dir}}", "-m", pkg)
+ cmd.Env = append(os.Environ(), "GO111MODULE=on")
+ cmd.Dir = e.rootDir.String()
+ cmd.Stdout = buf
+ err := cmd.Run()
+ if err != nil {
+ return "", errors.WithStack(err)
+ }
+ out := strings.TrimSpace(buf.String())
+ e.modCache.Store(pkg, out)
+
+ return out, nil
+}
+
+func (e *wrapperImpl) modulesEnabled() bool {
+ for _, f := range []string{"go.mod", "go.sum"} {
+ if ok, err := afero.Exists(e.fs, e.rootDir.Join(f).String()); err != nil || !ok {
+ return false
+ }
+ }
+ return true
+}
diff --git a/pkg/protoc/wrapper_test.go b/pkg/protoc/wrapper_test.go
new file mode 100644
index 00000000..417f0d5b
--- /dev/null
+++ b/pkg/protoc/wrapper_test.go
@@ -0,0 +1,91 @@
+package protoc_test
+
+import (
+ "context"
+ "os/exec"
+ "testing"
+
+ "github.com/bradleyjkemp/cupaloy/v2"
+ "github.com/izumin5210/clig/pkg/clib"
+ "github.com/izumin5210/execx"
+ "github.com/izumin5210/gex/pkg/tool"
+ "github.com/spf13/afero"
+
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/protoc"
+)
+
+func TestWrapper_Exec(t *testing.T) {
+ dieIf := func(t *testing.T, err error) {
+ t.Helper()
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ }
+
+ calls := [][]string{}
+
+ exec := execx.New(execx.WithFakeProcess(
+ func(ctx context.Context, cmd *exec.Cmd) error {
+ switch cmd.Args[0] {
+ case "go":
+ if out := cmd.Stdout; out != nil {
+ _, err := out.Write([]byte("/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5\n"))
+ dieIf(t, err)
+ }
+ case "protoc":
+ calls = append(calls, cmd.Args)
+ default:
+ t.Errorf("unexpected command execution: %v", cmd.Args)
+ }
+ if out := cmd.Stdout; out != nil {
+ _, err := out.Write([]byte("\n"))
+ dieIf(t, err)
+ }
+ return nil
+ },
+ ))
+
+ rootDir := cli.RootDir{Path: clib.Path("/go/src/awesomeapp")}
+ protosDir := rootDir.Join("api", "protos")
+
+ fs := afero.NewMemMapFs()
+ dieIf(t, fs.MkdirAll(rootDir.BinDir().String(), 0755))
+ dieIf(t, fs.MkdirAll(protosDir.String(), 0755))
+ dieIf(t, afero.WriteFile(fs, rootDir.Join("go.mod").String(), []byte("module example.com/awesomeapp"), 0644))
+ dieIf(t, afero.WriteFile(fs, rootDir.Join("go.sum").String(), []byte(""), 0644))
+ dieIf(t, afero.WriteFile(fs, rootDir.Join("api", "should_be_ignored.proto").String(), []byte{}, 0644))
+ dieIf(t, afero.WriteFile(fs, rootDir.Join("api", "should_be_ignored_proto").String(), []byte{}, 0644))
+ dieIf(t, afero.WriteFile(fs, protosDir.Join("book.proto").String(), []byte{}, 0644))
+ dieIf(t, afero.WriteFile(fs, protosDir.Join("types", "users.proto").String(), []byte{}, 0644))
+
+ cfg := &protoc.Config{
+ ImportDirs: []string{
+ `{{ module "github.com/grpc-ecosystem/grpc-gateway" }}`,
+ `{{ module "github.com/grpc-ecosystem/grpc-gateway" }}/third_party/googleapis`,
+ protosDir.String(),
+ },
+ ProtosDir: "./api/protos",
+ OutDir: "./api",
+ Plugins: []*protoc.Plugin{
+ {Name: "go", Args: map[string]interface{}{"plugins": "grpc"}},
+ {Name: "grpc-gateway", Args: map[string]interface{}{"logtostderr": true}},
+ {Name: "swagger", Args: map[string]interface{}{"logtostderr": true}},
+ },
+ }
+
+ wrapper := protoc.NewWrapper(cfg, fs, exec, cli.NopUI, &fakeToolRepository{}, rootDir)
+
+ err := wrapper.Exec(context.TODO())
+ if err != nil {
+ t.Fatalf("returned %v, want nil", err)
+ }
+
+ cupaloy.SnapshotT(t, calls)
+}
+
+type fakeToolRepository struct {
+ tool.Repository
+}
+
+func (*fakeToolRepository) BuildAll(context.Context) error { return nil }
diff --git a/pkg/svcgen/app.go b/pkg/svcgen/app.go
new file mode 100644
index 00000000..072ece43
--- /dev/null
+++ b/pkg/svcgen/app.go
@@ -0,0 +1,14 @@
+package svcgen
+
+import (
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ "github.com/izumin5210/grapi/pkg/protoc"
+ "github.com/izumin5210/grapi/pkg/svcgen/params"
+)
+
+type CreateAppFunc func(*gencmd.Command) (*App, error)
+
+type App struct {
+ ProtocWrapper protoc.Wrapper
+ ParamsBuilder params.Builder
+}
diff --git a/pkg/svcgen/params/builder.go b/pkg/svcgen/params/builder.go
new file mode 100644
index 00000000..3950e6a5
--- /dev/null
+++ b/pkg/svcgen/params/builder.go
@@ -0,0 +1,247 @@
+package params
+
+import (
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "github.com/pkg/errors"
+ "github.com/serenize/snaker"
+
+ "github.com/izumin5210/grapi/pkg/cli"
+ gencmdutil "github.com/izumin5210/grapi/pkg/gencmd/util"
+)
+
+type Builder interface {
+ Build(path string, resName string, methodNames []string) (*Params, error)
+}
+
+func NewBuilder(rootDir cli.RootDir, protoDir, protoOutDir, serverDir string, pkgName string) Builder {
+ if protoDir == "" {
+ protoDir = filepath.Join("api", "protos")
+ }
+ if protoOutDir == "" {
+ protoOutDir = filepath.Join("api")
+ }
+ if serverDir == "" {
+ serverDir = filepath.Join("app", "server")
+ }
+ return &builderImpl{
+ rootDir: rootDir,
+ protoDir: protoDir,
+ protoOutDir: protoOutDir,
+ serverDir: serverDir,
+ pkgName: pkgName,
+ }
+}
+
+type builderImpl struct {
+ rootDir cli.RootDir
+ protoDir string
+ protoOutDir string
+ serverDir string
+ pkgName string
+}
+
+func (b *builderImpl) Build(path string, resName string, methodNames []string) (*Params, error) {
+ protoParams, err := gencmdutil.BuildProtoParams(path, b.rootDir, b.protoOutDir, b.pkgName)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ // path => baz/qux/quux
+ path = protoParams.Proto.Path
+
+ // quux
+ name := filepath.Base(path)
+
+ names := gencmdutil.Inflect(name)
+
+ // Quux
+ serviceName := names.Camel.Singular
+ // quux
+ localServiceName := strings.ToLower(string(serviceName[0])) + serviceName[1:]
+
+ // baz/qux
+ packagePath := filepath.Dir(path)
+ // qux
+ packageName := filepath.Base(packagePath)
+
+ if packagePath == "." {
+ packagePath = filepath.Base(b.serverDir)
+ packageName = packagePath
+ }
+
+ protoImports := []string{
+ "google/api/annotations.proto",
+ }
+ goImports := []string{
+ "github.com/izumin5210/grapi/pkg/grapiserver",
+ "google.golang.org/grpc/codes",
+ "google.golang.org/grpc/status",
+ }
+
+ resNames := names
+ if resName != "" {
+ resNames = gencmdutil.Inflect(resName)
+ }
+ methods := b.buildMethodParams(resNames, methodNames)
+
+ protoImports = append(protoImports, methods.ProtoImports...)
+ sort.Strings(protoImports)
+ goImports = append(goImports, methods.GoImports...)
+ sort.Strings(goImports)
+
+ params := &Params{
+ ProtoDir: b.protoDir,
+ ProtoOutDir: b.protoOutDir,
+ ServerDir: b.serverDir,
+ Path: path,
+ ServiceName: serviceName,
+ Methods: methods.Methods,
+ Proto: ProtoParams{
+ Package: protoParams.Proto.Package,
+ Imports: protoImports,
+ Messages: methods.Messages,
+ },
+ PbGo: PbGoParams{
+ PackagePath: protoParams.PbGo.Package,
+ PackageName: protoParams.PbGo.ImportName,
+ },
+ Go: GoParams{
+ Package: packageName,
+ Imports: goImports,
+ ServerName: serviceName + "Service" + "Server",
+ StructName: localServiceName + "Service" + "Server" + "Impl",
+ },
+ }
+
+ return params, nil
+}
+
+func (b *builderImpl) buildMethodParams(name gencmdutil.String, methods []string) (
+ params MethodsParams,
+) {
+ id := name.Snake.Singular + "_id"
+ resource := &MethodMessage{
+ Name: name.Camel.Singular,
+ Fields: []MethodMessageField{{Name: id, Type: "string", Tag: 1}},
+ }
+
+ basicMethods := [5]*MethodParams{}
+ customMethods := []MethodParams{}
+ basicMessages := [7]*MethodMessage{}
+ customMessages := []MethodMessage{}
+
+ for _, meth := range methods {
+ switch strings.ToLower(meth) {
+ case "list":
+ methodName := "List" + name.Camel.Plural
+ reqName := methodName + "Request"
+ respName := methodName + "Response"
+ basicMethods[0] = &MethodParams{
+ Method: methodName,
+ requestCommon: reqName,
+ responseCommon: respName,
+ HTTP: MethodHTTPParams{Method: "get", Path: name.Snake.Plural},
+ }
+ basicMessages[0] = resource
+ basicMessages[1] = &MethodMessage{Name: reqName}
+ basicMessages[2] = &MethodMessage{
+ Name: respName,
+ Fields: []MethodMessageField{{Name: name.Snake.Plural, Type: name.Camel.Singular, Repeated: true, Tag: 1}},
+ }
+ case "get":
+ methodName := "Get" + name.Camel.Singular
+ reqName := methodName + "Request"
+ basicMethods[1] = &MethodParams{
+ Method: methodName,
+ requestCommon: reqName,
+ responseCommon: resource.Name,
+ HTTP: MethodHTTPParams{Method: "get", Path: name.Snake.Plural + "/{" + id + "}"},
+ }
+ basicMessages[0] = resource
+ basicMessages[3] = &MethodMessage{
+ Name: reqName,
+ Fields: []MethodMessageField{{Name: id, Type: "string", Tag: 1}},
+ }
+ case "create":
+ methodName := "Create" + name.Camel.Singular
+ reqName := methodName + "Request"
+ basicMethods[2] = &MethodParams{
+ Method: methodName,
+ requestCommon: reqName,
+ responseCommon: resource.Name,
+ HTTP: MethodHTTPParams{Method: "post", Path: name.Snake.Plural, Body: name.Snake.Singular},
+ }
+ basicMessages[0] = resource
+ basicMessages[4] = &MethodMessage{
+ Name: reqName,
+ Fields: []MethodMessageField{{Name: name.Snake.Singular, Type: name.Camel.Singular, Tag: 1}},
+ }
+ case "update":
+ methodName := "Update" + name.Camel.Singular
+ reqName := methodName + "Request"
+ basicMethods[3] = &MethodParams{
+ Method: methodName,
+ requestCommon: reqName,
+ responseCommon: resource.Name,
+ HTTP: MethodHTTPParams{Method: "patch", Path: name.Snake.Plural + "/{" + name.Snake.Singular + "." + id + "}", Body: name.Snake.Singular},
+ }
+ basicMessages[0] = resource
+ basicMessages[5] = &MethodMessage{
+ Name: reqName,
+ Fields: []MethodMessageField{{Name: name.Snake.Singular, Type: name.Camel.Singular, Tag: 1}},
+ }
+ case "delete":
+ methodName := "Delete" + name.Camel.Singular
+ reqName := methodName + "Request"
+ basicMethods[4] = &MethodParams{
+ Method: methodName,
+ requestCommon: reqName,
+ responseProto: "google.protobuf.Empty",
+ responseGo: "empty.Empty",
+ HTTP: MethodHTTPParams{Method: "delete", Path: name.Snake.Plural + "/{" + id + "}"},
+ }
+ basicMessages[6] = &MethodMessage{
+ Name: reqName,
+ Fields: []MethodMessageField{{Name: id, Type: "string", Tag: 1}},
+ }
+ params.ProtoImports = append(params.ProtoImports, "google/protobuf/empty.proto")
+ params.GoImports = append(params.GoImports, "github.com/golang/protobuf/ptypes/empty")
+ default:
+ methodName := snaker.SnakeToCamel(meth)
+ reqName := methodName + "Request"
+ respName := methodName + "Response"
+ customMethods = append(customMethods, MethodParams{
+ Method: methodName,
+ requestCommon: reqName,
+ responseCommon: respName,
+ HTTP: MethodHTTPParams{Method: "get", Path: name.Snake.Plural + "/" + snaker.CamelToSnake(meth)},
+ })
+ customMessages = append(
+ customMessages,
+ MethodMessage{Name: reqName},
+ MethodMessage{Name: respName},
+ )
+ }
+ }
+
+ for _, meth := range basicMethods {
+ if meth != nil {
+ params.Methods = append(params.Methods, *meth)
+ }
+ }
+ for _, msg := range basicMessages {
+ if msg != nil {
+ params.Messages = append(params.Messages, *msg)
+ }
+ }
+ for _, meth := range customMethods {
+ params.Methods = append(params.Methods, meth)
+ }
+ for _, msg := range customMessages {
+ params.Messages = append(params.Messages, msg)
+ }
+ return
+}
diff --git a/pkg/svcgen/params/params.go b/pkg/svcgen/params/params.go
new file mode 100644
index 00000000..aeabb026
--- /dev/null
+++ b/pkg/svcgen/params/params.go
@@ -0,0 +1,96 @@
+package params
+
+type Params struct {
+ ProtoDir string
+ ProtoOutDir string
+ ServerDir string
+ Path string
+ ServiceName string
+ Methods []MethodParams
+ Proto ProtoParams
+ PbGo PbGoParams
+ Go GoParams
+}
+
+type ProtoParams struct {
+ Package string
+ Imports []string
+ Messages []MethodMessage
+}
+
+type PbGoParams struct {
+ PackageName string
+ PackagePath string
+}
+
+type GoParams struct {
+ Package string
+ Imports []string
+ TestImports []string
+ ServerName string
+ StructName string
+}
+
+type MethodsParams struct {
+ Methods []MethodParams
+ ProtoImports []string
+ GoImports []string
+ Messages []MethodMessage
+}
+
+type MethodParams struct {
+ Method string
+ HTTP MethodHTTPParams
+ requestCommon string
+ requestGo string
+ requestProto string
+ responseCommon string
+ responseGo string
+ responseProto string
+}
+
+func (p *MethodParams) RequestGo(pkg string) string {
+ if p.requestGo == "" {
+ return pkg + "." + p.requestCommon
+ }
+ return p.requestGo
+}
+
+func (p *MethodParams) RequestProto() string {
+ if p.requestProto == "" {
+ return p.requestCommon
+ }
+ return p.requestProto
+}
+
+func (p *MethodParams) ResponseGo(pkg string) string {
+ if p.responseGo == "" {
+ return pkg + "." + p.responseCommon
+ }
+ return p.responseGo
+}
+
+func (p *MethodParams) ResponseProto() string {
+ if p.responseProto == "" {
+ return p.responseCommon
+ }
+ return p.responseProto
+}
+
+type MethodMessage struct {
+ Name string
+ Fields []MethodMessageField
+}
+
+type MethodMessageField struct {
+ Name string
+ Type string
+ Repeated bool
+ Tag uint
+}
+
+type MethodHTTPParams struct {
+ Method string
+ Path string
+ Body string
+}
diff --git a/pkg/svcgen/providers.go b/pkg/svcgen/providers.go
new file mode 100644
index 00000000..0e3309fd
--- /dev/null
+++ b/pkg/svcgen/providers.go
@@ -0,0 +1,26 @@
+package svcgen
+
+import (
+ "github.com/google/wire"
+
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/izumin5210/grapi/pkg/protoc"
+ "github.com/izumin5210/grapi/pkg/svcgen/params"
+ _ "github.com/izumin5210/grapi/pkg/svcgen/template"
+)
+
+func ProvideParamsBuilder(rootDir cli.RootDir, protocCfg *protoc.Config, grapiCfg *grapicmd.Config) params.Builder {
+ return params.NewBuilder(
+ rootDir,
+ protocCfg.ProtosDir,
+ protocCfg.OutDir,
+ grapiCfg.Grapi.ServerDir,
+ grapiCfg.Package,
+ )
+}
+
+var Set = wire.NewSet(
+ ProvideParamsBuilder,
+ App{},
+)
diff --git a/pkg/svcgen/template/.gitignore b/pkg/svcgen/template/.gitignore
new file mode 100644
index 00000000..e69de29b
diff --git a/pkg/grapicmd/internal/module/generator/template/service/{{.ProtoDir}}/{{.Path}}.proto.tmpl b/pkg/svcgen/template/_data/{{.ProtoDir}}/{{.Path}}.proto.tmpl
similarity index 89%
rename from pkg/grapicmd/internal/module/generator/template/service/{{.ProtoDir}}/{{.Path}}.proto.tmpl
rename to pkg/svcgen/template/_data/{{.ProtoDir}}/{{.Path}}.proto.tmpl
index e8ab09be..9cd6974e 100644
--- a/pkg/grapicmd/internal/module/generator/template/service/{{.ProtoDir}}/{{.Path}}.proto.tmpl
+++ b/pkg/svcgen/template/_data/{{.ProtoDir}}/{{.Path}}.proto.tmpl
@@ -1,6 +1,9 @@
syntax = "proto3";
-option go_package = "{{ .PbGo.PackageName }}";
+
package {{ .Proto.Package }};
+
+option go_package = "{{ .PbGo.PackagePath }};{{ .PbGo.PackageName }}";
+
{{range .Proto.Imports}}
import "{{.}}";
{{- end}}
diff --git a/pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server.go.tmpl b/pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server.go.tmpl
similarity index 76%
rename from pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server.go.tmpl
rename to pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server.go.tmpl
index 308e89b8..8801f816 100644
--- a/pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server.go.tmpl
+++ b/pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server.go.tmpl
@@ -9,11 +9,14 @@ import (
{{.PbGo.PackageName}} "{{ .PbGo.PackagePath }}"
)
-// New{{.Go.ServerName}} creates a new {{.Go.ServerName}} instance.
-func New{{.Go.ServerName}}() interface {
+// {{.Go.ServerName}} is a composite interface of {{.PbGo.PackageName }}.{{.Go.ServerName}} and grapiserver.Server.
+type {{.Go.ServerName}} interface {
{{.PbGo.PackageName }}.{{.Go.ServerName}}
grapiserver.Server
-} {
+}
+
+// New{{.Go.ServerName}} creates a new {{.Go.ServerName}} instance.
+func New{{.Go.ServerName}}() {{.Go.ServerName}} {
return &{{.Go.StructName}}{}
}
diff --git a/pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl b/pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl
similarity index 91%
rename from pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl
rename to pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl
index b9b1ad61..1f820020 100644
--- a/pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl
+++ b/pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl
@@ -1,3 +1,5 @@
+// Code generated by github.com/izumin5210/grapi. DO NOT EDIT.
+
package {{.Go.Package }}
import (
diff --git a/pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl b/pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl
similarity index 92%
rename from pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl
rename to pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl
index aa7cbdc7..14207074 100644
--- a/pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl
+++ b/pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl
@@ -3,7 +3,8 @@ package {{.Go.Package }}
import (
"context"
"testing"
-{{range .Go.TestImports}}
+
+{{- range .Go.TestImports}}
"{{.}}"
{{- end}}
@@ -20,6 +21,8 @@ func Test_{{$go.ServerName}}_{{.Method}}(t *testing.T) {
resp, err := svr.{{.Method}}(ctx, req)
+ t.SkipNow()
+
if err != nil {
t.Errorf("returned an error %v", err)
}
diff --git a/pkg/svcgen/template/gen.go b/pkg/svcgen/template/gen.go
new file mode 100644
index 00000000..ba18632b
--- /dev/null
+++ b/pkg/svcgen/template/gen.go
@@ -0,0 +1,3 @@
+//go:generate statik -src ./_data -dest .. -p template -f -m
+
+package template
diff --git a/pkg/svcgen/template/statik.go b/pkg/svcgen/template/statik.go
new file mode 100644
index 00000000..57219db3
--- /dev/null
+++ b/pkg/svcgen/template/statik.go
@@ -0,0 +1,13 @@
+// Code generated by statik. DO NOT EDIT.
+
+// Package statik contains static assets.
+package template
+
+import (
+ "github.com/rakyll/statik/fs"
+)
+
+func init() {
+ data := "PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00 \x00{{.ProtoDir}}/{{.Path}}.proto.tmplUT\x05\x00\x01\x80Cm8\x8cP\xbbn\xc30\x0c\x9c\xab\xaf <%C\xdc\xa1[\x02/\x1d\xfa\x18R\x18I\xf6B\x89Y\xc5h,\xa9\x92R\xd4\x10\xf8\xef\x05e\xd9p3\xd5\x13u<\xdf\x1d\xcf\xf7:\xc8\x1f\xa8\xa0\xb0\xce\x04\xf3Pl\x84\xb0\xf2\xf4)\x15B\x8cP\xd6\x8c\x96uF\x886B\x18\x1bZ\xa3A\x99\xf7\x91XA\x91\xb8\xc7\xe7\x89Z\xcbpf\xfa-\xfe&;\x96a\x9b\x18\x9d\xd4\nG\x8f\xd7\xce\x1a\x17<\x91h\xd3\xc4\x9aeb\xc6\xb8\x02\xd4\x0d\x91\x10\x1e\xddw{\x1a\xa2\xed\x879+\xee\xc7\xcd]\xe2g\xe9-\x86\xb3iX\x14\xc0\xd9\x13\xc4\x98!\"X\xc4X\xee\xf0\xeb\x8a>\xa4\x04DKp\x18\xaeN\xfb\xbc\xf3\xd6h\x8f\xd32\n\x00\x80|\xfeB\x19\xa3.XJ\xdb\x96\xe7\x10\xec\x12\xaaL\x00vy9\x1c\xea\xc9j\x0d\xc5\xfd\x88q1D\xc5\xc4\\A\xfb\x01\xc3\xea\xd14}J\xca\xdf\xd14\xfd:u0\xdb\xcd\x7f\x1b\x1a\xe1\x17m\x04\x00\xcdj\xa2\xdbn\xb7\xe8\xbdT\xc8=t\xc3\xc8\x19\xb99\xa2\x94zV\xd9S\x8b\x97\xdc\xd8\x98n\x87\x16e\xc0\xc1\xcf\xe5\x07+\x1cz\x9b\x14&\xad*\xa1RQ\xca\x94\x12],(\xbbI\xf3\x8c\x86\x98\xc6\xba^\xfe\n\x84\xcf\x0f\x04\x11E\x08\xef\x8f\x16w\x9f\xd2/\xc7:\xe6\xc2\xf8\xb0\x9dK)\x94\xa5\xb8\xc6r\x19\xdf\x89O\xb6Kg\x91\x07n<>f\x8d\x1b\x87<\xf0\xdc\x19c\xa3\xf9\x19\xcb\x89\xc9\xcf\xf3w\x05G\x8f\x89)\x7f\xd0\xe3D\x9e\xb7\x16\xd9\xf7\xf6\x9eZ4s\x8f\x1f\xad\xf1TnZ\x81\x9c\xb3\xaeM\x89l6\xf8u\xff\xe5\xfe\x0e;\xcbx!F?\x8cg\x1a\xc80u\xf250\xd3\x9fW\xf0\xacx\xf2\xf2k\xe26\xdav\xe4\xe5os\xd5\xbeB=K\xfd\xb1\x13\xfc\xc9N\xe7\xee\xa2\x86\x9e\xdf\xd5m\xce\x8fL\x973\xfa\x1f\x00\x00\xff\xffPK\x07\x08\x1an\x9c\xd3\x89\x01\x00\x00j\x03\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x006\x00 \x00{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmplUT\x05\x00\x01\x80Cm8\x94Q\xcd\xce\xd30\x10<\x7f~\x8aUNIU\x1c@\xe2 \xf2!\xe0@[\xd1J\x9c]wq-b;\xda\xacKJ\x94wGN\x0c\x14\x15\x84z\xb2\xf7gfvv\xeb\x1a\x9apB0\xe8\x91\x14\xe3 \x8eW0\x96\xcf\xf1(up\xb5\xfd\x1e\x9d\xf5o^\xbfzY\x1bR\x9d\x95\xf0\xbc\x85\xcd\xf6\x00o\x9f?\x1c\xa4\x10\x9d\xd2_\x95A\x18G\xf9.\xc8]\x8e\xa6I\x08\xeb\xba@\x0c\xa5x*t\xf0\x8c\x03\x17B<\x157\xdc\x86:\xfd\x02u\xe8\xaf=c\x0e\x8db\xfc\xa6\xae5E\xcf\xd6a\x91\x10!\x98\x16\xa5 \xad\xf2F\x062sg\"\x1bG\xb9;\xfe\x96\xdd(\x87\xd3\x04\xc58\xc2\x1f\xf9\x9d\xe23LS!*!\xea\x1a>\xa1\xb1=#}\xb6|\xde#]\x90\xc0\xba\xaeE\x87\x9e{\x98]\xf6sZ.Uy\x0f\x90\xe2K\xf4\x1a\xca\x1eV\x8b\xf1=S\xd4\xbc\x0cP\xfdE\xa1L#\xef/\x04\xab\xf4\xc9\xc4\x15\x8c\xff\xf0\xf0K2\xb3\xcf\xedK\xe9'\xd3\x1a\xfaJLw\x86\xde+\x7fj\x1fr\x94\x11\x0fX\xca\x88R\xf3\x00\xf9\xb4\xb2Y\xde5\xb88\xc0*_o\x91\xfb\x18\x87u\xea\xf3\xd9|\xd3Z\xf4\xdc\x04\xef+@\xa2@i\x0d\x84\x1c\xc9\xc3\x7f\xb7\x91\x18\xad\xce\x85\x1c\xdc\xcc3\xeb/ji;?\x02\x00\x00\xff\xffPK\x07\x084\x1fAoU\x01\x00\x00\xde\x02\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,\x00 \x00{{.ServerDir}}/{{.Path}}_server_test.go.tmplUT\x05\x00\x01\x80Cm8d\x90Ao\xdb0\x0c\x85\xcf\xd2\xaf\xe0\x84n\xb0\x87V?`@.\x03\x86`\x87\x05\xc1\xda{\xe1\xda\xb4c\xa4\x95\x1c\x8av\x03\x10\xfa\xef\x03\x15o]\x90\x9bM\xbd\x8f\xef\xf1MM{l\x06\x04\x11\xbf\x8d~\xbf\xfe\xe5lE\xc6\x1e\xfc/\xe4C\xecR\xcev|\x9b\"1T\xd6\xb86\x06\xc63;k\x1cc\xe21\x0c\xceZ\x91\x07\xa0&\x0c\x08\xba\xe8 \x13\xff,\x84\xb2\xc6\x89\xf8\x9c]\x11a\xe8r\xb6\xd6\x88\xf8\xfd\xcb\x87\xe7\xaey\xc3\x9c\xc1\x89\xc0\xd5|\xdf\xf0\x01\x94\xad\xad\xc8\xdd\x10\xe1\xdbF\x1d\xe0\xa1d\xbc\x9b^\xb6\x97\x912\xeb\xf0_\x90\x8f\xf4\xfd\x1cZ\xd0P\xcfe\x89\x7fDZ\x90.\x9e\xcf\"\xab2\xe7\x8a\xe1\xebz\x93\x7f\xaaA\xacI\x0b\xe9\xfe\x1d\xbe\xdf\x92Um\xadi\xf9\xac\x82\xb5\x14\xff\xbdi\x8f\x03\xc59tUm\x0d\xe1I\x1f\xbf\x88\xf8\xdfx\x9a1\xf16B }}\xb7h#\x84i\xba\x07\xa4b\x98\x16\xf2\xff\x07k\xf9|\x0f\x84'\xb5d\xffx\x1c\xa7]|/\x01\xc6\xbe0\x9f6\x10\xc6W\x8dl\xd8\xff \x8a\xd4W\x8e\x90g\n\xd8A\x13T\x14 >/\xaex\xd4\xd6\xe4\x0b\xac\xb6\xb0\xb9\xa1\x15NS\x0c !\x1d\xe2\xfc\xdaA\x88\xac\"WP-\x1aC\xb7V\xfe\xf7\xebO\x00\x00\x00\xff\xffPK\x07\x08\xa73igR\x01\x00\x00O\x02\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(7\x19\x19\x13B\x01\x00\x00\xae\x02\x00\x00\"\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00{{.ProtoDir}}/{{.Path}}.proto.tmplUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\x1an\x9c\xd3\x89\x01\x00\x00j\x03\x00\x00'\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x9b\x01\x00\x00{{.ServerDir}}/{{.Path}}_server.go.tmplUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(4\x1fAoU\x01\x00\x00\xde\x02\x00\x006\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x82\x03\x00\x00{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmplUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xa73igR\x01\x00\x00O\x02\x00\x00,\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81D\x05\x00\x00{{.ServerDir}}/{{.Path}}_server_test.go.tmplUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x04\x00\x04\x00\x87\x01\x00\x00\xf9\x06\x00\x00\x00\x00"
+ fs.Register(data)
+}
diff --git a/pkg/svcgen/testing/run.go b/pkg/svcgen/testing/run.go
new file mode 100644
index 00000000..ec419288
--- /dev/null
+++ b/pkg/svcgen/testing/run.go
@@ -0,0 +1,103 @@
+package testing
+
+import (
+ "go/build"
+ "testing"
+
+ "github.com/bradleyjkemp/cupaloy/v2"
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ "github.com/izumin5210/grapi/pkg/grapicmd/util/fs"
+ "github.com/spf13/afero"
+)
+
+type Ctx struct {
+ GOPATH string
+ RootDir cli.RootDir
+ CreateCmd func(*testing.T, afero.Fs, Case) gencmd.Executor
+ Cases []Case
+}
+
+type Case struct {
+ Test string
+ GArgs []string
+ DArgs []string
+ Files []string
+ SkippedFiles map[string]struct{}
+ ProtoDir string
+ ProtoOutDir string
+ ServerDir string
+ PkgName string
+}
+
+func Run(t *testing.T, ctx *Ctx) {
+ t.Helper()
+
+ defer func(c build.Context) { fs.BuildContext = c }(fs.BuildContext)
+ fs.BuildContext = build.Context{GOPATH: ctx.GOPATH}
+
+ for _, tc := range ctx.Cases {
+ t.Run(tc.Test, func(t *testing.T) {
+ fs := afero.NewMemMapFs()
+ afero.WriteFile(fs, ctx.RootDir.Join("grapi.toml").String(), []byte{}, 0755)
+
+ t.Run("generate", func(t *testing.T) {
+ cmd := ctx.CreateCmd(t, fs, tc)
+ cmd.Command().SetArgs(append([]string{"generate"}, tc.GArgs...))
+ err := cmd.Execute()
+
+ if err != nil {
+ t.Errorf("returned an error: %+v", err)
+ }
+
+ for _, file := range tc.Files {
+ t.Run(file, func(t *testing.T) {
+ if _, ok := tc.SkippedFiles[file]; ok {
+ ok, err := afero.Exists(fs, file)
+
+ if err != nil {
+ t.Errorf("returned an error: %v", err)
+ }
+
+ if ok {
+ t.Error("should not exist")
+ }
+ } else {
+ data, err := afero.ReadFile(fs, ctx.RootDir.Join(file).String())
+
+ if err != nil {
+ t.Errorf("returned an error: %v", err)
+ }
+
+ cupaloy.SnapshotT(t, string(data))
+ }
+ })
+ }
+ })
+
+ t.Run("destroy", func(t *testing.T) {
+ cmd := ctx.CreateCmd(t, fs, tc)
+ cmd.Command().SetArgs(append([]string{"destroy"}, tc.DArgs...))
+ err := cmd.Execute()
+
+ if err != nil {
+ t.Errorf("returned an error: %+v", err)
+ }
+
+ for _, file := range tc.Files {
+ t.Run(file, func(t *testing.T) {
+ ok, err := afero.Exists(fs, ctx.RootDir.Join(file).String())
+
+ if err != nil {
+ t.Errorf("Exists(fs, %q) returned an error: %v", file, err)
+ }
+
+ if ok {
+ t.Errorf("%q should not exist", file)
+ }
+ })
+ }
+ })
+ })
+ }
+}
diff --git a/pkg/svcgen/testing/wire.go b/pkg/svcgen/testing/wire.go
new file mode 100644
index 00000000..472ff6cd
--- /dev/null
+++ b/pkg/svcgen/testing/wire.go
@@ -0,0 +1,21 @@
+//+build wireinject
+
+package testing
+
+import (
+ "github.com/google/wire"
+
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ "github.com/izumin5210/grapi/pkg/protoc"
+ "github.com/izumin5210/grapi/pkg/svcgen"
+)
+
+func NewTestApp(*gencmd.Command, protoc.Wrapper, cli.UI) (*svcgen.App, error) {
+ wire.Build(
+ gencmd.Set,
+ svcgen.ProvideParamsBuilder,
+ svcgen.App{},
+ )
+ return nil, nil
+}
diff --git a/pkg/svcgen/testing/wire_gen.go b/pkg/svcgen/testing/wire_gen.go
new file mode 100644
index 00000000..541ddff6
--- /dev/null
+++ b/pkg/svcgen/testing/wire_gen.go
@@ -0,0 +1,30 @@
+// Code generated by Wire. DO NOT EDIT.
+
+//go:generate wire
+//+build !wireinject
+
+package testing
+
+import (
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/izumin5210/grapi/pkg/protoc"
+ "github.com/izumin5210/grapi/pkg/svcgen"
+)
+
+// Injectors from wire.go:
+
+func NewTestApp(command *gencmd.Command, wrapper protoc.Wrapper, ui cli.UI) (*svcgen.App, error) {
+ ctx := gencmd.ProvideCtx(command)
+ grapicmdCtx := gencmd.ProvideGrapiCtx(ctx)
+ rootDir := grapicmd.ProvideRootDir(grapicmdCtx)
+ config := grapicmd.ProvideProtocConfig(grapicmdCtx)
+ grapicmdConfig := grapicmd.ProvideConfig(grapicmdCtx)
+ builder := svcgen.ProvideParamsBuilder(rootDir, config, grapicmdConfig)
+ app := &svcgen.App{
+ ProtocWrapper: wrapper,
+ ParamsBuilder: builder,
+ }
+ return app, nil
+}
diff --git a/pkg/svcgen/wire.go b/pkg/svcgen/wire.go
new file mode 100644
index 00000000..6b9f94b7
--- /dev/null
+++ b/pkg/svcgen/wire.go
@@ -0,0 +1,21 @@
+//+build wireinject
+
+package svcgen
+
+import (
+ "github.com/google/wire"
+
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ "github.com/izumin5210/grapi/pkg/protoc"
+)
+
+func NewApp(*gencmd.Command) (*App, error) {
+ wire.Build(
+ Set,
+ gencmd.Set,
+ cli.UIInstance,
+ protoc.WrapperSet,
+ )
+ return nil, nil
+}
diff --git a/pkg/svcgen/wire_gen.go b/pkg/svcgen/wire_gen.go
new file mode 100644
index 00000000..43376044
--- /dev/null
+++ b/pkg/svcgen/wire_gen.go
@@ -0,0 +1,43 @@
+// Code generated by Wire. DO NOT EDIT.
+
+//go:generate wire
+//+build !wireinject
+
+package svcgen
+
+import (
+ "github.com/izumin5210/grapi/pkg/cli"
+ "github.com/izumin5210/grapi/pkg/gencmd"
+ "github.com/izumin5210/grapi/pkg/grapicmd"
+ "github.com/izumin5210/grapi/pkg/protoc"
+)
+
+import (
+ _ "github.com/izumin5210/grapi/pkg/svcgen/template"
+)
+
+// Injectors from wire.go:
+
+func NewApp(command *gencmd.Command) (*App, error) {
+ ctx := gencmd.ProvideCtx(command)
+ grapicmdCtx := gencmd.ProvideGrapiCtx(ctx)
+ config := grapicmd.ProvideProtocConfig(grapicmdCtx)
+ fs := grapicmd.ProvideFS(grapicmdCtx)
+ executor := grapicmd.ProvideExec(grapicmdCtx)
+ io := grapicmd.ProvideIO(grapicmdCtx)
+ ui := cli.UIInstance(io)
+ rootDir := grapicmd.ProvideRootDir(grapicmdCtx)
+ gexConfig := protoc.ProvideGexConfig(fs, executor, io, rootDir)
+ repository, err := protoc.ProvideToolRepository(gexConfig)
+ if err != nil {
+ return nil, err
+ }
+ wrapper := protoc.NewWrapper(config, fs, executor, ui, repository, rootDir)
+ grapicmdConfig := grapicmd.ProvideConfig(grapicmdCtx)
+ builder := ProvideParamsBuilder(rootDir, config, grapicmdConfig)
+ app := &App{
+ ProtocWrapper: wrapper,
+ ParamsBuilder: builder,
+ }
+ return app, nil
+}
diff --git a/testing/.gitignore b/testing/.gitignore
deleted file mode 100644
index c1a21e90..00000000
--- a/testing/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-*.so
-/vendor
-/bin
-/tmp
diff --git a/testing/Gopkg.lock b/testing/Gopkg.lock
deleted file mode 100644
index 890a80b6..00000000
--- a/testing/Gopkg.lock
+++ /dev/null
@@ -1,149 +0,0 @@
-# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
-
-
-[[projects]]
- branch = "master"
- name = "github.com/golang/glog"
- packages = ["."]
- revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
-
-[[projects]]
- name = "github.com/golang/protobuf"
- packages = [
- "jsonpb",
- "proto",
- "protoc-gen-go",
- "protoc-gen-go/descriptor",
- "protoc-gen-go/generator",
- "protoc-gen-go/grpc",
- "protoc-gen-go/plugin",
- "ptypes",
- "ptypes/any",
- "ptypes/duration",
- "ptypes/struct",
- "ptypes/timestamp"
- ]
- revision = "925541529c1fa6821df4e44ce2723319eb2be768"
- version = "v1.0.0"
-
-[[projects]]
- branch = "master"
- name = "github.com/grpc-ecosystem/go-grpc-middleware"
- packages = ["."]
- revision = "aed189ae50cf2ee326c1de8c083f3187e574e0d8"
-
-[[projects]]
- name = "github.com/grpc-ecosystem/grpc-gateway"
- packages = [
- "protoc-gen-grpc-gateway",
- "protoc-gen-grpc-gateway/descriptor",
- "protoc-gen-grpc-gateway/generator",
- "protoc-gen-grpc-gateway/gengateway",
- "protoc-gen-grpc-gateway/httprule",
- "protoc-gen-swagger",
- "protoc-gen-swagger/genswagger",
- "protoc-gen-swagger/options",
- "runtime",
- "runtime/internal",
- "utilities"
- ]
- revision = "07f5e79768022f9a3265235f0db4ac8c3f675fec"
- version = "v1.3.1"
-
-[[projects]]
- branch = "master"
- name = "github.com/izumin5210/grapi"
- packages = [
- "pkg/grapiserver",
- "pkg/grapiserver/internal"
- ]
- revision = "b01e710255e4d296d7b3920d0159dbdf4c91b7ed"
-
-[[projects]]
- name = "github.com/pkg/errors"
- packages = ["."]
- revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
- version = "v0.8.0"
-
-[[projects]]
- branch = "master"
- name = "golang.org/x/net"
- packages = [
- "context",
- "http2",
- "http2/hpack",
- "idna",
- "internal/timeseries",
- "lex/httplex",
- "trace"
- ]
- revision = "6078986fec03a1dcc236c34816c71b0e05018fda"
-
-[[projects]]
- name = "golang.org/x/text"
- packages = [
- "collate",
- "collate/build",
- "internal/colltab",
- "internal/gen",
- "internal/tag",
- "internal/triegen",
- "internal/ucd",
- "language",
- "secure/bidirule",
- "transform",
- "unicode/bidi",
- "unicode/cldr",
- "unicode/norm",
- "unicode/rangetable"
- ]
- revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
- version = "v0.3.0"
-
-[[projects]]
- branch = "master"
- name = "google.golang.org/genproto"
- packages = [
- "googleapis/api/annotations",
- "googleapis/rpc/status"
- ]
- revision = "ab0870e398d5dd054b868c0db1481ab029b9a9f2"
-
-[[projects]]
- name = "google.golang.org/grpc"
- packages = [
- ".",
- "balancer",
- "balancer/base",
- "balancer/roundrobin",
- "codes",
- "connectivity",
- "credentials",
- "encoding",
- "encoding/proto",
- "grpclb/grpc_lb_v1/messages",
- "grpclog",
- "internal",
- "keepalive",
- "metadata",
- "naming",
- "peer",
- "reflection",
- "reflection/grpc_reflection_v1alpha",
- "resolver",
- "resolver/dns",
- "resolver/passthrough",
- "stats",
- "status",
- "tap",
- "transport"
- ]
- revision = "8e4536a86ab602859c20df5ebfd0bd4228d08655"
- version = "v1.10.0"
-
-[solve-meta]
- analyzer-name = "dep"
- analyzer-version = 1
- inputs-digest = "7700d77807c6f5a7c34e5a5a6f4b90c028b59ef4a1e6f2a3496a7cb66a5e1548"
- solver-name = "gps-cdcl"
- solver-version = 1
diff --git a/testing/Gopkg.toml b/testing/Gopkg.toml
deleted file mode 100644
index 3c4a6881..00000000
--- a/testing/Gopkg.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-required = [
- "github.com/golang/protobuf/protoc-gen-go",
- "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway",
- "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger",
-]
-
-[prune]
- go-tests = true
- unused-packages = true
-
- [[prune.project]]
- name = "github.com/grpc-ecosystem/grpc-gateway"
- non-go = false
- unused-packages = false
-
-[[constraint]]
- branch = "master"
- name = "github.com/izumin5210/grapi"
diff --git a/testing/app/run.go b/testing/app/run.go
deleted file mode 100644
index c9c90169..00000000
--- a/testing/app/run.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package app
-
-import (
- "github.com/izumin5210/grapi/pkg/grapiserver"
-)
-
-// Run starts the grapiserver.
-func Run() error {
- s := grapiserver.New(
- grapiserver.WithDefaultLogger(),
- grapiserver.WithServers(
- // TODO
- ),
- )
- return s.Serve()
-}
diff --git a/testing/cmd/server/run.go b/testing/cmd/server/run.go
deleted file mode 100644
index 547f3748..00000000
--- a/testing/cmd/server/run.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package main
-
-import (
- "os"
-
- "github.com/izumin5210/grapi/testing/app"
-)
-
-func main() {
- os.Exit(run())
-}
-
-func run() int {
- err := app.Run()
- if err != nil {
- return 1
- }
- return 0
-}
diff --git a/testing/grapi.toml b/testing/grapi.toml
deleted file mode 100644
index 6eec4edd..00000000
--- a/testing/grapi.toml
+++ /dev/null
@@ -1,25 +0,0 @@
-[grapi]
-server_dir = "./app/server"
-
-[protoc]
-protos_dir = "./api/protos"
-out_dir = "./api"
-import_dirs = [
- "./vendor/github.com/grpc-ecosystem/grpc-gateway",
- "./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis",
-]
-
- [[protoc.plugins]]
- path = "./vendor/github.com/golang/protobuf/protoc-gen-go"
- name = "go"
- args = { plugins = "grpc" }
-
- [[protoc.plugins]]
- path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"
- name = "grpc-gateway"
- args = { logtostderr = true }
-
- [[protoc.plugins]]
- path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"
- name = "swagger"
- args = { logtostderr = true }
diff --git a/tools.go b/tools.go
new file mode 100644
index 00000000..fa75c87f
--- /dev/null
+++ b/tools.go
@@ -0,0 +1,21 @@
+// Code generated by github.com/izumin5210/gex. DO NOT EDIT.
+
+// +build tools
+
+package tools
+
+// tool dependencies
+import (
+ _ "github.com/golang/mock/mockgen"
+ _ "github.com/google/wire/cmd/wire"
+ _ "github.com/izumin5210/gex/cmd/gex"
+ _ "github.com/rakyll/statik"
+)
+
+// If you want to use tools, please run the following command:
+// go generate ./tools.go
+//
+//go:generate go build -v -o=./bin/mockgen github.com/golang/mock/mockgen
+//go:generate go build -v -o=./bin/wire github.com/google/wire/cmd/wire
+//go:generate go build -v -o=./bin/gex github.com/izumin5210/gex/cmd/gex
+//go:generate go build -v -o=./bin/statik github.com/rakyll/statik