diff --git a/.github/example.png b/.github/example.png new file mode 100644 index 0000000..9a13a3c Binary files /dev/null and b/.github/example.png differ diff --git a/README.md b/README.md index 3f929a7..559ddde 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,10 @@ `moldable` builds precise interfaces from any package so you can plug in `mockery`, `gomock`, `moq`, or any other mock tool you like. +| ![Example](./.github/example.png) | +| :--: | +| *Example: every exported AWS S3 struct becomes its own ready-to-use interface.* | + > [!WARNING] > This project is in active development and may contain bugs or breaking changes. We recommend testing thoroughly in your environment before using in production. Issues and contributions are welcome! @@ -105,7 +109,6 @@ moldable version Re-run `moldable` whenever the upstream code changes; imports and file lists are recalculated automatically. --------------------------------------------------- ## Configuration The file `moldable.yaml` is created by `moldable init` command. diff --git a/go.mod b/go.mod index 5b5bbc4..b0f4342 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/spf13/cobra v1.10.1 github.com/spf13/pflag v1.0.10 github.com/stretchr/testify v1.11.1 - golang.org/x/exp v0.0.0-20250911091902-df9299821621 golang.org/x/tools v0.37.0 ) @@ -37,6 +36,7 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect golang.org/x/mod v0.28.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/go.sum b/go.sum index 2d25fc5..1cc29d1 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,6 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0= -golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU= golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk= golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= diff --git a/internal/astfile/file.go b/internal/astfile/file.go index 9c86aeb..46dbc32 100644 --- a/internal/astfile/file.go +++ b/internal/astfile/file.go @@ -22,13 +22,15 @@ type InterfaceSpec struct { } type File struct { + fset *token.FileSet packageName string imports []*ImportSpec interfaces []*InterfaceSpec } -func New(packageName string) *File { +func New(fset *token.FileSet, packageName string) *File { return &File{ + fset: fset, packageName: packageName, imports: make([]*ImportSpec, 0), interfaces: make([]*InterfaceSpec, 0), @@ -61,8 +63,15 @@ func (f *File) Build(qual types.Qualifier) (*ast.File, error) { decls = append(decls, interfaces...) return &ast.File{ - Name: ast.NewIdent(f.packageName), - Decls: decls, + Doc: &ast.CommentGroup{ + List: []*ast.Comment{{ + Slash: 1, + Text: "// Code generated by moldable; DO NOT EDIT.", + }}, + }, + Package: 2, + Name: ast.NewIdent(f.packageName), + Decls: decls, }, nil } diff --git a/internal/astfile/writer.go b/internal/astfile/writer.go index 51f006a..5135805 100644 --- a/internal/astfile/writer.go +++ b/internal/astfile/writer.go @@ -18,13 +18,11 @@ func NewWriter() *Writer { return &Writer{} } -func (Writer) Write(file *ast.File, path string) error { +func (Writer) Write(fset *token.FileSet, file *ast.File, path string) error { if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { return fmt.Errorf("creating directory: %w", err) } - fset := token.NewFileSet() - var buf bytes.Buffer if err := format.Node(&buf, fset, file); err != nil { diff --git a/internal/generator/generator.go b/internal/generator/generator.go index e77cd78..4cf5a22 100644 --- a/internal/generator/generator.go +++ b/internal/generator/generator.go @@ -2,6 +2,7 @@ package generator import ( "fmt" + "go/token" "go/types" "path/filepath" "strings" @@ -57,7 +58,13 @@ func (g Generator) Generate() error { func (g Generator) processPackage(pkg *types.Package) error { g.reporter.ProcessingPackage(pkg.Path()) - builder := astfile.New(g.config.Output.Package) + filename := strings.ReplaceAll(g.config.Output.Filename, "{package}", pkg.Name()) + + fset := token.NewFileSet() + + fset.AddFile(filename, fset.Base(), 1) + + builder := astfile.New(fset, g.config.Output.Package) is := importset.New() is.Import(pkg) @@ -106,12 +113,9 @@ func (g Generator) processPackage(pkg *types.Package) error { return fmt.Errorf("building ast file: %w", err) } - path := filepath.Join( - g.config.Output.Dir, - strings.ReplaceAll(g.config.Output.Filename, "{package}", pkg.Name()), - ) + output := filepath.Join(g.config.Output.Dir, filename) - if err := g.writer.Write(file, path); err != nil { + if err := g.writer.Write(fset, file, output); err != nil { return fmt.Errorf("writing file: %w", err) }