1// Copyright (c) Microsoft Corporation. All rights reserved. 2// Licensed under the MIT License. See License.txt in the project root for license information. 3 4package delta 5 6import ( 7 "sort" 8 9 "github.com/Azure/azure-sdk-for-go/tools/internal/exports" 10) 11 12// Content defines the set of exported constants, funcs, and structs. 13type Content struct { 14 exports.Content 15 16 // contains the names of structs that are modified in whole (i.e. new/removed) 17 CompleteStructs []string `json:"newStructs,omitempty"` 18} 19 20// Count returns the count of items 21func (c Content) Count() int { 22 return c.Content.Count() + len(c.CompleteStructs) 23} 24 25// NewContent returns an initialized Content object. 26func NewContent() Content { 27 return Content{ 28 Content: exports.NewContent(), 29 } 30} 31 32// GetModifiedStructs returns the subset, if any, of structs that are modified. 33func (c Content) GetModifiedStructs() map[string]exports.Struct { 34 if len(c.CompleteStructs) == 0 { 35 return c.Structs 36 } 37 ms := map[string]exports.Struct{} 38 for k, v := range c.Structs { 39 if contains(c.CompleteStructs, k) { 40 continue 41 } 42 ms[k] = v 43 } 44 return ms 45} 46 47// returns true if sl contains x 48func contains(sl []string, x string) bool { 49 for _, s := range sl { 50 if s == x { 51 return true 52 } 53 } 54 return false 55} 56 57// GetExports returns a exports.Content struct containing all exports in rhs that aren't in lhs. 58// This includes any new fields added to structs or methods added to interfaces. 59func GetExports(lhs, rhs exports.Content) Content { 60 nc := NewContent() 61 62 for n, v := range rhs.Consts { 63 if _, ok := lhs.Consts[n]; !ok { 64 nc.Consts[n] = v 65 } 66 } 67 68 for n, v := range rhs.Funcs { 69 if _, ok := lhs.Funcs[n]; !ok { 70 nc.Funcs[n] = v 71 } 72 } 73 74 for n, v := range rhs.Interfaces { 75 if _, ok := lhs.Interfaces[n]; !ok { 76 nc.Interfaces[n] = v 77 } 78 } 79 80 for n, v := range rhs.Structs { 81 if _, ok := lhs.Structs[n]; !ok { 82 nc.Structs[n] = v 83 nc.CompleteStructs = append(nc.CompleteStructs, n) 84 } 85 } 86 87 structFields := GetStructFields(lhs, rhs) 88 if len(structFields) > 0 { 89 for k, v := range structFields { 90 nc.Structs[k] = v 91 } 92 } 93 94 intMethods := GetInterfaceMethods(lhs, rhs) 95 if len(intMethods) > 0 { 96 for k, v := range intMethods { 97 nc.Interfaces[k] = v 98 } 99 } 100 101 sort.Strings(nc.CompleteStructs) 102 return nc 103} 104 105// GetStructFields returns structs common to lhs and rhs where structs in rhs contain 106// fields not in lhs. Key is the struct type name, value contains the added content. 107func GetStructFields(lhs, rhs exports.Content) map[string]exports.Struct { 108 nf := map[string]exports.Struct{} 109 110 for rhsKey, rhsVal := range rhs.Structs { 111 if lhsStruct, ok := lhs.Structs[rhsKey]; ok { 112 nc := exports.Struct{} 113 for _, rhsAnon := range rhsVal.AnonymousFields { 114 found := false 115 for _, lhsAnon := range lhsStruct.AnonymousFields { 116 if lhsAnon == rhsAnon { 117 found = true 118 break 119 } 120 } 121 if !found { 122 nc.AnonymousFields = append(nc.AnonymousFields, rhsAnon) 123 } 124 } 125 for fn, fv := range rhsVal.Fields { 126 if _, ok := lhsStruct.Fields[fn]; !ok { 127 if nc.Fields == nil { 128 nc.Fields = map[string]string{} 129 } 130 nc.Fields[fn] = fv 131 } 132 } 133 // only add it if there's new content 134 if len(nc.AnonymousFields) > 0 || len(nc.Fields) > 0 { 135 nf[rhsKey] = nc 136 } 137 } 138 } 139 return nf 140} 141 142// GetInterfaceMethods returns interfaces common to lhs and rhs where interfaces in rhs contain 143// methods not in lhs. Key is the interface type name, value contains the added content. 144func GetInterfaceMethods(lhs, rhs exports.Content) map[string]exports.Interface { 145 ni := map[string]exports.Interface{} 146 147 for rhsKey, rhsValue := range rhs.Interfaces { 148 if lhsInterface, ok := lhs.Interfaces[rhsKey]; ok { 149 nc := exports.Interface{} 150 for in, iv := range rhsValue.Methods { 151 if _, ok := lhsInterface.Methods[in]; !ok { 152 if nc.Methods == nil { 153 nc.Methods = map[string]exports.Func{} 154 } 155 nc.Methods[in] = iv 156 } 157 } 158 // only add it if there's new content 159 if len(nc.Methods) > 0 { 160 ni[rhsKey] = nc 161 } 162 } 163 } 164 return ni 165} 166 167// Signature contains the details of how a type signature changed (e.g. From:"int" To:"string"). 168type Signature struct { 169 // From contains the original signature. 170 From string `json:"from"` 171 172 // To contains the new signature. 173 To string `json:"to"` 174} 175 176// GetConstTypeChanges returns a collection of const where the type has changed. 177// Key is the const name, value contains the type change information. 178func GetConstTypeChanges(lhs, rhs exports.Content) map[string]Signature { 179 cc := map[string]Signature{} 180 181 for rhsKey, rhsValue := range rhs.Consts { 182 if _, ok := lhs.Consts[rhsKey]; !ok { 183 continue 184 } 185 if lhs.Consts[rhsKey].Type != rhsValue.Type { 186 cc[rhsKey] = Signature{ 187 From: lhs.Consts[rhsKey].Type, 188 To: rhsValue.Type, 189 } 190 } 191 } 192 return cc 193} 194 195// None is the value used for functions with no parameters and/or no return values. 196const None = "<none>" 197 198// FuncSig contains the details of how a function's signature changed. 199type FuncSig struct { 200 // Params contains the parameter signature changes, may be nil. 201 Params *Signature `json:"params,omitempty"` 202 203 // Returns contains the return signature changes, may be nil. 204 Returns *Signature `json:"returns,omitempty"` 205} 206 207func (fs FuncSig) isEmpty() bool { 208 return fs.Params == nil && fs.Returns == nil 209} 210 211// GetFuncSigChanges returns a collection of functions that contain signature changes (params and/or returns). 212// Key is the function name, value contains the signature change information. 213func GetFuncSigChanges(lhs, rhs exports.Content) map[string]FuncSig { 214 fsc := map[string]FuncSig{} 215 216 for rhsKey, rhsValue := range rhs.Funcs { 217 if _, ok := lhs.Funcs[rhsKey]; !ok { 218 continue 219 } 220 sig := FuncSig{} 221 if !safeStrCmp(lhs.Funcs[rhsKey].Params, rhsValue.Params) { 222 sig.Params = &Signature{ 223 From: safeFuncSig(lhs.Funcs[rhsKey].Params), 224 To: safeFuncSig(rhsValue.Params), 225 } 226 } 227 if !safeStrCmp(lhs.Funcs[rhsKey].Returns, rhsValue.Returns) { 228 sig.Returns = &Signature{ 229 From: safeFuncSig(lhs.Funcs[rhsKey].Returns), 230 To: safeFuncSig(rhsValue.Returns), 231 } 232 } 233 234 if !sig.isEmpty() { 235 fsc[rhsKey] = sig 236 } 237 } 238 return fsc 239} 240 241// InterfaceDef contains a collection of interface methods with signature changes. 242// Key is the method name, value contains the signature change information. 243type InterfaceDef struct { 244 MethodSigs map[string]FuncSig `json:"funcSig"` 245} 246 247// GetInterfaceMethodSigChanges returns a collection of interfaces with method signature changes. 248// Key is the interface name, value contains the method signature change information. 249func GetInterfaceMethodSigChanges(lhs, rhs exports.Content) map[string]InterfaceDef { 250 isc := map[string]InterfaceDef{} 251 252 for rhsKey, rhsValue := range rhs.Interfaces { 253 if _, ok := lhs.Interfaces[rhsKey]; !ok { 254 continue 255 } 256 id := InterfaceDef{} 257 258 for rhsMethod, rhsSig := range rhsValue.Methods { 259 if _, ok := lhs.Interfaces[rhsKey].Methods[rhsMethod]; !ok { 260 continue 261 } 262 sig := FuncSig{} 263 if !safeStrCmp(lhs.Interfaces[rhsKey].Methods[rhsMethod].Params, rhsSig.Params) { 264 sig.Params = &Signature{ 265 From: safeFuncSig(lhs.Interfaces[rhsKey].Methods[rhsMethod].Params), 266 To: safeFuncSig(rhsSig.Params), 267 } 268 } 269 if !safeStrCmp(lhs.Interfaces[rhsKey].Methods[rhsMethod].Returns, rhsSig.Returns) { 270 sig.Returns = &Signature{ 271 From: safeFuncSig(lhs.Interfaces[rhsKey].Methods[rhsMethod].Returns), 272 To: safeFuncSig(rhsSig.Returns), 273 } 274 } 275 276 if !sig.isEmpty() { 277 if id.MethodSigs == nil { 278 id.MethodSigs = map[string]FuncSig{} 279 } 280 id.MethodSigs[rhsMethod] = sig 281 } 282 } 283 284 if len(id.MethodSigs) > 0 { 285 isc[rhsKey] = id 286 } 287 } 288 return isc 289} 290 291// StructDef contains a collection of fields within a struct where the field's type has changed. 292// Key is the field name, value contains the signature change information. 293type StructDef struct { 294 Fields map[string]Signature `json:"fields"` 295} 296 297// GetStructFieldChanges returns a collection of structs with fields that changed their type. 298// Key is the struct name, value contains fields with signature changes. 299func GetStructFieldChanges(lhs, rhs exports.Content) map[string]StructDef { 300 sfc := map[string]StructDef{} 301 302 for rhsKey, rhsValue := range rhs.Structs { 303 if _, ok := lhs.Structs[rhsKey]; !ok { 304 continue 305 } 306 sd := StructDef{} 307 308 for rhsField, rhsSig := range rhsValue.Fields { 309 if _, ok := lhs.Structs[rhsKey].Fields[rhsField]; !ok { 310 continue 311 } 312 if lhs.Structs[rhsKey].Fields[rhsField] != rhsSig { 313 if sd.Fields == nil { 314 sd.Fields = map[string]Signature{} 315 } 316 sd.Fields[rhsField] = Signature{ 317 From: lhs.Structs[rhsKey].Fields[rhsField], 318 To: rhsSig, 319 } 320 } 321 } 322 323 if len(sd.Fields) > 0 { 324 sfc[rhsKey] = sd 325 } 326 } 327 return sfc 328} 329 330func safeFuncSig(s *string) string { 331 if s == nil { 332 return None 333 } 334 return *s 335} 336 337func safeStrCmp(lhs, rhs *string) bool { 338 if lhs == nil && rhs == nil { 339 return true 340 } 341 if lhs == nil || rhs == nil { 342 return false 343 } 344 return *lhs == *rhs 345} 346