hexdigest / gowrap Goto Github PK
View Code? Open in Web Editor NEWGoWrap is a command line tool for generating decorators for Go interfaces
License: MIT License
GoWrap is a command line tool for generating decorators for Go interfaces
License: MIT License
When generating tests, the package inferred by gowrap is incorrect, and there doesn't seem to be a way to override it.
Very minor point about the provided log template.
If I have a method that returns a single result, but not an error, the template generates a deferred function to set the results but these are not emitted by a log call.
Consequently, the generated code fails to compile because of an unused variable.
I addressed the issue by adding the following before line 42 of the template:
{{else}}
_d._stdlog.Println(_results...)
good afternoon
I've tried to generate a tracing wrapper by my custom teplate which is uploading from github https://github.com/XeniaBgd/templates/blob/main/tracing
The command is :
gowrap gen -p ./internal/domain/repository -i Repository -t https://raw.githubusercontent.com/XeniaBgd/templates/main/tracing -o ./internal/providers/analytics/tracing.go
But I've got this error:
"failed to load template: open https://raw.githubusercontent.com/XeniaBgd/templates/main/tracing: The filename, directory name, or volume label syntax is incorrect"
I've checked code
https://github.com/hexdigest/gowrap/blob/master/cmd_generate.go#L191
func underlyingErrorIs(err, target error) bool {
// Note that this function is not errors.Is:
// underlyingError only unwraps the specific error-wrapping types
// that it historically did, not all errors implementing Unwrap().
err = underlyingError(err)
if err == target {
return true
}
// To preserve prior behavior, only examine syscall errors.
e, ok := err.(syscallErrorType)
return ok && e.Is(target)
}
My error was "internal/syscall/windows.ERROR_INVALID_NAME (123)". That's why the function above returned false.
//if !os.IsNotExist(err) {
// return
//}
Without this code everything is ok.
Could somebody explain me why was that?
I want to edit generated methods and I want them not to be overwritten by next generation. If new methods are added to interface I want them to be generated and added to the existing file.
So what I want is some kind of strategy. Invocation may look like:
gowrap gen -i Some -t mine.tmpl -o generated.go -strategy merge|overwrite
How woild this work:
Template:
type {{$decorator}} struct {
logger *zap.Logger
next {{.Interface.Type}}
}
{{range $method := .Interface.Methods}}
func (m *{{$decorator}}) {{$method.Declaration}} {
m.logger.Info("{{ $method.Name }}")
{{ $method.Pass "m.next." }}
}
{{end}}
Initial interface:
type Some interface {
A()
}
Initial generated file:
type decorator struct {
logger *zap.Logger
next Some
}
func (d *decorator) A() {
m.logger.Info("A")
m.next.A()
}
Updated file:
type decorator struct {
logger *zap.Logger
next Some
}
func (d *decorator) A() {
m.logger.Info("A with updates") // updated generated file
m.next.A()
}
Updated interface:
type Some interface {
A()
B() // New added method
}
Generated with stratedy 'merge' file:
type decorator struct {
logger *zap.Logger
next Some
}
func (d *decorator) A() {
m.logger.Info("A with updates") // updates are not overwritten
m.next.A()
}
func (d *decorator) B() {
m.logger.Info("B")
m.next.A()
}
If you believe there is a place for such a feature in this library I might be able to implement it myself.
When I try to generate a wrapper from an interface in an internal package, gowrap fails.
/path/to/package/internal/generated/prom_test.go
:
package generated
//go:generate gowrap gen -g -p . -i TestInterface -t prometheus -o prom.go
type TestInterface interface{
TestMethod()
}
go generate ./...
failed to parse source package: open repo.git/path/to/package/internal/generated: no such file or directory
internal/generated/prom_test.go:3: running "gowrap": exit status 1
Moving the file out of the internal subpackage makes it work.
We need access to the interface’s package name in a template.
We can see this is available when the header template is executed, but don’t think it is available to body templates.
Could this be made available in the map?
Also, is there any way to customise the header template?
I had a situation, where it would have been helpful, if gowrap
would allow some basic artithmetic in the templates. Go templates by default only have a very limited set of functions available.
https://github.com/Masterminds/sprig provides a rich set of useful functions for Go templates and therefore I propose to add this package to gowrap
and make these functions available.
I'm building a universal development environment for a team using Nix, only gowrap is missing for a complete spec. Could you consider adding your package to the Nixpkgs repository?
Here is docs
https://nixos.wiki/wiki/Nixpkgs/Create_and_debug_packages
And here is oapi-codegen tool example
https://github.com/NixOS/nixpkgs/blob/nixos-22.11/pkgs/tools/networking/oapi-codegen/default.nix
When using file paths to specify the location of a template, the generated code will use either a "/" or a "\" to separate directories in the path. This is an issue if you checkin the generated code and you have some users that generate using a Linux system and other users that generate using a Windows system.
The expectation is that the generated code should not change based on what type of system the generation is performed on.
Propose to do a simple search/replace on the file path and replace any "\" with a "/"
The
func (_d {{$decorator}}) {{$method.Declaration}} {
line generates
func (_d wrapped) Method(ctx context.Context, paramName ParamType) (returnName ReturnType)
instead of the expected
func (_d wrapped) Method(ctx context.Context, paramName wrappedtype.ParamType) (returnName wrappedtype.ReturnType)
This change in behaviour has happened between release 1.2.7 and 1.3.0
Prometheus metrics must be registered. Not registered metrics are not avaliable in metrics path.
There are two ways to resolve it:
*Add metric registration in constructor
*Make metric variable public and delegate registration to developer
I think, it should not be difficult to change usage of time.After
to time.NewTimer
.
The problem is described in docs of time.After.
I some edge cases it should reduce memory consumption.
Because it is a template, it should not break back capability.
{{range $method := .Interface.Methods}}
{{if $method.ReturnsError}}
// {{$method.Name}} implements {{$.Interface.Type}}
func (_d {{$decorator}}) {{$method.Declaration}} {
for _i := 0; _i < _d._retryCount; _i++ {
{{$method.ResultsNames}} = _d.{{$.Interface.Name}}.{{$method.Call}}
if err == nil {
break
}
if _d._retryCount > 1 {
{{- if $method.AcceptsContext}}
_t := time.NewTimer(_d._retryInterval)
defer _t.Stop()
select {
case <-ctx.Done():
return {{$method.ResultsNames}}
case <-_t.C:
}
{{else}}
time.Sleep(_d._retryInterval)
{{end -}}
}
}
return {{$method.ResultsNames}}
}
{{end}}
{{end}}
When trying to generate files with -o option pointing to an empty directory the command fails with
failed to load destination package <relative_destination_package_path> -: unknown import path <full_destination_package_path>: cannot find module providing package <full_destination_package_path>
The error goes away if any .go file is created into destination directory.
The generator does not work with the case when the name of the package imported into source file does not match last import path segment.
For example, source file like
package authz
import (
"mymodulename/pkg/api/iam/v1" // Go package name is "iamv1"
)
type Enforcer interface {
Enforce(ctx context.Context, request *iamv1.Request) ([]string, error)
}
Will result generated code that is using *iamv1.Request
type in method signatures, however mymodulename/pkg/api/iam/v1
will not be added to the imports section.
I tried all sorts of aliases for this module, however, nothing helps. Generator only works if I rename imported package to v1
.
I'm trying to install gowrap using command go get -u github.com/hexdigest/gowrap/cmd/gowrap
And receiving an error:
go get: github.com/cavaliercoder/[email protected] updating to
github.com/cavaliercoder/[email protected]: parsing go.mod:
module declares its path as: github.com/cavaliergopher/cpio
but was required as: github.com/cavaliercoder/go-cpio
Greetings!
We use gowrap and golines in out project. As you can see here golines checks a few top lines of each file to check if it generated, but gowrap places comments after imports block
Could you fix it and place comments on the top?
When attempting to generate from a file with a build tag, gowrap exits with an error.
//go:build unit
package main
//go:generate gowrap gen -g -p . -i TestInterface -t prometheus -o prom.go
type TestInterface interface{
TestMethod()
}
go generate -tags unit ./...
failed to load source package: -: build constraints exclude all Go files in /path/to/package
path/to/package/prom_test.go:5: running "gowrap": exit status 1
We would like to be able to make interface and method ‘documentation’ comments available to templates.
For example, we would like to use a method comment to provide hints to a logging decorator about what and how to log specific params and results (avoiding monolithic log output, hiding sensitive data etc)
The documentary comments are readily available in the ast and so could be copied to the template input.
I’m happy to prototype this and raise a pull request, but would welcome your opinion on whether this would be a useful feature.
proofOfBug.tpl:
{{range $method := .Interface.Methods}}
func {{$method.Declaration}}{}
{{end}}
proofOfBug.go:
//go:generate gowrap gen -g -v DecoratorName=POC -i Base -t proofOfBug.tpl -o proofOfBug.gen.go
type Base interface {
A()
Nested
}
type Nested interface {
B()
}
version 1.2.7 did not ignore nesteds
Would be very useful to have some basic gowrap version
command.
If template has own import
section (e.g. with github.com/prometheus/client_golang/prometheus
) then the generated file header looks correct :
package name
// DO NOT EDIT!
// This code is generated with http://github.com/hexdigest/gowrap tool
// using templates/metrics template
import (
"context"
"github.com/prometheus/client_golang/prometheus"
)
But if template doesn't have own import
section then package comment goes after import
in generated file. It looks bad:
package name
import (
"context"
)
// DO NOT EDIT!
// This code is generated with http://github.com/hexdigest/gowrap tool
// using templates/metrics template
import()
in the beginning of template fixes it but looks weird.
I have code that implements a more advanced error model which is passed around as its own interface rather than error
(which it does, implement). The code:
https://github.com/hexdigest/gowrap/blob/master/generator/types.go#L95
Dees not do an error interface check and therefore does not work for anything except a literal error
type. A proper type check would look something like:
var paramType ast.Type
types.Implements(paramType, ErrorInterface)
where:
// ErrorInterface defines the error interface as a type for comparison.
var ErrorInterface = types.NewInterfaceType([]*types.Func{
types.NewFunc(
0,
nil,
"Error",
types.NewSignatureType(nil, nil, nil, nil,
types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.String])),
false)),
}, nil)
Unfortunately this would require a much larger re-writing of the existing code so I'm only filing an issue instead of a fix :(.
I am curious to know why the use of an unexported type should abort decorator generation (line 138 of printer.go).
I want to generate decorators for an interface that is only used in the context of the package in which it is defined, but this error blocks me from doing that.
If I comment out the check, everything is generated as I need.
I am trying to implement a custom template which needs to provide each of the method's parameters in a certain way
Based on the log
template here: https://github.com/hexdigest/gowrap/blob/master/templates/log
I need to make this code
_params := []interface{}{"{{$decorator}}: calling {{$method.Name}} with params:", {{$method.ParamsNames}} }
To be this code instead
var _params []zapcore.Field
var _field zapcore.Field
{{range $param := $method.ParamsSlice}}
_field = zapcore.Field{ Key:"{{$decorator}}: calling {{$method.Name}} with params:", Type: zapcore.ObjectMarshalerType, Interface: {{$param.Name}}}
_params = append(_params, _field)
{{end}}
To make it happen had to add the ParamsSlice
function to gowrap
code, so the slice is public and available to be used. I may be missing something or not?
This is the code I've added to types.go
to make it work
// ParamsSlice returns a list of method params
func (m Method) ParamsSlice() []Param {
return m.Params
}
Hope this makes sense or please point me to the best practice to consuming that data
Thanks
I as a developer want to be able to pass in a function which will populate the tracing span with domain specific logs / baggage items. This would enable easier to read traces + allow more fine grained control of what NOT to trace (i.e. the context object for example). This would also be needed to prevent sensitive data like passwords to be logged.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.