-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
caddyfile: Implement variadics for import args placeholders (#5249)
* implement variadic placeholders imported snippets reflect actual lines in file * add import directive line number for imported snippets add tests for parsing * add realfile field to help debug import cycle detection. * use file field to reflect import chain * Switch syntax, deprecate old syntax, refactoring - Moved the import args handling to a separate file - Using {args[0:1]} syntax now - Deprecate {args.*} syntax - Use a replacer map for better control over the parsing - Add plenty of warnings when invalid placeholders are detected - Renaming variables, cleanup comments for readability - More tests to cover edgecases I could think of - Minor cleanup to snippet tracking in tokens, drop a redundant boolean field in tokens --------- Co-authored-by: Francis Lavoie <lavofr@gmail.com>
- Loading branch information
1 parent
bf54892
commit 8bc05e5
Showing
5 changed files
with
313 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
// Copyright 2015 Matthew Holt and The Caddy Authors | ||
// | ||
// 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 caddyfile | ||
|
||
import ( | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/caddyserver/caddy/v2" | ||
"go.uber.org/zap" | ||
) | ||
|
||
// parseVariadic determines if the token is a variadic placeholder, | ||
// and if so, determines the index range (start/end) of args to use. | ||
// Returns a boolean signaling whether a variadic placeholder was found, | ||
// and the start and end indices. | ||
func parseVariadic(token Token, argCount int) (bool, int, int) { | ||
if !strings.HasPrefix(token.Text, "{args[") { | ||
return false, 0, 0 | ||
} | ||
if !strings.HasSuffix(token.Text, "]}") { | ||
return false, 0, 0 | ||
} | ||
|
||
argRange := strings.TrimSuffix(strings.TrimPrefix(token.Text, "{args["), "]}") | ||
if argRange == "" { | ||
caddy.Log().Named("caddyfile").Warn( | ||
"Placeholder "+token.Text+" cannot have an empty index", | ||
zap.String("file", token.File+":"+strconv.Itoa(token.Line))) | ||
return false, 0, 0 | ||
} | ||
|
||
start, end, found := strings.Cut(argRange, ":") | ||
|
||
// If no ":" delimiter is found, this is not a variadic. | ||
// The replacer will pick this up. | ||
if !found { | ||
return false, 0, 0 | ||
} | ||
|
||
var ( | ||
startIndex = 0 | ||
endIndex = argCount | ||
err error | ||
) | ||
if start != "" { | ||
startIndex, err = strconv.Atoi(start) | ||
if err != nil { | ||
caddy.Log().Named("caddyfile").Warn( | ||
"Variadic placeholder "+token.Text+" has an invalid start index", | ||
zap.String("file", token.File+":"+strconv.Itoa(token.Line))) | ||
return false, 0, 0 | ||
} | ||
} | ||
if end != "" { | ||
endIndex, err = strconv.Atoi(end) | ||
if err != nil { | ||
caddy.Log().Named("caddyfile").Warn( | ||
"Variadic placeholder "+token.Text+" has an invalid end index", | ||
zap.String("file", token.File+":"+strconv.Itoa(token.Line))) | ||
return false, 0, 0 | ||
} | ||
} | ||
|
||
// bound check | ||
if startIndex < 0 || startIndex > endIndex || endIndex > argCount { | ||
caddy.Log().Named("caddyfile").Warn( | ||
"Variadic placeholder "+token.Text+" indices are out of bounds, only "+strconv.Itoa(argCount)+" argument(s) exist", | ||
zap.String("file", token.File+":"+strconv.Itoa(token.Line))) | ||
return false, 0, 0 | ||
} | ||
return true, startIndex, endIndex | ||
} | ||
|
||
// makeArgsReplacer prepares a Replacer which can replace | ||
// non-variadic args placeholders in imported tokens. | ||
func makeArgsReplacer(args []string) *caddy.Replacer { | ||
repl := caddy.NewEmptyReplacer() | ||
repl.Map(func(key string) (any, bool) { | ||
// TODO: Remove the deprecated {args.*} placeholder | ||
// support at some point in the future | ||
if matches := argsRegexpIndexDeprecated.FindStringSubmatch(key); len(matches) > 0 { | ||
value, err := strconv.Atoi(matches[1]) | ||
if err != nil { | ||
caddy.Log().Named("caddyfile").Warn( | ||
"Placeholder {args." + matches[1] + "} has an invalid index") | ||
return nil, false | ||
} | ||
if value >= len(args) { | ||
caddy.Log().Named("caddyfile").Warn( | ||
"Placeholder {args." + matches[1] + "} index is out of bounds, only " + strconv.Itoa(len(args)) + " argument(s) exist") | ||
return nil, false | ||
} | ||
caddy.Log().Named("caddyfile").Warn( | ||
"Placeholder {args." + matches[1] + "} deprecated, use {args[" + matches[1] + "]} instead") | ||
return args[value], true | ||
} | ||
|
||
// Handle args[*] form | ||
if matches := argsRegexpIndex.FindStringSubmatch(key); len(matches) > 0 { | ||
if strings.Contains(matches[1], ":") { | ||
caddy.Log().Named("caddyfile").Warn( | ||
"Variadic placeholder {args[" + matches[1] + "]} must be a token on its own") | ||
return nil, false | ||
} | ||
value, err := strconv.Atoi(matches[1]) | ||
if err != nil { | ||
caddy.Log().Named("caddyfile").Warn( | ||
"Placeholder {args[" + matches[1] + "]} has an invalid index") | ||
return nil, false | ||
} | ||
if value >= len(args) { | ||
caddy.Log().Named("caddyfile").Warn( | ||
"Placeholder {args[" + matches[1] + "]} index is out of bounds, only " + strconv.Itoa(len(args)) + " argument(s) exist") | ||
return nil, false | ||
} | ||
return args[value], true | ||
} | ||
|
||
// Not an args placeholder, ignore | ||
return nil, false | ||
}) | ||
return repl | ||
} | ||
|
||
var ( | ||
argsRegexpIndexDeprecated = regexp.MustCompile(`args\.(.+)`) | ||
argsRegexpIndex = regexp.MustCompile(`args\[(.+)]`) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.