From ec2463f2f8f67f4cc436d3604270409cb938987f Mon Sep 17 00:00:00 2001
From: kinggo <1963359402@qq.com>
Date: Wed, 16 Oct 2024 12:39:34 +0800
Subject: [PATCH] Revert "feat: swagger2idl plugin (#9)"
This reverts commit afce6c58ea3f94c16fbc6ab2e6d588ec08238596.
---
README.md | 21 +-
README_CN.md | 23 +-
common/consts/consts.go | 12 -
common/utils/utils.go | 315 -----
go.mod | 1 -
go.sum | 2 -
licenses/LICENSE-cli.txt | 21 -
licenses/LICENSE-kin-openapi.txt | 21 -
swagger2idl/LICENSE | 201 ----
swagger2idl/README.md | 84 --
swagger2idl/README_CN.md | 82 --
swagger2idl/converter/converter.go | 42 -
swagger2idl/converter/proto_converter.go | 1327 ---------------------
swagger2idl/converter/thrift_converter.go | 1297 --------------------
swagger2idl/example/openapi.yaml | 224 ----
swagger2idl/generate/generate.go | 22 -
swagger2idl/generate/proto_generate.go | 321 -----
swagger2idl/generate/thrift_generate.go | 293 -----
swagger2idl/go.mod | 26 -
swagger2idl/go.sum | 38 -
swagger2idl/main.go | 177 ---
swagger2idl/parser/parser.go | 46 -
swagger2idl/protobuf/protobuf.go | 91 --
swagger2idl/thrift/thrift.go | 90 --
swagger2idl/utils/utils.go | 102 --
25 files changed, 13 insertions(+), 4866 deletions(-)
delete mode 100644 licenses/LICENSE-cli.txt
delete mode 100644 licenses/LICENSE-kin-openapi.txt
delete mode 100644 swagger2idl/LICENSE
delete mode 100644 swagger2idl/README.md
delete mode 100644 swagger2idl/README_CN.md
delete mode 100644 swagger2idl/converter/converter.go
delete mode 100644 swagger2idl/converter/proto_converter.go
delete mode 100644 swagger2idl/converter/thrift_converter.go
delete mode 100644 swagger2idl/example/openapi.yaml
delete mode 100644 swagger2idl/generate/generate.go
delete mode 100644 swagger2idl/generate/proto_generate.go
delete mode 100644 swagger2idl/generate/thrift_generate.go
delete mode 100644 swagger2idl/go.mod
delete mode 100644 swagger2idl/go.sum
delete mode 100644 swagger2idl/main.go
delete mode 100644 swagger2idl/parser/parser.go
delete mode 100644 swagger2idl/protobuf/protobuf.go
delete mode 100644 swagger2idl/thrift/thrift.go
delete mode 100644 swagger2idl/utils/utils.go
diff --git a/README.md b/README.md
index 8a6ea6a..a9d5768 100644
--- a/README.md
+++ b/README.md
@@ -2,17 +2,14 @@
English | [中文](README_CN.md)
-**Swagger Generate** is a set of plugin tools designed for HTTP and RPC services, supporting the automatic generation of Swagger documentation and integration with Swagger-UI for debugging. Additionally, it provides the ability to convert Swagger documents into Protobuf or Thrift IDL files, simplifying the API development and maintenance process.
-
-This project is compatible with the [CloudWeGo](https://www.cloudwego.io) ecosystem frameworks such as [Cwgo](https://github.com/cloudwego/cwgo), [Hertz](https://github.com/cloudwego/hertz), and [Kitex](https://github.com/cloudwego/kitex). It offers a convenient toolset for developers to automatically generate Swagger documentation, simplifying the API documentation and debugging process.
+**Swagger Generate** is a collection of plugins that generate Swagger documentation and provide Swagger-UI access for debugging HTTP and RPC services. This project is compatible with the [CloudWeGo](https://www.cloudwego.io) ecosystem frameworks such as [Cwgo](https://github.com/cloudwego/cwgo), [Hertz](https://github.com/cloudwego/hertz), and [Kitex](https://github.com/cloudwego/kitex). It offers a convenient toolset for developers to automatically generate Swagger documentation, simplifying the API documentation and debugging process.
## Included Plugins
-- **[protoc-gen-http-swagger](https://github.com/hertz-contrib/swagger-generate/tree/main/thrift-gen-rpc-swagger)**: Generates Swagger documentation and provides Swagger UI debugging for HTTP services based on Protobuf.
-- **[thrift-gen-http-swagger](https://github.com/hertz-contrib/swagger-generate/tree/main/thrift-gen-http-swagger)**: Generates Swagger documentation and provides Swagger UI debugging for HTTP services based on Thrift.
-- **[protoc-gen-rpc-swagger](https://github.com/hertz-contrib/swagger-generate/tree/main/protoc-gen-rpc-swagger)**: Generates Swagger documentation and provides Swagger UI debugging for RPC services based on Protobuf.
-- **[thrift-gen-rpc-swagger](https://github.com/hertz-contrib/swagger-generate/tree/main/thrift-gen-rpc-swagger)**: Generates Swagger documentation and provides Swagger UI debugging for RPC services based on Thrift.
-- **[swagger2idl](https://github.com/hertz-contrib/swagger-generate/tree/main/swagger2idl)**: Converts Swagger documents into Protobuf or Thrift IDL files.
+- **protoc-gen-http-swagger**: Generates Swagger documentation and provides Swagger UI debugging for HTTP services based on Protobuf.
+- **thrift-gen-http-swagger**: Generates Swagger documentation and provides Swagger UI debugging for HTTP services based on Thrift.
+- **protoc-gen-rpc-swagger**: Generates Swagger documentation and provides Swagger UI debugging for RPC services based on Protobuf.
+- **thrift-gen-rpc-swagger**: Generates Swagger documentation and provides Swagger UI debugging for RPC services based on Thrift.
## Key Advantages
@@ -20,7 +17,6 @@ This project is compatible with the [CloudWeGo](https://www.cloudwego.io) ecosys
- **Integrated Debugging**: The generated Swagger UI can be used directly for service debugging, supporting both HTTP and RPC modes.
- **Hertz and Kitex Integration**: Provides seamless documentation generation and debugging support for [Hertz](https://github.com/cloudwego/hertz) and [Kitex](https://github.com/cloudwego/kitex).
- **Flexible Annotation Support**: Allows extending the generated Swagger documentation through annotations, supporting OpenAPI annotations such as `openapi.operation`, `openapi.schema`, etc.
-- **IDL Conversion**: Supports converting Swagger documents into Protobuf or Thrift IDL files, making it easier for developers to switch between different frameworks.
## Installation
@@ -103,13 +99,8 @@ func main() {
}
}
```
-For more examples, please refer to [kitex_swagger_gen](https://github.com/cloudwego/kitex-examples/tree/main/bizdemo/kitex_swagger_gen) and [hertz_swagger_gen](https://github.com/cloudwego/hertz-examples/tree/main/bizdemo/hertz_swagger_gen).
-
-### Converting Swagger Documents to IDL Files
-```sh
-swagger2idl -o my_output.proto -oa -a openapi.yaml
-```
+For more examples, please refer to [kitex_swagger_gen](https://github.com/cloudwego/kitex-examples/tree/main/bizdemo/kitex_swagger_gen) and [hertz_swagger_gen](https://github.com/cloudwego/hertz-examples/tree/main/bizdemo/hertz_swagger_gen).
## More Information
diff --git a/README_CN.md b/README_CN.md
index bacdbc4..00b1fb5 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -2,17 +2,14 @@
[English](README.md) | 中文
-**Swagger Generate** 是一组插件工具,专为 HTTP 和 RPC 服务设计,支持自动生成 Swagger 文档,并集成 Swagger-UI 进行调试。此外,它还提供将 Swagger 文档转换为 Protobuf 或 Thrift IDL 文件的功能,简化了 API 开发与维护的流程。
-
-该项目适用于 [CloudWeGo](https://www.cloudwego.io) 生态下的 [Cwgo](https://github.com/cloudwego/cwgo)、 [Hertz](https://github.com/cloudwego/hertz) 和 [Kitex](https://github.com/cloudwego/kitex) 框架。它提供了一套便捷的工具来帮助开发者自动生成 Swagger 文档,从而简化 API 文档编写及调试过程。
+**Swagger Generate** 是一个为 HTTP 和 RPC 服务生成 Swagger 文档及 Swagger-UI 访问调试的插件集合。该项目适用于 [CloudWeGo](https://www.cloudwego.io) 生态下的 [Cwgo](https://github.com/cloudwego/cwgo)、 [Hertz](https://github.com/cloudwego/hertz) 和 [Kitex](https://github.com/cloudwego/kitex) 框架。它提供了一套便捷的工具来帮助开发者自动生成 Swagger 文档,从而简化 API 文档编写及调试过程。
## 包含的插件
-- **[protoc-gen-http-swagger](https://github.com/hertz-contrib/swagger-generate/tree/main/thrift-gen-rpc-swagger)**:为基于 Protobuf 的 HTTP 服务生成 Swagger 文档和 Swagger UI 进行调试。
-- **[thrift-gen-http-swagger](https://github.com/hertz-contrib/swagger-generate/tree/main/thrift-gen-http-swagger)**:为基于 Thrift 的 HTTP 服务生成 Swagger 文档和 Swagger UI 进行调试。
-- **[protoc-gen-rpc-swagger](https://github.com/hertz-contrib/swagger-generate/tree/main/protoc-gen-rpc-swagger)**:为基于 Protobuf 的 RPC 服务生成 Swagger 文档和 Swagger UI 进行调试。
-- **[thrift-gen-rpc-swagger](https://github.com/hertz-contrib/swagger-generate/tree/main/thrift-gen-rpc-swagger)**:为基于 Thrift 的 RPC 服务生成 Swagger 文档和 Swagger UI 进行调试。
-- **[swagger2idl](https://github.com/hertz-contrib/swagger-generate/tree/main/swagger2idl)**:将 Swagger 文档转换为 Protobuf 或 Thrift IDL 文件。
+- **protoc-gen-http-swagger**:为基于 Protobuf 的 HTTP 服务生成 Swagger 文档和 Swagger UI 进行调试。
+- **thrift-gen-http-swagger**:为基于 Thrift 的 HTTP 服务生成 Swagger 文档和 Swagger UI 进行调试。
+- **protoc-gen-rpc-swagger**:为基于 Protobuf 的 RPC 服务生成 Swagger 文档和 Swagger UI 进行调试。
+- **thrift-gen-rpc-swagger**:为基于 Thrift 的 RPC 服务生成 Swagger 文档和 Swagger UI 进行调试。
## 项目优势
@@ -20,7 +17,6 @@
- **集成调试**:生成的 Swagger UI 能直接用于调试服务,支持 HTTP 和 RPC 两种模式。
- **Hertz 和 Kitex 集成**:为 [Hertz](https://github.com/cloudwego/hertz) 和 [Kitex](https://github.com/cloudwego/kitex) 提供了无缝的文档生成和调试支持。
- **灵活的注解支持**:允许通过注解扩展生成的 Swagger 文档内容,支持 `openapi.operation`、`openapi.schema` 等 OpenAPI 注解。
-- **IDL 转换**:支持将 Swagger 文档转换为 Protobuf 或 Thrift IDL 文件,方便开发者在不同框架间切换。
## 安装
@@ -102,14 +98,9 @@ func main() {
}
}
```
-请参考 [kitex_swagger_gen](https://github.com/cloudwego/kitex-examples/tree/main/bizdemo/kitex_swagger_gen) 和 [hertz_swagger_gen](https://github.com/cloudwego/hertz-examples/tree/main/bizdemo/hertz_swagger_gen) 获取更多使用场景示例。
-
-### 将 Swagger 文档转换为 IDL 文件
-```sh
-swagger2idl -o my_output.proto -oa -a openapi.yaml
-```
+请参考 [kitex_swagger_gen](https://github.com/cloudwego/kitex-examples/tree/main/bizdemo/kitex_swagger_gen) 和 [hertz_swagger_gen](https://github.com/cloudwego/hertz-examples/tree/main/bizdemo/hertz_swagger_gen) 获取更多使用场景示例。
## 更多信息
-请参考各个插件的 README 文档获取更多使用细节。
\ No newline at end of file
+请参考各个插件的 readme 文档获取更多使用细节。
\ No newline at end of file
diff --git a/common/consts/consts.go b/common/consts/consts.go
index d82ffd5..0357119 100644
--- a/common/consts/consts.go
+++ b/common/consts/consts.go
@@ -74,9 +74,6 @@ const (
DefaultInfoDesc = "API description"
DefaultInfoVersion = "0.0.1"
- IDLProto = "proto"
- IDLThrift = "thrift"
-
DocumentOptionServiceType = "service"
DocumentOptionStructType = "struct"
@@ -103,13 +100,6 @@ const (
DefaultOutputDir = "swagger"
DefaultOutputYamlFile = "openapi.yaml"
DefaultOutputSwaggerFile = "swagger.go"
- DefaultProtoFilename = "output.proto"
- DefaultThriftFilename = "output.thrift"
- OpenapiThriftFile = "openapi.thrift"
- ApiProtoFile = "api.proto"
- OpenapiProtoFile = "openapi/annotations.proto"
- EmptyProtoFile = "google/protobuf/empty.proto"
- TimestampProtoFile = "google/protobuf/timestamp.proto"
DefaultServerURL = "http://127.0.0.1:8888"
DefaultKitexAddr = "127.0.0.1:8888"
@@ -122,6 +112,4 @@ const (
ProtobufValueName = "GoogleProtobufValue"
ProtobufAnyName = "GoogleProtobufAny"
- EmptyMessage = "google.protobuf.Empty"
- TimestampMessage = "google.protobuf.Timestamp"
)
diff --git a/common/utils/utils.go b/common/utils/utils.go
index 32652b8..e38512a 100644
--- a/common/utils/utils.go
+++ b/common/utils/utils.go
@@ -21,12 +21,8 @@ import (
"fmt"
"os"
"reflect"
- "regexp"
"strconv"
"strings"
- "unicode"
-
- "github.com/iancoleman/strcase"
)
// Contains returns true if an array Contains a specified string.
@@ -166,314 +162,3 @@ func FileExists(filePath string) bool {
_, err := os.Stat(filePath)
return err == nil
}
-
-// Stringify converts a value to a string
-func Stringify(value interface{}) string {
- switch v := value.(type) {
- case string:
- return fmt.Sprintf("%q", v) // Add quotes around strings
- case int, int64, float64:
- return fmt.Sprintf("%v", v) // Output numbers directly
- case *uint64:
- return fmt.Sprintf("%d", *v) // Handle *uint64 pointer type
- case []string:
- return fmt.Sprintf("[%s]", strings.Join(v, ", ")) // Output string arrays as a list
- case []interface{}:
- // Handle arrays of arbitrary types
- var strValues []string
- for _, item := range v {
- strValues = append(strValues, Stringify(item))
- }
- return fmt.Sprintf("[%s]", strings.Join(strValues, ", "))
- default:
- return fmt.Sprintf("%v", v) // Convert other types directly to string
- }
-}
-
-// StructToOption converts a struct to an option string
-func StructToOption(value interface{}, indent string) string {
- var sb strings.Builder
- v := reflect.ValueOf(value)
- t := reflect.TypeOf(value)
-
- // If it's a pointer, get the actual value
- if v.Kind() == reflect.Ptr {
- if v.IsNil() {
- return "" // Skip nil pointers
- }
- v = v.Elem()
- t = t.Elem()
- }
-
- // Handle slice types
- if v.Kind() == reflect.Slice {
- if v.Len() == 0 {
- return "" // Skip empty slices
- }
- sb.WriteString("[\n")
- for i := 0; i < v.Len(); i++ {
- sb.WriteString(fmt.Sprintf("%s ", indent))
- sb.WriteString(StructToOption(v.Index(i).Interface(), indent+" "))
- if i < v.Len()-1 {
- sb.WriteString(",\n")
- }
- }
- sb.WriteString(fmt.Sprintf("\n%s]", indent))
- return sb.String()
- }
-
- // Handle map types
- if v.Kind() == reflect.Map {
- if v.Len() == 0 {
- return "" // Skip empty maps
- }
- sb.WriteString("{\n")
- for _, key := range v.MapKeys() {
- if isZeroValue(v.MapIndex(key)) {
- continue
- }
- sb.WriteString(fmt.Sprintf("%s %v: ", indent, reflect.ValueOf(ToSnakeCase(key.String()))))
- sb.WriteString(StructToOption(v.MapIndex(key).Interface(), indent+" "))
- sb.WriteString(",\n")
- }
- sb.WriteString(fmt.Sprintf("%s}", indent))
- return sb.String()
- }
-
- // Handle struct types
- if v.Kind() == reflect.Struct {
- sb.WriteString("{\n")
- for i := 0; i < v.NumField(); i++ {
- field := v.Field(i)
- fieldType := t.Field(i)
-
- // Skip unexported fields
- if !field.CanInterface() {
- continue
- }
-
- // Skip fields with zero values
- if isZeroValue(field) {
- continue
- }
-
- fieldName := fieldType.Tag.Get("json")
- if fieldName == "" {
- fieldName = fieldType.Name // If no json tag, use field name
- }
- fieldName = strings.Split(fieldName, ",")[0] // Remove options from json tag, e.g., "omitempty"
-
- // Skip specific fields (Parameters, RequestBody, Responses)
- if fieldName == "parameters" || fieldName == "requestBody" || fieldName == "responses" ||
- fieldName == "schemas" || fieldName == "requestBodies" || fieldName == "items" ||
- fieldName == "paths" || fieldName == "properties" || fieldName == "content" ||
- fieldName == "schema" || fieldName == "oneOf" || fieldName == "allOf" || fieldName == "anyOf" ||
- fieldName == "additionalProperties" || fieldName == "-" ||
- fieldName == "components" {
- continue
- }
-
- fieldName = ToSnakeCase(fieldName) // Convert field name to snake_case
-
- // Use the field name as the Protobuf key
- sb.WriteString(fmt.Sprintf("%s %s: ", indent, fieldName))
-
- // Recursively handle the field
- sb.WriteString(StructToOption(field.Interface(), indent+" "))
- sb.WriteString(";\n")
- }
- sb.WriteString(fmt.Sprintf("%s}", indent))
- return sb.String()
- }
-
- // Handle other basic types
- switch v.Kind() {
- case reflect.String:
- if v.String() == "" {
- return "" // Skip empty strings
- }
-
- // Process multi-line strings by replacing actual newlines with "\n"
- multiLineStr := strings.ReplaceAll(v.String(), "\n", "\\n")
- return fmt.Sprintf("\"%s\"", multiLineStr)
- case reflect.Int, reflect.Int64, reflect.Int32:
- if v.Int() == 0 {
- return "" // Skip 0 values
- }
- return fmt.Sprintf("%d", v.Int())
- case reflect.Float64:
- if v.Float() == 0 {
- return "" // Skip 0.0
- }
- return fmt.Sprintf("%f", v.Float())
- case reflect.Bool:
- if !v.Bool() {
- return "" // Skip false
- }
- return fmt.Sprintf("%t", v.Bool())
- case reflect.Ptr:
- if !v.IsNil() {
- return StructToOption(v.Interface(), indent)
- }
- return ""
- default:
- // Skip zero values
- if !v.IsValid() || v.IsZero() {
- return ""
- }
- return fmt.Sprintf("\"%v\"", v.Interface())
- }
-}
-
-// isZeroValue checks if a value is the zero value for its type
-func isZeroValue(v reflect.Value) bool {
- switch v.Kind() {
- case reflect.String:
- return v.String() == ""
- case reflect.Bool:
- return !v.Bool()
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return v.Int() == 0
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- return v.Uint() == 0
- case reflect.Float32, reflect.Float64:
- return v.Float() == 0
- case reflect.Complex64, reflect.Complex128:
- return v.Complex() == 0
- case reflect.Slice, reflect.Array:
- return v.Len() == 0 // Check if slice or array is empty
- case reflect.Map:
- if v.Len() == 0 {
- return true
- }
- for _, key := range v.MapKeys() {
- value := v.MapIndex(key)
- if !isZeroValue(value) {
- return false
- }
- }
- return true
- case reflect.Struct:
- for i := 0; i < v.NumField(); i++ {
- if !isZeroValue(v.Field(i)) {
- return false
- }
- }
- return true
- case reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
- return v.IsNil()
- default:
- return !v.IsValid()
- }
-}
-
-// ToUpperCase converts the first letter of a string to uppercase
-func ToUpperCase(s string) string {
- if len(s) == 0 {
- return s
- }
-
- firstChar := unicode.ToUpper(rune(s[0]))
-
- if len(s) == 1 {
- return string(firstChar)
- }
-
- return string(firstChar) + s[1:]
-}
-
-// FormatStr formats a string to remove special characters
-func FormatStr(str string) string {
- str = strings.ReplaceAll(str, " ", "_")
- str = strings.ReplaceAll(str, "/", "_")
- str = strings.ReplaceAll(str, "-", "_")
- reg, _ := regexp.Compile(`[^a-zA-Z0-9_]`)
- str = reg.ReplaceAllString(str, "")
- return str
-}
-
-// ToPascaleCase converts a string to PascalCase
-func ToPascaleCase(name string) string {
- name = strcase.ToCamel(name)
- name = ToUpperCase(name)
- return name
-}
-
-// ToSnakeCase converts a string to snake_case
-func ToSnakeCase(name string) string {
- name = FormatStr(name)
- name = ToSnake(name)
- return name
-}
-
-// ToSnake converts a string to snake_case
-func ToSnake(s string) string {
- return ToDelimited(s, '_')
-}
-
-// ToDelimited converts a string to delimited.snake.case
-// (in this case `delimiter = '.'`)
-func ToDelimited(s string, delimiter uint8) string {
- return ToScreamingDelimited(s, delimiter, "", false)
-}
-
-// ToScreamingDelimited converts a string to SCREAMING.DELIMITED.SNAKE.CASE
-// (in this case `delimiter = '.'; screaming = true`)
-// or delimited.snake.case
-// (in this case `delimiter = '.'; screaming = false`)
-func ToScreamingDelimited(s string, delimiter uint8, ignore string, screaming bool) string {
- s = strings.TrimSpace(s)
- n := strings.Builder{}
- n.Grow(len(s) + 2) // nominal 2 bytes of extra space for inserted delimiters
- for i, v := range []byte(s) {
- vIsCap := v >= 'A' && v <= 'Z'
- vIsLow := v >= 'a' && v <= 'z'
- if vIsLow && screaming {
- v += 'A'
- v -= 'a'
- } else if vIsCap && !screaming {
- v += 'a'
- v -= 'A'
- }
-
- // treat acronyms as words, eg for JSONData -> JSON is a whole word
- if i+1 < len(s) {
- next := s[i+1]
- vIsNum := v >= '0' && v <= '9'
- nextIsCap := next >= 'A' && next <= 'Z'
- nextIsLow := next >= 'a' && next <= 'z'
- nextIsNum := next >= '0' && next <= '9'
-
- // add delimiter if the next character is of a different type
- // but do not insert delimiter between a letter and a number
- if (vIsCap && (nextIsLow || nextIsNum)) || (vIsLow && (nextIsCap || nextIsNum)) || (vIsNum && (nextIsCap || nextIsLow)) {
- prevIgnore := ignore != "" && i > 0 && strings.ContainsAny(string(s[i-1]), ignore)
- if !prevIgnore {
- if vIsCap && nextIsLow {
- if prevIsCap := i > 0 && s[i-1] >= 'A' && s[i-1] <= 'Z'; prevIsCap {
- n.WriteByte(delimiter)
- }
- }
-
- // Skip adding delimiter if current character is a letter followed by a number
- if !(vIsLow && nextIsNum) && !(vIsCap && nextIsNum) {
- n.WriteByte(v)
- if vIsLow || vIsNum || nextIsNum {
- n.WriteByte(delimiter)
- }
- continue
- }
- }
- }
- }
-
- if (v == ' ' || v == '_' || v == '-' || v == '.') && !strings.ContainsAny(string(v), ignore) {
- // replace space/underscore/hyphen/dot with delimiter
- n.WriteByte(delimiter)
- } else {
- n.WriteByte(v)
- }
- }
-
- return n.String()
-}
diff --git a/go.mod b/go.mod
index 1743fa6..3a3e045 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,6 @@ go 1.18
require (
github.com/apache/thrift v0.13.0
github.com/google/gnostic-models v0.6.8
- github.com/iancoleman/strcase v0.3.0
google.golang.org/protobuf v1.34.2
gopkg.in/yaml.v3 v3.0.1
)
diff --git a/go.sum b/go.sum
index 4b49dc0..31e1cfc 100644
--- a/go.sum
+++ b/go.sum
@@ -5,8 +5,6 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
-github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
-github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
diff --git a/licenses/LICENSE-cli.txt b/licenses/LICENSE-cli.txt
deleted file mode 100644
index 2c84c78..0000000
--- a/licenses/LICENSE-cli.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2022 urfave/cli maintainers
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/licenses/LICENSE-kin-openapi.txt b/licenses/LICENSE-kin-openapi.txt
deleted file mode 100644
index 992b983..0000000
--- a/licenses/LICENSE-kin-openapi.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2017-2018 the project authors.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/swagger2idl/LICENSE b/swagger2idl/LICENSE
deleted file mode 100644
index 261eeb9..0000000
--- a/swagger2idl/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- 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.
diff --git a/swagger2idl/README.md b/swagger2idl/README.md
deleted file mode 100644
index 59fc40f..0000000
--- a/swagger2idl/README.md
+++ /dev/null
@@ -1,84 +0,0 @@
-# swagger2idl
-
-ENGLISH | [中文](README_CN.md)
-
-`swagger2idl` is a tool designed to convert Swagger documentation into Thrift or Proto files. It supports relevant annotations from [swagger-generate](https://github.com/hertz-contrib/swagger-generate), [cloudwego/cwgo](https://github.com/cloudwego/cwgo), [hertz](https://github.com/cloudwego/hertz), and [kitex](https://github.com/cloudwego/kitex).
-
-## Installation
-
-```sh
-# Install from the official repository
-
-git clone https://github.com/hertz-contrib/swagger-generate
-cd swagger2idl
-go install
-
-# Direct installation
-go install github.com/hertz-contrib/swagger-generate/swagger2idl@latest
-```
-
-## Usage
-
-### Parameter Description
-
-| Parameter | Abbreviation | Default Value | Description |
-|-----------------|--------------|--------------------------------|--------------------------------------------------------------------------------------------------------------------|
-| `--type` | `-t` | Inferred from the output file extension | Specify the output type, either `'proto'` or `'thrift'`. If not provided, it is inferred from the output file extension. |
-| `--output` | `-o` | `filename.proto` or `filename.thrift` | Specify the output file path. If not provided, it defaults to `output.proto` or `output.thrift`, depending on the output type. |
-| `--openapi` | `-oa` | `false` | Includes OpenAPI-specific annotations and adds references. The related reference files can be found in [idl](https://github.com/hertz-contrib/swagger-generate/idl). |
-| `--api` | `-a` | `false` | Adds annotations for compatibility with Cwgo/Hertz and adds references. The related reference files are in [idl](https://github.com/hertz-contrib/swagger-generate/idl). |
-| `--naming` | `-n` | `true` | Use naming conventions in the output IDL file. |
-
-### Usage Examples
-
-1. Convert to Protobuf format and specify the output path:
-```bash
- swagger2idl --output my_output.proto --openapi --api --naming=false openapi.yaml
-```
-or
-```bash
- swagger2idl -o my_output.proto -oa -a -n=false openapi.yaml
-```
-
-### Extensions
-You can add extensions like `x-options` to parameters in the `openapi.yaml` file. More extensions will be supported in the future.
-
-For Proto files:
-```yaml
-x-options:
- go_package: myawesomepackage
-```
-Generates:
-```protobuf
-option go_package = "myawesomepackage";
-```
-
-For Thrift files:
-```yaml
-x-options:
- go: myawesomepackage
-```
-Generates:
-```thrift
-namespace go myawesomepackage
-```
-
-### Naming Conventions
-
-| **Category** | **Thrift/Proto Naming Rules** |
-|------------------------------------|-------------------------------------------------------------------------------|
-| **Struct/Message** | - Use **PascalCase**.
Example: `UserInfo` |
-| **Field** | - Use **snake_case**.
Example: `user_id`. If a field name contains a number, the number should follow a letter, not an underscore. |
-| **Enum**, **Service**, **Union** | - Use **PascalCase**.
Example: `UserType` |
-| **Enum Values** | - Use **UPPER_SNAKE_CASE**.
Example: `ADMIN_USER` |
-| **RPC Methods** | - Use **PascalCase**.
Example: `GetUserInfo` |
-| **Package/Namespace** | - Use **snake_case**, typically based on the project structure.
Example: `com.project.service` |
-
-#### Naming Conventions Explained:
-- **PascalCase**: Capitalize the first letter of each word, such as `UserInfo`.
-- **snake_case**: All lowercase with underscores separating words, such as `user_info`.
-- **UPPER_SNAKE_CASE**: All uppercase letters with underscores separating words, such as `ADMIN_USER`.
-
-## More Information
-
-For more usage details, refer to the [Examples](example).
\ No newline at end of file
diff --git a/swagger2idl/README_CN.md b/swagger2idl/README_CN.md
deleted file mode 100644
index aaac445..0000000
--- a/swagger2idl/README_CN.md
+++ /dev/null
@@ -1,82 +0,0 @@
-# swagger2idl
-
-[English](README.md) | 中文
-
-swagger2idl 是一个用于将 Swagger 文档转换为 Thrift 或 Proto 文件的工具。
-适配了[swagger-generate](https://github.com/hertz-contrib/swagger-generate)、[cloudwego/cwgo](https://github.com/cloudwego/cwgo)、[hertz](https://github.com/cloudwego/hertz)及[kitex](https://github.com/cloudwego/kitex)中的相关注解。
-
-## 安装
-
-```sh
-# 官方仓库安装
-
-git clone https://github.com/hertz-contrib/swagger-generate
-cd swagger2idl
-go install
-
-# 直接安装
-go install github.com/hertz-contrib/swagger-generate/swagger2idl@latest
-```
-
-## 使用
-### 参数说明
-
-| 参数名称 | 缩写 | 默认值 | 说明 |
-|-------------|-------|----------------------------|-------------------------------------------------------------------------------------------------------|
-| `--type` | `-t` | 自动根据输出文件扩展名推断 | 指定输出类型,可选值为 `'proto'` 或 `'thrift'`。如果未提供,则从输出文件扩展名推断。 |
-| `--output` | `-o` | `文件名.proto` 或 `文件名.thrift` | 指定输出文件的路径。如果未提供,默认为 `output.proto` 或 `output.thrift`,具体取决于输出类型。 |
-| `--openapi` | `-oa` | `false` | 会生成相应的openapi注解,并添加引用,相关引用文件可以在[idl](https://github.com/hertz-contrib/swagger-generate/idl)中找到。 |
-| `--api` | `-a` | `false` | 会生成相应的适配Cwgo/Hertz的注解,并添加引用,相关引用文件可以在[idl](https://github.com/hertz-contrib/swagger-generate/idl)中找到。 |
-| `--naming` | `-n` | `true` | 在输出的 IDL 文件中使用命名约定。 |
-
-### 使用示例
-
-1. 指定输出为 Protobuf 格式,并输出到指定路径:
-```bash
- swagger2idl --output my_output.proto --openapi --api --naming=false openapi.yaml
-```
-or
-```bash
- swagger2idl -o my_output.proto -oa -a -n=false openapi.yaml
-```
-
-### 扩展
-支持向openapi.yaml中的参数添加扩展,如`x-options`,后面会增加更多扩展。
-
-如果是proto文件
-```yaml
-x-options:
- go_package: myawesomepackage
-```
-会生成
-```protobuf
-option go_package = "myawesomepackage";
-```
-如果是thrift文件
-```yaml
-x-options:
- go: myawesomepackage
-```
-会生成
-```thrift
-namespace go myawesomepackage
-```
-### 命名约定
-
-| **类别** | **Thrift/Proto 命名规范** |
-|----------------------------------|-------------------------------------------------------------------------------|
-| **Struct/Message** | - 使用 **PascalCase** 命名。
- 例:`UserInfo` |
-| **Field** | - 使用 **snake_case** 命名。
- 例:`user_id`, 如果你的字段名包含一个数字,数字应该出现在字母后面,而不是下划线后面 |
-| **Enum**, **Service**, **Union** | - 使用 **PascalCase**。
- 例:`UserType` |
-| **Enum 值** | - 使用 **UPPER_SNAKE_CASE** 命名。
- 例:`ADMIN_USER` |
-| **RPC 方法** | - 使用 **PascalCase** 命名。
- 例:`GetUserInfo` |
-| **Package/Namespace** | - 使用 **snake_case**,通常基于项目结构命名。
例:`com.project.service` |
-
-#### 详细说明:
-- **PascalCase**: 首字母大写,每个单词的首字母都大写,例如 `UserInfo`。
-- **snake_case**: 全部小写,单词之间使用下划线分隔,例如 `user_info`。
-- **UPPER_SNAKE_CASE**: 全部字母大写,单词之间用下划线分隔,例如 `ADMIN_USER`。
-
-## 更多信息
-
-更多的使用方法请参考 [示例](example)
\ No newline at end of file
diff --git a/swagger2idl/converter/converter.go b/swagger2idl/converter/converter.go
deleted file mode 100644
index 888d10b..0000000
--- a/swagger2idl/converter/converter.go
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2024 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 converter
-
-import "github.com/hertz-contrib/swagger-generate/common/consts"
-
-// Converter is an interface for converting files
-type Converter interface {
- Convert() error
- GetIdl() interface{}
-}
-
-// ConvertOption adds a struct for conversion options
-type ConvertOption struct {
- OpenapiOption bool
- ApiOption bool
- NamingOption bool
-}
-
-var MethodToOption = map[string]string{
- consts.HttpMethodGet: consts.ApiGet,
- consts.HttpMethodPost: consts.ApiPost,
- consts.HttpMethodPut: consts.ApiPut,
- consts.HttpMethodPatch: consts.ApiPatch,
- consts.HttpMethodDelete: consts.ApiDelete,
- consts.HttpMethodOptions: consts.ApiOptions,
- consts.HttpMethodHead: consts.ApiHEAD,
-}
diff --git a/swagger2idl/converter/proto_converter.go b/swagger2idl/converter/proto_converter.go
deleted file mode 100644
index 413680d..0000000
--- a/swagger2idl/converter/proto_converter.go
+++ /dev/null
@@ -1,1327 +0,0 @@
-/*
- * Copyright 2024 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 converter
-
-import (
- "errors"
- "fmt"
-
- "github.com/getkin/kin-openapi/openapi3"
- "github.com/hertz-contrib/swagger-generate/common/consts"
- common "github.com/hertz-contrib/swagger-generate/common/utils"
- "github.com/hertz-contrib/swagger-generate/swagger2idl/protobuf"
- "github.com/hertz-contrib/swagger-generate/swagger2idl/utils"
-)
-
-// ProtoConverter struct, used to convert OpenAPI specifications into Proto files
-type ProtoConverter struct {
- spec *openapi3.T
- ProtoFile *protobuf.ProtoFile
- converterOption *ConvertOption
-}
-
-// NewProtoConverter creates and initializes a ProtoConverter
-func NewProtoConverter(spec *openapi3.T, option *ConvertOption) *ProtoConverter {
- return &ProtoConverter{
- spec: spec,
- ProtoFile: &protobuf.ProtoFile{
- PackageName: utils.GetPackageName(spec),
- Messages: []*protobuf.ProtoMessage{},
- Services: []*protobuf.ProtoService{},
- Enums: []*protobuf.ProtoEnum{},
- Imports: []string{},
- Options: []*protobuf.Option{},
- },
- converterOption: option,
- }
-}
-
-// Convert converts the OpenAPI specification to a Proto file
-func (c *ProtoConverter) Convert() error {
- // Convert the go Option to Proto
- err := c.addExtensionsToProtoOptions()
- if err != nil {
- return fmt.Errorf("error parsing extensions to proto options: %w", err)
- }
-
- // Convert tags into Proto services
- c.convertTagsToProtoServices()
-
- // Convert components into Proto messages
- err = c.convertComponentsToProtoMessages()
- if err != nil {
- return fmt.Errorf("error converting components to proto messages: %w", err)
- }
-
- // Convert paths into Proto services
- err = c.convertPathsToProtoServices()
- if err != nil {
- return fmt.Errorf("error converting paths to proto services: %w", err)
- }
-
- if c.converterOption.OpenapiOption {
- c.addOptionsToProto()
- }
-
- return nil
-}
-
-func (c *ProtoConverter) GetIdl() interface{} {
- return c.ProtoFile
-}
-
-// convertTagsToProtoServices converts OpenAPI tags into Proto services and stores them in the ProtoFile
-func (c *ProtoConverter) convertTagsToProtoServices() {
- tags := c.spec.Tags
- for _, tag := range tags {
- serviceName := common.ToPascaleCase(tag.Name)
- service := &protobuf.ProtoService{
- Name: serviceName,
- Description: tag.Description,
- }
- c.ProtoFile.Services = append(c.ProtoFile.Services, service)
- }
-}
-
-// convertComponentsToProtoMessages converts OpenAPI components into Proto messages and stores them in the ProtoFile
-func (c *ProtoConverter) convertComponentsToProtoMessages() error {
- components := c.spec.Components
- if components == nil {
- return nil
- }
-
- if components.Schemas == nil {
- return nil
- }
-
- for name, schemaRef := range components.Schemas {
- schema := schemaRef
-
- if c.converterOption.NamingOption {
- name = common.ToPascaleCase(name)
- }
-
- protoType, err := c.ConvertSchemaToProtoType(schema, name, nil)
- if err != nil {
- return fmt.Errorf("error converting schema %s: %w", name, err)
- }
-
- switch v := protoType.(type) {
- case *protobuf.ProtoField:
- message := &protobuf.ProtoMessage{
- Name: name,
- Fields: []*protobuf.ProtoField{v},
- }
-
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(schema.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiSchema,
- Value: optionStr,
- }
- message.Options = append(message.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- c.addMessageToProto(message)
- case *protobuf.ProtoMessage:
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(schema.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiSchema,
- Value: optionStr,
- }
- v.Options = append(v.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- c.addMessageToProto(v)
- case *protobuf.ProtoEnum:
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(schema.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiSchema,
- Value: optionStr,
- }
- v.Options = append(v.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- c.addEnumToProto(v)
- case *protobuf.ProtoOneOf:
- message := &protobuf.ProtoMessage{
- Name: name,
- OneOfs: []*protobuf.ProtoOneOf{v},
- }
-
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(schema.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiSchema,
- Value: optionStr,
- }
- message.Options = append(message.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- c.addMessageToProto(message)
- }
- }
- return nil
-}
-
-// convertPathsToProtoServices converts OpenAPI path items into Proto services and stores them in the ProtoFile
-func (c *ProtoConverter) convertPathsToProtoServices() error {
- paths := c.spec.Paths
- services, err := c.ConvertPathsToProtoServices(paths)
- if err != nil {
- return fmt.Errorf("error converting paths to proto services: %w", err)
- }
-
- c.ProtoFile.Services = append(c.ProtoFile.Services, services...)
- return nil
-}
-
-// ConvertPathsToProtoServices converts OpenAPI path items into Proto services
-func (c *ProtoConverter) ConvertPathsToProtoServices(paths *openapi3.Paths) ([]*protobuf.ProtoService, error) {
- var services []*protobuf.ProtoService
-
- for path, pathItem := range paths.Map() {
- for method, operation := range pathItem.Operations() {
- serviceName := utils.GetServiceName(operation)
- methodName := utils.GetMethodName(operation, path, method)
-
- if c.converterOption.NamingOption {
- serviceName = common.ToPascaleCase(serviceName)
- methodName = common.ToPascaleCase(methodName)
- }
-
- inputMessage, err := c.generateRequestMessage(operation, methodName)
- if err != nil {
- return nil, fmt.Errorf("error generating request message for %s: %w", methodName, err)
- }
-
- outputMessage, err := c.generateResponseMessage(operation, methodName)
- if err != nil {
- return nil, fmt.Errorf("error generating response message for %s: %w", methodName, err)
- }
-
- service := c.findOrCreateService(serviceName)
-
- if !c.methodExistsInService(service, methodName) {
- protoMethod := &protobuf.ProtoMethod{
- Name: methodName,
- Input: inputMessage,
- Output: outputMessage,
- }
-
- if c.converterOption.ApiOption {
- if optionName, ok := MethodToOption[method]; ok {
- option := &protobuf.Option{
- Name: optionName,
- Value: fmt.Sprintf("%q", utils.ConvertPath(path)),
- }
- protoMethod.Options = append(protoMethod.Options, option)
- c.AddProtoImport(consts.ApiProtoFile)
- }
- }
-
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(operation, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiOperation,
- Value: optionStr,
- }
- protoMethod.Options = append(protoMethod.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
-
- }
- service.Methods = append(service.Methods, protoMethod)
- }
- }
- }
-
- return services, nil
-}
-
-// generateRequestMessage generates a request message for an operation
-func (c *ProtoConverter) generateRequestMessage(operation *openapi3.Operation, methodName string) (string, error) {
- messageName := utils.GetMessageName(operation, methodName, "Request")
-
- if c.converterOption.NamingOption {
- messageName = common.ToPascaleCase(messageName)
- }
-
- message := &protobuf.ProtoMessage{Name: messageName}
-
- if operation.RequestBody == nil && len(operation.Parameters) == 0 {
- c.AddProtoImport(consts.EmptyProtoFile)
- return consts.EmptyMessage, nil
- }
-
- if operation.RequestBody != nil {
- if operation.RequestBody.Ref != "" {
- return common.ToPascaleCase(utils.ExtractMessageNameFromRef(operation.RequestBody.Ref)), nil
- }
-
- if operation.RequestBody.Value != nil && len(operation.RequestBody.Value.Content) > 0 {
- for mediaTypeStr, mediaType := range operation.RequestBody.Value.Content {
- schema := mediaType.Schema
- if schema != nil {
- protoType, err := c.ConvertSchemaToProtoType(schema, common.FormatStr(mediaTypeStr), message)
- if err != nil {
- return "", err
- }
-
- switch v := protoType.(type) {
- case *protobuf.ProtoField:
- if c.converterOption.ApiOption {
- var optionName string
- if mediaTypeStr == "application/json" {
- optionName = "api.body"
- } else if mediaTypeStr == "application/x-www-form-urlencoded" || mediaTypeStr == "multipart/form-data" {
- optionName = "api.form"
- }
- if optionName != "" {
- v.Options = append(v.Options, &protobuf.Option{
- Name: optionName,
- Value: fmt.Sprintf("%q", v.Name),
- })
- c.AddProtoImport(consts.ApiProtoFile)
- }
- }
- c.addFieldIfNotExists(&message.Fields, v)
- case *protobuf.ProtoMessage:
- for _, field := range v.Fields {
- if c.converterOption.ApiOption {
- var optionName string
- if mediaTypeStr == "application/json" {
- optionName = "api.body"
- } else if mediaTypeStr == "application/x-www-form-urlencoded" || mediaTypeStr == "multipart/form-data" {
- optionName = "api.form"
- }
- if optionName != "" {
- field.Options = append(field.Options, &protobuf.Option{
- Name: optionName,
- Value: fmt.Sprintf("%q", field.Name),
- })
- c.AddProtoImport(consts.ApiProtoFile)
- }
- }
- c.addFieldIfNotExists(&message.Fields, field)
- }
-
- message.Enums = append(message.Enums, v.Enums...)
-
- message.OneOfs = append(message.OneOfs, v.OneOfs...)
-
- for _, nestedMessage := range v.Messages {
- c.addMessageIfNotExists(&message.Messages, nestedMessage)
- }
- case *protobuf.ProtoEnum:
- name := mediaTypeStr
- if c.converterOption.NamingOption {
- name = common.ToSnakeCase(name)
- } else {
- name = common.FormatStr(name)
- }
- newField := &protobuf.ProtoField{
- Name: name + "_field",
- Type: v.Name,
- }
- if c.converterOption.ApiOption {
- var optionName string
- if mediaTypeStr == "application/json" {
- optionName = "api.body"
- } else if mediaTypeStr == "application/x-www-form-urlencoded" || mediaTypeStr == "multipart/form-data" {
- optionName = "api.form"
- }
- if optionName != "" {
- newField.Options = append(newField.Options, &protobuf.Option{
- Name: optionName,
- Value: fmt.Sprintf("%q", v.Name),
- })
- c.AddProtoImport(consts.ApiProtoFile)
- }
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(schema.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- newField.Options = append(newField.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- message.Enums = append(message.Enums, v)
- message.Fields = append(message.Fields, newField)
- case *protobuf.ProtoOneOf:
- message.OneOfs = append(message.OneOfs, v)
- }
- }
- }
- }
- }
-
- if len(operation.Parameters) > 0 {
- for _, param := range operation.Parameters {
- if param.Value.Schema != nil {
- fieldOrMessage, err := c.ConvertSchemaToProtoType(param.Value.Schema, param.Value.Name, message)
- if err != nil {
- return "", err
- }
- description := param.Value.Description
- switch v := fieldOrMessage.(type) {
- case *protobuf.ProtoField:
- if c.converterOption.ApiOption {
- v.Options = append(v.Options, &protobuf.Option{
- Name: "api." + param.Value.In,
- Value: fmt.Sprintf("%q", param.Value.Name),
- })
- c.AddProtoImport(consts.ApiProtoFile)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(param.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiParameter,
- Value: optionStr,
- }
- v.Options = append(v.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- v.Description = description
- c.addFieldIfNotExists(&message.Fields, v)
- case *protobuf.ProtoMessage:
- for _, field := range v.Fields {
- if c.converterOption.ApiOption {
- field.Options = append(field.Options, &protobuf.Option{
- Name: "api." + param.Value.In,
- Value: fmt.Sprintf("%q", param.Value.Name),
- })
- c.AddProtoImport(consts.ApiProtoFile)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(param.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiParameter,
- Value: optionStr,
- }
- field.Options = append(field.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- c.addFieldIfNotExists(&message.Fields, field)
- }
- message.Enums = append(message.Enums, v.Enums...)
-
- message.OneOfs = append(message.OneOfs, v.OneOfs...)
-
- for _, nestedMessage := range v.Messages {
- c.addMessageIfNotExists(&message.Messages, nestedMessage)
- }
- case *protobuf.ProtoEnum:
- name := param.Value.Name
- if c.converterOption.NamingOption {
- name = common.ToPascaleCase(name)
- }
- newField := &protobuf.ProtoField{
- Name: name + "_field",
- Type: v.Name,
- }
- if c.converterOption.ApiOption {
- newField.Options = append(newField.Options, &protobuf.Option{
- Name: "api." + param.Value.In,
- Value: fmt.Sprintf("%q", param.Value.Name),
- })
- c.AddProtoImport(consts.ApiProtoFile)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(param.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiParameter,
- Value: optionStr,
- }
- newField.Options = append(newField.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- message.Enums = append(message.Enums, v)
- message.Fields = append(message.Fields, newField)
- case *protobuf.ProtoOneOf:
- message.OneOfs = append(message.OneOfs, v)
- }
- }
- }
- }
-
- // if there are no fields or messages, return an empty message
- if len(message.Fields) > 0 || len(message.Messages) > 0 || len(message.Enums) > 0 {
- c.addMessageToProto(message)
- return message.Name, nil
- }
-
- return "", nil
-}
-
-// generateResponseMessage generates a response message for an operation
-func (c *ProtoConverter) generateResponseMessage(operation *openapi3.Operation, methodName string) (string, error) {
- if operation.Responses == nil {
- return "", nil
- }
-
- responses := operation.Responses.Map()
- responseCount := 0
- for _, responseRef := range responses {
- if responseRef.Ref == "" && (responseRef.Value == nil || (len(responseRef.Value.Content) == 0 && len(responseRef.Value.Headers) == 0)) {
- continue
- }
- responseCount++
- }
-
- if responseCount == 1 {
- for _, responseRef := range responses {
- if responseRef.Ref == "" && (responseRef.Value == nil || (len(responseRef.Value.Content) == 0 && len(responseRef.Value.Headers) == 0)) {
- continue
- }
- return c.processSingleResponse("", responseRef, operation, methodName)
- }
- }
-
- if responseCount == 0 {
- c.AddProtoImport(consts.EmptyProtoFile)
- return consts.EmptyMessage, nil
- }
-
- // create a wrapper message for multiple responses
- wrapperMessageName := utils.GetMessageName(operation, methodName, "Response")
- if c.converterOption.NamingOption {
- wrapperMessageName = common.ToPascaleCase(wrapperMessageName)
- }
-
- wrapperMessage := &protobuf.ProtoMessage{Name: wrapperMessageName}
-
- emptyFlag := true
-
- for statusCode, responseRef := range responses {
- if responseRef.Ref == "" && (responseRef.Value == nil || len(responseRef.Value.Content) == 0) {
- break
- }
- emptyFlag = false
- messageName, err := c.processSingleResponse(statusCode, responseRef, operation, methodName)
- if err != nil {
- return "", err
- }
-
- name := "Response_" + statusCode
- if c.converterOption.NamingOption {
- name = common.ToSnakeCase(name)
- }
- field := &protobuf.ProtoField{
- Name: name,
- Type: messageName,
- }
- wrapperMessage.Fields = append(wrapperMessage.Fields, field)
- }
-
- if emptyFlag {
- c.AddProtoImport(consts.EmptyProtoFile)
- return consts.EmptyMessage, nil
- }
-
- c.addMessageToProto(wrapperMessage)
-
- return wrapperMessageName, nil
-}
-
-// processSingleResponse deals with a single response in an operation
-func (c *ProtoConverter) processSingleResponse(statusCode string, responseRef *openapi3.ResponseRef, operation *openapi3.Operation, methodName string) (string, error) {
- if responseRef.Ref != "" {
- return common.ToPascaleCase(utils.ExtractMessageNameFromRef(responseRef.Ref)), nil
- }
-
- response := responseRef.Value
- messageName := utils.GetMessageName(operation, methodName, "Response") + common.ToUpperCase(statusCode)
-
- if c.converterOption.NamingOption {
- messageName = common.ToPascaleCase(messageName)
- }
-
- message := &protobuf.ProtoMessage{Name: messageName}
-
- if len(response.Headers) > 0 {
- for headerName, headerRef := range response.Headers {
- if headerRef != nil {
-
- fieldOrMessage, err := c.ConvertSchemaToProtoType(headerRef.Value.Schema, headerName, message)
- if err != nil {
- return "", err
- }
-
- switch v := fieldOrMessage.(type) {
- case *protobuf.ProtoField:
- if c.converterOption.ApiOption {
- option := &protobuf.Option{
- Name: "api.header",
- Value: fmt.Sprintf("%q", headerName),
- }
- v.Options = append(v.Options, option)
- c.AddProtoImport(consts.ApiProtoFile)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(headerRef.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- v.Options = append(v.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- c.addFieldIfNotExists(&message.Fields, v)
- case *protobuf.ProtoMessage:
- for _, field := range v.Fields {
- if c.converterOption.ApiOption {
- option := &protobuf.Option{
- Name: "api.header",
- Value: fmt.Sprintf("%q", field.Name),
- }
- field.Options = append(field.Options, option)
- c.AddProtoImport(consts.ApiProtoFile)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(headerRef.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- field.Options = append(field.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- c.addFieldIfNotExists(&message.Fields, field)
- }
- message.Enums = append(message.Enums, v.Enums...)
-
- message.OneOfs = append(message.OneOfs, v.OneOfs...)
-
- for _, nestedMessage := range v.Messages {
- c.addMessageIfNotExists(&message.Messages, nestedMessage)
- }
- case *protobuf.ProtoEnum:
- name := headerName
- if c.converterOption.NamingOption {
- name = common.ToSnakeCase(name)
- }
- newField := &protobuf.ProtoField{
- Name: name + "_field",
- Type: v.Name,
- }
- if c.converterOption.ApiOption {
- option := &protobuf.Option{
- Name: "api.header",
- Value: fmt.Sprintf("%q", headerName),
- }
- newField.Options = append(newField.Options, option)
- c.AddProtoImport(consts.ApiProtoFile)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(headerRef.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- newField.Options = append(newField.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- message.Enums = append(message.Enums, v)
- message.Fields = append(message.Fields, newField)
- case *protobuf.ProtoOneOf:
- message.OneOfs = append(message.OneOfs, v)
- }
- }
- }
- }
-
- for mediaTypeStr, mediaType := range response.Content {
- schema := mediaType.Schema
- if schema != nil {
-
- protoType, err := c.ConvertSchemaToProtoType(schema, common.FormatStr(mediaTypeStr), message)
- if err != nil {
- return "", err
- }
-
- switch v := protoType.(type) {
- case *protobuf.ProtoField:
- if c.converterOption.ApiOption && mediaTypeStr == "application/json" {
- option := &protobuf.Option{
- Name: "api.body",
- Value: fmt.Sprintf("%q", v.Name),
- }
- v.Options = append(v.Options, option)
- c.AddProtoImport(consts.ApiProtoFile)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(schema.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- v.Options = append(v.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- c.addFieldIfNotExists(&message.Fields, v)
- case *protobuf.ProtoMessage:
- for _, field := range v.Fields {
- if c.converterOption.ApiOption && mediaTypeStr == "application/json" {
- option := &protobuf.Option{
- Name: "api.body",
- Value: fmt.Sprintf("%q", field.Name),
- }
- field.Options = append(field.Options, option)
- c.AddProtoImport(consts.ApiProtoFile)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(schema.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- field.Options = append(field.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- c.addFieldIfNotExists(&message.Fields, field)
- }
- message.Enums = append(message.Enums, v.Enums...)
-
- message.OneOfs = append(message.OneOfs, v.OneOfs...)
-
- for _, nestedMessage := range v.Messages {
- c.addMessageIfNotExists(&message.Messages, nestedMessage)
- }
- case *protobuf.ProtoEnum:
- name := mediaTypeStr
- if c.converterOption.NamingOption {
- name = common.ToSnakeCase(mediaTypeStr)
- } else {
- name = common.ToUpperCase(name)
- }
- newField := &protobuf.ProtoField{
- Name: name + "_field",
- Type: v.Name,
- }
- if c.converterOption.ApiOption && mediaTypeStr == "application/json" {
- option := &protobuf.Option{
- Name: "api.body",
- Value: fmt.Sprintf("%q", v.Name),
- }
- newField.Options = append(newField.Options, option)
- c.AddProtoImport(consts.ApiProtoFile)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(schema.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- newField.Options = append(newField.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- message.Enums = append(message.Enums, v)
- message.Fields = append(message.Fields, newField)
- case *protobuf.ProtoOneOf:
- message.OneOfs = append(message.OneOfs, v)
- }
- }
- }
-
- if len(message.Fields) > 0 || len(message.Messages) > 0 || len(message.Enums) > 0 {
- c.addMessageToProto(message)
- return message.Name, nil
- }
- return "", nil
-}
-
-// ConvertSchemaToProtoType converts an OpenAPI schema to a Proto field or message
-func (c *ProtoConverter) ConvertSchemaToProtoType(
- schemaRef *openapi3.SchemaRef,
- protoName string,
- parentMessage *protobuf.ProtoMessage,
-) (interface{}, error) {
- var protoType string
- var result interface{}
-
- // Handle referenced schema
- if schemaRef.Ref != "" {
- name := c.applySnakeCaseNamingOption(utils.ExtractMessageNameFromRef(schemaRef.Ref))
- return &protobuf.ProtoField{
- Name: name,
- Type: common.ToPascaleCase(utils.ExtractMessageNameFromRef(schemaRef.Ref)),
- }, nil
- }
-
- // Ensure schema value is valid
- if schemaRef.Value == nil {
- return nil, errors.New("schema type is required")
- }
-
- schema := schemaRef.Value
- description := schema.Description
-
- // Handle oneOf, allOf, anyOf even if schema.Type is nil
- if len(schema.OneOf) > 0 {
- protoUnion, err := c.handleOneOf(schema.OneOf, protoName, parentMessage)
- if err != nil {
- return nil, err
- }
- return protoUnion, nil
- } else if len(schema.AllOf) > 0 {
- protoMessage, err := c.handleAllOf(schema.AllOf, protoName, parentMessage)
- if err != nil {
- return nil, err
- }
- return protoMessage, nil
- } else if len(schema.AnyOf) > 0 {
- protoMessage, err := c.handleAnyOf(schema.AnyOf, protoName, parentMessage)
- if err != nil {
- return nil, err
- }
- return protoMessage, nil
- }
-
- // Process schema type
- switch {
- case schema.Type.Includes("string"):
- if schema.Format == "date" || schema.Format == "date-time" {
- protoType = consts.TimestampMessage
- c.AddProtoImport(consts.TimestampProtoFile)
- } else if len(schema.Enum) != 0 {
- var name string
- if parentMessage == nil {
- name = protoName
- } else {
- name = c.applyPascaleCaseNamingOption(common.ToUpperCase(protoName))
- }
- protoEnum := &protobuf.ProtoEnum{
- Name: name,
- Description: description,
- }
- for i, enumValue := range schema.Enum {
- protoEnum.Values = append(protoEnum.Values, &protobuf.ProtoEnumValue{
- Index: i,
- Value: enumValue,
- })
- }
- result = protoEnum
- } else {
- protoType = "string"
- }
-
- case schema.Type.Includes("integer"):
- if len(schema.Enum) != 0 {
- var name string
- if parentMessage == nil {
- name = protoName
- } else {
- name = c.applyPascaleCaseNamingOption(common.ToUpperCase(protoName))
- }
- protoEnum := &protobuf.ProtoEnum{
- Name: name,
- Description: description,
- }
- for i, enumValue := range schema.Enum {
- protoEnum.Values = append(protoEnum.Values, &protobuf.ProtoEnumValue{
- Index: i,
- Value: enumValue,
- })
- }
- result = protoEnum
- } else if schema.Format == "int32" {
- protoType = "int32"
- } else {
- protoType = "int64"
- }
-
- case schema.Type.Includes("number"):
- if len(schema.Enum) != 0 {
- var name string
- if parentMessage == nil {
- name = protoName
- } else {
- name = c.applyPascaleCaseNamingOption(common.ToUpperCase(protoName))
- }
- protoEnum := &protobuf.ProtoEnum{
- Name: name,
- Description: description,
- }
- for i, enumValue := range schema.Enum {
- protoEnum.Values = append(protoEnum.Values, &protobuf.ProtoEnumValue{
- Index: i,
- Value: enumValue,
- })
- }
- result = protoEnum
- } else if schema.Format == "float" {
- protoType = "float"
- } else {
- protoType = "double"
- }
-
- case schema.Type.Includes("boolean"):
- protoType = "bool"
-
- case schema.Type.Includes("array"):
- if schema.Items != nil {
- fieldOrMessage, err := c.ConvertSchemaToProtoType(schema.Items, protoName+"Item", parentMessage)
- if err != nil {
- return nil, err
- }
-
- fieldType := ""
- if field, ok := fieldOrMessage.(*protobuf.ProtoField); ok {
- fieldType = field.Type
- } else if nestedMessage, ok := fieldOrMessage.(*protobuf.ProtoMessage); ok {
- fieldType = nestedMessage.Name
- c.addNestedMessageToParent(parentMessage, nestedMessage)
- } else if enum, ok := fieldOrMessage.(*protobuf.ProtoEnum); ok {
- fieldType = enum.Name
- c.addNestedEnumToParent(parentMessage, enum)
- }
-
- result = &protobuf.ProtoField{
- Name: c.applySnakeCaseNamingOption(protoName),
- Type: fieldType,
- Repeated: true,
- Description: description,
- }
- }
-
- case schema.Type.Includes("object"):
- var message *protobuf.ProtoMessage
- if parentMessage == nil {
- message = &protobuf.ProtoMessage{Name: protoName}
- } else {
- message = &protobuf.ProtoMessage{Name: c.applyPascaleCaseNamingOption(common.ToUpperCase(protoName))}
- }
- for propName, propSchema := range schema.Properties {
- protoType, err := c.ConvertSchemaToProtoType(propSchema, propName, message)
- if err != nil {
- return nil, err
- }
-
- if field, ok := protoType.(*protobuf.ProtoField); ok {
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(propSchema.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- field.Options = append(field.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- message.Fields = append(message.Fields, field)
- } else if nestedMessage, ok := protoType.(*protobuf.ProtoMessage); ok {
- var name string
- if c.converterOption.NamingOption {
- name = common.ToSnakeCase(nestedMessage.Name)
- }
- newField := &protobuf.ProtoField{
- Name: name + "_field",
- Type: nestedMessage.Name,
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(propSchema.Value, " ")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- newField.Options = append(newField.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
- }
- c.addNestedMessageToParent(message, nestedMessage)
- message.Fields = append(message.Fields, newField)
- } else if enum, ok := protoType.(*protobuf.ProtoEnum); ok {
- c.addNestedEnumToParent(message, enum)
- message.Fields = append(message.Fields, &protobuf.ProtoField{
- Name: c.applySnakeCaseNamingOption(propName + "_field"),
- Type: enum.Name,
- })
- } else if oneOf, ok := protoType.(*protobuf.ProtoOneOf); ok {
- c.addNestedOneOfToParent(message, oneOf)
- }
- }
-
- if schema.AdditionalProperties.Schema != nil {
- mapValueType := "string"
- additionalPropMessage, err := c.ConvertSchemaToProtoType(schema.AdditionalProperties.Schema, protoName+"AdditionalProperties", parentMessage)
- if err != nil {
- return nil, err
- }
- if msg, ok := additionalPropMessage.(*protobuf.ProtoMessage); ok {
- mapValueType = msg.Name
- } else if enum, ok := additionalPropMessage.(*protobuf.ProtoEnum); ok {
- mapValueType = enum.Name
- }
-
- message.Fields = append(message.Fields, &protobuf.ProtoField{
- Name: "additional_properties",
- Type: "map",
- })
- }
-
- message.Description = description
- result = message
- }
-
- // If result is still nil, construct a default ProtoField
- if result == nil {
- result = &protobuf.ProtoField{
- Name: c.applySnakeCaseNamingOption(protoName),
- Type: protoType,
- Description: description,
- }
- }
-
- return result, nil
-}
-
-// handleOneOf processes oneOf schemas
-func (c *ProtoConverter) handleOneOf(oneOfSchemas []*openapi3.SchemaRef, protoName string, parentMessage *protobuf.ProtoMessage) (*protobuf.ProtoOneOf, error) {
- oneOf := &protobuf.ProtoOneOf{
- Name: c.applyPascaleCaseNamingOption(protoName + "OneOf"),
- }
-
- for i, schemaRef := range oneOfSchemas {
- fieldName := fmt.Sprintf("%sOption%d", protoName, i+1)
- protoType, err := c.ConvertSchemaToProtoType(schemaRef, fieldName, parentMessage)
- if err != nil {
- return nil, err
- }
- switch v := protoType.(type) {
- case *protobuf.ProtoField:
- oneOf.Fields = append(oneOf.Fields, v)
- case *protobuf.ProtoMessage:
- newField := &protobuf.ProtoField{
- Name: c.applySnakeCaseNamingOption(v.Name + "_field"),
- Type: v.Name,
- }
- c.addNestedMessageToParent(parentMessage, v)
- oneOf.Fields = append(oneOf.Fields, newField)
- case *protobuf.ProtoEnum:
- newField := &protobuf.ProtoField{
- Name: c.applySnakeCaseNamingOption(v.Name + "_field"),
- Type: v.Name,
- }
- c.addNestedEnumToParent(parentMessage, v)
- oneOf.Fields = append(oneOf.Fields, newField)
- case *protobuf.ProtoOneOf:
- c.addNestedOneOfToParent(parentMessage, v)
- }
- }
- return oneOf, nil
-}
-
-// handleAllOf processes allOf schemas
-func (c *ProtoConverter) handleAllOf(allOfSchemas []*openapi3.SchemaRef, protoName string, parentMessage *protobuf.ProtoMessage) (*protobuf.ProtoMessage, error) {
- allOfMessage := &protobuf.ProtoMessage{
- Name: c.applyPascaleCaseNamingOption(protoName + "AllOf"),
- }
-
- for i, schemaRef := range allOfSchemas {
- fieldName := fmt.Sprintf("%sPart%d", protoName, i+1)
- protoType, err := c.ConvertSchemaToProtoType(schemaRef, fieldName, parentMessage)
- if err != nil {
- return nil, err
- }
-
- switch v := protoType.(type) {
- case *protobuf.ProtoField:
- allOfMessage.Fields = append(allOfMessage.Fields, v)
- case *protobuf.ProtoMessage:
- newField := &protobuf.ProtoField{
- Name: c.applySnakeCaseNamingOption(v.Name + "_field"),
- Type: v.Name,
- }
- c.addNestedMessageToParent(allOfMessage, v)
- allOfMessage.Fields = append(allOfMessage.Fields, newField)
- case *protobuf.ProtoEnum:
- newField := &protobuf.ProtoField{
- Name: c.applySnakeCaseNamingOption(v.Name + "_field"),
- Type: v.Name,
- }
- c.addNestedEnumToParent(allOfMessage, v)
- allOfMessage.Fields = append(allOfMessage.Fields, newField)
- case *protobuf.ProtoOneOf:
- c.addNestedOneOfToParent(allOfMessage, v)
- }
- }
-
- return allOfMessage, nil
-}
-
-// handleAnyOf processes anyOf schemas
-func (c *ProtoConverter) handleAnyOf(anyOfSchemas []*openapi3.SchemaRef, protoName string, parentMessage *protobuf.ProtoMessage) (*protobuf.ProtoMessage, error) {
- anyOfMessage := &protobuf.ProtoMessage{
- Name: c.applyPascaleCaseNamingOption(protoName + "AnyOf"),
- }
-
- for i, schemaRef := range anyOfSchemas {
- fieldName := fmt.Sprintf("%sOption%d", protoName, i+1)
- protoType, err := c.ConvertSchemaToProtoType(schemaRef, fieldName, parentMessage)
- if err != nil {
- return nil, err
- }
-
- switch v := protoType.(type) {
- case *protobuf.ProtoField:
- anyOfMessage.Fields = append(anyOfMessage.Fields, v)
- case *protobuf.ProtoMessage:
- newField := &protobuf.ProtoField{
- Name: c.applySnakeCaseNamingOption(v.Name + "_field"),
- Type: v.Name,
- }
- c.addNestedMessageToParent(anyOfMessage, v)
- anyOfMessage.Fields = append(anyOfMessage.Fields, newField)
- case *protobuf.ProtoEnum:
- newField := &protobuf.ProtoField{
- Name: c.applySnakeCaseNamingOption(v.Name + "_field"),
- Type: v.Name,
- }
- c.addNestedEnumToParent(anyOfMessage, v)
- anyOfMessage.Fields = append(anyOfMessage.Fields, newField)
- case *protobuf.ProtoOneOf:
- c.addNestedOneOfToParent(anyOfMessage, v)
- }
- }
-
- return anyOfMessage, nil
-}
-
-// applyPascaleCaseNamingOption applies naming convention based on the converter's naming option
-func (c *ProtoConverter) applyPascaleCaseNamingOption(name string) string {
- if c.converterOption.NamingOption {
- return common.ToPascaleCase(name)
- }
- return name
-}
-
-// applySnakeCaseNamingOption applies naming convention based on the converter's naming option
-func (c *ProtoConverter) applySnakeCaseNamingOption(name string) string {
- if c.converterOption.NamingOption {
- return common.ToSnakeCase(name)
- }
- return name
-}
-
-// addOptionsToProto adds options to the ProtoFile
-func (c *ProtoConverter) addOptionsToProto() {
- optionStr := common.StructToOption(c.spec, "")
-
- schemaOption := &protobuf.Option{
- Name: consts.OpenapiDocument,
- Value: optionStr,
- }
- c.ProtoFile.Options = append(c.ProtoFile.Options, schemaOption)
- c.AddProtoImport(consts.OpenapiProtoFile)
-}
-
-// Add a new method to handle structured extensions
-func (c *ProtoConverter) addExtensionsToProtoOptions() error {
- // Check for x-option in spec extensions
- if xOption, ok := c.spec.Extensions["x-options"]; ok {
- if optionMap, ok := xOption.(map[string]interface{}); ok {
- for key, value := range optionMap {
- option := &protobuf.Option{
- Name: key,
- Value: fmt.Sprintf("%q", value),
- }
- c.ProtoFile.Options = append(c.ProtoFile.Options, option)
- }
- }
- }
-
- // Check for x-option in spec.info.extensions
- if c.spec.Info != nil {
- if xOption, ok := c.spec.Info.Extensions["x-options"]; ok {
- if optionMap, ok := xOption.(map[string]interface{}); ok {
- for key, value := range optionMap {
- option := &protobuf.Option{
- Name: key,
- Value: fmt.Sprintf("%q", value),
- }
- c.ProtoFile.Options = append(c.ProtoFile.Options, option)
- }
- }
- }
- }
-
- return nil
-}
-
-// addNestedMessageToParent adds a nested message to a parent message
-func (c *ProtoConverter) addNestedMessageToParent(parentMessage, nestedMessage *protobuf.ProtoMessage) {
- if parentMessage != nil && nestedMessage != nil {
- parentMessage.Messages = append(parentMessage.Messages, nestedMessage)
- }
-}
-
-// addNestedEnum adds a nested Enum to a parent message
-func (c *ProtoConverter) addNestedEnumToParent(parentMessage *protobuf.ProtoMessage, nestedEnum *protobuf.ProtoEnum) {
- if parentMessage != nil && nestedEnum != nil {
- parentMessage.Enums = append(parentMessage.Enums, nestedEnum)
- }
-}
-
-// addNestedOneOfToParent adds a nested oneOf to a parent message
-func (c *ProtoConverter) addNestedOneOfToParent(parentMessage *protobuf.ProtoMessage, nestedOneOf *protobuf.ProtoOneOf) {
- if parentMessage != nil && nestedOneOf != nil {
- parentMessage.OneOfs = append(parentMessage.OneOfs, nestedOneOf)
- }
-}
-
-// mergeProtoMessage merges a ProtoMessage into the ProtoFile
-func (c *ProtoConverter) addMessageToProto(message *protobuf.ProtoMessage) error {
- var existingMessage *protobuf.ProtoMessage
- for _, msg := range c.ProtoFile.Messages {
- if msg.Name == message.Name {
- existingMessage = msg
- break
- }
- }
-
- // merge message
- if existingMessage != nil {
- // merge Fields
- fieldNames := make(map[string]struct{})
- for _, field := range existingMessage.Fields {
- fieldNames[field.Name] = struct{}{}
- }
- for _, newField := range message.Fields {
- if _, exists := fieldNames[newField.Name]; !exists {
- existingMessage.Fields = append(existingMessage.Fields, newField)
- }
- }
-
- // merge Messages
- messageNames := make(map[string]struct{})
- for _, nestedMsg := range existingMessage.Messages {
- messageNames[nestedMsg.Name] = struct{}{}
- }
- for _, newMessage := range message.Messages {
- if _, exists := messageNames[newMessage.Name]; !exists {
- existingMessage.Messages = append(existingMessage.Messages, newMessage)
- }
- }
-
- // merge Enums
- enumNames := make(map[string]struct{})
- for _, enum := range existingMessage.Enums {
- enumNames[enum.Name] = struct{}{}
- }
- for _, newEnum := range message.Enums {
- if _, exists := enumNames[newEnum.Name]; !exists {
- existingMessage.Enums = append(existingMessage.Enums, newEnum)
- }
- }
-
- // merge Options
- optionNames := make(map[string]struct{})
- for _, option := range existingMessage.Options {
- optionNames[option.Name] = struct{}{}
- }
- for _, newOption := range message.Options {
- if _, exists := optionNames[newOption.Name]; !exists {
- existingMessage.Options = append(existingMessage.Options, newOption)
- }
- }
- } else {
- c.ProtoFile.Messages = append(c.ProtoFile.Messages, message)
- }
-
- return nil
-}
-
-// addEnumToProto adds an enum to the ProtoFile
-func (c *ProtoConverter) addEnumToProto(enum *protobuf.ProtoEnum) {
- c.ProtoFile.Enums = append(c.ProtoFile.Enums, enum)
-}
-
-// AddProtoImport adds an import to the ProtoFile
-func (c *ProtoConverter) AddProtoImport(importFile string) {
- if c.ProtoFile != nil {
- for _, existingImport := range c.ProtoFile.Imports {
- if existingImport == importFile {
- return
- }
- }
- c.ProtoFile.Imports = append(c.ProtoFile.Imports, importFile)
- }
-}
-
-// addFieldIfNotExists adds a field to Fields if it does not already exist
-func (c *ProtoConverter) addFieldIfNotExists(fields *[]*protobuf.ProtoField, field *protobuf.ProtoField) {
- for _, existingField := range *fields {
- if existingField.Name == field.Name {
- return
- }
- }
- *fields = append(*fields, field)
-}
-
-// addMessageIfNotExists adds a message to Messages if it does not already exist
-func (c *ProtoConverter) addMessageIfNotExists(messages *[]*protobuf.ProtoMessage, nestedMessage *protobuf.ProtoMessage) {
- for _, existingMessage := range *messages {
- if existingMessage.Name == nestedMessage.Name {
- return
- }
- }
- *messages = append(*messages, nestedMessage)
-}
-
-// methodExistsInService checks if a method exists in a service
-func (c *ProtoConverter) methodExistsInService(service *protobuf.ProtoService, methodName string) bool {
- for _, method := range service.Methods {
- if method.Name == methodName {
- return true
- }
- }
- return false
-}
-
-// findOrCreateService finds an existing service by name or creates a new one if it doesn't exist
-func (c *ProtoConverter) findOrCreateService(serviceName string) *protobuf.ProtoService {
- // Iterate over existing services to find a match
- for i := range c.ProtoFile.Services {
- if c.ProtoFile.Services[i].Name == serviceName {
- return c.ProtoFile.Services[i]
- }
- }
-
- // If no existing service is found, create a new one
- newService := &protobuf.ProtoService{Name: serviceName}
- c.ProtoFile.Services = append(c.ProtoFile.Services, newService)
- return newService
-}
diff --git a/swagger2idl/converter/thrift_converter.go b/swagger2idl/converter/thrift_converter.go
deleted file mode 100644
index e24532c..0000000
--- a/swagger2idl/converter/thrift_converter.go
+++ /dev/null
@@ -1,1297 +0,0 @@
-/*
- * Copyright 2024 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 converter
-
-import (
- "errors"
- "fmt"
-
- "github.com/getkin/kin-openapi/openapi3"
- "github.com/hertz-contrib/swagger-generate/common/consts"
- common "github.com/hertz-contrib/swagger-generate/common/utils"
- "github.com/hertz-contrib/swagger-generate/swagger2idl/thrift"
- "github.com/hertz-contrib/swagger-generate/swagger2idl/utils"
-)
-
-// ThriftConverter struct, used to convert OpenAPI specifications into Thrift files
-type ThriftConverter struct {
- spec *openapi3.T
- ThriftFile *thrift.ThriftFile
- converterOption *ConvertOption
-}
-
-// NewThriftConverter creates and initializes a ThriftConverter
-func NewThriftConverter(spec *openapi3.T, option *ConvertOption) *ThriftConverter {
- return &ThriftConverter{
- spec: spec,
- ThriftFile: &thrift.ThriftFile{
- Namespace: map[string]string{},
- Includes: []string{},
- Structs: []*thrift.ThriftStruct{},
- Enums: []*thrift.ThriftEnum{},
- Services: []*thrift.ThriftService{},
- },
- converterOption: option,
- }
-}
-
-// Convert converts the OpenAPI specification to a Thrift file
-func (c *ThriftConverter) Convert() error {
- // Convert the go Option to Thrift
- err := c.addExtensionsToProtoOptions()
- if err != nil {
- return fmt.Errorf("error parsing extensions to proto options: %w", err)
- }
-
- // Convert tags into Thrift services
- c.convertTagsToThriftServices()
-
- // Convert components into Thrift messages
- err = c.convertComponentsToThriftMessages()
- if err != nil {
- return fmt.Errorf("error converting components to thrift messages: %w", err)
- }
-
- // Convert paths into Thrift services
- err = c.convertPathsToThriftServices()
- if err != nil {
- return fmt.Errorf("error converting paths to thrift services: %w", err)
- }
-
- if c.converterOption.OpenapiOption {
- c.addOptionsToThrift()
- }
-
- return nil
-}
-
-func (c *ThriftConverter) GetIdl() interface{} {
- return c.ThriftFile
-}
-
-// convertTagsToThriftServices converts OpenAPI tags into Thrift services and stores them in the ThriftFile
-func (c *ThriftConverter) convertTagsToThriftServices() {
- tags := c.spec.Tags
- for _, tag := range tags {
- serviceName := common.ToPascaleCase(tag.Name)
- service := &thrift.ThriftService{
- Name: serviceName,
- Description: tag.Description,
- }
- c.ThriftFile.Services = append(c.ThriftFile.Services, service)
- }
-}
-
-// convertComponentsToThriftMessages converts OpenAPI components into Thrift messages and stores them in the ThriftFile
-func (c *ThriftConverter) convertComponentsToThriftMessages() error {
- components := c.spec.Components
- if components == nil {
- return nil
- }
-
- if components.Schemas == nil {
- return nil
- }
-
- for name, schemaRef := range components.Schemas {
- schema := schemaRef
-
- if c.converterOption.NamingOption {
- name = common.ToPascaleCase(name)
- }
-
- thriftType, err := c.ConvertSchemaToThriftType(schema, name, nil)
- if err != nil {
- return fmt.Errorf("error converting schema %s: %w", name, err)
- }
-
- switch v := thriftType.(type) {
- case *thrift.ThriftField:
- message := &thrift.ThriftStruct{
- Name: name,
- Fields: []*thrift.ThriftField{v},
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(schema.Value, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiSchema,
- Value: optionStr,
- }
- message.Options = append(message.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- c.addMessageToThrift(message)
- case *thrift.ThriftStruct:
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(schema.Value, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiSchema,
- Value: optionStr,
- }
- v.Options = append(v.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- c.addMessageToThrift(v)
- case *thrift.ThriftEnum:
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(schema.Value, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiSchema,
- Value: optionStr,
- }
- v.Options = append(v.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- c.addEnumToThrift(v)
- case *thrift.ThriftUnion:
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(schema.Value, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiSchema,
- Value: optionStr,
- }
- v.Options = append(v.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- c.addUnionToThrift(v)
- }
- }
- return nil
-}
-
-// convertPathsToThriftServices converts OpenAPI path items into Thrift services and stores them in the ThriftFile
-func (c *ThriftConverter) convertPathsToThriftServices() error {
- paths := c.spec.Paths
- services, err := c.ConvertPathsToThriftServices(paths)
- if err != nil {
- return fmt.Errorf("error converting paths to thrift services: %w", err)
- }
-
- c.ThriftFile.Services = append(c.ThriftFile.Services, services...)
- return nil
-}
-
-// ConvertPathsToThriftServices converts OpenAPI path items into Thrift services
-func (c *ThriftConverter) ConvertPathsToThriftServices(paths *openapi3.Paths) ([]*thrift.ThriftService, error) {
- var services []*thrift.ThriftService
-
- for path, pathItem := range paths.Map() {
- for method, operation := range pathItem.Operations() {
- serviceName := utils.GetServiceName(operation)
- methodName := utils.GetMethodName(operation, path, method)
-
- if c.converterOption.NamingOption {
- serviceName = common.ToPascaleCase(serviceName)
- methodName = common.ToPascaleCase(methodName)
- }
-
- inputMessage, err := c.generateRequestMessage(operation, methodName)
- if err != nil {
- return nil, fmt.Errorf("error generating request message for %s: %w", methodName, err)
- }
-
- outputMessage, err := c.generateResponseMessage(operation, methodName)
- if err != nil {
- return nil, fmt.Errorf("error generating response message for %s: %w", methodName, err)
- }
-
- service := c.findOrCreateService(serviceName)
-
- if !c.methodExistsInService(service, methodName) {
- thriftMethod := &thrift.ThriftMethod{
- Name: methodName,
- Input: inputMessage,
- Output: outputMessage,
- }
-
- if c.converterOption.ApiOption {
- if optionName, ok := MethodToOption[method]; ok {
- option := &thrift.Option{
- Name: optionName,
- Value: fmt.Sprintf("%q", utils.ConvertPath(path)),
- }
- thriftMethod.Options = append(thriftMethod.Options, option)
- }
- }
-
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(operation, " ")
-
- schemaOption := &thrift.Option{
- Name: "openapi.operation",
- Value: optionStr,
- }
- thriftMethod.Options = append(thriftMethod.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- service.Methods = append(service.Methods, thriftMethod)
- }
- }
- }
-
- return services, nil
-}
-
-// generateRequestMessage generates a request message for an operation
-func (c *ThriftConverter) generateRequestMessage(operation *openapi3.Operation, methodName string) ([]string, error) {
- messageName := utils.GetMessageName(operation, methodName, "Request")
-
- if c.converterOption.NamingOption {
- messageName = common.ToPascaleCase(messageName)
- }
-
- message := &thrift.ThriftStruct{Name: messageName}
-
- if operation.RequestBody == nil && len(operation.Parameters) == 0 {
- return []string{""}, nil
- }
-
- if operation.RequestBody != nil {
- if operation.RequestBody.Ref != "" {
- return []string{common.ToPascaleCase(utils.ExtractMessageNameFromRef(operation.RequestBody.Ref))}, nil
- }
-
- if operation.RequestBody.Value != nil && len(operation.RequestBody.Value.Content) > 0 {
- for mediaTypeStr, mediaType := range operation.RequestBody.Value.Content {
- schema := mediaType.Schema
- if schema != nil {
- thriftType, err := c.ConvertSchemaToThriftType(schema, common.FormatStr(mediaTypeStr), message)
- if err != nil {
- return []string{""}, err
- }
-
- switch v := thriftType.(type) {
- case *thrift.ThriftField:
- if c.converterOption.ApiOption {
- var optionName string
- if mediaTypeStr == "application/json" {
- optionName = "api.body"
- } else if mediaTypeStr == "application/x-www-form-urlencoded" || mediaTypeStr == "multipart/form-data" {
- optionName = "api.form"
- }
- if optionName != "" {
- v.Options = append(v.Options, &thrift.Option{
- Name: optionName,
- Value: fmt.Sprintf("%q", v.Name),
- })
- }
- }
- c.addFieldIfNotExists(&message.Fields, v)
- case *thrift.ThriftStruct:
- for _, field := range v.Fields {
- if c.converterOption.ApiOption {
- var optionName string
- if mediaTypeStr == "application/json" {
- optionName = "api.body"
- } else if mediaTypeStr == "application/x-www-form-urlencoded" || mediaTypeStr == "multipart/form-data" {
- optionName = "api.form"
- }
- if optionName != "" {
- field.Options = append(field.Options, &thrift.Option{
- Name: optionName,
- Value: fmt.Sprintf("%q", field.Name),
- })
- }
- }
- c.addFieldIfNotExists(&message.Fields, field)
- }
- case *thrift.ThriftEnum:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(mediaTypeStr + "_field"),
- Type: v.Name,
- }
- if c.converterOption.ApiOption {
- var optionName string
- if mediaTypeStr == "application/json" {
- optionName = "api.body"
- } else if mediaTypeStr == "application/x-www-form-urlencoded" || mediaTypeStr == "multipart/form-data" {
- optionName = "api.form"
- }
- if optionName != "" {
- newField.Options = append(newField.Options, &thrift.Option{
- Name: optionName,
- Value: fmt.Sprintf("%q", v.Name),
- })
- }
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(operation.RequestBody.Value, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- newField.Options = append(newField.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- message.Fields = append(message.Fields, newField)
- c.addEnumToThrift(v)
- case *thrift.ThriftUnion:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(mediaTypeStr + "_field"),
- Type: v.Name,
- }
- if c.converterOption.ApiOption {
- var optionName string
- if mediaTypeStr == "application/json" {
- optionName = "api.body"
- } else if mediaTypeStr == "application/x-www-form-urlencoded" || mediaTypeStr == "multipart/form-data" {
- optionName = "api.form"
- }
- if optionName != "" {
- newField.Options = append(newField.Options, &thrift.Option{
- Name: optionName,
- Value: fmt.Sprintf("%q", v.Name),
- })
- }
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(operation.RequestBody.Value, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- newField.Options = append(newField.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- message.Fields = append(message.Fields, newField)
- c.addUnionToThrift(v)
- }
- }
- }
- }
- }
-
- if len(operation.Parameters) > 0 {
- for _, param := range operation.Parameters {
- if param.Value.Schema != nil {
- fieldOrMessage, err := c.ConvertSchemaToThriftType(param.Value.Schema, param.Value.Name, message)
- if err != nil {
- return []string{""}, err
- }
-
- switch v := fieldOrMessage.(type) {
- case *thrift.ThriftField:
- if c.converterOption.ApiOption {
- v.Options = append(v.Options, &thrift.Option{
- Name: "api." + param.Value.In,
- Value: fmt.Sprintf("%q", param.Value.Name),
- })
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(param.Value, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiParameter,
- Value: optionStr,
- }
- v.Options = append(v.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- v.Description = param.Value.Description
- c.addFieldIfNotExists(&message.Fields, v)
- case *thrift.ThriftStruct:
- for _, field := range v.Fields {
- if c.converterOption.ApiOption {
- field.Options = append(field.Options, &thrift.Option{
- Name: "api." + param.Value.In,
- Value: fmt.Sprintf("%q", param.Value.Name),
- })
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(param.Value, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiParameter,
- Value: optionStr,
- }
- field.Options = append(field.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- c.addFieldIfNotExists(&message.Fields, field)
- }
- case *thrift.ThriftEnum:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(param.Value.Name + "_field"),
- Type: v.Name,
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(param.Value, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiParameter,
- Value: optionStr,
- }
- newField.Options = append(newField.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- message.Fields = append(message.Fields, newField)
- c.addEnumToThrift(v)
- case *thrift.ThriftUnion:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(param.Value.Name + "_field"),
- Type: v.Name,
- }
- if c.converterOption.ApiOption {
- newField.Options = append(newField.Options, &thrift.Option{
- Name: "api." + param.Value.In,
- Value: fmt.Sprintf("%q", param.Value.Name),
- })
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(param.Value, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiParameter,
- Value: optionStr,
- }
- newField.Options = append(newField.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- message.Fields = append(message.Fields, newField)
- c.addUnionToThrift(v)
- }
- }
- }
- }
-
- // if there are no fields or messages, return an empty message
- if len(message.Fields) > 0 {
- c.addMessageToThrift(message)
- return []string{message.Name}, nil
- }
-
- return []string{""}, nil
-}
-
-// generateResponseMessage generates a response message for an operation
-func (c *ThriftConverter) generateResponseMessage(operation *openapi3.Operation, methodName string) (string, error) {
- if operation.Responses == nil {
- return "", nil
- }
-
- responses := operation.Responses.Map()
- responseCount := 0
- for _, responseRef := range responses {
- if responseRef.Ref == "" && (responseRef.Value == nil || (len(responseRef.Value.Content) == 0 && len(responseRef.Value.Headers) == 0)) {
- continue
- }
- responseCount++
- }
-
- if responseCount == 1 {
- for _, responseRef := range responses {
- if responseRef.Ref == "" && (responseRef.Value == nil || (len(responseRef.Value.Content) == 0 && len(responseRef.Value.Headers) == 0)) {
- continue
- }
- return c.processSingleResponse("", responseRef, operation, methodName)
- }
- }
-
- if responseCount == 0 {
- return "void", nil
- }
-
- // create a wrapper message for multiple responses
- wrapperMessageName := utils.GetMessageName(operation, methodName, "Response")
- if c.converterOption.NamingOption {
- wrapperMessageName = common.ToPascaleCase(wrapperMessageName)
- }
-
- wrapperMessage := &thrift.ThriftStruct{Name: wrapperMessageName}
-
- emptyFlag := true
-
- for statusCode, responseRef := range responses {
- if responseRef.Ref == "" && (responseRef.Value == nil || len(responseRef.Value.Content) == 0) {
- break
- }
- emptyFlag = false
- messageName, err := c.processSingleResponse(statusCode, responseRef, operation, methodName)
- if err != nil {
- return "", err
- }
-
- name := "Response_" + statusCode
- if c.converterOption.NamingOption {
- name = common.ToSnakeCase(name)
- }
- field := &thrift.ThriftField{
- Name: name,
- Type: messageName,
- }
- wrapperMessage.Fields = append(wrapperMessage.Fields, field)
- }
-
- if emptyFlag {
- // c.AddThriftInclude(emptyThriftFile)
- return "void", nil
- }
-
- c.addMessageToThrift(wrapperMessage)
-
- return wrapperMessage.Name, nil
-}
-
-// processSingleResponse deals with a single response in an operation
-func (c *ThriftConverter) processSingleResponse(statusCode string, responseRef *openapi3.ResponseRef, operation *openapi3.Operation, methodName string) (string, error) {
- if responseRef.Ref != "" {
- return common.ToPascaleCase(utils.ExtractMessageNameFromRef(responseRef.Ref)), nil
- }
-
- response := responseRef.Value
- messageName := utils.GetMessageName(operation, methodName, "Response") + common.ToUpperCase(statusCode)
-
- if c.converterOption.NamingOption {
- messageName = common.ToPascaleCase(messageName)
- }
-
- message := &thrift.ThriftStruct{Name: messageName}
-
- if len(response.Headers) > 0 {
- for headerName, headerRef := range response.Headers {
- if headerRef != nil {
-
- fieldOrMessage, err := c.ConvertSchemaToThriftType(headerRef.Value.Schema, headerName, message)
- if err != nil {
- return "", err
- }
-
- switch v := fieldOrMessage.(type) {
- case *thrift.ThriftField:
- if c.converterOption.ApiOption {
- option := &thrift.Option{
- Name: "api.header",
- Value: fmt.Sprintf("%q", headerName),
- }
- v.Options = append(v.Options, option)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(headerRef.Value, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- v.Options = append(v.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- c.addFieldIfNotExists(&message.Fields, v)
- case *thrift.ThriftStruct:
- for _, field := range v.Fields {
- if c.converterOption.ApiOption {
- option := &thrift.Option{
- Name: "api.header",
- Value: fmt.Sprintf("%q", field.Name),
- }
- field.Options = append(field.Options, option)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(headerRef.Value, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- field.Options = append(field.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- c.addFieldIfNotExists(&message.Fields, field)
- }
- case *thrift.ThriftEnum:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(headerName + "_field"),
- Type: v.Name,
- }
- if c.converterOption.ApiOption {
- option := &thrift.Option{
- Name: "api.header",
- Value: fmt.Sprintf("%q", headerName),
- }
- newField.Options = append(newField.Options, option)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(headerRef.Value, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- newField.Options = append(newField.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- message.Fields = append(message.Fields, newField)
- c.addEnumToThrift(v)
- case *thrift.ThriftUnion:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(headerName + "_field"),
- Type: v.Name,
- }
- if c.converterOption.ApiOption {
- option := &thrift.Option{
- Name: "api.header",
- Value: fmt.Sprintf("%q", headerName),
- }
- newField.Options = append(newField.Options, option)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(headerRef.Value, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- newField.Options = append(newField.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- message.Fields = append(message.Fields, newField)
- c.addUnionToThrift(v)
- }
- }
- }
- }
-
- for mediaTypeStr, mediaType := range response.Content {
- schema := mediaType.Schema
- if schema != nil {
-
- thriftType, err := c.ConvertSchemaToThriftType(schema, common.FormatStr(mediaTypeStr), message)
- if err != nil {
- return "", err
- }
-
- switch v := thriftType.(type) {
- case *thrift.ThriftField:
- if c.converterOption.ApiOption && mediaTypeStr == "application/json" {
- option := &thrift.Option{
- Name: "api.body",
- Value: fmt.Sprintf("%q", v.Name),
- }
- v.Options = append(v.Options, option)
- }
- c.addFieldIfNotExists(&message.Fields, v)
- case *thrift.ThriftStruct:
- for _, field := range v.Fields {
- if c.converterOption.ApiOption && mediaTypeStr == "application/json" {
- option := &thrift.Option{
- Name: "api.body",
- Value: fmt.Sprintf("%q", field.Name),
- }
- field.Options = append(field.Options, option)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(schema, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- field.Options = append(field.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- c.addFieldIfNotExists(&message.Fields, field)
- }
- case *thrift.ThriftEnum:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(mediaTypeStr + "_field"),
- Type: v.Name,
- }
- if c.converterOption.ApiOption && mediaTypeStr == "application/json" {
- option := &thrift.Option{
- Name: "api.body",
- Value: fmt.Sprintf("%q", v.Name),
- }
- newField.Options = append(newField.Options, option)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(schema, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- newField.Options = append(newField.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- message.Fields = append(message.Fields, newField)
- c.addEnumToThrift(v)
- case *thrift.ThriftUnion:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(mediaTypeStr + "_field"),
- Type: v.Name,
- }
- if c.converterOption.ApiOption && mediaTypeStr == "application/json" {
- option := &thrift.Option{
- Name: "api.body",
- Value: fmt.Sprintf("%q", v.Name),
- }
- newField.Options = append(newField.Options, option)
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(schema, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- newField.Options = append(newField.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- message.Fields = append(message.Fields, newField)
- c.addUnionToThrift(v)
- }
- }
- }
-
- if len(message.Fields) > 0 {
- c.addMessageToThrift(message)
- return message.Name, nil
- }
- return "", nil
-}
-
-// ConvertSchemaToThriftType converts an OpenAPI schema to a Thrift field or message
-func (c *ThriftConverter) ConvertSchemaToThriftType(
- schemaRef *openapi3.SchemaRef,
- thriftName string,
- parentMessage *thrift.ThriftStruct,
-) (interface{}, error) {
- var thriftType string
- var result interface{}
-
- // Handle referenced schema
- if schemaRef.Ref != "" {
- name := c.applySnakeCaseNamingOption(utils.ExtractMessageNameFromRef(schemaRef.Ref))
- return &thrift.ThriftField{
- Name: name,
- Type: common.ToPascaleCase(utils.ExtractMessageNameFromRef(schemaRef.Ref)),
- }, nil
- }
-
- // Ensure schema value is valid
- if schemaRef.Value == nil {
- return nil, errors.New("schema type is required")
- }
-
- schema := schemaRef.Value
- description := schema.Description
-
- // Handle oneOf, allOf, anyOf even if schema.Type is nil
- if len(schema.OneOf) > 0 {
- thriftStruct, err := c.handleOneOf(schema.OneOf, thriftName, parentMessage)
- if err != nil {
- return nil, err
- }
- return thriftStruct, nil
- } else if len(schema.AllOf) > 0 {
- thriftStruct, err := c.handleAllOf(schema.AllOf, thriftName, parentMessage)
- if err != nil {
- return nil, err
- }
- return thriftStruct, nil
- } else if len(schema.AnyOf) > 0 {
- thriftStruct, err := c.handleAnyOf(schema.AnyOf, thriftName, parentMessage)
- if err != nil {
- return nil, err
- }
- return thriftStruct, nil
- }
-
- // Process schema type
- switch {
- case schema.Type.Includes("string"):
- if schema.Format == "date" || schema.Format == "date-time" {
- thriftType = "string"
- } else if schema.Format == "byte" || schema.Format == "binary" {
- thriftType = "binary"
- } else if len(schema.Enum) != 0 {
- var name string
- if parentMessage == nil {
- name = thriftName
- } else {
- name = c.applyPascalseCaseNamingOption(common.ToUpperCase(thriftName))
- }
- thriftEnum := &thrift.ThriftEnum{
- Name: name,
- Description: description,
- }
- for i, enumValue := range schema.Enum {
- thriftEnum.Values = append(thriftEnum.Values, &thrift.ThriftEnumValue{
- Index: i,
- Value: enumValue,
- })
- }
- result = thriftEnum
- } else {
- thriftType = "string"
- }
-
- case schema.Type.Includes("integer"):
- if len(schema.Enum) != 0 {
- var name string
- if parentMessage == nil {
- name = thriftName
- } else {
- name = c.applyPascalseCaseNamingOption(common.ToUpperCase(thriftName))
- }
- thriftEnum := &thrift.ThriftEnum{
- Name: name,
- Description: description,
- }
- for i, enumValue := range schema.Enum {
- thriftEnum.Values = append(thriftEnum.Values, &thrift.ThriftEnumValue{
- Index: i,
- Value: enumValue,
- })
- }
- result = thriftEnum
- } else if schema.Format == "int32" {
- thriftType = "i32"
- } else {
- thriftType = "i64"
- }
-
- case schema.Type.Includes("number"):
- if len(schema.Enum) != 0 {
- var name string
- if parentMessage == nil {
- name = thriftName
- } else {
- name = c.applyPascalseCaseNamingOption(common.ToUpperCase(thriftName))
- }
- thriftEnum := &thrift.ThriftEnum{
- Name: name,
- Description: description,
- }
- for i, enumValue := range schema.Enum {
- thriftEnum.Values = append(thriftEnum.Values, &thrift.ThriftEnumValue{
- Index: i,
- Value: enumValue,
- })
- }
- result = thriftEnum
- } else if schema.Format == "float" {
- thriftType = "float"
- } else {
- thriftType = "double"
- }
-
- case schema.Type.Includes("boolean"):
- thriftType = "bool"
-
- case schema.Type.Includes("array"):
- if schema.Items != nil {
- fieldOrMessage, err := c.ConvertSchemaToThriftType(schema.Items, thriftName+"Item", parentMessage)
- if err != nil {
- return nil, err
- }
-
- fieldType := ""
- if field, ok := fieldOrMessage.(*thrift.ThriftField); ok {
- fieldType = field.Type
- } else if nestedMessage, ok := fieldOrMessage.(*thrift.ThriftStruct); ok {
- fieldType = nestedMessage.Name
- c.addMessageToThrift(nestedMessage)
- } else if enum, ok := fieldOrMessage.(*thrift.ThriftEnum); ok {
- fieldType = enum.Name
- c.addEnumToThrift(enum)
- } else if union, ok := fieldOrMessage.(*thrift.ThriftUnion); ok {
- fieldType = union.Name
- c.addUnionToThrift(union)
- }
-
- result = &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(thriftName),
- Type: fieldType,
- Repeated: true,
- Description: description,
- }
- }
-
- case schema.Type.Includes("object"):
-
- // Regular object handling
- var message *thrift.ThriftStruct
- if parentMessage == nil {
- message = &thrift.ThriftStruct{Name: thriftName}
- } else {
- message = &thrift.ThriftStruct{Name: c.applyPascalseCaseNamingOption(common.ToUpperCase(thriftName))}
- }
-
- // Process each property in the object
- for propName, propSchema := range schema.Properties {
- thriftType, err := c.ConvertSchemaToThriftType(propSchema, propName, message)
- if err != nil {
- return nil, err
- }
-
- // Add the converted fields to the message
- if field, ok := thriftType.(*thrift.ThriftField); ok {
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(propSchema, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- field.Options = append(field.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- message.Fields = append(message.Fields, field)
- } else if nestedMessage, ok := thriftType.(*thrift.ThriftStruct); ok {
- var name string
- if c.converterOption.NamingOption {
- name = common.ToSnakeCase(nestedMessage.Name)
- }
- newField := &thrift.ThriftField{
- Name: name + "_field",
- Type: nestedMessage.Name,
- }
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(propSchema.Value, " ")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiProperty,
- Value: optionStr,
- }
- newField.Options = append(newField.Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- c.addMessageToThrift(nestedMessage)
- message.Fields = append(message.Fields, newField)
- } else if enum, ok := thriftType.(*thrift.ThriftEnum); ok {
- c.addEnumToThrift(enum)
- message.Fields = append(message.Fields, &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(propName + "_field"),
- Type: enum.Name,
- })
- } else if union, ok := thriftType.(*thrift.ThriftUnion); ok {
- c.addUnionToThrift(union)
- message.Fields = append(message.Fields, &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(propName + "_field"),
- Type: union.Name,
- })
- }
- }
-
- // Handle additionalProperties if present
- if schema.AdditionalProperties.Schema != nil {
- mapValueType := "string"
- additionalPropMessage, err := c.ConvertSchemaToThriftType(schema.AdditionalProperties.Schema, thriftName+"AdditionalProperties", parentMessage)
- if err != nil {
- return nil, err
- }
- if msg, ok := additionalPropMessage.(*thrift.ThriftStruct); ok {
- mapValueType = msg.Name
- } else if enum, ok := additionalPropMessage.(*thrift.ThriftEnum); ok {
- mapValueType = enum.Name
- }
-
- message.Fields = append(message.Fields, &thrift.ThriftField{
- Name: "additionalProperties",
- Type: "map",
- })
- }
-
- // Set the result as the final message
- message.Description = description
- result = message
- }
-
- // If result is still nil, construct a default ThriftField
- if result == nil {
- result = &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(thriftName),
- Type: thriftType,
- Description: description,
- }
- }
-
- return result, nil
-}
-
-func (c *ThriftConverter) handleOneOf(oneOfSchemas []*openapi3.SchemaRef, thriftName string, parentMessage *thrift.ThriftStruct) (*thrift.ThriftUnion, error) {
- oneOfUnion := &thrift.ThriftUnion{
- Name: c.applyPascalseCaseNamingOption(thriftName + "OneOf"),
- }
-
- for i, schemaRef := range oneOfSchemas {
- fieldName := fmt.Sprintf("%sOption%d", thriftName, i+1)
- thriftType, err := c.ConvertSchemaToThriftType(schemaRef, fieldName, parentMessage)
- if err != nil {
- return nil, err
- }
-
- switch v := thriftType.(type) {
- case *thrift.ThriftField:
- oneOfUnion.Fields = append(oneOfUnion.Fields, v)
- case *thrift.ThriftStruct:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(v.Name + "_field"),
- Type: v.Name,
- }
- c.addMessageToThrift(v)
- oneOfUnion.Fields = append(oneOfUnion.Fields, newField)
- case *thrift.ThriftEnum:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(v.Name + "_field"),
- Type: v.Name,
- }
- c.addEnumToThrift(v)
- oneOfUnion.Fields = append(oneOfUnion.Fields, newField)
- case *thrift.ThriftUnion:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(v.Name + "_field"),
- Type: v.Name,
- }
- c.addUnionToThrift(v)
- oneOfUnion.Fields = append(oneOfUnion.Fields, newField)
- }
- }
-
- return oneOfUnion, nil
-}
-
-func (c *ThriftConverter) handleAllOf(allOfSchemas []*openapi3.SchemaRef, thriftName string, parentMessage *thrift.ThriftStruct) (*thrift.ThriftStruct, error) {
- allOfStruct := &thrift.ThriftStruct{
- Name: c.applyPascalseCaseNamingOption(thriftName + "AllOf"),
- }
-
- for i, schemaRef := range allOfSchemas {
- fieldName := fmt.Sprintf("%sPart%d", thriftName, i+1)
- thriftType, err := c.ConvertSchemaToThriftType(schemaRef, fieldName, parentMessage)
- if err != nil {
- return nil, err
- }
-
- switch v := thriftType.(type) {
- case *thrift.ThriftField:
- allOfStruct.Fields = append(allOfStruct.Fields, v)
- case *thrift.ThriftStruct:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(v.Name + "_field"),
- Type: v.Name,
- }
- c.addMessageToThrift(v)
- allOfStruct.Fields = append(allOfStruct.Fields, newField)
- case *thrift.ThriftEnum:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(v.Name + "_field"),
- Type: v.Name,
- }
- c.addEnumToThrift(v)
- allOfStruct.Fields = append(allOfStruct.Fields, newField)
- case *thrift.ThriftUnion:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(v.Name + "_field"),
- Type: v.Name,
- }
- c.addUnionToThrift(v)
- allOfStruct.Fields = append(allOfStruct.Fields, newField)
- }
- }
-
- return allOfStruct, nil
-}
-
-func (c *ThriftConverter) handleAnyOf(anyOfSchemas []*openapi3.SchemaRef, thriftName string, parentMessage *thrift.ThriftStruct) (*thrift.ThriftStruct, error) {
- anyOfStruct := &thrift.ThriftStruct{
- Name: c.applyPascalseCaseNamingOption(thriftName + "AnyOf"),
- }
-
- for i, schemaRef := range anyOfSchemas {
- fieldName := fmt.Sprintf("%sOption%d", thriftName, i+1)
- thriftType, err := c.ConvertSchemaToThriftType(schemaRef, fieldName, parentMessage)
- if err != nil {
- return nil, err
- }
-
- switch v := thriftType.(type) {
- case *thrift.ThriftField:
- anyOfStruct.Fields = append(anyOfStruct.Fields, v)
- case *thrift.ThriftStruct:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(v.Name + "_field"),
- Type: v.Name,
- }
- c.addMessageToThrift(v)
- anyOfStruct.Fields = append(anyOfStruct.Fields, newField)
- case *thrift.ThriftEnum:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(v.Name + "_field"),
- Type: v.Name,
- }
- c.addEnumToThrift(v)
- anyOfStruct.Fields = append(anyOfStruct.Fields, newField)
- case *thrift.ThriftUnion:
- newField := &thrift.ThriftField{
- Name: c.applySnakeCaseNamingOption(v.Name + "_field"),
- Type: v.Name,
- }
- c.addUnionToThrift(v)
- anyOfStruct.Fields = append(anyOfStruct.Fields, newField)
- }
- }
-
- return anyOfStruct, nil
-}
-
-// applyPascalseCaseNamingOption applies naming convention based on the converter's naming option
-func (c *ThriftConverter) applyPascalseCaseNamingOption(name string) string {
- if c.converterOption.NamingOption {
- return common.ToPascaleCase(name)
- }
- return name
-}
-
-// applySnakeCaseNamingOption applies naming convention based on the converter's naming option
-func (c *ThriftConverter) applySnakeCaseNamingOption(name string) string {
- if c.converterOption.NamingOption {
- return common.ToSnakeCase(name)
- }
- return name
-}
-
-// Add a new method to handle structured extensions
-func (c *ThriftConverter) addExtensionsToProtoOptions() error {
- // Check for x-option in spec extensions
- if xOption, ok := c.spec.Extensions["x-options"]; ok {
- if optionMap, ok := xOption.(map[string]interface{}); ok {
- for key, value := range optionMap {
- c.ThriftFile.Namespace[key] = fmt.Sprintf("%q", value)
- }
- }
- }
-
- // Check for x-option in spec.info.extensions
- if c.spec.Info != nil {
- if xOption, ok := c.spec.Info.Extensions["x-options"]; ok {
- if optionMap, ok := xOption.(map[string]interface{}); ok {
- for key, value := range optionMap {
- c.ThriftFile.Namespace[key] = fmt.Sprintf("%q", value)
- }
- }
- }
- }
-
- return nil
-}
-
-// addMessageToThrift adds a ThriftStruct to the ThriftFile globally
-func (c *ThriftConverter) addMessageToThrift(message *thrift.ThriftStruct) error {
- if message == nil {
- return errors.New("message is nil")
- }
-
- // Check if the message already exists in the ThriftFile
- for _, existingMessage := range c.ThriftFile.Structs {
- if existingMessage.Name == message.Name {
- // Merge fields if the message already exists
- fieldNames := make(map[string]struct{})
- for _, field := range existingMessage.Fields {
- fieldNames[field.Name] = struct{}{}
- }
- for _, newField := range message.Fields {
- if _, exists := fieldNames[newField.Name]; !exists {
- existingMessage.Fields = append(existingMessage.Fields, newField)
- }
- }
- return nil
- }
- }
-
- // Add the message globally
- c.ThriftFile.Structs = append(c.ThriftFile.Structs, message)
- return nil
-}
-
-// addEnumToThrift adds an enum to the ThriftFile
-func (c *ThriftConverter) addEnumToThrift(enum *thrift.ThriftEnum) {
- c.ThriftFile.Enums = append(c.ThriftFile.Enums, enum)
-}
-
-// addUnionToThrift adds a union to the ThriftFile
-func (c *ThriftConverter) addUnionToThrift(union *thrift.ThriftUnion) {
- c.ThriftFile.Unions = append(c.ThriftFile.Unions, union)
-}
-
-// AddThriftInclude adds an include to the ThriftFile
-func (c *ThriftConverter) AddThriftInclude(includeFile string) {
- if c.ThriftFile != nil {
- for _, existingInclude := range c.ThriftFile.Includes {
- if existingInclude == includeFile {
- return
- }
- }
- c.ThriftFile.Includes = append(c.ThriftFile.Includes, includeFile)
- }
-}
-
-// addOptionsToThrift adds options to the Thrift file
-func (c *ThriftConverter) addOptionsToThrift() {
- if len(c.ThriftFile.Services) > 0 {
- if c.converterOption.OpenapiOption {
- optionStr := common.StructToOption(c.spec, "")
-
- schemaOption := &thrift.Option{
- Name: consts.OpenapiDocument,
- Value: optionStr,
- }
- c.ThriftFile.Services[0].Options = append(c.ThriftFile.Services[0].Options, schemaOption)
- c.AddThriftInclude(consts.OpenapiThriftFile)
- }
- }
-}
-
-// addFieldIfNotExists adds a field to Fields if it does not already exist
-func (c *ThriftConverter) addFieldIfNotExists(fields *[]*thrift.ThriftField, field *thrift.ThriftField) {
- for _, existingField := range *fields {
- if existingField.Name == field.Name {
- return
- }
- }
- *fields = append(*fields, field)
-}
-
-// methodExistsInService checks if a method exists in a service
-func (c *ThriftConverter) methodExistsInService(service *thrift.ThriftService, methodName string) bool {
- for _, method := range service.Methods {
- if method.Name == methodName {
- return true
- }
- }
- return false
-}
-
-// findOrCreateService finds or creates a service
-func (c *ThriftConverter) findOrCreateService(serviceName string) *thrift.ThriftService {
- for i := range c.ThriftFile.Services {
- if c.ThriftFile.Services[i].Name == serviceName {
- return c.ThriftFile.Services[i]
- }
- }
-
- // If no existing service is found, create a new one
- newService := &thrift.ThriftService{Name: serviceName}
- c.ThriftFile.Services = append(c.ThriftFile.Services, newService)
- return newService
-}
diff --git a/swagger2idl/example/openapi.yaml b/swagger2idl/example/openapi.yaml
deleted file mode 100644
index 65019ca..0000000
--- a/swagger2idl/example/openapi.yaml
+++ /dev/null
@@ -1,224 +0,0 @@
-# Generated with protoc-gen-http-swagger
-# https://github.com/hertz-contrib/swagger-generate/protoc-gen-http-swagger
-
-openapi: 3.0.3
-info:
- title: example swagger doc
- version: Version from annotation
-servers:
- - url: http://127.0.0.1:8888
- - url: http://127.0.0.1:8889
-paths:
- /body:
- post:
- tags:
- - HelloService1
- operationId: HelloService1_BodyMethod
- parameters:
- - name: query2
- in: query
- description: 'field: query描述'
- schema:
- type: string
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/BodyReqBody'
- responses:
- "200":
- description: HelloResp描述
- headers:
- token:
- schema:
- type: string
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/HelloRespBody'
- servers:
- - url: http://127.0.0.1:8888
- /form:
- post:
- tags:
- - HelloService1
- operationId: HelloService1_FormMethod
- requestBody:
- content:
- multipart/form-data:
- schema:
- $ref: '#/components/schemas/FormReqForm'
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/FormReqForm'
- responses:
- "200":
- description: HelloResp描述
- headers:
- token:
- schema:
- type: string
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/HelloRespBody'
- servers:
- - url: http://127.0.0.1:8888
- /hello1:
- get:
- tags:
- - HelloService1
- operationId: HelloService1_QueryMethod1
- parameters:
- - name: query1
- in: query
- schema:
- type: object
- additionalProperties:
- type: string
- - name: items
- in: query
- schema:
- type: array
- items:
- type: string
- - name: query2
- in: query
- description: QueryValue描述
- required: true
- schema:
- title: Name
- maxLength: 50
- minLength: 1
- type: string
- description: Name
- responses:
- "200":
- description: HelloResp描述
- headers:
- token:
- schema:
- type: string
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/HelloRespBody'
- servers:
- - url: http://127.0.0.1:8888
- /hello2:
- get:
- tags:
- - HelloService2
- summary: Hello - Get
- description: Hello - Get
- operationId: HelloService2_QueryMethod2
- parameters:
- - name: query1
- in: query
- schema:
- type: object
- additionalProperties:
- type: string
- - name: items
- in: query
- schema:
- type: array
- items:
- type: string
- - name: query2
- in: query
- description: QueryValue描述
- required: true
- schema:
- title: Name
- maxLength: 50
- minLength: 1
- type: string
- description: Name
- responses:
- "200":
- description: HelloResp描述
- headers:
- token:
- schema:
- type: string
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/HelloRespBody'
- servers:
- - url: http://127.0.0.1:8889
- /path{path1}:
- get:
- tags:
- - HelloService1
- operationId: HelloService1_PathMethod
- parameters:
- - name: path1
- in: path
- description: 'field: path描述'
- required: true
- schema:
- type: string
- responses:
- "200":
- description: HelloResp描述
- headers:
- token:
- schema:
- type: string
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/HelloRespBody'
- servers:
- - url: http://127.0.0.1:8888
-components:
- schemas:
- BodyReqBody:
- type: object
- properties:
- body:
- type: string
- description: 'field: body描述'
- body1:
- type: string
- description: 'field: body1描述'
- FormReqForm:
- title: Hello - request
- required:
- - form1
- type: object
- properties:
- form1:
- title: this is an override field schema title
- maxLength: 255
- type: string
- form2:
- $ref: '#/components/schemas/FormReq_InnerForm'
- description: Hello - request
- FormReq_InnerForm:
- type: object
- properties:
- form3:
- type: string
- description: 内嵌message描述
- HelloRespBody:
- title: Hello - response
- required:
- - body
- type: object
- properties:
- body:
- title: response content
- maxLength: 80
- minLength: 1
- type: string
- description: response content
- description: Hello - response
-tags:
- - name: HelloService1
- description: HelloService1描述
- - name: HelloService2
-x-options:
- go_package: example
\ No newline at end of file
diff --git a/swagger2idl/generate/generate.go b/swagger2idl/generate/generate.go
deleted file mode 100644
index 748bf27..0000000
--- a/swagger2idl/generate/generate.go
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2024 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 generate
-
-// Generator is an interface for generating files
-type Generator interface {
- Generate(fileContent interface{}) (string, error)
-}
diff --git a/swagger2idl/generate/proto_generate.go b/swagger2idl/generate/proto_generate.go
deleted file mode 100644
index 2feac1a..0000000
--- a/swagger2idl/generate/proto_generate.go
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright 2024 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 generate
-
-import (
- "fmt"
- "sort"
- "strconv"
- "strings"
-
- common "github.com/hertz-contrib/swagger-generate/common/utils"
- "github.com/hertz-contrib/swagger-generate/swagger2idl/protobuf"
-)
-
-// ProtoGenerate is used to handle the encoding context
-type ProtoGenerate struct {
- dst *strings.Builder // The target for output
-}
-
-// NewProtoGenerate creates a new ProtoGenerate instance
-func NewProtoGenerate() *ProtoGenerate {
- return &ProtoGenerate{dst: &strings.Builder{}}
-}
-
-// Generate converts the ProtoFile structure into Proto file content
-func (e *ProtoGenerate) Generate(fileContent interface{}) (string, error) {
- protoFile, ok := fileContent.(*protobuf.ProtoFile)
- if !ok {
- return "", fmt.Errorf("invalid type: expected *protobuf.ProtoFile")
- }
-
- e.dst.WriteString("syntax = \"proto3\";\n\n")
- e.dst.WriteString(fmt.Sprintf("package %s;\n\n", protoFile.PackageName))
-
- // Generate imports
- if len(protoFile.Imports) > 0 {
- for _, importFile := range protoFile.Imports {
- e.dst.WriteString(fmt.Sprintf("import \"%s\";\n", importFile))
- }
- e.dst.WriteString("\n")
- }
-
- // Generate file-level options
- if len(protoFile.Options) > 0 {
- for _, value := range protoFile.Options {
- e.dst.WriteString(fmt.Sprintf("option %s = %s;\n", value.Name, value.Value))
- }
- e.dst.WriteString("\n")
- }
-
- // Sort enums by name
- sort.Slice(protoFile.Enums, func(i, j int) bool {
- return protoFile.Enums[i].Name < protoFile.Enums[j].Name
- })
-
- // Generate enums
- for _, enum := range protoFile.Enums {
- if err := e.encodeEnum(enum, 0); err != nil {
- return "", fmt.Errorf("failed to encode enum %s: %w", enum.Name, err)
- }
- }
-
- // Sort messages by name
- sort.Slice(protoFile.Messages, func(i, j int) bool {
- return protoFile.Messages[i].Name < protoFile.Messages[j].Name
- })
-
- if len(protoFile.Messages) > 0 {
- for _, message := range protoFile.Messages {
- if err := e.encodeMessage(message, 0); err != nil {
- return "", fmt.Errorf("failed to encode message %s: %w", message.Name, err)
- }
- }
- }
-
- // Sort services by name
- sort.Slice(protoFile.Services, func(i, j int) bool {
- return protoFile.Services[i].Name < protoFile.Services[j].Name
- })
-
- // Generate services
- for _, service := range protoFile.Services {
- if err := e.encodeService(service); err != nil {
- return "", fmt.Errorf("failed to encode service %s: %w", service.Name, err)
- }
- }
-
- return e.dst.String(), nil
-}
-
-// encodeService encodes service types
-func (e *ProtoGenerate) encodeService(service *protobuf.ProtoService) error {
- if service.Description != "" {
- e.dst.WriteString(fmt.Sprintf("// %s\n", service.Description))
- }
- e.dst.WriteString(fmt.Sprintf("service %s {\n", service.Name))
-
- // Sort methods by name
- sort.Slice(service.Methods, func(i, j int) bool {
- return service.Methods[i].Name < service.Methods[j].Name
- })
-
- for _, method := range service.Methods {
- if method.Description != "" {
- e.dst.WriteString(fmt.Sprintf(" // %s\n", method.Description))
- }
- e.dst.WriteString(fmt.Sprintf(" rpc %s(%s) returns (%s)", method.Name, method.Input, method.Output))
- if len(method.Options) > 0 {
- sort.Slice(method.Options, func(i, j int) bool {
- return method.Options[i].Name < method.Options[j].Name
- })
- e.dst.WriteString(" {\n")
- for _, option := range method.Options {
- e.dst.WriteString(" option ")
- if err := e.encodeFieldOption(option); err != nil {
- return fmt.Errorf("failed to encode option for method %s: %w", method.Name, err)
- }
- e.dst.WriteString(";\n")
- }
- e.dst.WriteString(" }\n")
- } else {
- e.dst.WriteString(";\n")
- }
- }
- e.dst.WriteString("}\n\n")
- return nil
-}
-
-// encodeMessage recursively encodes messages, including nested messages, enums, and oneofs
-func (e *ProtoGenerate) encodeMessage(message *protobuf.ProtoMessage, indentLevel int) error {
- if indentLevel > 0 {
- e.dst.WriteString("\n")
- }
- indent := strings.Repeat(" ", indentLevel)
- if message.Description != "" {
- e.dst.WriteString(fmt.Sprintf("%s// %s\n", indent, message.Description))
- }
- e.dst.WriteString(fmt.Sprintf("%smessage %s {\n", indent, message.Name))
-
- // Generate message-level options
- if len(message.Options) > 0 {
- sort.Slice(message.Options, func(i, j int) bool {
- return message.Options[i].Name < message.Options[j].Name
- })
- e.dst.WriteString(fmt.Sprintf("%s option", indent))
- for _, option := range message.Options {
- if err := e.encodeFieldOption(option); err != nil {
- return fmt.Errorf("failed to encode option for message %s: %w", message.Name, err)
- }
- e.dst.WriteString(";\n")
- }
- }
-
- // Sort fields by name
- sort.Slice(message.Fields, func(i, j int) bool {
- return message.Fields[i].Name < message.Fields[j].Name
- })
-
- // Generate fields
- for i, field := range message.Fields {
- err := e.encodeField(field, i+1, indentLevel)
- if err != nil {
- return fmt.Errorf("failed to encode field %s: %w", field.Name, err)
- }
- }
-
- // Generate oneofs
- for _, oneOf := range message.OneOfs {
- err := e.encodeOneOf(oneOf, indentLevel+1, len(message.Fields)+1)
- if err != nil {
- return fmt.Errorf("failed to encode oneof %s: %w", oneOf.Name, err)
- }
- }
-
- // Generate nested enums
- if len(message.Enums) > 0 {
- e.dst.WriteString("\n")
- for _, nestedEnum := range message.Enums {
- if err := e.encodeEnum(nestedEnum, indentLevel+1); err != nil {
- return fmt.Errorf("failed to encode nested enum %s: %w", nestedEnum.Name, err)
- }
- }
- }
-
- // Generate nested messages
- for _, nestedMessage := range message.Messages {
- if err := e.encodeMessage(nestedMessage, indentLevel+1); err != nil {
- return fmt.Errorf("failed to encode nested message %s: %w", nestedMessage.Name, err)
- }
- }
-
- e.dst.WriteString(fmt.Sprintf("%s}\n\n", indent))
- return nil
-}
-
-// encodeEnum encodes enum types
-func (e *ProtoGenerate) encodeEnum(enum *protobuf.ProtoEnum, indentLevel int) error {
- indent := strings.Repeat(" ", indentLevel)
- e.dst.WriteString(fmt.Sprintf("%senum %s {\n", indent, enum.Name))
-
- // Generate enum values
- for _, value := range enum.Values {
- valueStr := fmt.Sprintf("%v", value.Value)
- enumValueName := valueStr
- if _, err := strconv.Atoi(valueStr); err == nil {
- enumValueName = fmt.Sprintf("%s%s", enum.Name, valueStr)
- }
- enumValueName = strings.ToUpper(common.FormatStr(enumValueName))
- e.dst.WriteString(fmt.Sprintf("%s %s = %d;\n", indent, enumValueName, value.Index))
- }
-
- e.dst.WriteString(fmt.Sprintf("%s}\n\n", indent))
- return nil
-}
-
-// encodeField encodes a single field in the message.
-func (e *ProtoGenerate) encodeField(field *protobuf.ProtoField, fieldNumber, indentLevel int) error {
- indent := strings.Repeat(" ", indentLevel)
-
- // Add field description if present
- if field.Description != "" {
- e.dst.WriteString(fmt.Sprintf("%s // %s\n", indent, field.Description))
- }
-
- // Determine if the field is repeated
- repeated := ""
- if field.Repeated {
- repeated = "repeated "
- }
-
- // Write the field definition
- e.dst.WriteString(fmt.Sprintf("%s %s%s %s = %d", indent, repeated, field.Type, common.FormatStr(field.Name), fieldNumber))
-
- // Generate field-level options if present
- if len(field.Options) > 0 {
- sort.Slice(field.Options, func(i, j int) bool {
- return field.Options[i].Name < field.Options[j].Name
- })
- e.dst.WriteString(" [\n ")
- for j, option := range field.Options {
- if err := e.encodeFieldOption(option); err != nil {
- return fmt.Errorf("failed to encode option for field %s: %w", field.Name, err)
- }
- if j < len(field.Options)-1 {
- e.dst.WriteString(",\n ")
- }
- }
- e.dst.WriteString("\n ]")
- }
-
- e.dst.WriteString(";\n")
- return nil
-}
-
-// encodeOneOf encodes oneof types
-func (e *ProtoGenerate) encodeOneOf(oneOf *protobuf.ProtoOneOf, indentLevel, fieldNumber int) error {
- indent := strings.Repeat(" ", indentLevel)
- e.dst.WriteString(fmt.Sprintf("%soneof %s {\n", indent, oneOf.Name))
- // Generate oneof fields
- for _, field := range oneOf.Fields {
- e.dst.WriteString(fmt.Sprintf("%s %s %s = %d;\n", indent, field.Type, field.Name, fieldNumber))
- fieldNumber++
- }
-
- e.dst.WriteString(fmt.Sprintf("%s}\n", indent))
- return nil
-}
-
-// encodeFieldOption encodes an option for a single field
-func (e *ProtoGenerate) encodeFieldOption(opt *protobuf.Option) error {
- // Output the option name
- fmt.Fprintf(e.dst, "(%s) = ", opt.Name) // Add indentation for consistency
-
- // Check if the option value is a complex structure
- switch value := opt.Value.(type) {
- case map[string]interface{}:
- // If it's a map type, it needs to output as a nested structure
- fmt.Fprintf(e.dst, "{\n") // Newline after {
- e.encodeFieldOptionMap(value, 6) // Output map content, passing the current indentation level
- fmt.Fprintf(e.dst, " }") // Indent and output the closing }, with the appropriate indentation level
- default:
- fmt.Fprintf(e.dst, "%s", value) // For simple types, output directly
- }
-
- return nil
-}
-
-// encodeFieldOptionMap encodes a complex map type option value
-func (e *ProtoGenerate) encodeFieldOptionMap(optionMap map[string]interface{}, indent int) error {
- keys := make([]string, 0, len(optionMap))
- for k := range optionMap {
- keys = append(keys, k)
- }
- sort.Strings(keys) // Sort keys to ensure consistent output order
-
- indentSpace := strings.Repeat(" ", indent) // Dynamically generate indent spaces
-
- for _, key := range keys {
- value := optionMap[key]
- // Output key-value pairs with appropriate indentation
- fmt.Fprintf(e.dst, "%s%s: %s", indentSpace, key, common.Stringify(value)) // Add deeper indentation
- // Don't add a semicolon after the last item, maintain correct format
- fmt.Fprintf(e.dst, ";\n")
- }
-
- return nil
-}
diff --git a/swagger2idl/generate/thrift_generate.go b/swagger2idl/generate/thrift_generate.go
deleted file mode 100644
index bfaa0d6..0000000
--- a/swagger2idl/generate/thrift_generate.go
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright 2024 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 generate
-
-import (
- "fmt"
- "sort"
- "strconv"
- "strings"
-
- common "github.com/hertz-contrib/swagger-generate/common/utils"
- "github.com/hertz-contrib/swagger-generate/swagger2idl/thrift"
-)
-
-// ThriftGenerate handles the encoding context for Thrift files
-type ThriftGenerate struct {
- dst *strings.Builder // Output destination
-}
-
-// NewThriftGenerate creates a new instance of ThriftGenerate
-func NewThriftGenerate() *ThriftGenerate {
- return &ThriftGenerate{dst: &strings.Builder{}}
-}
-
-// Generate converts a ThriftFile structure into Thrift file content
-func (e *ThriftGenerate) Generate(fileContent interface{}) (string, error) {
- thriftFile, ok := fileContent.(*thrift.ThriftFile)
- if !ok {
- return "", fmt.Errorf("invalid type: expected *ThriftFile")
- }
-
- if len(thriftFile.Namespace) == 0 {
- e.dst.WriteString("namespace go example\n\n")
- } else {
- for language, ns := range thriftFile.Namespace {
- e.dst.WriteString(fmt.Sprintf("namespace %s %s\n", language, ns))
- }
- e.dst.WriteString("\n")
- }
-
- // Generate includes
- if len(thriftFile.Includes) > 0 {
- for _, include := range thriftFile.Includes {
- e.dst.WriteString(fmt.Sprintf("include \"%s\"\n", include))
- }
-
- e.dst.WriteString("\n")
- }
-
- // sort the enums
- sort.Slice(thriftFile.Enums, func(i, j int) bool {
- return thriftFile.Enums[i].Name < thriftFile.Enums[j].Name
- })
-
- // Generate enums
- for _, enum := range thriftFile.Enums {
- e.encodeEnum(enum, 0)
- }
-
- // sort the structs
- sort.Slice(thriftFile.Structs, func(i, j int) bool {
- return thriftFile.Structs[i].Name < thriftFile.Structs[j].Name
- })
-
- // Generate structs
- for _, message := range thriftFile.Structs {
- e.encodeMessage(message, 0)
- }
-
- // sort the unions
- sort.Slice(thriftFile.Unions, func(i, j int) bool {
- return thriftFile.Unions[i].Name < thriftFile.Unions[j].Name
- })
-
- // Generate unions
- for _, union := range thriftFile.Unions {
- e.encodeUnion(union, 0)
- }
-
- // sort the services
- sort.Slice(thriftFile.Services, func(i, j int) bool {
- return thriftFile.Services[i].Name < thriftFile.Services[j].Name
- })
-
- // Generate services
- for _, service := range thriftFile.Services {
- e.encodeService(service)
- }
-
- return e.dst.String(), nil
-}
-
-// encodeService encodes service definitions
-func (e *ThriftGenerate) encodeService(service *thrift.ThriftService) {
- if service.Description != "" {
- e.dst.WriteString(fmt.Sprintf("// %s\n", service.Description))
- }
-
- e.dst.WriteString(fmt.Sprintf("service %s {\n", service.Name)) // Service declaration
-
- // Methods
- for _, method := range service.Methods {
- e.encodeMethod(method)
- }
-
- e.dst.WriteString("}")
-
- // Service options
- if len(service.Options) > 0 {
- e.dst.WriteString("(")
- for i, option := range service.Options {
- if i > 0 {
- e.dst.WriteString(", ")
- }
- e.encodeOption(option)
- }
- e.dst.WriteString(")\n")
- } else {
- e.dst.WriteString("\n")
- }
-
- e.dst.WriteString("\n")
-}
-
-// encodeMethod encodes methods within a service
-func (e *ThriftGenerate) encodeMethod(method *thrift.ThriftMethod) {
- if method.Description != "" {
- e.dst.WriteString(fmt.Sprintf(" // %s\n", method.Description))
- }
-
- e.dst.WriteString(fmt.Sprintf(" %s %s (", method.Output, method.Name)) // Method signature
-
- // Input parameters
- for i, input := range method.Input {
- if i > 0 {
- e.dst.WriteString(", ")
- }
- e.dst.WriteString(fmt.Sprintf("%d: %s req", i+1, input))
- }
-
- e.dst.WriteString(")")
-
- // Method options
- if len(method.Options) > 0 {
- e.dst.WriteString(" (\n")
- for i, option := range method.Options {
- if i > 0 {
- e.dst.WriteString(",\n")
- }
- e.dst.WriteString(" ")
- e.encodeOption(option)
- }
- e.dst.WriteString("\n )")
- }
-
- e.dst.WriteString("\n")
-}
-
-// encodeMessage recursively encodes structs, including nested structs and enums
-func (e *ThriftGenerate) encodeMessage(message *thrift.ThriftStruct, indentLevel int) {
- indent := strings.Repeat(" ", indentLevel)
-
- if message.Description != "" {
- e.dst.WriteString(fmt.Sprintf("%s// %s\n", indent, message.Description))
- }
-
- e.dst.WriteString(fmt.Sprintf("%sstruct %s {\n", indent, message.Name))
-
- // Fields: Traverse the fields and assign indexes
- for i, field := range message.Fields {
- e.encodeField(field, i+1, indentLevel+1) // Use 1-based indexing with `i+1`
- }
-
- e.dst.WriteString(fmt.Sprintf("%s}", indent))
-
- // Struct options
- if len(message.Options) > 0 {
- e.dst.WriteString(indent + "(\n")
- for i, option := range message.Options {
- if i > 0 {
- e.dst.WriteString(",\n")
- }
- e.dst.WriteString(indent + " ") // Increase indentation
- e.encodeOption(option)
- }
- e.dst.WriteString("\n" + indent + ")\n")
- } else {
- e.dst.WriteString("\n")
- }
-
- e.dst.WriteString("\n")
-}
-
-// encodeUnion encodes a Thrift union
-func (e *ThriftGenerate) encodeUnion(union *thrift.ThriftUnion, indentLevel int) {
- indent := strings.Repeat(" ", indentLevel)
-
- e.dst.WriteString(fmt.Sprintf("%sunion %s {\n", indent, union.Name))
-
- // Traverse union fields
- for i, field := range union.Fields {
- e.encodeField(field, i+1, indentLevel+1) // Use 1-based indexing with `i+1`
- }
-
- e.dst.WriteString(fmt.Sprintf("%s}\n\n", indent))
-}
-
-// encodeEnum encodes enum types
-func (e *ThriftGenerate) encodeEnum(enum *thrift.ThriftEnum, indentLevel int) {
- indent := strings.Repeat(" ", indentLevel)
-
- e.dst.WriteString(fmt.Sprintf("%senum %s {\n", indent, enum.Name))
-
- for _, value := range enum.Values {
- valueStr := fmt.Sprintf("%v", value.Value) // Convert the value to a string
-
- // Check if the value is numeric and generate a name if needed
- enumValueName := valueStr
- if _, err := strconv.Atoi(valueStr); err == nil {
- enumValueName = fmt.Sprintf("%s%s", enum.Name, valueStr)
- }
-
- enumValueName = strings.ToUpper(common.FormatStr(enumValueName))
-
- e.dst.WriteString(fmt.Sprintf("%s %s = %d;\n", indent, enumValueName, value.Index))
- }
-
- e.dst.WriteString(fmt.Sprintf("%s}\n\n", indent))
-}
-
-// encodeField encodes a single field within a struct
-func (e *ThriftGenerate) encodeField(field *thrift.ThriftField, index, indentLevel int) {
- indent := strings.Repeat(" ", indentLevel)
-
- if field.Description != "" {
- e.dst.WriteString(fmt.Sprintf("%s// %s\n", indent, field.Description))
- }
-
- // Field index and type
- fieldType := field.Type
- if field.Repeated {
- fieldType = fmt.Sprintf("list<%s>", field.Type)
- }
-
- // Handle optional fields
- optionalFlag := ""
- if field.Optional {
- optionalFlag = "optional "
- }
-
- // Assign the provided index to the field
- e.dst.WriteString(fmt.Sprintf("%s%d: %s%s %s", indent, index, optionalFlag, fieldType, common.FormatStr(field.Name)))
-
- // Field options
- if len(field.Options) > 0 {
- e.dst.WriteString(" (")
- for i, option := range field.Options {
- if i > 0 {
- e.dst.WriteString(",\n" + indent)
- }
- e.encodeOption(option)
- }
- e.dst.WriteString(")\n") // Close parentheses aligned with the field
- } else {
- e.dst.WriteString("\n")
- }
-}
-
-// encodeOption handles the encoding of options for methods, structs, and fields
-func (e *ThriftGenerate) encodeOption(option *thrift.Option) {
- // If the option key starts with "api", use double quotes for the value
- if strings.HasPrefix(option.Name, "api.") {
- e.dst.WriteString(fmt.Sprintf("%s = %s", option.Name, option.Value))
- } else if strings.HasPrefix(option.Name, "openapi.") {
- e.dst.WriteString(fmt.Sprintf("%s = '%s'", option.Name, option.Value))
- } else {
- e.dst.WriteString(fmt.Sprintf("%s = %s", option.Name, option.Value))
- }
-}
diff --git a/swagger2idl/go.mod b/swagger2idl/go.mod
deleted file mode 100644
index bff41fd..0000000
--- a/swagger2idl/go.mod
+++ /dev/null
@@ -1,26 +0,0 @@
-module github.com/hertz-contrib/swagger-generate/swagger2idl
-
-go 1.18
-
-require (
- github.com/getkin/kin-openapi v0.127.0
- github.com/hertz-contrib/swagger-generate v0.0.0-00010101000000-000000000000
- github.com/urfave/cli/v2 v2.27.4
-)
-
-require (
- github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
- github.com/go-openapi/jsonpointer v0.21.0 // indirect
- github.com/go-openapi/swag v0.23.0 // indirect
- github.com/iancoleman/strcase v0.3.0 // indirect
- github.com/invopop/yaml v0.3.1 // indirect
- github.com/josharian/intern v1.0.0 // indirect
- github.com/mailru/easyjson v0.7.7 // indirect
- github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
- github.com/perimeterx/marshmallow v1.1.5 // indirect
- github.com/russross/blackfriday/v2 v2.1.0 // indirect
- github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
- gopkg.in/yaml.v3 v3.0.1 // indirect
-)
-
-replace github.com/hertz-contrib/swagger-generate => ../../swagger-generate
diff --git a/swagger2idl/go.sum b/swagger2idl/go.sum
deleted file mode 100644
index ca53ed7..0000000
--- a/swagger2idl/go.sum
+++ /dev/null
@@ -1,38 +0,0 @@
-github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
-github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/getkin/kin-openapi v0.127.0 h1:Mghqi3Dhryf3F8vR370nN67pAERW+3a95vomb3MAREY=
-github.com/getkin/kin-openapi v0.127.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM=
-github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
-github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
-github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
-github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
-github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
-github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
-github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
-github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
-github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
-github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
-github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
-github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
-github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
-github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
-github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
-github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
-github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
-github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8=
-github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ=
-github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
-github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/swagger2idl/main.go b/swagger2idl/main.go
deleted file mode 100644
index 5b60b9d..0000000
--- a/swagger2idl/main.go
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright 2024 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 (
- "log"
- "os"
- "path/filepath"
-
- "github.com/hertz-contrib/swagger-generate/common/consts"
- "github.com/hertz-contrib/swagger-generate/swagger2idl/converter"
- "github.com/hertz-contrib/swagger-generate/swagger2idl/generate"
- "github.com/hertz-contrib/swagger-generate/swagger2idl/parser"
- "github.com/urfave/cli/v2"
-)
-
-var (
- outputType string
- outputFile string
- openapiOption bool
- apiOption bool
- namingOption bool
-)
-
-func main() {
- app := &cli.App{
- Name: "swagger2idl",
- Usage: "Convert OpenAPI specs to Protobuf or Thrift IDL",
- Flags: []cli.Flag{
- &cli.StringFlag{
- Name: "type",
- Aliases: []string{"t"},
- Usage: "Specify output type: 'proto' or 'thrift'. If not provided, inferred from output file extension.",
- Destination: &outputType,
- },
- &cli.StringFlag{
- Name: "output",
- Aliases: []string{"o"},
- Usage: "Specify output file path. If not provided, defaults to output.proto or output.thrift based on the output type.",
- Destination: &outputFile,
- },
- &cli.BoolFlag{
- Name: "openapi",
- Aliases: []string{"oa"},
- Usage: "Include OpenAPI specific options in the output",
- Destination: &openapiOption,
- },
- &cli.BoolFlag{
- Name: "api",
- Aliases: []string{"a"},
- Usage: "Include API specific options in the output",
- Destination: &apiOption,
- },
- &cli.BoolFlag{
- Name: "naming",
- Aliases: []string{"n"},
- Usage: "use naming conventions for the output IDL file",
- Value: true,
- Destination: &namingOption,
- },
- },
- Action: func(c *cli.Context) error {
- args := c.Args().Slice()
-
- if len(args) < 1 {
- log.Fatal("Please provide the path to the OpenAPI file.")
- }
-
- openapiFile := args[0]
-
- // Automatically determine output type based on file extension if not provided
- if outputType == "" && outputFile != "" {
- ext := filepath.Ext(outputFile)
- switch ext {
- case ".proto":
- outputType = consts.IDLProto
- case ".thrift":
- outputType = consts.IDLThrift
- default:
- log.Fatalf("Cannot determine output type from file extension: %s. Use --type to specify explicitly.", ext)
- }
- }
-
- if outputFile == "" {
- if outputType == consts.IDLProto {
- outputFile = consts.DefaultProtoFilename
- } else if outputType == consts.IDLThrift {
- outputFile = consts.DefaultThriftFilename
- } else {
- log.Fatal("Output file must be specified if output type is not provided.")
- }
- }
-
- spec, err := parser.LoadOpenAPISpec(openapiFile)
- if err != nil {
- log.Fatalf("Failed to load OpenAPI file: %v", err)
- }
-
- converterOption := &converter.ConvertOption{
- OpenapiOption: openapiOption,
- ApiOption: apiOption,
- NamingOption: namingOption,
- }
-
- var idlContent string
- var file *os.File
- var errFile error
-
- switch outputType {
- case consts.IDLProto:
- protoConv := converter.NewProtoConverter(spec, converterOption)
-
- if err = protoConv.Convert(); err != nil {
- log.Fatalf("Error during conversion: %v", err)
- }
- protoEngine := generate.NewProtoGenerate()
-
- idlContent, err = protoEngine.Generate(protoConv.GetIdl())
- if err != nil {
- log.Fatalf("Error generating proto docs: %v", err)
- }
-
- file, errFile = os.Create(outputFile)
- case consts.IDLThrift:
- thriftConv := converter.NewThriftConverter(spec, converterOption)
-
- if err = thriftConv.Convert(); err != nil {
- log.Fatalf("Error during conversion: %v", err)
- }
- thriftEngine := generate.NewThriftGenerate()
-
- idlContent, err = thriftEngine.Generate(thriftConv.GetIdl())
- if err != nil {
- log.Fatalf("Error generating thrift docs: %v", err)
- }
-
- file, errFile = os.Create(outputFile)
- default:
- log.Fatalf("Invalid output type: %s", outputType)
- }
-
- if errFile != nil {
- log.Fatalf("Failed to create file: %v", errFile)
- }
- defer func() {
- if err := file.Close(); err != nil {
- log.Printf("Error closing file: %v", err)
- }
- }()
-
- if _, err = file.WriteString(idlContent); err != nil {
- log.Fatalf("Error writing to file: %v", err)
- }
-
- return nil
- },
- }
-
- err := app.Run(os.Args)
- if err != nil {
- log.Fatal(err)
- }
-}
diff --git a/swagger2idl/parser/parser.go b/swagger2idl/parser/parser.go
deleted file mode 100644
index 7712ca4..0000000
--- a/swagger2idl/parser/parser.go
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2024 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 parser
-
-import (
- "fmt"
- "os"
-
- "github.com/getkin/kin-openapi/openapi3"
-)
-
-// LoadOpenAPISpec parses an OpenAPI spec from a file and returns it.
-func LoadOpenAPISpec(filePath string) (*openapi3.T, error) {
- loader := openapi3.NewLoader()
- var err error
- var spec *openapi3.T
-
- if _, err = os.Stat(filePath); os.IsNotExist(err) {
- return nil, fmt.Errorf("file %s does not exist", filePath)
- }
-
- spec, err = loader.LoadFromFile(filePath)
- if err != nil {
- return nil, fmt.Errorf("failed to load OpenAPI spec: %v", err)
- }
-
- if err = spec.Validate(loader.Context); err != nil {
- return nil, fmt.Errorf("failed to validate OpenAPI spec: %v", err)
- }
-
- return spec, nil
-}
diff --git a/swagger2idl/protobuf/protobuf.go b/swagger2idl/protobuf/protobuf.go
deleted file mode 100644
index eb52a14..0000000
--- a/swagger2idl/protobuf/protobuf.go
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2024 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 protobuf
-
-// ProtoFile represents a complete Proto file
-type ProtoFile struct {
- PackageName string // The package name of the Proto file
- Messages []*ProtoMessage // List of Proto messages
- Services []*ProtoService // List of Proto services
- Enums []*ProtoEnum // List of Proto enums
- Imports []string // List of imported Proto files
- Options []*Option // File-level options
-}
-
-// ProtoService represents a Proto service
-type ProtoService struct {
- Name string // Name of the service
- Description string // Description for the service
- Methods []*ProtoMethod // List of methods in the service
- Options []*Option // Service-level options
-}
-
-// ProtoMethod represents a method in a Proto service
-type ProtoMethod struct {
- Name string // Name of the method
- Description string // Description for the method
- Input string // Input message type
- Output string // Output message type
- Options []*Option // Options for the method
-}
-
-// ProtoMessage represents a Proto message
-type ProtoMessage struct {
- Name string
- Description string // Description for the Proto message
- Fields []*ProtoField // List of fields in the Proto message
- Messages []*ProtoMessage // Nested Proto messages
- Enums []*ProtoEnum // Enums within the Proto message
- OneOfs []*ProtoOneOf // OneOfs within the Proto message
- Options []*Option // Options specific to this Proto message
-}
-
-// ProtoField represents a field in a Proto message
-type ProtoField struct {
- Name string // Name of the field
- Type string // Type of the field
- Description string // Description for the field
- Repeated bool // Indicates if the field is repeated (array)
- Options []*Option // Additional options for this field
-}
-
-// Option represents an option in a Proto field or message
-type Option struct {
- Name string // Name of the option
- Value interface{} // Value of the option
-}
-
-// ProtoEnum represents a Proto enum
-type ProtoEnum struct {
- Name string // Name of the enum
- Description string // Description for the enum
- Values []*ProtoEnumValue // Values within the enum
- Options []*Option // Enum-level options
-}
-
-// ProtoEnumValue represents a value in a Proto enum
-type ProtoEnumValue struct {
- Index int // index of the enum value
- Value any // Corresponding integer value for the enum
-}
-
-// ProtoOneOf represents a oneof in a Proto message
-type ProtoOneOf struct {
- Name string // Name of the oneof
- Fields []*ProtoField // List of fields in the oneof
- Options []*Option // Options specific to this oneof
-}
diff --git a/swagger2idl/thrift/thrift.go b/swagger2idl/thrift/thrift.go
deleted file mode 100644
index 45947d4..0000000
--- a/swagger2idl/thrift/thrift.go
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2024 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 thrift
-
-// ThriftFile represents a complete Thrift file
-type ThriftFile struct {
- Namespace map[string]string // Namespace for the Thrift file
- Includes []string // List of included Thrift files
- Structs []*ThriftStruct // List of Thrift structs
- Unions []*ThriftUnion // List of Thrift unions
- Enums []*ThriftEnum // List of Thrift enums
- Services []*ThriftService // List of Thrift services
-}
-
-// ThriftService represents a Thrift service
-type ThriftService struct {
- Name string // Name of the service
- Description string // Description of the service
- Methods []*ThriftMethod // List of methods in the service
- Options []*Option // Service-level options
-}
-
-// ThriftMethod represents a method in a Thrift service
-type ThriftMethod struct {
- Name string // Name of the method
- Description string // Description of the method
- Input []string // List of input fields for the method
- Output string // Output field for the method
- Options []*Option // Options for the method
-}
-
-// ThriftStruct represents a Thrift struct
-type ThriftStruct struct {
- Name string // Name of the struct
- Description string // Description of the struct
- Fields []*ThriftField // List of fields in the struct
- Options []*Option // Options specific to this struct
-}
-
-// ThriftField represents a field in a Thrift struct or union
-type ThriftField struct {
- ID int // Field ID for Thrift
- Name string // Name of the field
- Description string // Description of the field
- Type string // Type of the field (Thrift types)
- Optional bool // Indicates if the field is optional
- Repeated bool // Indicates if the field is repeated (list)
- Options []*Option // Additional options for this field
-}
-
-// ThriftUnion represents a Thrift union (similar to a struct but only one field can be set at a time)
-type ThriftUnion struct {
- Name string // Name of the union
- Fields []*ThriftField // List of fields in the union
- Options []*Option // Options specific to this union
-}
-
-// ThriftEnum represents a Thrift enum
-type ThriftEnum struct {
- Name string // Name of the enum
- Description string // Description of the enum
- Values []*ThriftEnumValue // Values within the enum
- Options []*Option // Enum-level options
-}
-
-// ThriftEnumValue represents a value in a Thrift enum
-type ThriftEnumValue struct {
- Index int // Index of the enum value
- Value any // Enum values are integers in Thrift
-}
-
-// Option represents an option in a Thrift field or struct
-type Option struct {
- Name string // Name of the option
- Value interface{} // Value of the option
-}
diff --git a/swagger2idl/utils/utils.go b/swagger2idl/utils/utils.go
deleted file mode 100644
index b347840..0000000
--- a/swagger2idl/utils/utils.go
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2024 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 utils
-
-import (
- "regexp"
- "strings"
-
- "github.com/getkin/kin-openapi/openapi3"
- "github.com/hertz-contrib/swagger-generate/common/utils"
-)
-
-// GetMethodName generates a method name from the OpenAPI spec
-func GetMethodName(operation *openapi3.Operation, path, method string) string {
- if operation.OperationID != "" {
- return operation.OperationID
- }
- if operation.Tags != nil {
- return operation.Tags[0]
- }
- if path != "" {
- // Convert path to PascalCase, replacing placeholders with a suitable format
- convertedPath := ConvertPathToPascalCase(path)
- return convertedPath + strings.Title(strings.ToLower(method))
- }
- // If no OperationID, generate using HTTP method
- return strings.Title(strings.ToLower(method)) + "Method"
-}
-
-// GetServiceName generates a service name from the OpenAPI spec
-func GetServiceName(operation *openapi3.Operation) string {
- if len(operation.Tags) > 0 {
- return operation.Tags[0]
- }
- return "DefaultService"
-}
-
-// GetMessageName generates a message name from the OpenAPI spec
-func GetMessageName(operation *openapi3.Operation, methodName, suffix string) string {
- if operation.OperationID != "" {
- return operation.OperationID + suffix
- }
- return methodName + suffix
-}
-
-// GetPackageName generates a package name from the OpenAPI spec
-func GetPackageName(spec *openapi3.T) string {
- if spec.Info.Title != "" {
- return utils.FormatStr(utils.ToSnakeCase(spec.Info.Title))
- }
- if spec.Info.Description != "" {
- return utils.FormatStr(utils.ToSnakeCase(spec.Info.Description))
- }
- return "DefaultPackage"
-}
-
-// ConvertPathToPascalCase converts a path with placeholders to PascalCase
-func ConvertPathToPascalCase(path string) string {
- // Replace placeholders like {orderId} with OrderId
- re := regexp.MustCompile(`\{(\w+)\}`)
- path = re.ReplaceAllStringFunc(path, func(s string) string {
- return utils.ToPascaleCase(strings.Trim(s, "{}"))
- })
-
- // Split the path by '/' and convert each segment to PascalCase
- segments := strings.Split(path, "/")
- for i, segment := range segments {
- segments[i] = utils.ToPascaleCase(segment)
- }
-
- // Join the segments back together
- return strings.Join(segments, "")
-}
-
-// ExtractMessageNameFromRef extracts the name of a message from a reference
-func ExtractMessageNameFromRef(ref string) string {
- parts := strings.Split(ref, "/")
- return parts[len(parts)-1] // Return the last part, usually the name of the reference
-}
-
-// ConvertPath converts a path with placeholders to a format that can be used in a URL
-func ConvertPath(path string) string {
- // Regular expression to match content inside {}
- re := regexp.MustCompile(`\{(\w+)\}`)
- // Replace {param} with :param
- result := re.ReplaceAllString(path, ":$1")
- return result
-}