github.com/grpc-ecosystem/grpc-gateway/v2@v2.19.1/protoc-gen-openapiv2/internal/genopenapi/naming.go (about) 1 package genopenapi 2 3 import ( 4 "reflect" 5 "strings" 6 ) 7 8 // LookupNamingStrategy looks up the given naming strategy and returns the naming 9 // strategy function for it. The naming strategy function takes in the list of all 10 // fully-qualified proto message names, and returns a mapping from fully-qualified 11 // name to OpenAPI name. 12 func LookupNamingStrategy(strategyName string) func([]string) map[string]string { 13 switch strings.ToLower(strategyName) { 14 case "fqn": 15 return resolveNamesFQN 16 case "legacy": 17 return resolveNamesLegacy 18 case "simple": 19 return resolveNamesSimple 20 } 21 return nil 22 } 23 24 // resolveNamesFQN uses the fully-qualified proto message name as the 25 // OpenAPI name, stripping the leading dot. 26 func resolveNamesFQN(messages []string) map[string]string { 27 uniqueNames := make(map[string]string, len(messages)) 28 for _, p := range messages { 29 // strip leading dot from proto fqn 30 uniqueNames[p] = p[1:] 31 } 32 return uniqueNames 33 } 34 35 // resolveNamesLegacy takes the names of all proto messages and generates unique references by 36 // applying the legacy heuristics for deriving unique names: starting from the bottom of the name hierarchy, it 37 // determines the minimum number of components necessary to yield a unique name, adds one 38 // to that number, and then concatenates those last components with no separator in between 39 // to form a unique name. 40 // 41 // E.g., if the fully qualified name is `.a.b.C.D`, and there are other messages with fully 42 // qualified names ending in `.D` but not in `.C.D`, it assigns the unique name `bCD`. 43 func resolveNamesLegacy(messages []string) map[string]string { 44 return resolveNamesUniqueWithContext(messages, 1, "") 45 } 46 47 // resolveNamesSimple takes the names of all proto messages and generates unique references by using a simple 48 // heuristic: starting from the bottom of the name hierarchy, it determines the minimum 49 // number of components necessary to yield a unique name, and then concatenates those last 50 // components with a "." separator in between to form a unique name. 51 // 52 // E.g., if the fully qualified name is `.a.b.C.D`, and there are other messages with 53 // fully qualified names ending in `.D` but not in `.C.D`, it assigns the unique name `C.D`. 54 func resolveNamesSimple(messages []string) map[string]string { 55 return resolveNamesUniqueWithContext(messages, 0, ".") 56 } 57 58 // Take the names of every proto message and generates a unique reference by: 59 // first, separating each message name into its components by splitting at dots. Then, 60 // take the shortest suffix slice from each components slice that is unique among all 61 // messages, and convert it into a component name by taking extraContext additional 62 // components into consideration and joining all components with componentSeparator. 63 func resolveNamesUniqueWithContext(messages []string, extraContext int, componentSeparator string) map[string]string { 64 packagesByDepth := make(map[int][][]string) 65 uniqueNames := make(map[string]string) 66 67 hierarchy := func(pkg string) []string { 68 return strings.Split(pkg, ".") 69 } 70 71 for _, p := range messages { 72 h := hierarchy(p) 73 for depth := range h { 74 if _, ok := packagesByDepth[depth]; !ok { 75 packagesByDepth[depth] = make([][]string, 0) 76 } 77 packagesByDepth[depth] = append(packagesByDepth[depth], h[len(h)-depth:]) 78 } 79 } 80 81 count := func(list [][]string, item []string) int { 82 i := 0 83 for _, element := range list { 84 if reflect.DeepEqual(element, item) { 85 i++ 86 } 87 } 88 return i 89 } 90 91 for _, p := range messages { 92 h := hierarchy(p) 93 depth := 0 94 for ; depth < len(h); depth++ { 95 // depth + extraContext > 0 ensures that we only break for values of depth when the 96 // resulting slice of name components is non-empty. Otherwise, we would return the 97 // empty string as the concise unique name is len(messages) == 1 (which is 98 // technically correct). 99 if depth+extraContext > 0 && count(packagesByDepth[depth], h[len(h)-depth:]) == 1 { 100 break 101 } 102 } 103 start := len(h) - depth - extraContext 104 if start < 0 { 105 start = 0 106 } 107 uniqueNames[p] = strings.Join(h[start:], componentSeparator) 108 } 109 return uniqueNames 110 }