genblog: Further simplify code.

This commit is contained in:
Qi Xiao 2020-06-14 13:40:09 +01:00
parent fa77792ad5
commit 03cb64ea2f
4 changed files with 56 additions and 95 deletions

View File

@ -4,7 +4,7 @@ import (
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
"strings"
"github.com/BurntSushi/toml"
@ -92,65 +92,40 @@ func (ra *recentArticles) insert(a article) {
ra.articles[i] = a
}
func articlesToDots(b *baseDot, as []article) []articleDot {
ads := make([]articleDot, len(as))
for i, a := range as {
ads[i] = articleDot(articleDot{b, a})
}
return ads
}
// decodeFile decodes the named file in TOML into a pointer.
func decodeFile(fname string, v interface{}) {
func decodeTOML(fname string, v interface{}) {
_, err := toml.DecodeFile(fname, v)
if err != nil {
log.Fatalln(err)
}
}
// readCatetoryConf reads a category configuration file.
func readCategoryConf(cat, fname string) *categoryConf {
conf := &categoryConf{}
decodeFile(fname, conf)
return conf
func readFile(fname string) string {
content, err := ioutil.ReadFile(fname)
if err != nil {
log.Fatal(err)
}
return string(content)
}
// readAllAndStat retrieves all content of the named file and its stat.
func readAllAndStat(fname string) (string, os.FileInfo) {
file, err := os.Open(fname)
if err != nil {
log.Fatalln(err)
}
defer file.Close()
content, err := ioutil.ReadAll(file)
if err != nil {
log.Fatalln(err)
}
fi, err := file.Stat()
if err != nil {
log.Fatalln(err)
}
return string(content), fi
}
func readAll(fname string) string {
all, _ := readAllAndStat(fname)
return all
}
func catAllInDir(dirname string, fnames []string) string {
func catInDir(dirname string, fnames []string) string {
var sb strings.Builder
for _, fname := range fnames {
sb.WriteString(readAll(path.Join(dirname, fname)))
sb.WriteString(readFile(filepath.Join(dirname, fname)))
}
return sb.String()
}
func getArticle(a article, am articleMeta, dir string) article {
content, fi := readAllAndStat(path.Join(dir, am.Name+".html"))
modTime := fi.ModTime()
css := catAllInDir(dir, am.ExtraCSS)
js := catAllInDir(dir, am.ExtraJS)
fname := filepath.Join(dir, am.Name+".html")
content := readFile(fname)
fileInfo, err := os.Stat(fname)
if err != nil {
log.Fatal(err)
}
modTime := fileInfo.ModTime()
css := catInDir(dir, am.ExtraCSS)
js := catInDir(dir, am.ExtraJS)
return article{
am, a.IsHomepage, a.Category, content, css, js, rfc3339Time(modTime)}
}

View File

@ -6,7 +6,8 @@ import (
"fmt"
"log"
"os"
"path"
"path/filepath"
"sort"
"time"
)
@ -16,10 +17,18 @@ func main() {
log.Fatal("Usage: genblog <src dir> <dst dir>")
}
srcDir, dstDir := args[0], args[1]
srcFile := func(elem ...string) string {
elem = append([]string{srcDir}, elem...)
return filepath.Join(elem...)
}
dstFile := func(elem ...string) string {
elem = append([]string{dstDir}, elem...)
return filepath.Join(elem...)
}
// Read blog configuration.
conf := &blogConf{}
decodeFile(path.Join(srcDir, "index.toml"), conf)
decodeTOML(srcFile("index.toml"), conf)
if conf.RootURL == "" {
log.Fatal("RootURL must be specified; needed by feed and sitemap")
}
@ -30,8 +39,8 @@ func main() {
log.Fatal("BaseCSS must be specified")
}
template := readAll(path.Join(srcDir, conf.Template))
baseCSS := catAllInDir(srcDir, conf.BaseCSS)
template := readFile(srcFile(conf.Template))
baseCSS := catInDir(srcDir, conf.BaseCSS)
// Initialize templates. They are all initialized from the same source code,
// plus a snippet to fix the "content" reference.
@ -63,7 +72,7 @@ func main() {
// Add category index to the sitemap, without "/index.html"
allPaths = append(allPaths, name)
// Create directory
catDir := path.Join(dstDir, name)
catDir := dstFile(name)
err := os.MkdirAll(catDir, 0755)
if err != nil {
log.Fatal(err)
@ -71,7 +80,7 @@ func main() {
// Generate index
cd := &categoryDot{base, name, prelude, articles, css, js}
executeToFile(categoryTmpl, cd, path.Join(catDir, "index.html"))
executeToFile(categoryTmpl, cd, filepath.Join(catDir, "index.html"))
}
for _, cat := range conf.Categories {
@ -84,24 +93,24 @@ func main() {
continue
}
catConf := readCategoryConf(cat.Name, path.Join(srcDir, cat.Name, "index.toml"))
catConf := &categoryConf{}
decodeTOML(srcFile(cat.Name, "index.toml"), catConf)
prelude := ""
if catConf.Prelude != "" {
prelude = readAll(
path.Join(srcDir, cat.Name, catConf.Prelude+".html"))
prelude = readFile(srcFile(cat.Name, catConf.Prelude+".html"))
}
css := catAllInDir(path.Join(srcDir, cat.Name), catConf.ExtraCSS)
js := catAllInDir(path.Join(srcDir, cat.Name), catConf.ExtraJS)
css := catInDir(srcFile(cat.Name), catConf.ExtraCSS)
js := catInDir(srcFile(cat.Name), catConf.ExtraJS)
renderCategoryIndex(cat.Name, prelude, css, js, catConf.Articles)
// Generate articles
for _, am := range catConf.Articles {
// Add article URL to sitemap.
p := path.Join(cat.Name, am.Name+".html")
p := filepath.Join(cat.Name, am.Name+".html")
allPaths = append(allPaths, p)
a := getArticle(article{Category: cat.Name}, am, path.Join(srcDir, cat.Name))
a := getArticle(article{Category: cat.Name}, am, srcFile(cat.Name))
modTime := time.Time(a.LastModified)
if modTime.After(lastModified) {
lastModified = modTime
@ -109,7 +118,7 @@ func main() {
// Generate article page.
ad := &articleDot{base, a}
executeToFile(articleTmpl, ad, path.Join(dstDir, p))
executeToFile(articleTmpl, ad, dstFile(p))
allArticleMetas = append(allArticleMetas, a.articleMeta)
recents.insert(a)
@ -118,7 +127,9 @@ func main() {
// Generate "all category"
if hasAllCategory {
sortArticleMetas(allArticleMetas)
sort.Slice(allArticleMetas, func(i, j int) bool {
return allArticleMetas[i].Timestamp > allArticleMetas[j].Timestamp
})
renderCategoryIndex("all", "", "", "", allArticleMetas)
}
@ -126,18 +137,15 @@ func main() {
// article pages.
a := getArticle(article{IsHomepage: true, Category: "homepage"}, conf.Index, srcDir)
ad := &articleDot{base, a}
executeToFile(homepageTmpl, ad, path.Join(dstDir, "index.html"))
executeToFile(homepageTmpl, ad, dstFile("index.html"))
// Generate feed.
feedArticles := recents.articles
fd := feedDot{base, feedArticles, rfc3339Time(lastModified)}
executeToFile(feedTmpl, fd, path.Join(dstDir, "feed.atom"))
executeToFile(feedTmpl, fd, dstFile("feed.atom"))
// Generate site map.
file, err := openForWrite(path.Join(dstDir, "sitemap.txt"))
if err != nil {
log.Fatal(err)
}
file := openForWrite(dstFile("sitemap.txt"))
defer file.Close()
for _, p := range allPaths {
fmt.Fprintf(file, "%s/%s\n", conf.RootURL, p)

View File

@ -78,17 +78,18 @@ func newTemplate(name, root string, sources ...string) *template.Template {
return t
}
func openForWrite(fname string) (*os.File, error) {
return os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
}
func executeToFile(t *template.Template, data interface{}, fname string) {
file, err := openForWrite(fname)
func openForWrite(fname string) *os.File {
file, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
log.Fatal(err)
}
return file
}
func executeToFile(t *template.Template, data interface{}, fname string) {
file := openForWrite(fname)
defer file.Close()
err = t.Execute(file, data)
err := t.Execute(file, data)
if err != nil {
log.Fatalf("rendering %q: %s", fname, err)
}

View File

@ -1,23 +0,0 @@
package main
import "sort"
func sortArticleMetas(a []articleMeta) {
sort.Slice(a, func(i, j int) bool {
return a[i].Timestamp > a[j].Timestamp
})
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a < b {
return a
}
return b
}