utils.go (3595B)
1 // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 // Use of this source code is governed by a MIT style 3 // license that can be found in the LICENSE file. 4 5 package gin 6 7 import ( 8 "encoding/xml" 9 "net/http" 10 "os" 11 "path" 12 "reflect" 13 "runtime" 14 "strings" 15 "unicode" 16 ) 17 18 // BindKey indicates a default bind key. 19 const BindKey = "_gin-gonic/gin/bindkey" 20 21 // Bind is a helper function for given interface object and returns a Gin middleware. 22 func Bind(val any) HandlerFunc { 23 value := reflect.ValueOf(val) 24 if value.Kind() == reflect.Ptr { 25 panic(`Bind struct can not be a pointer. Example: 26 Use: gin.Bind(Struct{}) instead of gin.Bind(&Struct{}) 27 `) 28 } 29 typ := value.Type() 30 31 return func(c *Context) { 32 obj := reflect.New(typ).Interface() 33 if c.Bind(obj) == nil { 34 c.Set(BindKey, obj) 35 } 36 } 37 } 38 39 // WrapF is a helper function for wrapping http.HandlerFunc and returns a Gin middleware. 40 func WrapF(f http.HandlerFunc) HandlerFunc { 41 return func(c *Context) { 42 f(c.Writer, c.Request) 43 } 44 } 45 46 // WrapH is a helper function for wrapping http.Handler and returns a Gin middleware. 47 func WrapH(h http.Handler) HandlerFunc { 48 return func(c *Context) { 49 h.ServeHTTP(c.Writer, c.Request) 50 } 51 } 52 53 // H is a shortcut for map[string]any 54 type H map[string]any 55 56 // MarshalXML allows type H to be used with xml.Marshal. 57 func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error { 58 start.Name = xml.Name{ 59 Space: "", 60 Local: "map", 61 } 62 if err := e.EncodeToken(start); err != nil { 63 return err 64 } 65 for key, value := range h { 66 elem := xml.StartElement{ 67 Name: xml.Name{Space: "", Local: key}, 68 Attr: []xml.Attr{}, 69 } 70 if err := e.EncodeElement(value, elem); err != nil { 71 return err 72 } 73 } 74 75 return e.EncodeToken(xml.EndElement{Name: start.Name}) 76 } 77 78 func assert1(guard bool, text string) { 79 if !guard { 80 panic(text) 81 } 82 } 83 84 func filterFlags(content string) string { 85 for i, char := range content { 86 if char == ' ' || char == ';' { 87 return content[:i] 88 } 89 } 90 return content 91 } 92 93 func chooseData(custom, wildcard any) any { 94 if custom != nil { 95 return custom 96 } 97 if wildcard != nil { 98 return wildcard 99 } 100 panic("negotiation config is invalid") 101 } 102 103 func parseAccept(acceptHeader string) []string { 104 parts := strings.Split(acceptHeader, ",") 105 out := make([]string, 0, len(parts)) 106 for _, part := range parts { 107 if i := strings.IndexByte(part, ';'); i > 0 { 108 part = part[:i] 109 } 110 if part = strings.TrimSpace(part); part != "" { 111 out = append(out, part) 112 } 113 } 114 return out 115 } 116 117 func lastChar(str string) uint8 { 118 if str == "" { 119 panic("The length of the string can't be 0") 120 } 121 return str[len(str)-1] 122 } 123 124 func nameOfFunction(f any) string { 125 return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() 126 } 127 128 func joinPaths(absolutePath, relativePath string) string { 129 if relativePath == "" { 130 return absolutePath 131 } 132 133 finalPath := path.Join(absolutePath, relativePath) 134 if lastChar(relativePath) == '/' && lastChar(finalPath) != '/' { 135 return finalPath + "/" 136 } 137 return finalPath 138 } 139 140 func resolveAddress(addr []string) string { 141 switch len(addr) { 142 case 0: 143 if port := os.Getenv("PORT"); port != "" { 144 debugPrint("Environment variable PORT=\"%s\"", port) 145 return ":" + port 146 } 147 debugPrint("Environment variable PORT is undefined. Using port :8080 by default") 148 return ":8080" 149 case 1: 150 return addr[0] 151 default: 152 panic("too many parameters") 153 } 154 } 155 156 // https://stackoverflow.com/questions/53069040/checking-a-string-contains-only-ascii-characters 157 func isASCII(s string) bool { 158 for i := 0; i < len(s); i++ { 159 if s[i] > unicode.MaxASCII { 160 return false 161 } 162 } 163 return true 164 }