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 }