Canonical Wiki Paths Enforcement (#6)
### Summary - Change generated topdata wiki links to target public wiki namespace and canonical slug paths. - Add generated public path metadata to wiki page generation and page-index output. - Add YAML-driven generated slug overrides and public-target collision validation. ### Details - Generated relation links now render public targets such as: - `[[feat/hardiness-vs-enchantments|Hardiness vs. Enchantments]]` - `[[feat/favored-enemy-elves|Favored Enemy: Elves]]` - Topdata page IDs remain the internal identity for output layout, managed markers, deploy state, and bookkeeping. - Generated managed markers now include the effective public slug: - `<!-- sow-topdata-wiki:page=... wiki_slug=... -->` - Page-index entries now include: - `public_slug` - `public_target` - Added parsing and validation for `page_paths.slug_overrides` from `topdata/wiki/wiki.yaml`. - Generated public targets are validated for duplicate namespace-local collisions. - Table link helpers now derive public targets from target page titles when available, so display aliases do not accidentally change link destinations. ### Tests - Added and updated coverage for: - public target rendering - marker `wiki_slug` output - page-index public metadata - duplicate generated public target rejection - slug override parsing and validation - override-driven target selection - target title slugging independent of display labels ### Validation - `go test ./...` - `gofmt -w` on changed Go files - `./build-tool.sh` - `git diff --check` Reviewed-on: https://gitea.westgate.pw/ShadowsOverWestgate/sow-tools/pulls/6 Co-authored-by: vickydotbat <vickydotbat@tutamail.com> Co-committed-by: vickydotbat <vickydotbat@tutamail.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
package topdata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type wikiPagePathDocument struct {
|
||||
PagePaths wikiPagePaths `json:"page_paths" yaml:"page_paths"`
|
||||
}
|
||||
|
||||
type wikiPagePaths struct {
|
||||
SlugOverrides []wikiPageSlugOverride `json:"slug_overrides" yaml:"slug_overrides"`
|
||||
}
|
||||
|
||||
type wikiPageSlugOverride struct {
|
||||
PageID string `json:"page_id" yaml:"page_id"`
|
||||
Slug string `json:"slug" yaml:"slug"`
|
||||
}
|
||||
|
||||
func loadWikiPagePathDeclarations(path string) (map[string]string, error) {
|
||||
raw, err := os.ReadFile(path)
|
||||
if os.IsNotExist(err) {
|
||||
return map[string]string{}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parseWikiPagePathDeclarations(raw, path)
|
||||
}
|
||||
|
||||
func parseWikiPagePathDeclarations(raw []byte, path string) (map[string]string, error) {
|
||||
var doc wikiPagePathDocument
|
||||
if err := yaml.Unmarshal(raw, &doc); err != nil {
|
||||
return nil, fmt.Errorf("parse wiki page paths %s: %w", path, err)
|
||||
}
|
||||
overrides := map[string]string{}
|
||||
for _, override := range doc.PagePaths.SlugOverrides {
|
||||
pageID := strings.TrimSpace(override.PageID)
|
||||
slug := strings.TrimSpace(override.Slug)
|
||||
if pageID == "" {
|
||||
return nil, fmt.Errorf("parse wiki page paths %s: slug override has empty page_id", path)
|
||||
}
|
||||
if !isCanonicalWikiSlug(slug) {
|
||||
return nil, fmt.Errorf("parse wiki page paths %s: page_id %q has invalid slug %q", path, pageID, override.Slug)
|
||||
}
|
||||
if _, ok := overrides[pageID]; ok {
|
||||
return nil, fmt.Errorf("parse wiki page paths %s: duplicate page_id %q", path, pageID)
|
||||
}
|
||||
overrides[pageID] = slug
|
||||
}
|
||||
return overrides, nil
|
||||
}
|
||||
|
||||
func isCanonicalWikiSlug(value string) bool {
|
||||
if value == "" || value != strings.ToLower(value) {
|
||||
return false
|
||||
}
|
||||
parts := strings.Split(value, "-")
|
||||
for _, part := range parts {
|
||||
if part == "" {
|
||||
return false
|
||||
}
|
||||
for _, r := range part {
|
||||
if !((r >= 'a' && r <= 'z') || (r >= '0' && r <= '9')) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user