cs-midi-docs/internal/content/tree.go

147 lines
2.8 KiB
Go

package content
import (
"os"
"path/filepath"
"sort"
"strings"
)
type Node struct {
Title string
Path string // URL path relative to content root
FilePath string // absolute filesystem path
Children []*Node
IsDir bool
}
func BuildTree(root string) (*Node, error) {
tree := &Node{Title: "root", IsDir: true}
if err := buildDir(root, root, tree); err != nil {
return nil, err
}
return tree, nil
}
func buildDir(base, dir string, parent *Node) error {
entries, err := os.ReadDir(dir)
if err != nil {
return err
}
sort.Slice(entries, func(i, j int) bool {
return entries[i].Name() < entries[j].Name()
})
for _, e := range entries {
name := e.Name()
full := filepath.Join(dir, name)
if strings.HasPrefix(name, ".") || name == "book.yaml" {
continue
}
rel, _ := filepath.Rel(base, full)
urlPath := filepath.ToSlash(rel)
if e.IsDir() {
node := &Node{
Title: displayName(name),
Path: urlPath,
FilePath: full,
IsDir: true,
}
if err := buildDir(base, full, node); err != nil {
return err
}
if len(node.Children) > 0 || hasIndex(full) {
parent.Children = append(parent.Children, node)
}
} else if strings.HasSuffix(name, ".md") {
urlPath = strings.TrimSuffix(urlPath, ".md")
node := &Node{
Title: displayName(strings.TrimSuffix(name, ".md")),
Path: urlPath,
FilePath: full,
}
parent.Children = append(parent.Children, node)
}
}
return nil
}
func hasIndex(dir string) bool {
_, err := os.Stat(filepath.Join(dir, "_index.md"))
return err == nil
}
func displayName(name string) string {
if name == "_index" {
return "Overview"
}
if len(name) > 3 && name[2] == '-' &&
name[0] >= '0' && name[0] <= '9' &&
name[1] >= '0' && name[1] <= '9' {
name = name[3:]
}
name = strings.ReplaceAll(name, "-", " ")
if len(name) > 0 {
return strings.ToUpper(name[:1]) + name[1:]
}
return name
}
func (n *Node) Flatten() []*Node {
var out []*Node
n.flatten(&out)
return out
}
func (n *Node) flatten(out *[]*Node) {
if !n.IsDir {
*out = append(*out, n)
}
for _, c := range n.Children {
c.flatten(out)
}
}
func (n *Node) FindByPath(path string) *Node {
if !n.IsDir && n.Path == path {
return n
}
for _, c := range n.Children {
if found := c.FindByPath(path); found != nil {
return found
}
}
return nil
}
func (n *Node) FindDirByPath(path string) *Node {
if n.IsDir && n.Path == path {
return n
}
for _, c := range n.Children {
if found := c.FindDirByPath(path); found != nil {
return found
}
}
return nil
}
func (n *Node) IndexPath() string {
if n.IsDir {
return filepath.Join(n.FilePath, "_index.md")
}
return n.FilePath
}
func (n *Node) FirstPage() *Node {
pages := n.Flatten()
if len(pages) > 0 {
return pages[0]
}
return nil
}