-
-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Use a new vars template implementation #19156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fa841ce
9ef65b3
b8370d0
bbd6767
3a4d22a
b540046
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,105 @@ | ||||||||||||||
// Copyright 2022 The Gitea Authors. All rights reserved. | ||||||||||||||
// Use of this source code is governed by a MIT-style | ||||||||||||||
// license that can be found in the LICENSE file. | ||||||||||||||
|
||||||||||||||
package vars | ||||||||||||||
|
||||||||||||||
import ( | ||||||||||||||
"fmt" | ||||||||||||||
"strings" | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
// ErrWrongSyntax represents a syntax error within a template | ||||||||||||||
type ErrWrongSyntax struct { | ||||||||||||||
Template string | ||||||||||||||
Reason string | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func (err ErrWrongSyntax) Error() string { | ||||||||||||||
return fmt.Sprintf("Wrong syntax found in %s: %s", err.Template, err.Reason) | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// IsErrWrongSyntax returns true if the error is ErrWrongSyntax | ||||||||||||||
func IsErrWrongSyntax(err error) bool { | ||||||||||||||
_, ok := err.(ErrWrongSyntax) | ||||||||||||||
return ok | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// ErrNoMatchedVar represents an error that no matched vars | ||||||||||||||
type ErrNoMatchedVar struct { | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that really an error? |
||||||||||||||
Template string | ||||||||||||||
Var string | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func (err ErrNoMatchedVar) Error() string { | ||||||||||||||
return fmt.Sprintf("No matched variable %s found for %s", err.Var, err.Template) | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// IsErrNoMatchedVar returns true if the error is ErrNoMatchedVar | ||||||||||||||
func IsErrNoMatchedVar(err error) bool { | ||||||||||||||
_, ok := err.(ErrNoMatchedVar) | ||||||||||||||
return ok | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// Expand replaces all variables like {var} to match | ||||||||||||||
func Expand(template string, match map[string]string, subs ...string) (string, error) { | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could save all the extra trouble with the positional arguments by using something like
Suggested change
|
||||||||||||||
var ( | ||||||||||||||
buf strings.Builder | ||||||||||||||
keyStartPos = -1 | ||||||||||||||
) | ||||||||||||||
for i, c := range template { | ||||||||||||||
switch { | ||||||||||||||
case c == '{': | ||||||||||||||
if keyStartPos > -1 { | ||||||||||||||
return "", ErrWrongSyntax{ | ||||||||||||||
Template: template, | ||||||||||||||
lunny marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
Reason: "\"{\" is not allowed to occur again before closing the variable", | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
keyStartPos = i | ||||||||||||||
case c == '}': | ||||||||||||||
if keyStartPos == -1 { | ||||||||||||||
return "", ErrWrongSyntax{ | ||||||||||||||
Template: template, | ||||||||||||||
lunny marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
Reason: "\"}\" can only occur after an opening \"{\"", | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
if i-keyStartPos <= 1 { | ||||||||||||||
return "", ErrWrongSyntax{ | ||||||||||||||
Template: template, | ||||||||||||||
lunny marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
Reason: "the empty variable (\"{}\") is not allowed", | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
if len(match) == 0 { | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||
return "", ErrNoMatchedVar{ | ||||||||||||||
Template: template, | ||||||||||||||
Var: template[keyStartPos+1 : i], | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
v, ok := match[template[keyStartPos+1:i]] | ||||||||||||||
if !ok { | ||||||||||||||
if len(subs) == 0 { | ||||||||||||||
return "", ErrNoMatchedVar{ | ||||||||||||||
Template: template, | ||||||||||||||
Var: template[keyStartPos+1 : i], | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
v = subs[0] | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
if _, err := buf.WriteString(v); err != nil { | ||||||||||||||
return "", err | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
keyStartPos = -1 | ||||||||||||||
case keyStartPos > -1: | ||||||||||||||
default: | ||||||||||||||
if _, err := buf.WriteRune(c); err != nil { | ||||||||||||||
wxiaoguang marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
return "", err | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently, there is an edge case missing, as an unclosed parentheses at the end (i.e. |
||||||||||||||
return buf.String(), nil | ||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// Copyright 2022 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package vars | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestExpandVars(t *testing.T) { | ||
kases := []struct { | ||
template string | ||
maps map[string]string | ||
expected string | ||
fail bool | ||
}{ | ||
{ | ||
template: "{a}", | ||
maps: map[string]string{ | ||
"a": "1", | ||
}, | ||
expected: "1", | ||
}, | ||
{ | ||
template: "expand {a}, {b} and {c}", | ||
maps: map[string]string{ | ||
"a": "1", | ||
"b": "2", | ||
"c": "3", | ||
}, | ||
expected: "expand 1, 2 and 3", | ||
}, | ||
{ | ||
template: "中文内容 {一}, {二} 和 {三} 中文结尾", | ||
maps: map[string]string{ | ||
"一": "11", | ||
"二": "22", | ||
"三": "33", | ||
}, | ||
expected: "中文内容 11, 22 和 33 中文结尾", | ||
}, | ||
{ | ||
template: "expand {{a}, {b} and {c}", | ||
fail: true, | ||
}, | ||
{ | ||
template: "expand {}, {b} and {c}", | ||
fail: true, | ||
}, | ||
{ | ||
template: "expand }, {b} and {c}", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned above, the other case is currently missing. |
||
fail: true, | ||
}, | ||
} | ||
|
||
for _, kase := range kases { | ||
t.Run(kase.template, func(t *testing.T) { | ||
res, err := Expand(kase.template, kase.maps) | ||
if kase.fail { | ||
assert.Error(t, err) | ||
} else { | ||
assert.NoError(t, err) | ||
} | ||
assert.EqualValues(t, kase.expected, res) | ||
}) | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.