Skip to content

Commit 3c37b13

Browse files
committed
Update draft
1 parent 3566b7f commit 3c37b13

File tree

6 files changed

+292
-155
lines changed

6 files changed

+292
-155
lines changed

pkg/ottl/parser_collection.go

+129-87
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import (
1111
"go.uber.org/zap"
1212
)
1313

14-
var _ ottlParser[any] = (*Parser[any])(nil)
14+
var _ interface {
15+
ParseStatements(statements []string) ([]*Statement[any], error)
16+
} = (*Parser[any])(nil)
1517

1618
var _ ParsedStatementConverter[any, StatementsGetter, any] = func(
1719
_ *ParserCollection[StatementsGetter, any],
@@ -22,65 +24,93 @@ var _ ParsedStatementConverter[any, StatementsGetter, any] = func(
2224
return nil, nil
2325
}
2426

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
3328
type StatementsGetter interface {
29+
// GetStatements retrieves the OTTL statements to be parsed
3430
GetStatements() []string
3531
}
3632

33+
// ottlParserWrapper wraps an ottl.Parser using reflection, so it can invoke exported
34+
// methods without knowing its generic type (transform context).
3735
type ottlParserWrapper[S StatementsGetter] struct {
38-
reflect.Value
36+
parser reflect.Value
37+
prependContextToStatementPaths func(context string, statement string) (string, error)
3938
}
4039

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,
4744
}
48-
return psr[0], nil
4945
}
5046

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]
5551
if !err.IsNil() {
56-
return "", err.Interface().(error)
52+
return reflect.Value{}, err.Interface().(error)
5753
}
58-
return psr[0].Interface().(string), nil
54+
return parseStatementsRes[0], nil
5955
}
6056

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
6367
}
6468

69+
// statementsConverterWrapper is reflection-based wrapper to the ParsedStatementConverter function,
70+
// which does not require knowing all generic parameters to be called.
6571
type statementsConverterWrapper[S StatementsGetter] reflect.Value
6672

67-
func (s statementsConverterWrapper[S]) Call(in []reflect.Value) []reflect.Value {
68-
return reflect.Value(s).Call(in)
69-
}
70-
7173
func newStatementsConverterWrapper[K any, S StatementsGetter, R any](converter ParsedStatementConverter[K, S, R]) statementsConverterWrapper[S] {
7274
return statementsConverterWrapper[S](reflect.ValueOf(converter))
7375
}
7476

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 {
76104
ottlParser *ottlParserWrapper[S]
77105
statementsConverter statementsConverterWrapper[S]
78106
}
79107

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.
80110
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
84114
Settings component.TelemetrySettings
85115
ErrorMode ErrorMode
86116
}
@@ -92,8 +122,9 @@ func NewParserCollection[S StatementsGetter, R any](
92122
options ...ParserCollectionOption[S, R]) (*ParserCollection[S, R], error) {
93123

94124
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(),
97128
}
98129

99130
for _, op := range options {
@@ -103,13 +134,14 @@ func NewParserCollection[S StatementsGetter, R any](
103134
}
104135
}
105136

106-
if pc.contextInferrer == nil {
107-
pc.contextInferrer = NewDefaultContextInferrer()
108-
}
109-
110137
return pc, nil
111138
}
112139

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.
113145
type ParsedStatementConverter[T any, S StatementsGetter, R any] func(
114146
collection *ParserCollection[S, R],
115147
parser *Parser[T],
@@ -118,7 +150,7 @@ type ParsedStatementConverter[T any, S StatementsGetter, R any] func(
118150
parsedStatements []*Statement[T],
119151
) (R, error)
120152

121-
func NewNopParsedStatementConverter[T any, S StatementsGetter]() ParsedStatementConverter[T, S, any] {
153+
func newNopParsedStatementConverter[T any, S StatementsGetter]() ParsedStatementConverter[T, S, any] {
122154
return func(
123155
_ *ParserCollection[S, any],
124156
_ *Parser[T],
@@ -129,115 +161,125 @@ func NewNopParsedStatementConverter[T any, S StatementsGetter]() ParsedStatement
129161
}
130162
}
131163

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](
133168
context string,
134169
parser *Parser[K],
135170
converter ParsedStatementConverter[K, S, R],
136171
) ParserCollectionOption[S, R] {
137172
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)
140175
}
141-
mp.contextParsers[context] = &contextParserWrapper[S]{
176+
mp.contextParsers[context] = &parserCollectionParser[S]{
142177
ottlParser: newParserWrapper[K, S](parser),
143178
statementsConverter: newStatementsConverterWrapper(converter),
144179
}
145180
return nil
146181
}
147182
}
148183

184+
// WithParserCollectionErrorMode has no effect on the ParserCollection, but might be used
185+
// by the ParsedStatementConverter functions to handle/create StatementSequence.
149186
func WithParserCollectionErrorMode[S StatementsGetter, R any](errorMode ErrorMode) ParserCollectionOption[S, R] {
150187
return func(tp *ParserCollection[S, R]) error {
151188
tp.ErrorMode = errorMode
152189
return nil
153190
}
154191
}
155192

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] {
164197
return func(tp *ParserCollection[S, R]) error {
165-
tp.contextRewriteLogEnabled = enabled
198+
tp.modifiedStatementLogging = enabled
166199
return nil
167200
}
168201
}
169202

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.
170210
func (pc *ParserCollection[S, R]) ParseStatements(statements S) (R, error) {
171-
zero := *new(R)
172211
statementsValues := statements.GetStatements()
173-
174-
inferredContext, err := pc.contextInferrer.Infer(statementsValues)
212+
inferredContext, err := pc.contextInferrer.infer(statementsValues)
175213
if err != nil {
176-
return zero, err
214+
return *new(R), err
177215
}
178216

179217
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)
181219
}
182220

183221
return pc.ParseStatementsWithContext(inferredContext, statements, false)
184222
}
185223

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) {
188234
contextParser, ok := pc.contextParsers[context]
189235
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())
191237
}
192238

193-
var statementsValues []string
194-
if appendPathsContext {
239+
var err error
240+
var parsingStatements []string
241+
if prependPathsContext {
195242
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
202246
}
203-
if pc.contextRewriteLogEnabled {
204-
pc.logRewrittenStatements(originalStatements, statementsValues)
247+
if pc.modifiedStatementLogging {
248+
pc.logModifiedStatements(originalStatements, parsingStatements)
205249
}
206250
} else {
207-
statementsValues = statements.GetStatements()
251+
parsingStatements = statements.GetStatements()
208252
}
209253

210-
parsedStatements, err := contextParser.ottlParser.ParseStatements(statementsValues)
254+
parsedStatements, err := contextParser.ottlParser.parseStatements(parsingStatements)
211255
if err != nil {
212-
return zero, err
256+
return *new(R), err
213257
}
214258

215-
scr := contextParser.statementsConverter.Call([]reflect.Value{
259+
convertedStatements, err := contextParser.statementsConverter.call(
216260
reflect.ValueOf(pc),
217-
contextParser.ottlParser.Value,
218-
reflect.ValueOf(context),
219-
reflect.ValueOf(statements),
261+
contextParser.ottlParser,
262+
context,
263+
statements,
220264
parsedStatements,
221-
})
265+
)
222266

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
227269
}
228270

229-
return result.Interface().(R), nil
271+
return convertedStatements.Interface().(R), nil
230272
}
231273

232-
func (pc *ParserCollection[S, R]) logRewrittenStatements(originalStatements, rewrittenStatements []string) {
274+
func (pc *ParserCollection[S, R]) logModifiedStatements(originalStatements, modifiedStatements []string) {
233275
var fields []zap.Field
234276
for i, original := range originalStatements {
235-
if rewrittenStatements[i] != original {
277+
if modifiedStatements[i] != original {
236278
statementKey := fmt.Sprintf("[%v]", i)
237279
fields = append(fields, zap.Dict(
238280
statementKey,
239281
zap.String("original", original),
240-
zap.String("modified", rewrittenStatements[i])),
282+
zap.String("modified", modifiedStatements[i])),
241283
)
242284
}
243285
}

0 commit comments

Comments
 (0)