diff --git a/.gitignore b/.gitignore index 0226c18e..e62b5815 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .vscode +.idea coverage.out .DS_Store **/openapi.json diff --git a/extra/fuegoecho/adaptor.go b/extra/fuegoecho/adaptor.go new file mode 100644 index 00000000..b274eee6 --- /dev/null +++ b/extra/fuegoecho/adaptor.go @@ -0,0 +1,120 @@ +package fuegoecho + +import ( + "net/http" + + "github.com/labstack/echo/v4" + + "github.com/go-fuego/fuego" + "github.com/go-fuego/fuego/internal" +) + +type echoIRouter interface { + Add(method, path string, handler echo.HandlerFunc, middleware ...echo.MiddlewareFunc) *echo.Route +} + +func AddEcho(engine *fuego.Engine, echoRouter *echo.Echo, + method, path string, handler echo.HandlerFunc, + options ...func(*fuego.BaseRoute)) *fuego.Route[any, any] { + return handleEcho(engine, echoRouter, method, path, handler, options...) +} + +func GetEcho(engine *fuego.Engine, echoRouter *echo.Echo, + path string, handler echo.HandlerFunc, + options ...func(*fuego.BaseRoute)) *fuego.Route[any, any] { + return handleEcho(engine, echoRouter, http.MethodGet, path, handler, options...) +} + +func PostEcho(engine *fuego.Engine, echoRouter *echo.Echo, + path string, handler echo.HandlerFunc, + options ...func(*fuego.BaseRoute)) *fuego.Route[any, any] { + return handleEcho(engine, echoRouter, http.MethodPost, path, handler, options...) +} + +func PutEcho(engine *fuego.Engine, echoRouter *echo.Echo, + path string, handler echo.HandlerFunc, + options ...func(*fuego.BaseRoute)) *fuego.Route[any, any] { + return handleEcho(engine, echoRouter, http.MethodPut, path, handler, options...) +} + +func PatchEcho(engine *fuego.Engine, echoRouter *echo.Echo, + path string, handler echo.HandlerFunc, + options ...func(*fuego.BaseRoute)) *fuego.Route[any, any] { + return handleEcho(engine, echoRouter, http.MethodPatch, path, handler, options...) +} + +func DeleteEcho(engine *fuego.Engine, echoRouter *echo.Echo, + path string, handler echo.HandlerFunc, + options ...func(*fuego.BaseRoute)) *fuego.Route[any, any] { + return handleEcho(engine, echoRouter, http.MethodDelete, path, handler, options...) +} + +func Add[T, B any](engine *fuego.Engine, echoRouter *echo.Echo, method, path string, handler func(c fuego.ContextWithBody[B]) (T, error), options ...func(*fuego.BaseRoute)) *fuego.Route[T, B] { + return handleFuego(engine, echoRouter, method, path, handler, options...) +} + +func Get[T, B any](engine *fuego.Engine, echoRouter *echo.Echo, path string, handler func(c fuego.ContextWithBody[B]) (T, error), options ...func(*fuego.BaseRoute)) *fuego.Route[T, B] { + return handleFuego(engine, echoRouter, http.MethodGet, path, handler, options...) +} + +func Post[T, B any](engine *fuego.Engine, echoRouter *echo.Echo, path string, handler func(c fuego.ContextWithBody[B]) (T, error), options ...func(*fuego.BaseRoute)) *fuego.Route[T, B] { + return handleFuego(engine, echoRouter, http.MethodPost, path, handler, options...) +} + +func Put[T, B any](engine *fuego.Engine, echoRouter *echo.Echo, path string, handler func(c fuego.ContextWithBody[B]) (T, error), options ...func(*fuego.BaseRoute)) *fuego.Route[T, B] { + return handleFuego(engine, echoRouter, http.MethodPut, path, handler, options...) +} + +func Patch[T, B any](engine *fuego.Engine, echoRouter *echo.Echo, path string, handler func(c fuego.ContextWithBody[B]) (T, error), options ...func(*fuego.BaseRoute)) *fuego.Route[T, B] { + return handleFuego(engine, echoRouter, http.MethodPatch, path, handler, options...) +} + +func Delete[T, B any](engine *fuego.Engine, echoRouter *echo.Echo, path string, handler func(c fuego.ContextWithBody[B]) (T, error), options ...func(*fuego.BaseRoute)) *fuego.Route[T, B] { + return handleFuego(engine, echoRouter, http.MethodDelete, path, handler, options...) +} + +func handleFuego[T, B any](engine *fuego.Engine, echoRouter *echo.Echo, method, path string, fuegoHandler func(c fuego.ContextWithBody[B]) (T, error), options ...func(*fuego.BaseRoute)) *fuego.Route[T, B] { + baseRoute := fuego.NewBaseRoute(method, path, fuegoHandler, engine.OpenAPI, options...) + return fuego.Registers(engine, echoRouteRegisterer[T, B]{ + echoRouter: echoRouter, + route: fuego.Route[T, B]{BaseRoute: baseRoute}, + echoHandler: EchoHandler(engine, fuegoHandler, baseRoute), + }) +} + +func handleEcho(engine *fuego.Engine, echoRouter *echo.Echo, method, path string, echoHandler echo.HandlerFunc, options ...func(*fuego.BaseRoute)) *fuego.Route[any, any] { + baseRoute := fuego.NewBaseRoute(method, path, echoHandler, engine.OpenAPI, options...) + return fuego.Registers(engine, echoRouteRegisterer[any, any]{ + echoRouter: echoRouter, + route: fuego.Route[any, any]{BaseRoute: baseRoute}, + echoHandler: echoHandler, + }) +} + +type echoRouteRegisterer[T, B any] struct { + echoRouter echoIRouter + echoHandler echo.HandlerFunc + route fuego.Route[T, B] +} + +func (a echoRouteRegisterer[T, B]) Register() fuego.Route[T, B] { + route := a.echoRouter.Add(a.route.Method, a.route.Path, a.echoHandler) + a.route.Path = route.Path + return a.route +} + +// Convert a Fuego handler to a Gin handler. +func EchoHandler[B, T any](engine *fuego.Engine, handler func(c fuego.ContextWithBody[B]) (T, error), route fuego.BaseRoute) echo.HandlerFunc { + return func(c echo.Context) error { + context := &echoContext[B]{ + CommonContext: internal.CommonContext[B]{ + CommonCtx: c.Request().Context(), + UrlValues: c.Request().URL.Query(), + OpenAPIParams: route.Params, + }, + echoCtx: c, + } + fuego.Flow(engine, context, handler) + return nil + } +} diff --git a/extra/fuegoecho/context.go b/extra/fuegoecho/context.go new file mode 100644 index 00000000..a21862fa --- /dev/null +++ b/extra/fuegoecho/context.go @@ -0,0 +1,131 @@ +package fuegoecho + +import ( + "context" + "errors" + "net/http" + "strings" + + "github.com/go-fuego/fuego" + "github.com/go-fuego/fuego/internal" + "github.com/labstack/echo/v4" +) + +type echoContext[B any] struct { + internal.CommonContext[B] + echoCtx echo.Context +} + +var ( + _ fuego.ContextWithBody[any] = &echoContext[any]{} + _ fuego.ContextFlowable[any] = &echoContext[any]{} +) + +func (c echoContext[B]) Body() (B, error) { + var body B + err := c.echoCtx.Bind(&body) + if err != nil { + return body, err + } + + return fuego.TransformAndValidate(c, body) +} + +func (c echoContext[B]) Context() context.Context { + return c.echoCtx.Request().Context() +} + +func (c echoContext[B]) Cookie(name string) (*http.Cookie, error) { + return c.echoCtx.Request().Cookie(name) +} + +func (c echoContext[B]) Header(key string) string { + return c.echoCtx.Request().Header.Get(key) +} + +func (c echoContext[B]) MustBody() B { + body, err := c.Body() + if err != nil { + panic(err) + } + return body +} + +func (c echoContext[B]) PathParam(name string) string { + return c.echoCtx.Param(name) +} + +func (c echoContext[B]) MainLang() string { + return strings.Split(c.MainLocale(), "-")[0] +} + +func (c echoContext[B]) MainLocale() string { + return strings.Split(c.Request().Header.Get("Accept-Language"), ",")[0] +} + +func (c echoContext[B]) Redirect(code int, url string) (any, error) { + c.echoCtx.Redirect(code, url) + return nil, nil +} + +func (c echoContext[B]) Render(templateToExecute string, data any, templateGlobsToOverride ...string) (fuego.CtxRenderer, error) { + panic("unimplemented") +} + +func (c echoContext[B]) Request() *http.Request { + return c.echoCtx.Request() +} + +func (c echoContext[B]) Response() http.ResponseWriter { + return c.echoCtx.Response() +} + +func (c echoContext[B]) SetCookie(cookie http.Cookie) { + c.echoCtx.SetCookie(&cookie) +} + +func (c echoContext[B]) HasCookie(name string) bool { + _, err := c.Cookie(name) + return err == nil +} + +func (c echoContext[B]) HasHeader(key string) bool { + _, ok := c.echoCtx.Request().Header[key] + return ok +} + +func (c echoContext[B]) SetHeader(key, value string) { + c.echoCtx.Response().Header().Add(key, value) +} + +func (c echoContext[B]) SetStatus(code int) { + c.echoCtx.Response().WriteHeader(code) +} + +func (c echoContext[B]) Serialize(data any) error { + status := c.echoCtx.Response().Status + if status == 0 { + status = c.DefaultStatusCode + } + if status == 0 { + status = http.StatusOK + } + c.echoCtx.JSON(status, data) + return nil +} + +func (c echoContext[B]) SerializeError(err error) { + statusCode := http.StatusInternalServerError + var errorWithStatusCode fuego.ErrorWithStatus + if errors.As(err, &errorWithStatusCode) { + statusCode = errorWithStatusCode.StatusCode() + } + c.echoCtx.JSON(statusCode, err) +} + +func (c echoContext[B]) SetDefaultStatusCode() { + if c.DefaultStatusCode == 0 { + c.DefaultStatusCode = http.StatusOK + } + c.SetStatus(c.DefaultStatusCode) +} diff --git a/extra/fuegoecho/context_mock.go b/extra/fuegoecho/context_mock.go new file mode 100644 index 00000000..9bb2b7d0 --- /dev/null +++ b/extra/fuegoecho/context_mock.go @@ -0,0 +1,31 @@ +package fuegoecho + +import ( + "net/http" + "net/url" +) + +type ContextTest[B any] struct { + echoContext[B] + BodyInjected B + ErrorInjected error + + Params url.Values +} + +func (c *ContextTest[B]) Body() (B, error) { + return c.BodyInjected, c.ErrorInjected +} + +func (c *ContextTest[B]) Request() *http.Request { + return c.echoCtx.Request() +} + +func (c *ContextTest[B]) Response() http.ResponseWriter { + return c.echoCtx.Response().Writer +} + +// QueryParam implements fuego.Ctx +func (c *ContextTest[B]) QueryParam(key string) string { + return c.Params.Get(key) +} diff --git a/extra/fuegoecho/go.mod b/extra/fuegoecho/go.mod new file mode 100644 index 00000000..97bfbf35 --- /dev/null +++ b/extra/fuegoecho/go.mod @@ -0,0 +1,37 @@ +module github.com/go-fuego/fuego/extra/fuegoecho + +go 1.23.3 + +require ( + github.com/go-fuego/fuego v0.17.0 + github.com/labstack/echo/v4 v4.13.3 +) + +require ( + github.com/gabriel-vasile/mimetype v1.4.7 // indirect + github.com/getkin/kin-openapi v0.128.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.23.0 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/gorilla/schema v1.4.1 // indirect + github.com/invopop/yaml v0.3.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/labstack/gommon v0.4.2 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mailru/easyjson v0.9.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/extra/fuegoecho/go.sum b/extra/fuegoecho/go.sum new file mode 100644 index 00000000..f51c5c18 --- /dev/null +++ b/extra/fuegoecho/go.sum @@ -0,0 +1,80 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA= +github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU= +github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4= +github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= +github.com/go-fuego/fuego v0.17.0 h1:UBR0ib0Qq0XkGX8J3OTIwrlvHDGmysBVG3NHowA6UCQ= +github.com/go-fuego/fuego v0.17.0/go.mod h1:glaJIBAO3AaZx+c4jGYW4pHyhQkeMGuHx8qLchGAH8M= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= +github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E= +github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= +github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso= +github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY= +github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/thejerf/slogassert v0.3.4 h1:VoTsXixRbXMrRSSxDjYTiEDCM4VWbsYPW5rB/hX24kM= +github.com/thejerf/slogassert v0.3.4/go.mod h1:0zn9ISLVKo1aPMTqcGfG1o6dWwt+Rk574GlUxHD4rs8= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=