Skip to content

Commit

Permalink
feat: add IDL trimming (#118)
Browse files Browse the repository at this point in the history
Co-authored-by: HeyJavaBean <[email protected]>
  • Loading branch information
tksky1 and HeyJavaBean committed Aug 23, 2023
1 parent 1412205 commit 9124651
Show file tree
Hide file tree
Showing 32 changed files with 1,792 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ thriftgo
gen-*
.vscode
.idea
tool/trimmer/trimmer_test
3 changes: 2 additions & 1 deletion args.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package main
import (
"flag"
"fmt"
"github.com/cloudwego/thriftgo/version"
"log"
"os"
"runtime"
Expand Down Expand Up @@ -189,7 +190,7 @@ func (a *Arguments) Parse(argv []string) error {
}

func help() {
println("Version:", Version)
println("Version:", version.ThriftgoVersion)
println(`Usage: thriftgo [options] file
Options:
--version Print the compiler version and exit.
Expand Down
8 changes: 8 additions & 0 deletions generator/golang/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package golang

import (
"fmt"
"github.com/cloudwego/thriftgo/tool/trimmer/trim"
"go/format"
"path/filepath"
"strings"
Expand Down Expand Up @@ -83,6 +84,13 @@ func (g *GoBackend) Generate(req *plugin.Request, log backend.LogFunc) *plugin.R
g.res = plugin.NewResponse()
g.log = log
g.prepareUtilities()
if g.utils.Features().TrimIDL {
err := trim.TrimAST(req.AST)
if err != nil {
g.log.Warn("trim error:", err.Error())
}
g.log.Warn("You Are Using IDL Trimmer")
}
g.prepareTemplates()
g.fillRequisitions()
g.executeTemplates()
Expand Down
2 changes: 2 additions & 0 deletions generator/golang/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type Features struct {
EnumAsINT32 bool `enum_as_int_32:"Generate enum type as int32"`
CodeRefSlim bool `code_ref_slim:"Genenerate code ref by given idl-ref.yaml with less refs to avoid conflict"`
CodeRef bool `code_ref:"Genenerate code ref by given idl-ref.yaml"`
TrimIDL bool `trim_idl:"Simplify IDL to the most concise form before generating code."`
}

var defaultFeatures = Features{
Expand Down Expand Up @@ -78,6 +79,7 @@ var defaultFeatures = Features{
LowerCamelCaseJSONTag: false,
GenerateReflectionInfo: false,
EnumAsINT32: false,
TrimIDL: false,
}

type param struct {
Expand Down
18 changes: 13 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ package main

import (
"fmt"
"github.com/cloudwego/thriftgo/version"
"os"
"runtime/debug"

"github.com/cloudwego/thriftgo/generator"
"github.com/cloudwego/thriftgo/generator/golang"
Expand All @@ -25,9 +27,6 @@ import (
"github.com/cloudwego/thriftgo/semantic"
)

// Version of thriftgo.
const Version = "0.3.0"

var (
a Arguments
g generator.Generator
Expand All @@ -45,9 +44,10 @@ func check(err error) {
}

func main() {
defer handlePanic()
check(a.Parse(os.Args))
if a.AskVersion {
println("thriftgo", Version)
println("thriftgo", version.ThriftgoVersion)
os.Exit(0)
}

Expand All @@ -74,7 +74,7 @@ func main() {
check(semantic.ResolveSymbols(ast))

req := &plugin.Request{
Version: Version,
Version: version.ThriftgoVersion,
OutputPath: a.OutputPath,
Recursive: a.Recursive,
AST: ast,
Expand Down Expand Up @@ -105,3 +105,11 @@ func main() {
check(err)
}
}

func handlePanic() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:")
fmt.Println(r)
debug.PrintStack()
}
}
125 changes: 125 additions & 0 deletions tool/trimmer/args.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright 2021 CloudWeGo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"flag"
"fmt"
"github.com/cloudwego/thriftgo/plugin"
"github.com/cloudwego/thriftgo/version"
"os"
"strings"
)

// StringSlice implements the flag.Value interface on string slices
// to allow a flag to be set multiple times.
type StringSlice []string

func (ss *StringSlice) String() string {
return fmt.Sprintf("%v", *ss)
}

// Set implements the flag.Value interface.
func (ss *StringSlice) Set(value string) error {
*ss = append(*ss, value)
return nil
}

// Arguments contains command line arguments for thriftgo.
type Arguments struct {
AskVersion bool
OutputFile string
IDL string
Recurse string
}

// BuildFlags initializes command line flags.
func (a *Arguments) BuildFlags() *flag.FlagSet {
f := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)

f.BoolVar(&a.AskVersion, "version", false, "")

f.StringVar(&a.OutputFile, "o", "", "")
f.StringVar(&a.OutputFile, "out", "", "")

f.StringVar(&a.Recurse, "r", "", "")
f.StringVar(&a.Recurse, "recurse", "", "")

f.Usage = help
return f
}

// Parse parse command line arguments.
func (a *Arguments) Parse(argv []string) error {
f := a.BuildFlags()
if err := f.Parse(argv[1:]); err != nil {
return err
}

if a.AskVersion {
return nil
}

rest := f.Args()
if len(rest) != 1 {
return fmt.Errorf("require exactly 1 argument for the IDL parameter, got: %d", len(rest))
}

a.IDL = rest[0]
return nil
}

func help() {
println("Version:", version.ThriftgoVersion)
println(`Usage: trimmer [options] file
Options:
--version Print the compiler version and exit.
-h, --help Print help message and exit.
-o, --out [file/dir] Specify the output IDL file/dir.
-r, --recurse [dir] Specify a root dir and dump the included IDL recursively beneath the given root. -o should be set as a directory.
`)
// print backend options
for _, b := range g.AllBackend() {
name, lang := b.Name(), b.Lang()
println(fmt.Sprintf(" %s (%s):", name, lang))
println(align(b.Options()))
}
println()
os.Exit(2)
}

// align the help strings for plugin options.
func align(opts []plugin.Option) string {
var names, descs, ss []string
max := 0
for _, opt := range opts {
names = append(names, opt.Name)
descs = append(descs, opt.Desc)
if max <= len(opt.Name) {
max = len(opt.Name)
}
}

for i := range names {
rest := 2 + max - len(names[i])
ss = append(ss, fmt.Sprintf(
" %s:%s%s",
names[i],
strings.Repeat(" ", rest),
descs[i],
))
}
return strings.Join(ss, "\n")
}
95 changes: 95 additions & 0 deletions tool/trimmer/dirTree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright 2023 CloudWeGo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"fmt"
"os"
"path/filepath"
)

// create directory-tree before dump
func createDirTree(sourceDir string, destinationDir string) {
err := filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
newDir := filepath.Join(destinationDir, path[len(sourceDir):])
if path[len(sourceDir)-1] != filepath.Separator {
newDir = filepath.Join(destinationDir, path[len(sourceDir)-1:])
}
err := os.MkdirAll(newDir, os.ModePerm)
if err != nil {
return err
}
}
return nil
})

if err != nil {
fmt.Printf("manage output error: %v\n", err)
os.Exit(2)
}
}

// remove empty directory of output dir-tree
func removeEmptyDir(source string) {
err := filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
empty, err := isDirectoryEmpty(path)
if err != nil {
return err
}
if empty {
err := os.Remove(path)
if err != nil {
return err
}
}
}
return nil
})

parent := filepath.Dir(source)
if parent != source {
removeEmptyDir(parent)
}

if err != nil {
fmt.Printf("Error: %v\n", err)
}
}

func isDirectoryEmpty(path string) (bool, error) {
dir, err := os.Open(path)
if err != nil {
return false, err
}
defer dir.Close()

_, err = dir.Readdirnames(1)
if err == nil {
return false, nil
}

if len(err.Error()) > len("EOF") {
return false, err
}
return true, nil
}
41 changes: 41 additions & 0 deletions tool/trimmer/dump/dump.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2023 CloudWeGo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dump

import (
"bytes"
"github.com/cloudwego/thriftgo/parser"
"html"
"html/template"
"strings"
)

// DumpIDL Dump the ast to idl string
func DumpIDL(ast *parser.Thrift) (string, error) {
tmpl, _ := template.New("thrift").Funcs(template.FuncMap{"RemoveLastComma": RemoveLastComma,
"JoinQuotes": JoinQuotes, "ReplaceQuotes": ReplaceQuotes}).
Parse(IDLTemplate + TypeDefTemplate + AnnotationsTemplate +
ConstantTemplate + EnumTemplate + ConstValueTemplate + FieldTemplate + StructTemplate + UnionTemplate +
ExceptionTemplate + ServiceTemplate + FunctionTemplate + SingleLineFieldTemplate + TypeTemplate +
ConstListTemplate + ConstMapTemplate)
var buf bytes.Buffer
if err := tmpl.Execute(&buf, ast); err != nil {
return "", err
}
// deal with \\
escapedString := strings.Replace(buf.String(), "&#34;", "\\\"", -1)
outString := strings.Replace(escapedString, "#OUTQUOTES", "\"", -1)
return html.UnescapeString(outString), nil
}
Loading

0 comments on commit 9124651

Please sign in to comment.