// https://gobyexample.com/ package main import ( "bufio" "io" "os" "path/filepath" "strconv" "strings" "text/template" "time" ) type PageProperties struct { Title string StylePath string HeaderLinks [][]*HeaderLink } type HeaderLink struct { Name string Active bool } type Page struct { Name string Parent *Page Children []*Page } var rootPath = "." var now = time.Now() func main() { args := os.Args[1:] if len(args) != 0 { rootPath = args[0] } os.RemoveAll(filepath.Join("out")) os.Mkdir(filepath.Join("out"), 0777) buildPage(buildPageLinkedList()) cssFS := os.DirFS(filepath.Join(rootPath, "css")) os.CopyFS(filepath.Join("out", "css"), cssFS) os.Rename( filepath.Join("out", "css", "style.css"), filepath.Join("out", "css", "style."+strconv.FormatInt(now.Unix(), 10)+".css"), ) assetsFS := os.DirFS(filepath.Join(rootPath, "assets")) os.CopyFS(filepath.Join("out", "assets"), assetsFS) } func buildPageLinkedList() *Page { rootNode := &Page{ Name: "index", Parent: nil, } parentNode := rootNode tailNode := rootNode currentDepth := 0 tabFile, _ := os.Open(filepath.Join(rootPath, "site.tab")) scanner := bufio.NewScanner(tabFile) scanner.Split(bufio.ScanLines) for scanner.Scan() { depth := strings.Count(scanner.Text(), " ") pageName := strings.TrimLeft(scanner.Text(), " ") if len(pageName) > 0 { if depth == currentDepth { newNode := &Page{ Name: pageName, Parent: parentNode, } parentNode.Children = append(parentNode.Children, newNode) tailNode = newNode } else if depth > currentDepth { currentDepth = depth parentNode = tailNode newNode := &Page{ Name: pageName, Parent: parentNode, } parentNode.Children = append(parentNode.Children, newNode) tailNode = newNode } else if depth < currentDepth { unwind := (currentDepth - depth) + 1 currentDepth = depth for range unwind { tailNode = tailNode.Parent } parentNode = tailNode newNode := &Page{ Name: pageName, Parent: parentNode, } parentNode.Children = append(parentNode.Children, newNode) tailNode = newNode } } } return rootNode } func parentLinks(page *Page) []*HeaderLink { if page.Parent == nil { return nil } if page.Parent.Parent == nil { return nil } parentPages := page.Parent.Parent.Children parentLinks := make([]*HeaderLink, len(parentPages)) for i, parentPage := range parentPages { parentLinks[i] = &HeaderLink{ Name: parentPage.Name, Active: page.Parent == parentPage, } } return parentLinks } func siblingLinks(page *Page) []*HeaderLink { if page.Parent == nil { return nil } siblingPages := page.Parent.Children siblingLinks := make([]*HeaderLink, len(siblingPages)) for i, siblingPage := range siblingPages { siblingLinks[i] = &HeaderLink{ Name: siblingPage.Name, Active: page == siblingPage, } } return siblingLinks } func childLinks(page *Page) []*HeaderLink { childPages := page.Children childLinks := make([]*HeaderLink, len(childPages)) for i, childPage := range childPages { childLinks[i] = &HeaderLink{ Name: childPage.Name, Active: false, } } return childLinks } func buildPage(page *Page) { parents := parentLinks(page) siblings := siblingLinks(page) children := childLinks(page) headerLinks := [][]*HeaderLink{parents, siblings, children} // https://abhinavg.net/2019/07/11/zero-alloc-slice-filter/ filteredHeaderLinks := headerLinks[:0] for _, headerLink := range headerLinks { if headerLink != nil { filteredHeaderLinks = append(filteredHeaderLinks, headerLink) } } pageFilePath := filepath.Join(rootPath, "site", page.Name+".html") pageFileInfo, _ := os.Stat(pageFilePath) if pageFileInfo == nil { dst, _ := os.Create(pageFilePath) src, _ := os.Open(filepath.Join(rootPath, "templates", "page.html")) io.Copy(dst, src) dst.Sync() } outfile, _ := os.Create(filepath.Join("out", page.Name+".html")) template, _ := template.ParseFiles( filepath.Join(rootPath, "templates", "document.html"), filepath.Join(rootPath, "templates", "header.html"), filepath.Join(rootPath, "site", page.Name+".html"), ) template.Execute( outfile, &PageProperties{ Title: page.Name, StylePath: "/css/style." + strconv.FormatInt(now.Unix(), 10) + ".css", HeaderLinks: filteredHeaderLinks, }, ) for _, child := range page.Children { buildPage(child) } } func __(foo any) {}