resources/page: Adjust the permalinks colon implementation a little

Mostly to get back to an attribute regexp that's reasonably simle to read/understand.

Updates #12918
This commit is contained in:
Bjørn Erik Pedersen 2024-10-15 09:13:26 +02:00
parent e7d0757f95
commit 6e1c5b61b3
2 changed files with 36 additions and 22 deletions

View file

@ -107,44 +107,42 @@ func NewPermalinkExpander(urlize func(uri string) string, patterns map[string]ma
return p, nil return p, nil
} }
func (l PermalinkExpander) normalizeEscapeSequences(result string) string { // Escape sequence for colons in permalink patterns.
return strings.ReplaceAll(result, "\\:", ":") const escapePlaceholderColon = "\x00"
func (l PermalinkExpander) normalizeEscapeSequencesIn(s string) (string, bool) {
s2 := strings.ReplaceAll(s, "\\:", escapePlaceholderColon)
return s2, s2 != s
}
func (l PermalinkExpander) normalizeEscapeSequencesOut(result string) string {
return strings.ReplaceAll(result, escapePlaceholderColon, ":")
} }
// ExpandPattern expands the path in p with the specified expand pattern. // ExpandPattern expands the path in p with the specified expand pattern.
func (l PermalinkExpander) ExpandPattern(pattern string, p Page) (string, error) { func (l PermalinkExpander) ExpandPattern(pattern string, p Page) (string, error) {
expander, err := l.getOrParsePattern(pattern) expand, err := l.getOrParsePattern(pattern)
if err != nil { if err != nil {
return "", err return "", err
} }
result, err := expander(p) return expand(p)
if err != nil {
return "", err
}
return l.normalizeEscapeSequences(result), nil
} }
// Expand expands the path in p according to the rules defined for the given key. // Expand expands the path in p according to the rules defined for the given key.
// If no rules are found for the given key, an empty string is returned. // If no rules are found for the given key, an empty string is returned.
func (l PermalinkExpander) Expand(key string, p Page) (string, error) { func (l PermalinkExpander) Expand(key string, p Page) (string, error) {
expanders, found := l.expanders[p.Kind()] expanders, found := l.expanders[p.Kind()]
if !found { if !found {
return "", nil return "", nil
} }
expand, found := expanders[key] expand, found := expanders[key]
if !found { if !found {
return "", nil return "", nil
} }
result, err := expand(p) return expand(p)
if err != nil {
return "", err
}
return l.normalizeEscapeSequences(result), nil
} }
// Allow " " and / to represent the root section. // Allow " " and / to represent the root section.
@ -161,12 +159,24 @@ func (l PermalinkExpander) getOrParsePattern(pattern string) (func(Page) (string
if !l.validate(pattern) { if !l.validate(pattern) {
return nil, &permalinkExpandError{pattern: pattern, err: errPermalinkIllFormed} return nil, &permalinkExpandError{pattern: pattern, err: errPermalinkIllFormed}
} }
var normalized bool
pattern, normalized = l.normalizeEscapeSequencesIn(pattern)
matches := attributeRegexp.FindAllStringSubmatch(pattern, -1) matches := attributeRegexp.FindAllStringSubmatch(pattern, -1)
if matches == nil {
result := pattern
if normalized {
result = l.normalizeEscapeSequencesOut(result)
}
return func(p Page) (string, error) {
return result, nil
}, nil
}
callbacks := make([]pageToPermaAttribute, len(matches)) callbacks := make([]pageToPermaAttribute, len(matches))
replacements := make([]string, len(matches)) replacements := make([]string, len(matches))
for i, m := range matches { for i, m := range matches {
replacement := m[1] replacement := m[0]
attr := replacement[1:] attr := replacement[1:]
replacements[i] = replacement replacements[i] = replacement
callback, ok := l.callback(attr) callback, ok := l.callback(attr)
@ -179,10 +189,6 @@ func (l PermalinkExpander) getOrParsePattern(pattern string) (func(Page) (string
} }
return func(p Page) (string, error) { return func(p Page) (string, error) {
if matches == nil {
return pattern, nil
}
newField := pattern newField := pattern
for i, replacement := range replacements { for i, replacement := range replacements {
@ -196,6 +202,10 @@ func (l PermalinkExpander) getOrParsePattern(pattern string) (func(Page) (string
newField = strings.Replace(newField, replacement, newAttr, 1) newField = strings.Replace(newField, replacement, newAttr, 1)
} }
if normalized {
newField = l.normalizeEscapeSequencesOut(newField)
}
return newField, nil return newField, nil
}, nil }, nil
}) })
@ -222,7 +232,7 @@ func (l PermalinkExpander) parse(patterns map[string]string) (map[string]func(Pa
// can return a string to go in that position in the page (or an error) // can return a string to go in that position in the page (or an error)
type pageToPermaAttribute func(Page, string) (string, error) type pageToPermaAttribute func(Page, string) (string, error)
var attributeRegexp = regexp.MustCompile(`(?:^|[^\\])(:\w+(?:\[.+?])?)`) var attributeRegexp = regexp.MustCompile(`:\w+(\[.+?\])?`)
// validate determines if a PathPattern is well-formed // validate determines if a PathPattern is well-formed
func (l PermalinkExpander) validate(pp string) bool { func (l PermalinkExpander) validate(pp string) bool {
@ -246,7 +256,7 @@ func (l PermalinkExpander) validate(pp string) bool {
} }
for _, match := range matches { for _, match := range matches {
k := match[1][1:] k := match[0][1:]
if _, ok := l.callback(k); !ok { if _, ok := l.callback(k); !ok {
return false return false
} }

View file

@ -92,6 +92,10 @@ func TestPermalinkExpansion(t *testing.T) {
expanded, err := expander.Expand("posts", page) expanded, err := expander.Expand("posts", page)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
c.Assert(expanded, qt.Equals, item.expandsTo) c.Assert(expanded, qt.Equals, item.expandsTo)
expanded, err = expander.ExpandPattern(item.spec, page)
c.Assert(err, qt.IsNil)
c.Assert(expanded, qt.Equals, item.expandsTo)
}) })
} }