diff --git a/example/seaql/create_table.sql b/example/seaql/create_table.sql index e38d4a1..d4faebc 100644 --- a/example/seaql/create_table.sql +++ b/example/seaql/create_table.sql @@ -1,6 +1,6 @@ -- Code generated by protoc-gen-saber-seaql. DO NOT EDIT. -- versions: --- - protoc-gen-saber-seaql v1.0.0 +-- - protoc-gen-saber-seaql v0.1.0 -- - protoc v4.24.0 -- source: seaql.proto @@ -8,7 +8,7 @@ CREATE TABLE `dict` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '系统序号', - `key` varchar(64) NOT NULL DEFAULT '' COMMENT '名称', + `key` varchar(64) NOT NULL DEFAULT '' COMMENT '键,值', `name` varchar(64) NOT NULL DEFAULT '' COMMENT '名称', `is_pin` tinyint(1) NOT NULL COMMENT '是否锁定', `created_at` datetime NOT NULL COMMENT '创建时间', @@ -26,3 +26,26 @@ CREATE TABLE `created_at` datetime NOT NULL COMMENT '创建时间', `updated_at` datetime NOT NULL COMMENT '更新时间' ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '字典项表'; + +-- 字典表 +CREATE TABLE + `dict_annote` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '系统序号', + `key` varchar(64) NOT NULL DEFAULT '' COMMENT '键', + `name` varchar(64) NOT NULL DEFAULT '' COMMENT '名称', + `is_pin` tinyint(1) NOT NULL COMMENT '是否锁定', + `created_at` datetime NOT NULL COMMENT '创建时间', + `updated_at` datetime NOT NULL COMMENT '更新时间', + PRIMARY KEY (`id`) + ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '字典表'; + +-- 字典项表 +CREATE TABLE + `dict_item_annote` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '系统序号', + `key` varchar(64) NOT NULL DEFAULT '' COMMENT '键', + `name` varchar(64) NOT NULL DEFAULT '' COMMENT '名称', + `status` tinyint(3) unsigned NOT NULL COMMENT '状态,这是状态,[0:unspecified,1:nested1,2:关闭,3:左,4:右]', + `created_at` datetime NOT NULL COMMENT '创建时间', + `updated_at` datetime NOT NULL COMMENT '更新时间' + ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '字典项表'; diff --git a/example/seaql/dict.sql b/example/seaql/dict.sql index ab06fd1..6b320e8 100644 --- a/example/seaql/dict.sql +++ b/example/seaql/dict.sql @@ -1,6 +1,6 @@ -- Code generated by protoc-gen-saber-seaql. DO NOT EDIT. -- versions: --- - protoc-gen-saber-seaql v1.0.0 +-- - protoc-gen-saber-seaql v0.1.0 -- - protoc v4.24.0 -- source: seaql.proto @@ -8,7 +8,7 @@ CREATE TABLE `dict` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '系统序号', - `key` varchar(64) NOT NULL DEFAULT '' COMMENT '名称', + `key` varchar(64) NOT NULL DEFAULT '' COMMENT '键,值', `name` varchar(64) NOT NULL DEFAULT '' COMMENT '名称', `is_pin` tinyint(1) NOT NULL COMMENT '是否锁定', `created_at` datetime NOT NULL COMMENT '创建时间', diff --git a/example/seaql/dict_annote.sql b/example/seaql/dict_annote.sql new file mode 100644 index 0000000..a7bf3b9 --- /dev/null +++ b/example/seaql/dict_annote.sql @@ -0,0 +1,17 @@ +-- Code generated by protoc-gen-saber-seaql. DO NOT EDIT. +-- versions: +-- - protoc-gen-saber-seaql v0.1.0 +-- - protoc v4.24.0 +-- source: seaql.proto + +-- 字典表 +CREATE TABLE + `dict_annote` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '系统序号', + `key` varchar(64) NOT NULL DEFAULT '' COMMENT '键', + `name` varchar(64) NOT NULL DEFAULT '' COMMENT '名称', + `is_pin` tinyint(1) NOT NULL COMMENT '是否锁定', + `created_at` datetime NOT NULL COMMENT '创建时间', + `updated_at` datetime NOT NULL COMMENT '更新时间', + PRIMARY KEY (`id`) + ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '字典表'; diff --git a/example/seaql/dict_item.sql b/example/seaql/dict_item.sql index 71aec9f..94fd6d0 100644 --- a/example/seaql/dict_item.sql +++ b/example/seaql/dict_item.sql @@ -1,6 +1,6 @@ -- Code generated by protoc-gen-saber-seaql. DO NOT EDIT. -- versions: --- - protoc-gen-saber-seaql v1.0.0 +-- - protoc-gen-saber-seaql v0.1.0 -- - protoc v4.24.0 -- source: seaql.proto diff --git a/example/seaql/dict_item_annote.sql b/example/seaql/dict_item_annote.sql new file mode 100644 index 0000000..1b87401 --- /dev/null +++ b/example/seaql/dict_item_annote.sql @@ -0,0 +1,16 @@ +-- Code generated by protoc-gen-saber-seaql. DO NOT EDIT. +-- versions: +-- - protoc-gen-saber-seaql v0.1.0 +-- - protoc v4.24.0 +-- source: seaql.proto + +-- 字典项表 +CREATE TABLE + `dict_item_annote` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '系统序号', + `key` varchar(64) NOT NULL DEFAULT '' COMMENT '键', + `name` varchar(64) NOT NULL DEFAULT '' COMMENT '名称', + `status` tinyint(3) unsigned NOT NULL COMMENT '状态,这是状态,[0:unspecified,1:nested1,2:关闭,3:左,4:右]', + `created_at` datetime NOT NULL COMMENT '创建时间', + `updated_at` datetime NOT NULL COMMENT '更新时间' + ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '字典项表'; diff --git a/example/seaql/seaql.proto b/example/seaql/seaql.proto index 5f8bff6..bed61cb 100644 --- a/example/seaql/seaql.proto +++ b/example/seaql/seaql.proto @@ -13,7 +13,7 @@ import "enums.proto"; // Dict 字典表 message Dict { option (things_go.seaql.options) = { - table_name: "dict", + // table_name: "dict", engine: 'InnoDB', charset: "utf8mb4", index: [ @@ -23,7 +23,8 @@ message Dict { // 系统序号 int64 id = 1 [(things_go.seaql.field) = { type: "bigint NOT NULL AUTO_INCREMENT" }]; -// 名称 +// 键 +// 值 string key = 2 [(things_go.seaql.field) = { type: "varchar(64) NOT NULL DEFAULT ''" }]; // 名称 string name = 3 [(things_go.seaql.field) = { type: "varchar(64) NOT NULL DEFAULT ''" }]; @@ -55,7 +56,7 @@ message DictItem { int64 updated_at = 6 [(things_go.seaql.field) = { type: "datetime NOT NULL" }]; } -// Dict 字典表 +// DictAnnote 字典表 // #[seaql(name="dict")] // #[seaql(engine="InnoDB")] // #[seaql(charset="utf8mb4")] @@ -81,7 +82,8 @@ message DictAnnote { int64 updated_at = 6; } -// DictItem 字典项表 +// DictItemAnnote 字典项表 +// #[seaql] message DictItemAnnote { // 系统序号 // #[seaql(type="bigint NOT NULL AUTO_INCREMENT")] diff --git a/internal/protoenum/enum.go b/internal/protoenum/enum.go index d420f31..314bef5 100644 --- a/internal/protoenum/enum.go +++ b/internal/protoenum/enum.go @@ -10,6 +10,12 @@ import ( "google.golang.org/protobuf/proto" ) +// annotation const value +const ( + annotation_Path = "enum" + annotation_Key_Mapping = "mapping" +) + // EnumValue 枚举的枚举项 type EnumValue struct { Number int // 编号 @@ -56,9 +62,8 @@ func IntoEnums(nestedMessageName string, protoEnums []*protogen.Enum) []*Enum { } // 先判断注解, 再判断扩展 - emComment := protoutil.NewComments(pe.Comments.Leading) - annotate := emComment.FindAnnotation("enum") - if len(annotate) == 0 { + annotates := protoutil.NewComments(pe.Comments.Leading).FindAnnotation(annotation_Path) + if len(annotates) == 0 { isEnabled := proto.GetExtension(pe.Desc.Options(), enumerate.E_Enabled) if ok := isEnabled.(bool); !ok { continue @@ -72,9 +77,9 @@ func IntoEnums(nestedMessageName string, protoEnums []*protogen.Enum) []*Enum { mappingValue := "" comment := strings.TrimSpace(strings.TrimSuffix(string(v.Comments.Leading), "\n")) // 先判断注解, 再判断扩展 - annotateVal := protoutil.NewComments(v.Comments.Leading).FindAnnotationValues("enum", "mapping") - if len(annotateVal) > 0 && annotateVal[0] != "" { - mappingValue = annotateVal[0] + annotateValues := protoutil.NewComments(v.Comments.Leading).FindAnnotationValues(annotation_Path, annotation_Key_Mapping) + if len(annotateValues) > 0 && annotateValues[0] != "" { + mappingValue = annotateValues[0] } else { mpv := proto.GetExtension(v.Desc.Options(), enumerate.E_Mapping) mappingValue, _ = mpv.(string) @@ -124,7 +129,7 @@ func IntoEnumComment(pe *protogen.Enum) string { if pe == nil || len(pe.Values) == 0 { return "" } - annotate := protoutil.NewComments(pe.Comments.Leading).FindAnnotation("enum") + annotate := protoutil.NewComments(pe.Comments.Leading).FindAnnotation(annotation_Path) if len(annotate) == 0 { isEnabled := proto.GetExtension(pe.Desc.Options(), enumerate.E_Enabled) if ok := isEnabled.(bool); !ok { @@ -135,7 +140,7 @@ func IntoEnumComment(pe *protogen.Enum) string { emValueMp := make(map[int]string, len(pe.Values)) for _, v := range pe.Values { mappingValue := "" - annotateVal := protoutil.NewComments(v.Comments.Leading).FindAnnotationValues("enum", "mapping") + annotateVal := protoutil.NewComments(v.Comments.Leading).FindAnnotationValues(annotation_Path, annotation_Key_Mapping) if len(annotateVal) > 0 && annotateVal[0] != "" { mappingValue = annotateVal[0] } else { diff --git a/internal/protoseaql/seaql.go b/internal/protoseaql/seaql.go index 98fba48..ebe2879 100644 --- a/internal/protoseaql/seaql.go +++ b/internal/protoseaql/seaql.go @@ -7,11 +7,24 @@ import ( "github.com/things-go/protogen-saber/internal/infra" "github.com/things-go/protogen-saber/internal/protoenum" + "github.com/things-go/protogen-saber/internal/protoutil" "github.com/things-go/protogen-saber/protosaber/seaql" "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/proto" ) +// annotation const value +const ( + annotation_Path = "seaql" + annotation_Key_Name = "table_name" + annotation_Key_Engine = "engine" + annotation_Key_Charset = "charset" + annotation_Key_Collate = "collate" + annotation_Key_Index = "index" + annotation_Key_ForeignKey = "foreign_key" + annotation_Key_Type = "type" +) + type Schema struct { Tables []Table // 表 } @@ -68,61 +81,96 @@ func IntoTable(protoMessages []*protogen.Message) ([]Table, error) { if len(pe.Fields) == 0 { continue } - messageOptions := proto.GetExtension(pe.Desc.Options(), seaql.E_Options) - seaOptions, ok := messageOptions.(*seaql.Options) - if !ok || seaOptions == nil { - continue + rawTableName := string(pe.Desc.Name()) + var tableName = rawTableName + var engine = "InnoDB" + var charset = "utf8mb4" + var collate = "utf8mb4_general_ci" + var indexes []string + var foreignKey []string + + // 先判断注解, 再判断扩展 + annotates, remainComments := protoutil.NewComments(pe.Comments.Leading).FindAnnotation2(annotation_Path) + if len(annotates) > 0 { + for _, v := range annotates { + switch v.Key { + case annotation_Key_Name: + tableName = v.Value + case annotation_Key_Engine: + engine = v.Value + case annotation_Key_Charset: + charset = v.Value + case annotation_Key_Collate: + collate = v.Value + case annotation_Key_Index: + indexes = append(indexes, v.Value) + case annotation_Key_ForeignKey: + foreignKey = append(foreignKey, v.Value) + } + } + } else { + messageOptions := proto.GetExtension(pe.Desc.Options(), seaql.E_Options) + seaOptions, ok := messageOptions.(*seaql.Options) + if !ok || seaOptions == nil { + continue + } + if seaOptions.TableName != "" { + tableName = seaOptions.TableName + } + if seaOptions.Engine != "" { + engine = seaOptions.Engine + } + if seaOptions.Charset != "" { + charset = seaOptions.Charset + } + if seaOptions.Collate != "" { + collate = seaOptions.Collate + } + indexes = seaOptions.Index + foreignKey = seaOptions.ForeignKey } + comment := strings.TrimSpace(strings.TrimPrefix(remainComments.LineString(), rawTableName)) columns := make([]Column, 0, len(pe.Fields)) for _, v := range pe.Fields { - messageFieldOptions := proto.GetExtension(v.Desc.Options(), seaql.E_Field) - seaFieldOptions := messageFieldOptions.(*seaql.Field) - if seaFieldOptions == nil { - return nil, fmt.Errorf("seaql: message(%s) - field(%s) is not set seaql type", pe.Desc.Name(), string(v.Desc.Name())) - } - seaFieldOptions.Type = strings.TrimSpace(seaFieldOptions.Type) - if seaFieldOptions.Type == "" { - return nil, fmt.Errorf("seaql: message(%s) - field(%s) should be not empty", pe.Desc.Name(), string(v.Desc.Name())) + ty := "" + // 先判断注解, 再判断扩展 + annotateValues, remainComments := protoutil.NewComments(v.Comments.Leading).FindAnnotationValues2(annotation_Path, annotation_Key_Type) + if len(annotates) > 0 && annotateValues[0] != "" { + ty = annotateValues[0] + } else { + messageFieldOptions := proto.GetExtension(v.Desc.Options(), seaql.E_Field) + seaFieldOptions := messageFieldOptions.(*seaql.Field) + if seaFieldOptions == nil { + return nil, fmt.Errorf("seaql: message(%s) - field(%s) is not set seaql type", pe.Desc.Name(), string(v.Desc.Name())) + } + seaFieldOptions.Type = strings.TrimSpace(seaFieldOptions.Type) + if seaFieldOptions.Type == "" { + return nil, fmt.Errorf("seaql: message(%s) - field(%s) should be not empty", pe.Desc.Name(), string(v.Desc.Name())) + } + ty = seaFieldOptions.Type } - comment := strings.ReplaceAll(strings.ReplaceAll(strings.TrimSuffix(string(v.Comments.Leading), "\n"), "\n", ","), " ", "") + comment := strings.ReplaceAll(remainComments.LineString(), " ", "") if enumComment := protoenum.IntoEnumComment(v.Enum); enumComment != "" { comment += "," + enumComment } columns = append(columns, Column{ Name: string(v.Desc.Name()), - Type: seaFieldOptions.Type, + Type: ty, Comment: comment, }) } - rawTableName := string(pe.Desc.Name()) - tableName := rawTableName - if seaOptions.TableName != "" { - tableName = seaOptions.TableName - } - engine := "InnoDB" - if seaOptions.Engine != "" { - engine = seaOptions.Engine - } - charset := "utf8mb4" - if seaOptions.Charset != "" { - charset = seaOptions.Charset - } - collate := "utf8mb4_general_ci" - if seaOptions.Collate != "" { - collate = seaOptions.Collate - } tables = append(tables, Table{ Name: infra.SnakeCase(tableName), - Comment: strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(strings.ReplaceAll(string(pe.Comments.Leading), "\n", "")), rawTableName)), + Comment: comment, Engine: engine, Charset: charset, Collate: collate, Columns: columns, - Indexes: seaOptions.Index, - ForeignKeys: seaOptions.ForeignKey, + Indexes: indexes, + ForeignKeys: foreignKey, }) if len(pe.Messages) > 0 { tbs, err := IntoTable(pe.Messages) diff --git a/internal/protoutil/comment.go b/internal/protoutil/comment.go index 95400df..3fea7b1 100644 --- a/internal/protoutil/comment.go +++ b/internal/protoutil/comment.go @@ -37,6 +37,20 @@ func (c Comments) FindAnnotation(path string) Annotations { return ms } +func (c Comments) FindAnnotation2(path string) (Annotations, Comments) { + remain := make(Comments, 0, len(c)) + ms := make([]*Annotation, 0, len(c)) + for _, v := range c { + m := MatchAnnotation(v) + if m != nil && strings.EqualFold(m.Path, path) { + ms = append(ms, m) + } else { + remain = append(remain, v) + } + } + return ms, remain +} + func (c Comments) FindAnnotationValues(path, key string) []string { ms := make([]string, 0, len(c)) for _, v := range c { @@ -48,6 +62,20 @@ func (c Comments) FindAnnotationValues(path, key string) []string { return ms } +func (c Comments) FindAnnotationValues2(path, key string) ([]string, Comments) { + remain := make(Comments, 0, len(c)) + ms := make([]string, 0, len(c)) + for _, v := range c { + m := MatchAnnotation(v) + if m != nil && strings.EqualFold(m.Path, path) && strings.EqualFold(m.Key, key) { + ms = append(ms, m.Value) + } else { + remain = append(remain, v) + } + } + return ms, remain +} + func (c Comments) String() string { if len(c) == 0 { return "" @@ -61,3 +89,17 @@ func (c Comments) String() string { } return string(b) } + +func (c Comments) LineString() string { + if len(c) == 0 { + return "" + } + var b []byte + for i, line := range c { + b = append(b, []byte(strings.TrimSpace(strings.TrimPrefix(line, "//")))...) + if i+1 < len(c) { + b = append(b, ","...) + } + } + return string(b) +}