1+ // Package pkg is the public lib of SQLTemplate.
2+ // SQLTemplate is a simple template engine for writing
3+ // dynamic SQL queries.
14package pkg
25
36import (
7+ "bytes"
48 "fmt"
59 "io/fs"
6- "text/template"
10+ txtTemplate "text/template"
711)
812
9- // QueryTemplateEngine is the interface implemented by types that can parse sql templates.
10- type QueryTemplateEngine interface {
11- // Parse parses a sql template and returns the 'QueryTemplate'
12- Parse (namespace string , templateName string ) (QueryTemplate , error )
13- // ParseWithValuesFromMap parses a sql template with values from a map and returns the 'QueryTemplate'
14- ParseWithValuesFromMap (namespace string , templateName string , parameters map [string ]interface {}) (QueryTemplate , error )
15- // ParseWithValuesFromStruct parses a sql template with values from a struct and returns the 'QueryTemplate'
16- ParseWithValuesFromStruct (namespace string , templateName string , parameters interface {}) (QueryTemplate , error )
17- // Register registers a new namespace by template filesystem and extension
13+ // TemplateEngine is the interface implemented by types that can parse sql templates.
14+ type TemplateEngine interface {
15+ // Parse parses a sql template and returns the 'Template'.
16+ Parse (namespace string , templateName string ) (Template , error )
17+ // ParseWithValuesFromMap parses a sql template with values from a map and returns the 'Template'.
18+ ParseWithValuesFromMap (namespace string , templateName string , parameters map [string ]interface {}) (Template , error )
19+ // ParseWithValuesFromStruct parses a sql template with values from a struct and returns the 'Template'.
20+ ParseWithValuesFromStruct (namespace string , templateName string , parameters interface {}) (Template , error )
21+ // Register registers a new namespace by template filesystem and extension.
1822 Register (namespace string , filesystem fs.FS , extensions string ) error
1923}
2024
21- // QueryTemplate is the interface implemented by types that holds the parsed template sql query context.
22- type QueryTemplate interface {
25+ // QueryTemplateEngine is for backwards compatibility.
26+ // Deprecated: use TemplateEngine.
27+ type QueryTemplateEngine = TemplateEngine
28+
29+ // Template is the interface implemented by types that holds the parsed template sql context.
30+ type Template interface {
2331 // GetQuery returns the query containing named values.
2432 GetQuery () string
2533 // GetParams returns the values in order.
2634 GetParams () []interface {}
2735}
2836
37+ // QueryTemplate is for backwards compatibility.
38+ // Deprecated: use Template.
39+ type QueryTemplate = Template
40+
2941// Option definition.
30- type Option func (* queryTemplateEngine )
42+ type Option func (* templateEngine )
3143
32- type queryTemplateEngine struct {
44+ type templateEngine struct {
3345 repository * repository
3446 bindingEngine bindingEngine
3547}
3648
37- type queryTemplate struct {
49+ type template struct {
3850 template string
3951 params []interface {}
4052}
4153
42- func (t queryTemplate ) GetQuery () string {
54+ func (t template ) GetQuery () string {
4355 return t .template
4456}
4557
46- func (t queryTemplate ) GetParams () []interface {} {
58+ func (t template ) GetParams () []interface {} {
4759 return t .params
4860}
4961
5062// WithTemplateFunctions creates an Option func to set template functions.
5163// nolint:deadcode
52- func WithTemplateFunctions (funcMap template .FuncMap ) Option {
53- return func (queryTypeEngine * queryTemplateEngine ) {
54- queryTypeEngine .repository .addFunctions (funcMap )
64+ func WithTemplateFunctions (funcMap txtTemplate .FuncMap ) Option {
65+ return func (templateTypeEngine * templateEngine ) {
66+ templateTypeEngine .repository .addFunctions (funcMap )
5567 }
5668}
5769
5870// WithBindingEngine creates an Option func to set custom binding engine.
5971// nolint:deadcode
6072func WithBindingEngine (bEngine bindingEngine ) Option {
61- return func (queryTypeEngine * queryTemplateEngine ) {
62- queryTypeEngine .bindingEngine = bEngine
73+ return func (templateTypeEngine * templateEngine ) {
74+ templateTypeEngine .bindingEngine = bEngine
6375 }
6476}
6577
6678// WithPlaceholderFunc creates an Option func to set custom placeholder function.
6779// nolint:deadcode
6880func WithPlaceholderFunc (placeholderfunc placeholderFunc ) Option {
69- return func (queryTypeEngine * queryTemplateEngine ) {
70- if queryTypeEngine .bindingEngine == nil {
71- queryTypeEngine .bindingEngine = NewBindingEngine ()
81+ return func (templateTypeEngine * templateEngine ) {
82+ if templateTypeEngine .bindingEngine == nil {
83+ templateTypeEngine .bindingEngine = NewBindingEngine ()
7284 }
7385
74- queryTypeEngine .bindingEngine .SetPlaceholderFunc (placeholderfunc )
86+ templateTypeEngine .bindingEngine .SetPlaceholderFunc (placeholderfunc )
7587 }
7688}
7789
78- // NewQueryTemplateEngine returns a new instance of 'QueryTemplateEngine '.
79- func NewQueryTemplateEngine (options ... Option ) QueryTemplateEngine {
80- templateEngine := & queryTemplateEngine {repository : newRepository (), bindingEngine : nil }
90+ // NewTemplateEngine returns a new instance of 'TemplateEngine '.
91+ func NewTemplateEngine (options ... Option ) TemplateEngine {
92+ templateEngine := & templateEngine {repository : newRepository (), bindingEngine : nil }
8193
8294 // Apply options if there are any, can overwrite default
8395 for _ , option := range options {
@@ -87,7 +99,12 @@ func NewQueryTemplateEngine(options ...Option) QueryTemplateEngine {
8799 return templateEngine
88100}
89101
90- func (q queryTemplateEngine ) Register (namespace string , filesystem fs.FS , ext string ) error {
102+ // Deprecated: use NewTemplateEngine.
103+ func NewQueryTemplateEngine (options ... Option ) TemplateEngine {
104+ return NewTemplateEngine (options ... )
105+ }
106+
107+ func (q templateEngine ) Register (namespace string , filesystem fs.FS , ext string ) error {
91108 err := q .repository .add (namespace , filesystem , ext )
92109 if err != nil {
93110 return fmt .Errorf ("could not register the namespace %s %w" , namespace , err )
@@ -96,29 +113,59 @@ func (q queryTemplateEngine) Register(namespace string, filesystem fs.FS, ext st
96113 return nil
97114}
98115
99- func (q queryTemplateEngine ) Parse (namespace string , templateName string ) (QueryTemplate , error ) {
100- sqlQuery , bindings , err := q .repository . parse (namespace , templateName , nil , q . bindingEngine )
116+ func (q templateEngine ) Parse (namespace string , templateName string ) (Template , error ) {
117+ sqlQuery , bindings , err := q .parse (namespace , templateName , nil )
101118 if err != nil {
102119 return nil , fmt .Errorf ("unable to parse %s for namespace %s %w" , templateName , namespace , err )
103120 }
104121
105- return & queryTemplate {sqlQuery , bindings }, nil
122+ return & template {sqlQuery , bindings }, nil
106123}
107124
108- func (q queryTemplateEngine ) ParseWithValuesFromMap (namespace string , templateName string , parameters map [string ]interface {}) (QueryTemplate , error ) {
109- sqlQuery , bindings , err := q .repository . parse (namespace , templateName , parameters , q . bindingEngine )
125+ func (q templateEngine ) ParseWithValuesFromMap (namespace string , templateName string , parameters map [string ]interface {}) (Template , error ) {
126+ sqlQuery , bindings , err := q .parse (namespace , templateName , parameters )
110127 if err != nil {
111128 return nil , fmt .Errorf ("unable to parse %s for namespace %s %w" , templateName , namespace , err )
112129 }
113130
114- return & queryTemplate {sqlQuery , bindings }, nil
131+ return & template {sqlQuery , bindings }, nil
115132}
116133
117- func (q queryTemplateEngine ) ParseWithValuesFromStruct (namespace string , templateName string , parameters interface {}) (QueryTemplate , error ) {
118- sqlQuery , bindings , err := q .repository . parse (namespace , templateName , parameters , q . bindingEngine )
134+ func (q templateEngine ) ParseWithValuesFromStruct (namespace string , templateName string , parameters interface {}) (Template , error ) {
135+ sqlQuery , bindings , err := q .parse (namespace , templateName , parameters )
119136 if err != nil {
120137 return nil , fmt .Errorf ("unable to parse %s for namespace %s %w" , templateName , namespace , err )
121138 }
122139
123- return & queryTemplate {sqlQuery , bindings }, nil
140+ return & template {sqlQuery , bindings }, nil
141+ }
142+
143+ // parse executes the template and returns the resulting SQL or an error.
144+ func (q templateEngine ) parse (namespace string , name string , data interface {}) (string , []interface {}, error ) {
145+ tmpl , err := q .repository .getTemplate (namespace )
146+ if err != nil {
147+ return "" , nil , err
148+ }
149+
150+ var bEngine bindingEngine
151+
152+ // Apply the bind function which stores the values for any placeholder parameters
153+ if q .bindingEngine == nil {
154+ bEngine = & DefaultBindingEngine {values : []any {}, index : 0 , placeholderFunc : defaultPlaceholderFunc }
155+ } else {
156+ bEngine = q .bindingEngine .new ()
157+ }
158+
159+ tmpl .Funcs (txtTemplate.FuncMap {"bind" : func (value any ) string {
160+ index := bEngine .storeValue (value )
161+
162+ return bEngine .getPlaceholderFunc ()(value , index )
163+ }})
164+
165+ var b bytes.Buffer
166+ if err := tmpl .ExecuteTemplate (& b , name , data ); err != nil {
167+ return "" , nil , fmt .Errorf ("unable to execute template %w" , err )
168+ }
169+
170+ return b .String (), bEngine .getValues (), nil
124171}
0 commit comments