├── LICENSE ├── api.go ├── go.mod ├── resource.go ├── example └── sample.go ├── .gitignore ├── README.md └── go.sum /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 hwangseonu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /api.go: -------------------------------------------------------------------------------- 1 | package gin_restful 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | ) 6 | 7 | type API struct { 8 | Prefix string 9 | resources map[string]Resource 10 | } 11 | 12 | func NewAPI(prefix string) *API { 13 | api := new(API) 14 | api.Prefix = prefix 15 | api.resources = make(map[string]Resource) 16 | return api 17 | } 18 | 19 | func (api *API) RegisterResource(path string, resource Resource) { 20 | api.resources[path] = resource 21 | } 22 | 23 | func (api *API) RegisterHandlers(router *gin.RouterGroup) { 24 | for path, resource := range api.resources { 25 | path = api.Prefix + path 26 | handler := gin.HandlerFunc(func(c *gin.Context) { 27 | handleHTTP(resource, c) 28 | }) 29 | 30 | if resource.Create != nil { 31 | router.POST(path, handler) 32 | } 33 | 34 | if resource.ReadAll != nil { 35 | router.GET(path, handler) 36 | } 37 | 38 | path = path + "/:id" 39 | 40 | if resource.Read != nil { 41 | router.GET(path, handler) 42 | } 43 | 44 | if resource.Update != nil { 45 | router.PUT(path, handler) 46 | router.PATCH(path, handler) 47 | } 48 | 49 | if resource.Delete != nil { 50 | router.DELETE(path, handler) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hwangseonu/gin-restful 2 | 3 | go 1.25 4 | 5 | require github.com/gin-gonic/gin v1.10.1 6 | 7 | require ( 8 | github.com/bytedance/gopkg v0.1.3 // indirect 9 | github.com/bytedance/sonic v1.14.1 // indirect 10 | github.com/bytedance/sonic/loader v0.3.0 // indirect 11 | github.com/cloudwego/base64x v0.1.6 // indirect 12 | github.com/gabriel-vasile/mimetype v1.4.10 // indirect 13 | github.com/gin-contrib/sse v1.1.0 // indirect 14 | github.com/go-playground/locales v0.14.1 // indirect 15 | github.com/go-playground/universal-translator v0.18.1 // indirect 16 | github.com/go-playground/validator/v10 v10.27.0 // indirect 17 | github.com/goccy/go-json v0.10.5 // indirect 18 | github.com/json-iterator/go v1.1.12 // indirect 19 | github.com/klauspost/cpuid/v2 v2.3.0 // indirect 20 | github.com/leodido/go-urn v1.4.0 // indirect 21 | github.com/mattn/go-isatty v0.0.20 // indirect 22 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 23 | github.com/modern-go/reflect2 v1.0.2 // indirect 24 | github.com/pelletier/go-toml/v2 v2.2.4 // indirect 25 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 26 | github.com/ugorji/go/codec v1.3.0 // indirect 27 | golang.org/x/arch v0.21.0 // indirect 28 | golang.org/x/crypto v0.42.0 // indirect 29 | golang.org/x/net v0.44.0 // indirect 30 | golang.org/x/sys v0.36.0 // indirect 31 | golang.org/x/text v0.29.0 // indirect 32 | google.golang.org/protobuf v1.36.9 // indirect 33 | gopkg.in/yaml.v3 v3.0.1 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /resource.go: -------------------------------------------------------------------------------- 1 | package gin_restful 2 | 3 | import ( 4 | "net/http" 5 | "reflect" 6 | 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | type Resource interface { 11 | RequestBody(method string) any 12 | 13 | Create(body interface{}, c *gin.Context) (gin.H, int, error) 14 | Read(id string, c *gin.Context) (gin.H, int, error) 15 | ReadAll(c *gin.Context) (gin.H, int, error) 16 | Update(id string, body interface{}, c *gin.Context) (gin.H, int, error) 17 | Delete(id string, c *gin.Context) (gin.H, int, error) 18 | } 19 | 20 | func handleHTTP(resource Resource, c *gin.Context) { 21 | var result gin.H 22 | var status int 23 | var err error 24 | 25 | id := c.Param("id") 26 | body := resource.RequestBody(c.Request.Method) 27 | v := reflect.ValueOf(body) 28 | 29 | if v.IsValid() && !v.IsNil() { 30 | if err := c.ShouldBindJSON(body); err != nil { 31 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 32 | return 33 | } 34 | } 35 | 36 | if id == "" { 37 | switch c.Request.Method { 38 | case "POST": 39 | result, status, err = resource.Create(body, c) 40 | case "GET": 41 | result, status, err = resource.ReadAll(c) 42 | } 43 | } else { 44 | switch c.Request.Method { 45 | case "GET": 46 | result, status, err = resource.Read(id, c) 47 | case "PUT", "PATCH": 48 | result, status, err = resource.Update(id, body, c) 49 | case "DELETE": 50 | result, status, err = resource.Delete(id, c) 51 | } 52 | } 53 | 54 | if err != nil { 55 | _ = c.Error(err) 56 | c.Status(status) 57 | } else { 58 | c.JSON(status, result) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /example/sample.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "strconv" 7 | 8 | "github.com/gin-gonic/gin" 9 | restful "github.com/hwangseonu/gin-restful" 10 | ) 11 | 12 | type SampleSchema struct { 13 | Message string `json:"message"` 14 | } 15 | 16 | var offset = 0 17 | 18 | type Sample struct { 19 | database map[string]SampleSchema 20 | } 21 | 22 | func (r *Sample) RequestBody(method string) any { 23 | if method == "GET" { 24 | return nil 25 | } 26 | return new(SampleSchema) 27 | } 28 | 29 | func (r *Sample) Create(body interface{}, _ *gin.Context) (gin.H, int, error) { 30 | sample := body.(*SampleSchema) 31 | id := strconv.Itoa(offset) 32 | offset += 1 33 | r.database[id] = *sample 34 | 35 | return gin.H{ 36 | "message": sample.Message, 37 | }, http.StatusCreated, nil 38 | } 39 | 40 | func (r *Sample) Read(id string, _ *gin.Context) (gin.H, int, error) { 41 | sample, ok := r.database[id] 42 | 43 | if !ok { 44 | return gin.H{}, 404, nil 45 | } 46 | return gin.H{"message": sample.Message}, http.StatusOK, nil 47 | } 48 | 49 | func (r *Sample) ReadAll(_ *gin.Context) (gin.H, int, error) { 50 | samples := make(map[string]gin.H) 51 | for k, v := range r.database { 52 | samples[k] = gin.H{"message": v.Message} 53 | } 54 | 55 | return gin.H{ 56 | "samples": samples, 57 | }, http.StatusOK, nil 58 | } 59 | 60 | func (r *Sample) Update(id string, body interface{}, _ *gin.Context) (gin.H, int, error) { 61 | sample := body.(*SampleSchema) 62 | 63 | r.database[id] = *sample 64 | 65 | return gin.H{}, http.StatusNoContent, nil 66 | } 67 | 68 | func (r *Sample) Delete(id string, _ *gin.Context) (gin.H, int, error) { 69 | delete(r.database, id) 70 | return gin.H{}, http.StatusNoContent, nil 71 | } 72 | 73 | func main() { 74 | engine := gin.Default() 75 | api := restful.NewAPI("/api/v1") 76 | 77 | sample := &Sample{make(map[string]SampleSchema)} 78 | 79 | api.RegisterResource("/samples", sample) 80 | api.RegisterHandlers(&engine.RouterGroup) 81 | 82 | e := engine.Run(":8080") 83 | if e != nil { 84 | log.Fatalln(e) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/windows,goland,go 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=windows,goland,go 3 | 4 | ### Go ### 5 | # If you prefer the allow list template instead of the deny list, see community template: 6 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 7 | # 8 | # Binaries for programs and plugins 9 | *.exe 10 | *.exe~ 11 | *.dll 12 | *.so 13 | *.dylib 14 | 15 | # Test binary, built with `go test -c` 16 | *.test 17 | 18 | # Output of the go coverage tool, specifically when used with LiteIDE 19 | *.out 20 | 21 | # Dependency directories (remove the comment below to include it) 22 | # vendor/ 23 | 24 | # Go workspace file 25 | go.work 26 | 27 | ### GoLand ### 28 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 29 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 30 | 31 | # User-specific stuff 32 | .idea/** 33 | .idea/**/workspace.xml 34 | .idea/**/tasks.xml 35 | .idea/**/usage.statistics.xml 36 | .idea/**/dictionaries 37 | .idea/**/shelf 38 | 39 | # AWS User-specific 40 | .idea/**/aws.xml 41 | 42 | # Generated files 43 | .idea/**/contentModel.xml 44 | 45 | # Sensitive or high-churn files 46 | .idea/**/dataSources/ 47 | .idea/**/dataSources.ids 48 | .idea/**/dataSources.local.xml 49 | .idea/**/sqlDataSources.xml 50 | .idea/**/dynamic.xml 51 | .idea/**/uiDesigner.xml 52 | .idea/**/dbnavigator.xml 53 | 54 | # Gradle 55 | .idea/**/gradle.xml 56 | .idea/**/libraries 57 | 58 | # Gradle and Maven with auto-import 59 | # When using Gradle or Maven with auto-import, you should exclude module files, 60 | # since they will be recreated, and may cause churn. Uncomment if using 61 | # auto-import. 62 | # .idea/artifacts 63 | # .idea/compiler.xml 64 | # .idea/jarRepositories.xml 65 | # .idea/modules.xml 66 | # .idea/*.iml 67 | # .idea/modules 68 | # *.iml 69 | # *.ipr 70 | 71 | # CMake 72 | cmake-build-*/ 73 | 74 | # Mongo Explorer plugin 75 | .idea/**/mongoSettings.xml 76 | 77 | # File-based project format 78 | *.iws 79 | 80 | # IntelliJ 81 | out/ 82 | 83 | # mpeltonen/sbt-idea plugin 84 | .idea_modules/ 85 | 86 | # JIRA plugin 87 | atlassian-ide-plugin.xml 88 | 89 | # Cursive Clojure plugin 90 | .idea/replstate.xml 91 | 92 | # SonarLint plugin 93 | .idea/sonarlint/ 94 | 95 | # Crashlytics plugin (for Android Studio and IntelliJ) 96 | com_crashlytics_export_strings.xml 97 | crashlytics.properties 98 | crashlytics-build.properties 99 | fabric.properties 100 | 101 | # Editor-based Rest Client 102 | .idea/httpRequests 103 | 104 | # Android studio 3.1+ serialized cache file 105 | .idea/caches/build_file_checksums.ser 106 | 107 | ### GoLand Patch ### 108 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 109 | 110 | # *.iml 111 | # modules.xml 112 | # .idea/misc.xml 113 | # *.ipr 114 | 115 | # Sonarlint plugin 116 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 117 | .idea/**/sonarlint/ 118 | 119 | # SonarQube Plugin 120 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 121 | .idea/**/sonarIssues.xml 122 | 123 | # Markdown Navigator plugin 124 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 125 | .idea/**/markdown-navigator.xml 126 | .idea/**/markdown-navigator-enh.xml 127 | .idea/**/markdown-navigator/ 128 | 129 | # Cache file creation bug 130 | # See https://youtrack.jetbrains.com/issue/JBR-2257 131 | .idea/$CACHE_FILE$ 132 | 133 | # CodeStream plugin 134 | # https://plugins.jetbrains.com/plugin/12206-codestream 135 | .idea/codestream.xml 136 | 137 | # Azure Toolkit for IntelliJ plugin 138 | # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij 139 | .idea/**/azureSettings.xml 140 | 141 | ### Windows ### 142 | # Windows thumbnail cache files 143 | Thumbs.db 144 | Thumbs.db:encryptable 145 | ehthumbs.db 146 | ehthumbs_vista.db 147 | 148 | # Dump file 149 | *.stackdump 150 | 151 | # Folder config file 152 | [Dd]esktop.ini 153 | 154 | # Recycle Bin used on file shares 155 | $RECYCLE.BIN/ 156 | 157 | # Windows Installer files 158 | *.cab 159 | *.msi 160 | *.msix 161 | *.msm 162 | *.msp 163 | 164 | # Windows shortcuts 165 | *.lnk 166 | 167 | # End of https://www.toptal.com/developers/gitignore/api/windows,goland,go -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gin-restful 2 | [![Go Reference](https://pkg.go.dev/badge/github.com/hwangseonu/gin-restful.svg)](https://pkg.go.dev/github.com/hwangseonu/gin-restful) 3 | [![CodeFactor](https://www.codefactor.io/repository/github/hwangseonu/gin-restful/badge)](https://www.codefactor.io/repository/github/hwangseonu/gin-restful) 4 | 5 | A Go library that simplifies and accelerates RESTful API development using the Gin framework. It abstracts away repetitive routing and handler setups, allowing you to easily implement **Create, Read, Update, Delete (CRUD)** functionalities. 6 | 7 | ## Key Features 8 | 9 | * **Automatic Routing:** Automatically maps HTTP methods (POST, GET, PUT, DELETE) to the corresponding CRUD functions defined in the `restful.Resource` interface. 10 | * **Interface-Based Development:** Provides a consistent way to define API resources by simply implementing a predefined interface. 11 | * **Easy Schema Binding:** The `RequestBody` method allows you to easily define the JSON schema for incoming request bodies. 12 | 13 | ## Getting Started 14 | 15 | ### Installation 16 | 17 | First, install the Gin framework and the `gin-restful` library. 18 | 19 | ```bash 20 | go get github.com/gin-gonic/gin 21 | go get github.com/hwangseonu/gin-restful 22 | ``` 23 | 24 | ### Usage Example 25 | The following code is a complete example of how to use the gin-restful library to create a simple sample API. 26 | 27 | ```go 28 | package main 29 | 30 | import ( 31 | "log" 32 | "net/http" 33 | "strconv" 34 | 35 | "github.com/gin-gonic/gin" 36 | restful "github.com/hwangseonu/gin-restful" 37 | ) 38 | 39 | // Define the schema for the request body. 40 | type SampleSchema struct { 41 | Message string `json:"message"` 42 | } 43 | 44 | var offset = 0 45 | 46 | // Define the struct that will implement the restful.Resource interface. 47 | type Sample struct { 48 | database map[string]SampleSchema 49 | } 50 | 51 | // Returns the schema for the request body. 52 | func (r *Sample) RequestBody(_ string) any { 53 | return new(SampleSchema) 54 | } 55 | 56 | // Creates a new resource (POST). 57 | func (r *Sample) Create(body interface{}, _ *gin.Context) (gin.H, int, error) { 58 | sample := body.(*SampleSchema) 59 | id := strconv.Itoa(offset) 60 | offset += 1 61 | r.database[id] = *sample 62 | 63 | return gin.H{ 64 | "message": sample.Message, 65 | }, http.StatusCreated, nil 66 | } 67 | 68 | // Reads a specific resource (GET). 69 | func (r *Sample) Read(id string, _ *gin.Context) (gin.H, int, error) { 70 | sample, ok := r.database[id] 71 | 72 | if !ok { 73 | return gin.H{}, 404, nil 74 | } else { 75 | return gin.H{"message": sample.Message}, http.StatusOK, nil 76 | } 77 | } 78 | 79 | // Reads all resources (GET). 80 | func (r *Sample) ReadAll(_ *gin.Context) (gin.H, int, error) { 81 | samples := make(map[string]gin.H) 82 | for k, v := range r.database { 83 | samples[k] = gin.H{"message": v.Message} 84 | } 85 | 86 | return gin.H{ 87 | "samples": samples, 88 | }, http.StatusOK, nil 89 | } 90 | 91 | // Updates a specific resource (PUT). 92 | func (r *Sample) Update(id string, body interface{}, _ *gin.Context) (gin.H, int, error) { 93 | sample := body.(*SampleSchema) 94 | 95 | r.database[id] = *sample 96 | 97 | return gin.H{}, http.StatusNoContent, nil 98 | } 99 | 100 | // Deletes a specific resource (DELETE). 101 | func (r *Sample) Delete(id string, _ *gin.Context) (gin.H, int, error) { 102 | delete(r.database, id) 103 | return gin.H{}, http.StatusNoContent, nil 104 | } 105 | 106 | func main() { 107 | // Initialize the Gin engine and restful API. 108 | engine := gin.Default() 109 | api := restful.NewAPI("/api/v1") 110 | 111 | // Create a resource instance. 112 | sample := &Sample{make(map[string]SampleSchema)} 113 | 114 | // Register the resource routing. 115 | api.RegisterResource("/samples", sample) 116 | api.RegisterHandlers(engine) 117 | 118 | // Run the server. 119 | e := engine.Run(":8080") 120 | if e != nil { 121 | log.Fatalln(e) 122 | } 123 | } 124 | ``` 125 | 126 | ### API Endpoints 127 | The api.RegisterResource("/samples", sample) line in the example code automatically generates the following RESTful endpoints. 128 | 129 | | HTTP Method | Endpoint | Description | 130 | |:------------| :------------------ | :--------------------------- | 131 | | `POST` | `/api/v1/samples` | Creates a new resource | 132 | | `GET` | `/api/v1/samples` | Retrieves all resources | 133 | | `GET` | `/api/v1/samples/:id` | Retrieves a specific resource | 134 | | `PUT` | `/api/v1/samples/:id` | Updates a specific resource | 135 | | `PATCH` | `/api/v1/samples/:id` | Updates a specific resource | 136 | | `DELETE` | `/api/v1/samples/:id` | Deletes a specific resource | 137 | 138 | ## License 139 | This project is licensed under the MIT License. See the LICENSE file for details. -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= 2 | github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= 3 | github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w= 4 | github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc= 5 | github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= 6 | github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= 7 | github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= 8 | github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= 9 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 11 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= 13 | github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= 14 | github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= 15 | github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= 16 | github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= 17 | github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= 18 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 19 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 20 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 21 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 22 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 23 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 24 | github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= 25 | github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= 26 | github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= 27 | github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 28 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 29 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 30 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 31 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 32 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 33 | github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= 34 | github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= 35 | github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= 36 | github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= 37 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 38 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 39 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 40 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 41 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 42 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 43 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 44 | github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= 45 | github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= 46 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 47 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 48 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 49 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 50 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 51 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 52 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 53 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 54 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 55 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 56 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 57 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 58 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 59 | github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= 60 | github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= 61 | golang.org/x/arch v0.21.0 h1:iTC9o7+wP6cPWpDWkivCvQFGAHDQ59SrSxsLPcnkArw= 62 | golang.org/x/arch v0.21.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= 63 | golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= 64 | golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= 65 | golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= 66 | golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= 67 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 68 | golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= 69 | golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 70 | golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= 71 | golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= 72 | google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= 73 | google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= 74 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 75 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 76 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 77 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 78 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 79 | --------------------------------------------------------------------------------