repogen/internal/mongo/generator.go

273 lines
7.1 KiB
Go
Raw Normal View History

package mongo
import (
"bytes"
2021-01-23 13:03:16 +00:00
"io"
"strings"
"text/template"
"github.com/sunboyy/repogen/internal/code"
"github.com/sunboyy/repogen/internal/spec"
)
2021-01-23 13:03:16 +00:00
// NewGenerator creates a new instance of MongoDB repository generator
func NewGenerator(structModel code.Struct, interfaceName string) RepositoryGenerator {
return RepositoryGenerator{
StructModel: structModel,
2021-01-23 13:03:16 +00:00
InterfaceName: interfaceName,
}
}
2021-01-23 13:03:16 +00:00
// RepositoryGenerator provides repository constructor and method generation from provided specification
type RepositoryGenerator struct {
StructModel code.Struct
InterfaceName string
}
2021-01-23 13:03:16 +00:00
// GenerateConstructor generates mongo repository struct implementation and constructor for the struct
func (g RepositoryGenerator) GenerateConstructor(buffer io.Writer) error {
tmpl, err := template.New("mongo_repository_base").Parse(constructorTemplate)
if err != nil {
return err
}
2021-01-23 13:03:16 +00:00
tmplData := mongoConstructorTemplateData{
InterfaceName: g.InterfaceName,
ImplStructName: g.structName(),
}
if err := tmpl.Execute(buffer, tmplData); err != nil {
return err
}
return nil
}
2021-01-23 13:03:16 +00:00
// GenerateMethod generates implementation of from provided method specification
func (g RepositoryGenerator) GenerateMethod(methodSpec spec.MethodSpec, buffer io.Writer) error {
tmpl, err := template.New("mongo_repository_method").Parse(methodTemplate)
if err != nil {
return err
}
2021-01-23 13:03:16 +00:00
implementation, err := g.generateMethodImplementation(methodSpec)
if err != nil {
return err
}
var paramTypes []code.Type
2021-01-27 12:15:25 +00:00
for _, param := range methodSpec.Params {
paramTypes = append(paramTypes, param.Type)
}
tmplData := mongoMethodTemplateData{
StructName: g.structName(),
2021-01-23 13:03:16 +00:00
MethodName: methodSpec.Name,
ParamTypes: paramTypes,
2021-01-23 13:03:16 +00:00
ReturnTypes: methodSpec.Returns,
Implementation: implementation,
}
if err := tmpl.Execute(buffer, tmplData); err != nil {
return err
}
return nil
}
2021-01-23 13:03:16 +00:00
func (g RepositoryGenerator) generateMethodImplementation(methodSpec spec.MethodSpec) (string, error) {
switch operation := methodSpec.Operation.(type) {
2021-02-01 14:39:20 +00:00
case spec.InsertOperation:
return g.generateInsertImplementation(operation)
case spec.FindOperation:
return g.generateFindImplementation(operation)
2021-01-27 12:15:25 +00:00
case spec.UpdateOperation:
return g.generateUpdateImplementation(operation)
2021-01-26 13:23:52 +00:00
case spec.DeleteOperation:
return g.generateDeleteImplementation(operation)
2021-02-06 11:05:47 +00:00
case spec.CountOperation:
return g.generateCountImplementation(operation)
2021-02-14 04:48:09 +00:00
default:
return "", NewOperationNotSupportedError(operation.Name())
}
}
2021-02-01 14:39:20 +00:00
func (g RepositoryGenerator) generateInsertImplementation(operation spec.InsertOperation) (string, error) {
if operation.Mode == spec.QueryModeOne {
return insertOneTemplate, nil
}
return insertManyTemplate, nil
}
2021-01-23 13:03:16 +00:00
func (g RepositoryGenerator) generateFindImplementation(operation spec.FindOperation) (string, error) {
2021-01-26 13:23:52 +00:00
querySpec, err := g.mongoQuerySpec(operation.Query)
if err != nil {
return "", err
}
sorts, err := g.mongoSorts(operation.Sorts)
if err != nil {
return "", err
}
tmplData := mongoFindTemplateData{
2021-01-19 12:26:26 +00:00
EntityType: g.StructModel.Name,
2021-01-26 13:23:52 +00:00
QuerySpec: querySpec,
Sorts: sorts,
}
if operation.Mode == spec.QueryModeOne {
2021-02-06 11:05:47 +00:00
return generateFromTemplate("mongo_repository_findone", findOneTemplate, tmplData)
}
2021-02-06 11:05:47 +00:00
return generateFromTemplate("mongo_repository_findmany", findManyTemplate, tmplData)
}
func (g RepositoryGenerator) mongoSorts(sortSpec []spec.Sort) ([]sort, error) {
var sorts []sort
for _, s := range sortSpec {
bsonFieldReference, err := g.bsonFieldReference(s.FieldReference)
if err != nil {
return nil, err
}
sorts = append(sorts, sort{
BsonTag: bsonFieldReference,
Ordering: s.Ordering,
})
}
return sorts, nil
}
2021-01-27 12:15:25 +00:00
func (g RepositoryGenerator) generateUpdateImplementation(operation spec.UpdateOperation) (string, error) {
update, err := g.getMongoUpdate(operation.Update)
if err != nil {
return "", err
2021-01-27 12:15:25 +00:00
}
querySpec, err := g.mongoQuerySpec(operation.Query)
if err != nil {
return "", err
}
tmplData := mongoUpdateTemplateData{
Update: update,
QuerySpec: querySpec,
2021-01-27 12:15:25 +00:00
}
if operation.Mode == spec.QueryModeOne {
2021-02-06 11:05:47 +00:00
return generateFromTemplate("mongo_repository_updateone", updateOneTemplate, tmplData)
2021-01-27 12:15:25 +00:00
}
2021-02-06 11:05:47 +00:00
return generateFromTemplate("mongo_repository_updatemany", updateManyTemplate, tmplData)
2021-01-27 12:15:25 +00:00
}
func (g RepositoryGenerator) getMongoUpdate(updateSpec spec.Update) (update, error) {
switch updateSpec := updateSpec.(type) {
case spec.UpdateModel:
return updateModel{}, nil
case spec.UpdateFields:
var update updateFields
for _, field := range updateSpec {
bsonFieldReference, err := g.bsonFieldReference(field.FieldReference)
if err != nil {
return querySpec{}, err
}
update.Fields = append(update.Fields, updateField{BsonTag: bsonFieldReference, ParamIndex: field.ParamIndex})
}
return update, nil
default:
return nil, NewUpdateTypeNotSupportedError(updateSpec)
}
}
2021-01-26 13:23:52 +00:00
func (g RepositoryGenerator) generateDeleteImplementation(operation spec.DeleteOperation) (string, error) {
querySpec, err := g.mongoQuerySpec(operation.Query)
if err != nil {
return "", err
}
tmplData := mongoDeleteTemplateData{
QuerySpec: querySpec,
}
if operation.Mode == spec.QueryModeOne {
2021-02-06 11:05:47 +00:00
return generateFromTemplate("mongo_repository_deleteone", deleteOneTemplate, tmplData)
}
return generateFromTemplate("mongo_repository_deletemany", deleteManyTemplate, tmplData)
}
2021-01-26 13:23:52 +00:00
2021-02-06 11:05:47 +00:00
func (g RepositoryGenerator) generateCountImplementation(operation spec.CountOperation) (string, error) {
querySpec, err := g.mongoQuerySpec(operation.Query)
if err != nil {
return "", err
}
2021-01-26 13:23:52 +00:00
2021-02-06 11:05:47 +00:00
tmplData := mongoCountTemplateData{
QuerySpec: querySpec,
2021-01-26 13:23:52 +00:00
}
2021-02-06 11:05:47 +00:00
return generateFromTemplate("mongo_repository_count", countTemplate, tmplData)
2021-01-26 13:23:52 +00:00
}
func (g RepositoryGenerator) mongoQuerySpec(query spec.QuerySpec) (querySpec, error) {
var predicates []predicate
for _, predicateSpec := range query.Predicates {
bsonFieldReference, err := g.bsonFieldReference(predicateSpec.FieldReference)
2021-01-27 12:15:25 +00:00
if err != nil {
return querySpec{}, err
2021-01-26 13:23:52 +00:00
}
2021-01-27 12:15:25 +00:00
predicates = append(predicates, predicate{
Field: bsonFieldReference,
2021-01-27 12:15:25 +00:00
Comparator: predicateSpec.Comparator,
ParamIndex: predicateSpec.ParamIndex,
})
2021-01-26 13:23:52 +00:00
}
return querySpec{
Operator: query.Operator,
Predicates: predicates,
}, nil
}
func (g RepositoryGenerator) bsonFieldReference(fieldReference spec.FieldReference) (string, error) {
var bsonTags []string
for _, field := range fieldReference {
tag, err := g.bsonTagFromField(field)
if err != nil {
return "", err
}
bsonTags = append(bsonTags, tag)
2021-01-27 12:15:25 +00:00
}
return strings.Join(bsonTags, "."), nil
}
2021-01-27 12:15:25 +00:00
func (g RepositoryGenerator) bsonTagFromField(field code.StructField) (string, error) {
bsonTag, ok := field.Tags["bson"]
2021-01-27 12:15:25 +00:00
if !ok {
return "", NewBsonTagNotFoundError(field.Name)
2021-01-27 12:15:25 +00:00
}
return bsonTag[0], nil
}
2021-01-23 13:03:16 +00:00
func (g RepositoryGenerator) structName() string {
return g.InterfaceName + "Mongo"
}
2021-02-06 11:05:47 +00:00
func generateFromTemplate(name string, templateString string, tmplData interface{}) (string, error) {
tmpl, err := template.New(name).Parse(templateString)
if err != nil {
return "", err
}
buffer := new(bytes.Buffer)
if err := tmpl.Execute(buffer, tmplData); err != nil {
return "", err
}
return buffer.String(), nil
}