github.com/qiniu/go-sdk/v7@v7.27.0/internal/api-generator/path.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/dave/jennifer/jen"
     8  	"github.com/iancoleman/strcase"
     9  )
    10  
    11  type (
    12  	PathParams struct {
    13  		Named []NamedPathParam `yaml:"named,omitempty"`
    14  		Free  *FreePathParams  `yaml:"free,omitempty"`
    15  	}
    16  
    17  	NamedPathParam struct {
    18  		PathSegment        string             `yaml:"path_segment,omitempty"`
    19  		FieldName          string             `yaml:"field_name,omitempty"`
    20  		FieldCamelCaseName string             `yaml:"field_camel_case_name,omitempty"`
    21  		FieldSnakeCaseName string             `yaml:"field_snake_case_name,omitempty"`
    22  		Type               *StringLikeType    `yaml:"type,omitempty"`
    23  		Documentation      string             `yaml:"documentation,omitempty"`
    24  		Encode             *EncodeType        `yaml:"encode,omitempty"`
    25  		ServiceBucket      *ServiceBucketType `yaml:"service_bucket,omitempty"`
    26  		ServiceObject      *ServiceObjectType `yaml:"service_object,omitempty"`
    27  		Optional           *OptionalType      `yaml:"optional,omitempty"`
    28  	}
    29  
    30  	FreePathParams struct {
    31  		FieldName          string      `yaml:"field_name,omitempty"`
    32  		FieldCamelCaseName string      `yaml:"field_camel_case_name,omitempty"`
    33  		FieldSnakeCaseName string      `yaml:"field_snake_case_name,omitempty"`
    34  		Documentation      string      `yaml:"documentation,omitempty"`
    35  		EncodeParamKey     *EncodeType `yaml:"encode_param_key"`
    36  		EncodeParamValue   *EncodeType `yaml:"encode_param_value"`
    37  	}
    38  )
    39  
    40  func (pp *NamedPathParam) camelCaseName() string {
    41  	if pp.FieldCamelCaseName != "" {
    42  		return pp.FieldCamelCaseName
    43  	}
    44  	return strcase.ToCamel(pp.FieldName)
    45  }
    46  
    47  func (fpp *FreePathParams) camelCaseName() string {
    48  	if fpp.FieldCamelCaseName != "" {
    49  		return fpp.FieldCamelCaseName
    50  	}
    51  	return strcase.ToCamel(fpp.FieldName)
    52  }
    53  
    54  func (pp *PathParams) addFields(group *jen.Group) error {
    55  	for _, namedPathParam := range pp.Named {
    56  		nilable := namedPathParam.Encode.ToEncodeType() == EncodeTypeUrlsafeBase64OrNone || namedPathParam.Optional.ToOptionalType() == OptionalTypeNullable
    57  		code, err := namedPathParam.Type.AddTypeToStatement(jen.Id(namedPathParam.camelCaseName()), nilable)
    58  		if err != nil {
    59  			return err
    60  		}
    61  		if namedPathParam.Documentation != "" {
    62  			code = code.Comment(namedPathParam.Documentation)
    63  		}
    64  		group.Add(code)
    65  	}
    66  	if free := pp.Free; free != nil {
    67  		freeFieldName := free.camelCaseName()
    68  		code := jen.Id(freeFieldName).Map(jen.String()).String()
    69  		if free.Documentation != "" {
    70  			code = code.Comment(free.Documentation)
    71  		}
    72  		group.Add(code)
    73  	}
    74  	return nil
    75  }
    76  
    77  func (pp *PathParams) addGetBucketNameFunc(group *jen.Group, structName string) (bool, error) {
    78  	field := pp.getServiceBucketField()
    79  	if field == nil || field.ServiceBucket.ToServiceBucketType() == ServiceBucketTypeNone {
    80  		return false, nil
    81  	} else if field.Type.ToStringLikeType() != StringLikeTypeString {
    82  		panic(fmt.Sprintf("service bucket field must be string: %s", field.FieldName))
    83  	}
    84  	group.Add(jen.Func().
    85  		Params(jen.Id("pp").Op("*").Id(structName)).
    86  		Id("getBucketName").
    87  		Params(jen.Id("ctx").Qual("context", "Context")).
    88  		Params(jen.String(), jen.Error()).
    89  		BlockFunc(func(group *jen.Group) {
    90  			fieldName := field.camelCaseName()
    91  			switch field.ServiceBucket.ToServiceBucketType() {
    92  			case ServiceBucketTypePlainText:
    93  				group.Add(jen.Return(jen.Id("pp").Dot(fieldName), jen.Nil()))
    94  			case ServiceBucketTypeEntry:
    95  				group.Add(
    96  					jen.Return(
    97  						jen.Qual("strings", "SplitN").
    98  							Call(jen.Id("pp").Dot(fieldName), jen.Lit(":"), jen.Lit(2)).
    99  							Index(jen.Lit(0)),
   100  						jen.Nil(),
   101  					),
   102  				)
   103  			case ServiceBucketTypeUploadToken:
   104  				group.Add(
   105  					jen.If(jen.Id("putPolicy"), jen.Err()).
   106  						Op(":=").
   107  						Qual(PackageNameUpToken, "NewParser").
   108  						Call(jen.Id("pp").Dot(fieldName)).
   109  						Dot("GetPutPolicy").
   110  						Call(jen.Id("ctx")).
   111  						Op(";").
   112  						Err().
   113  						Op("!=").
   114  						Nil().
   115  						BlockFunc(func(group *jen.Group) {
   116  							group.Add(jen.Return(jen.Lit(""), jen.Err()))
   117  						}).
   118  						Else().
   119  						BlockFunc(func(group *jen.Group) {
   120  							group.Add(jen.Return(jen.Id("putPolicy").Dot("GetBucketName").Call()))
   121  						}),
   122  				)
   123  			default:
   124  				panic("unknown ServiceBucketType")
   125  			}
   126  		}))
   127  	return true, nil
   128  }
   129  
   130  func (pp *PathParams) addGetObjectNameFunc(group *jen.Group, structName string) (bool, error) {
   131  	field := pp.getServiceObjectField()
   132  	if field == nil || field.ServiceObject.ToServiceObjectType() == ServiceObjectTypeNone {
   133  		return false, nil
   134  	} else if field.Type.ToStringLikeType() != StringLikeTypeString {
   135  		panic(fmt.Sprintf("service object field must be string: %s", field.FieldName))
   136  	}
   137  	group.Add(jen.Func().
   138  		Params(jen.Id("pp").Op("*").Id(structName)).
   139  		Id("getObjectName").
   140  		Params().
   141  		Params(jen.String()).
   142  		BlockFunc(func(group *jen.Group) {
   143  			fieldName := field.camelCaseName()
   144  			switch field.ServiceObject.ToServiceObjectType() {
   145  			case ServiceObjectTypePlainText:
   146  				if field.Optional.ToOptionalType() == OptionalTypeNullable {
   147  					group.Add(jen.Var().Id("objectName").String())
   148  					group.Add(jen.If(jen.Id("pp").Dot(fieldName).Op("!=").Nil()).BlockFunc(func(group *jen.Group) {
   149  						group.Add(jen.Id("objectName").Op("=").Op("*").Id("pp").Dot(fieldName))
   150  					}))
   151  					group.Add(jen.Return(jen.Id("objectName")))
   152  				} else {
   153  					group.Return(jen.Id("pp").Dot(fieldName))
   154  				}
   155  			case ServiceObjectTypeEntry:
   156  				group.Add(
   157  					jen.Id("parts").
   158  						Op(":=").
   159  						Qual("strings", "SplitN").
   160  						Call(jen.Id("pp").Dot(fieldName), jen.Lit(":"), jen.Lit(2)))
   161  				group.Add(jen.If(jen.Len(jen.Id("parts")).Op(">").Lit(1)), jen.BlockFunc(func(group *jen.Group) {
   162  					group.Return(jen.Id("parts").Index(jen.Lit(1)))
   163  				}))
   164  				group.Add(jen.Return(jen.Lit("")))
   165  			default:
   166  				panic("unknown ServiceObjectType")
   167  			}
   168  		}))
   169  	return true, nil
   170  }
   171  
   172  func (pp *PathParams) addBuildFunc(group *jen.Group, structName string) error {
   173  	var err error
   174  	group.Add(
   175  		jen.Func().
   176  			Params(jen.Id("path").Op("*").Id(structName)).
   177  			Id("buildPath").
   178  			Params().
   179  			Params(jen.Index().Add(jen.String()), jen.Error()).
   180  			BlockFunc(func(group *jen.Group) {
   181  				guessPathSegmentsCount := 0
   182  				for _, namedPathParam := range pp.Named {
   183  					guessPathSegmentsCount += 1
   184  					if namedPathParam.PathSegment != "" {
   185  						guessPathSegmentsCount += 1
   186  					}
   187  				}
   188  				group.Add(jen.Id("allSegments").Op(":=").Make(jen.Index().Add(jen.String()), jen.Lit(0), jen.Lit(guessPathSegmentsCount)))
   189  				for _, namedPathParam := range pp.Named {
   190  					var (
   191  						code                jen.Code
   192  						urlSafeBase64IsNone = namedPathParam.Encode.ToEncodeType() == EncodeTypeUrlsafeBase64OrNone
   193  						nilable             = urlSafeBase64IsNone || namedPathParam.Optional.ToOptionalType() == OptionalTypeNullable
   194  						fieldName           = namedPathParam.camelCaseName()
   195  						field               = jen.Id("path").Dot(fieldName)
   196  						unreferencedField   = field.Clone()
   197  					)
   198  					if nilable {
   199  						unreferencedField = jen.Op("*").Add(unreferencedField)
   200  					}
   201  					switch namedPathParam.Type.ToStringLikeType() {
   202  					case StringLikeTypeString:
   203  						switch namedPathParam.Encode.ToEncodeType() {
   204  						case EncodeTypeNone:
   205  							code = unreferencedField.Clone()
   206  						case EncodeTypeUrlsafeBase64, EncodeTypeUrlsafeBase64OrNone:
   207  							code = jen.Qual("encoding/base64", "URLEncoding").Dot("EncodeToString").Call(jen.Index().Byte().Parens(unreferencedField.Clone()))
   208  						}
   209  					case StringLikeTypeInteger, StringLikeTypeFloat, StringLikeTypeBoolean:
   210  						code, _ = namedPathParam.Type.GenerateConvertCodeToString(unreferencedField.Clone())
   211  					default:
   212  						err = errors.New("unknown type")
   213  						return
   214  					}
   215  					zeroValue, e := namedPathParam.Type.ZeroValue()
   216  					if e != nil {
   217  						err = e
   218  						return
   219  					}
   220  					condition := field.Clone()
   221  					if nilable {
   222  						condition = condition.Op("!=").Nil()
   223  					} else if v, ok := zeroValue.(bool); !ok || v {
   224  						condition = condition.Op("!=").Lit(zeroValue)
   225  					}
   226  					appendPathSegment := func(pathSegment string, value jen.Code) func(group *jen.Group) {
   227  						return func(group *jen.Group) {
   228  							codes := []jen.Code{jen.Id("allSegments")}
   229  							if pathSegment != "" {
   230  								codes = append(codes, jen.Lit(pathSegment))
   231  							}
   232  							codes = append(codes, value)
   233  							group.Add(
   234  								jen.Id("allSegments").Op("=").Append(codes...),
   235  							)
   236  						}
   237  					}
   238  					appendMissingRequiredFieldErrorFunc := func(fieldName string) func(group *jen.Group) {
   239  						return func(group *jen.Group) {
   240  							group.Add(jen.Return(
   241  								jen.Nil(),
   242  								jen.Qual(PackageNameErrors, "MissingRequiredFieldError").
   243  									ValuesFunc(func(group *jen.Group) {
   244  										group.Add(jen.Id("Name").Op(":").Lit(fieldName))
   245  									}),
   246  							))
   247  						}
   248  					}
   249  
   250  					if urlSafeBase64IsNone {
   251  						group.Add(
   252  							jen.If(condition).
   253  								BlockFunc(appendPathSegment(namedPathParam.PathSegment, code)).
   254  								Else().
   255  								BlockFunc(appendPathSegment(namedPathParam.PathSegment, jen.Lit("~"))),
   256  						)
   257  					} else {
   258  						switch namedPathParam.Optional.ToOptionalType() {
   259  						case OptionalTypeRequired:
   260  							group.Add(
   261  								jen.If(condition).
   262  									BlockFunc(appendPathSegment(namedPathParam.PathSegment, code)).
   263  									Else().
   264  									BlockFunc(appendMissingRequiredFieldErrorFunc(fieldName)),
   265  							)
   266  						case OptionalTypeOmitEmpty, OptionalTypeNullable:
   267  							group.Add(
   268  								jen.If(condition).
   269  									BlockFunc(appendPathSegment(namedPathParam.PathSegment, code)),
   270  							)
   271  						case OptionalTypeKeepEmpty:
   272  							appendPathSegment(namedPathParam.PathSegment, code)(group)
   273  						}
   274  					}
   275  				}
   276  				if free := pp.Free; free != nil {
   277  					freeFieldName := free.camelCaseName()
   278  					group.Add(
   279  						jen.For(
   280  							jen.Id("key").
   281  								Op(",").
   282  								Id("value").
   283  								Op(":=").
   284  								Range().
   285  								Id("path").
   286  								Dot(freeFieldName)).
   287  							BlockFunc(func(group *jen.Group) {
   288  								var keyCode, valueCode jen.Code
   289  								switch free.EncodeParamKey.ToEncodeType() {
   290  								case EncodeTypeNone:
   291  									keyCode = jen.Id("key")
   292  								case EncodeTypeUrlsafeBase64, EncodeTypeUrlsafeBase64OrNone:
   293  									keyCode = jen.Qual("encoding/base64", "URLEncoding").
   294  										Dot("EncodeToString").
   295  										Call(jen.Index().Byte().Parens(jen.Id("key")))
   296  								}
   297  								group.Add(
   298  									jen.Id("allSegments").Op("=").Append(jen.Id("allSegments"), keyCode),
   299  								)
   300  								switch free.EncodeParamValue.ToEncodeType() {
   301  								case EncodeTypeNone:
   302  									valueCode = jen.Id("value")
   303  								case EncodeTypeUrlsafeBase64, EncodeTypeUrlsafeBase64OrNone:
   304  									valueCode = jen.Qual("encoding/base64", "URLEncoding").
   305  										Dot("EncodeToString").
   306  										Call(jen.Index().Byte().Parens(jen.Id("value")))
   307  								}
   308  								group.Add(
   309  									jen.Id("allSegments").Op("=").Append(jen.Id("allSegments"), valueCode),
   310  								)
   311  							}),
   312  					)
   313  				}
   314  				group.Add(jen.Return(jen.Id("allSegments"), jen.Nil()))
   315  			}),
   316  	)
   317  	return err
   318  }
   319  
   320  func (pp *PathParams) getServiceBucketField() *NamedPathParam {
   321  	var serviceBucketField *NamedPathParam
   322  
   323  	for i := range pp.Named {
   324  		if pp.Named[i].ServiceBucket.ToServiceBucketType() != ServiceBucketTypeNone {
   325  			if serviceBucketField == nil {
   326  				serviceBucketField = &pp.Named[i]
   327  			} else {
   328  				panic(fmt.Sprintf("multiple service bucket fields: %s & %s", pp.Named[i].FieldName, serviceBucketField.FieldName))
   329  			}
   330  		}
   331  	}
   332  	return serviceBucketField
   333  }
   334  
   335  func (pp *PathParams) getServiceObjectField() *NamedPathParam {
   336  	var serviceObjectField *NamedPathParam
   337  
   338  	for i := range pp.Named {
   339  		if pp.Named[i].ServiceObject.ToServiceObjectType() != ServiceObjectTypeNone {
   340  			if serviceObjectField == nil {
   341  				serviceObjectField = &pp.Named[i]
   342  			} else {
   343  				panic(fmt.Sprintf("multiple service object fields: %s & %s", pp.Named[i].FieldName, serviceObjectField.FieldName))
   344  			}
   345  		}
   346  	}
   347  	return serviceObjectField
   348  }