-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcodegen.go
More file actions
108 lines (91 loc) · 3.09 KB
/
codegen.go
File metadata and controls
108 lines (91 loc) · 3.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/*
Copyright 2024 Olivier Mengué
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package codegen provides utilities for writing short Go code generators from a text/template.
package codegen
import (
"bytes"
"errors"
"go/format"
"os"
"regexp"
"sync"
"text/template"
)
// GeneratedCodeRegexp checks the code generation standard
// defined at https://go.dev/s/generatedcode.
//
// Deprecated: use instead [go/ast.IsGenerated].
var GeneratedCodeRegexp = regexp.MustCompile(`(?m:^// Code generated .* DO NOT EDIT\.$)`)
// CreateFile runs the "text/template".Template with data, pass it through gofmt
// and saves it to filePath.
func CreateFile(filePath string, t *template.Template, data interface{}) (err error) {
return (&CodeTemplate{Template: t}).CreateFile(filePath, data)
}
// CodeTemplate is the precompiled template for generating one or multiple Go source files.
type CodeTemplate struct {
Template *template.Template // See "text/template"
Buffer bytes.Buffer // Used for sharing allocated memory between multiple CreateFile calls
mu sync.Mutex
}
// Parse creates a CodeTemplate from a "text/template" source.
//
// The expansion of the template is expected to be valid a Go source file
// containing the [code generation standard tag].
//
// [code generation standard tag]: https://go.dev/s/generatedcode].
func Parse(codeTemplate string) (*CodeTemplate, error) {
t, err := template.New("").Parse(codeTemplate)
if err != nil {
return nil, err
}
return &CodeTemplate{Template: t}, nil
}
// MustParse wraps Parse throwing errors as exception.
func MustParse(codeTemplate string) *CodeTemplate {
tmpl, err := Parse(codeTemplate)
if err != nil {
panic(err)
}
return tmpl
}
// CreateFile runs the template with data, pass it through gofmt
// and saves it to filePath.
//
// The code generation standard at https://golang.org/s/generatedcode is enforced.
func (t *CodeTemplate) CreateFile(filePath string, data interface{}) error {
// This anonymous function exists just to wrap the mutex protected block
out, err := func() ([]byte, error) {
// To protect t.Buffer
t.mu.Lock()
defer t.mu.Unlock()
t.Buffer.Reset()
if err := t.Template.Execute(&t.Buffer, data); err != nil {
return nil, err
}
code := t.Buffer.Bytes()
// Enforce code generation standard https://golang.org/s/generatedcode
if !GeneratedCodeRegexp.Match(code) {
return nil, errors.New("output does not follow standard defined at https://golang.org/s/generatedcode")
}
return format.Source(code)
}()
if err != nil {
return err
}
f, err := os.Create(filePath)
if err == nil {
defer f.Close()
_, err = f.Write(out)
}
return err
}