feat(ingestion): add wiki package with Page types and slug generation
This commit is contained in:
28
ingestion/internal/wiki/slug.go
Normal file
28
ingestion/internal/wiki/slug.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// ingestion/internal/wiki/slug.go
|
||||||
|
package wiki
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Slug converts a title to a kebab-case slug suitable for wiki filenames.
|
||||||
|
// Rules: lowercase, spaces/hyphens/underscores → hyphens, strip everything else.
|
||||||
|
func Slug(title string) string {
|
||||||
|
var b strings.Builder
|
||||||
|
prevHyphen := true // start true to trim leading hyphens
|
||||||
|
for _, r := range strings.ToLower(title) {
|
||||||
|
switch {
|
||||||
|
case r == ' ' || r == '-' || r == '_':
|
||||||
|
if !prevHyphen {
|
||||||
|
b.WriteRune('-')
|
||||||
|
prevHyphen = true
|
||||||
|
}
|
||||||
|
case unicode.IsLetter(r) || unicode.IsDigit(r):
|
||||||
|
b.WriteRune(r)
|
||||||
|
prevHyphen = false
|
||||||
|
// all other characters (apostrophes, colons, dots, etc.) are dropped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.TrimRight(b.String(), "-")
|
||||||
|
}
|
||||||
29
ingestion/internal/wiki/slug_test.go
Normal file
29
ingestion/internal/wiki/slug_test.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// ingestion/internal/wiki/slug_test.go
|
||||||
|
package wiki
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSlug(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"Domain Driven Design", "domain-driven-design"},
|
||||||
|
{"It's Complicated", "its-complicated"},
|
||||||
|
{"gRPC", "grpc"},
|
||||||
|
{"GPT-4o", "gpt-4o"},
|
||||||
|
{"Property 1: It's Rough", "property-1-its-rough"},
|
||||||
|
{" leading spaces ", "leading-spaces"},
|
||||||
|
{"multiple spaces", "multiple-spaces"},
|
||||||
|
{"already-kebab", "already-kebab"},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.input, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.want, Slug(tc.input))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
24
ingestion/internal/wiki/types.go
Normal file
24
ingestion/internal/wiki/types.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// ingestion/internal/wiki/types.go
|
||||||
|
package wiki
|
||||||
|
|
||||||
|
// PageType identifies the wiki subdirectory for a page.
|
||||||
|
type PageType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
PageTypeConcept PageType = "concepts"
|
||||||
|
PageTypeEntity PageType = "entities"
|
||||||
|
PageTypeSource PageType = "sources"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Page is a wiki page to be written to disk.
|
||||||
|
type Page struct {
|
||||||
|
Path string // relative to brainDir, e.g. "wiki/sources/foo.md"
|
||||||
|
Content string // full markdown including YAML frontmatter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry is a summary of an existing wiki page used to build the inventory.
|
||||||
|
type Entry struct {
|
||||||
|
Slug string
|
||||||
|
Title string
|
||||||
|
Type PageType
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user