@@ -11,7 +11,9 @@ import (
11
11
"go.uber.org/zap"
12
12
)
13
13
14
- var _ ottlParser [any ] = (* Parser [any ])(nil )
14
+ var _ interface {
15
+ ParseStatements (statements []string ) ([]* Statement [any ], error )
16
+ } = (* Parser [any ])(nil )
15
17
16
18
var _ ParsedStatementConverter [any , StatementsGetter , any ] = func (
17
19
_ * ParserCollection [StatementsGetter , any ],
@@ -22,65 +24,93 @@ var _ ParsedStatementConverter[any, StatementsGetter, any] = func(
22
24
return nil , nil
23
25
}
24
26
25
- type ottlParser [K any ] interface {
26
- // ParseStatements is the same as Parser.ParseStatements
27
- ParseStatements (statements []string ) ([]* Statement [K ], error )
28
- // AppendStatementPathsContext is the same as Parser.AppendStatementPathsContext
29
- AppendStatementPathsContext (context string , statement string ) (string , error )
30
- }
31
-
32
- // StatementsGetter represents the input statements to be parsed
27
+ // StatementsGetter represents a set of statements to be parsed
33
28
type StatementsGetter interface {
29
+ // GetStatements retrieves the OTTL statements to be parsed
34
30
GetStatements () []string
35
31
}
36
32
33
+ // ottlParserWrapper wraps an ottl.Parser using reflection, so it can invoke exported
34
+ // methods without knowing its generic type (transform context).
37
35
type ottlParserWrapper [S StatementsGetter ] struct {
38
- reflect.Value
36
+ parser reflect.Value
37
+ prependContextToStatementPaths func (context string , statement string ) (string , error )
39
38
}
40
39
41
- func (g * ottlParserWrapper [S ]) ParseStatements (statements []string ) (reflect.Value , error ) {
42
- method := g .MethodByName ("ParseStatements" )
43
- psr := method .Call ([]reflect.Value {reflect .ValueOf (statements )})
44
- err := psr [1 ]
45
- if ! err .IsNil () {
46
- return reflect.Value {}, err .Interface ().(error )
40
+ func newParserWrapper [K any , S StatementsGetter ](parser * Parser [K ]) * ottlParserWrapper [S ] {
41
+ return & ottlParserWrapper [S ]{
42
+ parser : reflect .ValueOf (parser ),
43
+ prependContextToStatementPaths : parser .prependContextToStatementPaths ,
47
44
}
48
- return psr [0 ], nil
49
45
}
50
46
51
- func (g * ottlParserWrapper [S ]) AppendStatementPathsContext ( context string , statement string ) (string , error ) {
52
- method := g .MethodByName ("AppendStatementPathsContext " )
53
- psr := method .Call ([]reflect.Value {reflect .ValueOf (context ), reflect . ValueOf ( statement )})
54
- err := psr [1 ]
47
+ func (g * ottlParserWrapper [S ]) parseStatements ( statements [] string ) (reflect. Value , error ) {
48
+ method := g .parser . MethodByName ("ParseStatements " )
49
+ parseStatementsRes := method .Call ([]reflect.Value {reflect .ValueOf (statements )})
50
+ err := parseStatementsRes [1 ]
55
51
if ! err .IsNil () {
56
- return "" , err .Interface ().(error )
52
+ return reflect. Value {} , err .Interface ().(error )
57
53
}
58
- return psr [0 ]. Interface ().( string ) , nil
54
+ return parseStatementsRes [0 ], nil
59
55
}
60
56
61
- func newParserWrapper [K any , S StatementsGetter ](parser * Parser [K ]) * ottlParserWrapper [S ] {
62
- return & ottlParserWrapper [S ]{reflect .ValueOf (parser )}
57
+ func (g * ottlParserWrapper [S ]) prependContextToStatementsPaths (context string , statements []string ) ([]string , error ) {
58
+ result := make ([]string , 0 , len (statements ))
59
+ for _ , s := range statements {
60
+ prependedStatement , err := g .prependContextToStatementPaths (context , s )
61
+ if err != nil {
62
+ return nil , err
63
+ }
64
+ result = append (result , prependedStatement )
65
+ }
66
+ return result , nil
63
67
}
64
68
69
+ // statementsConverterWrapper is reflection-based wrapper to the ParsedStatementConverter function,
70
+ // which does not require knowing all generic parameters to be called.
65
71
type statementsConverterWrapper [S StatementsGetter ] reflect.Value
66
72
67
- func (s statementsConverterWrapper [S ]) Call (in []reflect.Value ) []reflect.Value {
68
- return reflect .Value (s ).Call (in )
69
- }
70
-
71
73
func newStatementsConverterWrapper [K any , S StatementsGetter , R any ](converter ParsedStatementConverter [K , S , R ]) statementsConverterWrapper [S ] {
72
74
return statementsConverterWrapper [S ](reflect .ValueOf (converter ))
73
75
}
74
76
75
- type contextParserWrapper [S StatementsGetter ] struct {
77
+ func (s statementsConverterWrapper [S ]) call (
78
+ parserCollection reflect.Value ,
79
+ ottlParser * ottlParserWrapper [S ],
80
+ context string ,
81
+ statements S ,
82
+ parsedStatements reflect.Value ,
83
+ ) (reflect.Value , error ) {
84
+ result := reflect .Value (s ).Call ([]reflect.Value {
85
+ parserCollection ,
86
+ ottlParser .parser ,
87
+ reflect .ValueOf (context ),
88
+ reflect .ValueOf (statements ),
89
+ parsedStatements ,
90
+ })
91
+
92
+ resultValue := result [0 ]
93
+ resultError := result [1 ]
94
+ if ! resultError .IsNil () {
95
+ return reflect.Value {}, resultError .Interface ().(error )
96
+ }
97
+
98
+ return resultValue , nil
99
+ }
100
+
101
+ // parserCollectionParser holds an ottlParserWrapper and its respectively
102
+ // statementsConverter function.
103
+ type parserCollectionParser [S StatementsGetter ] struct {
76
104
ottlParser * ottlParserWrapper [S ]
77
105
statementsConverter statementsConverterWrapper [S ]
78
106
}
79
107
108
+ // ParserCollection is a configurable set of ottl.Parser that can handle multiple OTTL contexts
109
+ // parsings, inferring the context and choosing the right parser for the given statements.
80
110
type ParserCollection [S StatementsGetter , R any ] struct {
81
- contextParsers map [string ]* contextParserWrapper [S ]
82
- contextInferrer ContextInferrer
83
- contextRewriteLogEnabled bool
111
+ contextParsers map [string ]* parserCollectionParser [S ]
112
+ contextInferrer contextInferrer
113
+ modifiedStatementLogging bool
84
114
Settings component.TelemetrySettings
85
115
ErrorMode ErrorMode
86
116
}
@@ -92,8 +122,9 @@ func NewParserCollection[S StatementsGetter, R any](
92
122
options ... ParserCollectionOption [S , R ]) (* ParserCollection [S , R ], error ) {
93
123
94
124
pc := & ParserCollection [S , R ]{
95
- Settings : settings ,
96
- contextParsers : map [string ]* contextParserWrapper [S ]{},
125
+ Settings : settings ,
126
+ contextParsers : map [string ]* parserCollectionParser [S ]{},
127
+ contextInferrer : defaultPriorityContextInferrer (),
97
128
}
98
129
99
130
for _ , op := range options {
@@ -103,13 +134,14 @@ func NewParserCollection[S StatementsGetter, R any](
103
134
}
104
135
}
105
136
106
- if pc .contextInferrer == nil {
107
- pc .contextInferrer = NewDefaultContextInferrer ()
108
- }
109
-
110
137
return pc , nil
111
138
}
112
139
140
+ // ParsedStatementConverter is a function that converts the parsed ottl.Statement[K] into
141
+ // a common representation to all parser collection contexts WithParserCollectionContext.
142
+ // Given each parser has its own transform context type, they must agree on a common type [R]
143
+ // so is can be returned by the ParserCollection.ParseStatements and ParserCollection.ParseStatementsWithContext
144
+ // functions.
113
145
type ParsedStatementConverter [T any , S StatementsGetter , R any ] func (
114
146
collection * ParserCollection [S , R ],
115
147
parser * Parser [T ],
@@ -118,7 +150,7 @@ type ParsedStatementConverter[T any, S StatementsGetter, R any] func(
118
150
parsedStatements []* Statement [T ],
119
151
) (R , error )
120
152
121
- func NewNopParsedStatementConverter [T any , S StatementsGetter ]() ParsedStatementConverter [T , S , any ] {
153
+ func newNopParsedStatementConverter [T any , S StatementsGetter ]() ParsedStatementConverter [T , S , any ] {
122
154
return func (
123
155
_ * ParserCollection [S , any ],
124
156
_ * Parser [T ],
@@ -129,115 +161,125 @@ func NewNopParsedStatementConverter[T any, S StatementsGetter]() ParsedStatement
129
161
}
130
162
}
131
163
132
- func WithContextParser [K any , S StatementsGetter , R any ](
164
+ // WithParserCollectionContext configures an ottl.Parser for the given context.
165
+ // The provided ottl.Parser must be configured to support the provided context using
166
+ // the ottl.WithPathContextNames option.
167
+ func WithParserCollectionContext [K any , S StatementsGetter , R any ](
133
168
context string ,
134
169
parser * Parser [K ],
135
170
converter ParsedStatementConverter [K , S , R ],
136
171
) ParserCollectionOption [S , R ] {
137
172
return func (mp * ParserCollection [S , R ]) error {
138
- if len ( parser .pathContextNames ) == 0 {
139
- WithPathContextNames [ K ]([] string { context })( parser )
173
+ if _ , ok := parser .pathContextNames [ context ]; ! ok {
174
+ return fmt . Errorf ( ` context "%s" must be a valid "%T" path context name` , context , parser )
140
175
}
141
- mp .contextParsers [context ] = & contextParserWrapper [S ]{
176
+ mp .contextParsers [context ] = & parserCollectionParser [S ]{
142
177
ottlParser : newParserWrapper [K , S ](parser ),
143
178
statementsConverter : newStatementsConverterWrapper (converter ),
144
179
}
145
180
return nil
146
181
}
147
182
}
148
183
184
+ // WithParserCollectionErrorMode has no effect on the ParserCollection, but might be used
185
+ // by the ParsedStatementConverter functions to handle/create StatementSequence.
149
186
func WithParserCollectionErrorMode [S StatementsGetter , R any ](errorMode ErrorMode ) ParserCollectionOption [S , R ] {
150
187
return func (tp * ParserCollection [S , R ]) error {
151
188
tp .ErrorMode = errorMode
152
189
return nil
153
190
}
154
191
}
155
192
156
- func WithParserCollectionContextInferrer [S StatementsGetter , R any ](contextInferrer ContextInferrer ) ParserCollectionOption [S , R ] {
157
- return func (tp * ParserCollection [S , R ]) error {
158
- tp .contextInferrer = contextInferrer
159
- return nil
160
- }
161
- }
162
-
163
- func WithParserCollectionContextRewriteLog [S StatementsGetter , R any ](enabled bool ) ParserCollectionOption [S , R ] {
193
+ // EnableParserCollectionModifiedStatementLogging controls the statements modification logs.
194
+ // When enabled, it logs any statements modifications performed by the parsing operations,
195
+ // instructing users to rewrite the statements accordingly.
196
+ func EnableParserCollectionModifiedStatementLogging [S StatementsGetter , R any ](enabled bool ) ParserCollectionOption [S , R ] {
164
197
return func (tp * ParserCollection [S , R ]) error {
165
- tp .contextRewriteLogEnabled = enabled
198
+ tp .modifiedStatementLogging = enabled
166
199
return nil
167
200
}
168
201
}
169
202
203
+ // ParseStatements parses the given statements into [R] using the configured context's ottl.Parser
204
+ // and subsequently calling the ParsedStatementConverter function.
205
+ // The statement's context is automatically inferred from the [Path.Context] values, choosing the
206
+ // highest priority context found.
207
+ // If no contexts are present in the statements, or if the inferred value is not supported by
208
+ // the [ParserCollection], it returns an error.
209
+ // If parsing the statements fails, it returns the underline [ottl.Parser.ParseStatements] error.
170
210
func (pc * ParserCollection [S , R ]) ParseStatements (statements S ) (R , error ) {
171
- zero := * new (R )
172
211
statementsValues := statements .GetStatements ()
173
-
174
- inferredContext , err := pc .contextInferrer .Infer (statementsValues )
212
+ inferredContext , err := pc .contextInferrer .infer (statementsValues )
175
213
if err != nil {
176
- return zero , err
214
+ return * new ( R ) , err
177
215
}
178
216
179
217
if inferredContext == "" {
180
- return zero , fmt .Errorf ("unable to infer context from statements [%v], path's first segment must be a valid context name" , statementsValues )
218
+ return * new ( R ) , fmt .Errorf ("unable to infer context from statements [%v], path's first segment must be a valid context name" , statementsValues )
181
219
}
182
220
183
221
return pc .ParseStatementsWithContext (inferredContext , statements , false )
184
222
}
185
223
186
- func (pc * ParserCollection [S , R ]) ParseStatementsWithContext (context string , statements S , appendPathsContext bool ) (R , error ) {
187
- zero := * new (R )
224
+ // ParseStatementsWithContext parses the given statements into [R] using the configured
225
+ // context's ottl.Parser and subsequently calling the ParsedStatementConverter function.
226
+ // Differently from ParseStatements, it uses the provided context and does not infer it
227
+ // automatically. The context valuer must be supported by the [ParserCollection],
228
+ // otherwise an error is returned.
229
+ // If the statement's Path does not provide their Path.Context value, the prependPathsContext
230
+ // argument should be set to true, so it rewrites the statements prepending the missing paths
231
+ // contexts.
232
+ // If parsing the statements fails, it returns the underline [ottl.Parser.ParseStatements] error.
233
+ func (pc * ParserCollection [S , R ]) ParseStatementsWithContext (context string , statements S , prependPathsContext bool ) (R , error ) {
188
234
contextParser , ok := pc .contextParsers [context ]
189
235
if ! ok {
190
- return zero , fmt .Errorf (`unknown context "%s" for stataments: %v` , context , statements .GetStatements ())
236
+ return * new ( R ) , fmt .Errorf (`unknown context "%s" for stataments: %v` , context , statements .GetStatements ())
191
237
}
192
238
193
- var statementsValues []string
194
- if appendPathsContext {
239
+ var err error
240
+ var parsingStatements []string
241
+ if prependPathsContext {
195
242
originalStatements := statements .GetStatements ()
196
- for _ , s := range originalStatements {
197
- ctxStatement , err := contextParser .ottlParser .AppendStatementPathsContext (context , s )
198
- if err != nil {
199
- return zero , err
200
- }
201
- statementsValues = append (statementsValues , ctxStatement )
243
+ parsingStatements , err = contextParser .ottlParser .prependContextToStatementsPaths (context , originalStatements )
244
+ if err != nil {
245
+ return * new (R ), err
202
246
}
203
- if pc .contextRewriteLogEnabled {
204
- pc .logRewrittenStatements (originalStatements , statementsValues )
247
+ if pc .modifiedStatementLogging {
248
+ pc .logModifiedStatements (originalStatements , parsingStatements )
205
249
}
206
250
} else {
207
- statementsValues = statements .GetStatements ()
251
+ parsingStatements = statements .GetStatements ()
208
252
}
209
253
210
- parsedStatements , err := contextParser .ottlParser .ParseStatements ( statementsValues )
254
+ parsedStatements , err := contextParser .ottlParser .parseStatements ( parsingStatements )
211
255
if err != nil {
212
- return zero , err
256
+ return * new ( R ) , err
213
257
}
214
258
215
- scr := contextParser .statementsConverter .Call ([]reflect. Value {
259
+ convertedStatements , err := contextParser .statementsConverter .call (
216
260
reflect .ValueOf (pc ),
217
- contextParser .ottlParser . Value ,
218
- reflect . ValueOf ( context ) ,
219
- reflect . ValueOf ( statements ) ,
261
+ contextParser .ottlParser ,
262
+ context ,
263
+ statements ,
220
264
parsedStatements ,
221
- } )
265
+ )
222
266
223
- result := scr [0 ]
224
- converterErr := scr [1 ]
225
- if ! converterErr .IsNil () {
226
- return zero , converterErr .Interface ().(error )
267
+ if err != nil {
268
+ return * new (R ), err
227
269
}
228
270
229
- return result .Interface ().(R ), nil
271
+ return convertedStatements .Interface ().(R ), nil
230
272
}
231
273
232
- func (pc * ParserCollection [S , R ]) logRewrittenStatements (originalStatements , rewrittenStatements []string ) {
274
+ func (pc * ParserCollection [S , R ]) logModifiedStatements (originalStatements , modifiedStatements []string ) {
233
275
var fields []zap.Field
234
276
for i , original := range originalStatements {
235
- if rewrittenStatements [i ] != original {
277
+ if modifiedStatements [i ] != original {
236
278
statementKey := fmt .Sprintf ("[%v]" , i )
237
279
fields = append (fields , zap .Dict (
238
280
statementKey ,
239
281
zap .String ("original" , original ),
240
- zap .String ("modified" , rewrittenStatements [i ])),
282
+ zap .String ("modified" , modifiedStatements [i ])),
241
283
)
242
284
}
243
285
}
0 commit comments