Repos / s4g / 3e3f45cb6d
commit 3e3f45cb6d296202e1364077f2c40c75e1618ed8
Author: Nhân <hi@imnhan.com>
Date:   Wed Aug 23 17:18:06 2023 +0700

    implement standard PageTypes
    
    TODO: write actual series-index template

diff --git a/Makefile b/Makefile
index 1f406a3..d9bbd9c 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,8 @@ build:
 	go build -o dist/
 
 watch:
-	fd -E docs -E theme | entr -rc go run . serve -f docs -p 8000
+	fd -E docs -E theme | entr -rc -s \
+		'go build -o dist/ && ./dist/s4g serve -f docs -p 8000'
 
 watch-theme:
 	find theme/* | entr -c rsync -av theme/ docs/_s4g/theme/
diff --git a/docs/index.dj b/docs/index.dj
index d75a6e5..ea36263 100644
--- a/docs/index.dj
+++ b/docs/index.dj
@@ -1,4 +1,4 @@
 Title: Home
 ShowInFeed: false
-Templates: $base.tmpl, $includes.tmpl, $home.tmpl
+PageType: home
 ---
diff --git a/docs/scale/index.dj b/docs/scale/index.dj
index 1aa7db7..73a7ad2 100644
--- a/docs/scale/index.dj
+++ b/docs/scale/index.dj
@@ -1,5 +1,6 @@
 Title: I'm Going To Scale My Foot Up Your Ass
 PostedAt: 2008-04-24
+PageType: custom
 Templates: $base.tmpl, $includes.tmpl, scale.tmpl
 ---
 
diff --git a/main.go b/main.go
index 17c4b12..56a35a1 100644
--- a/main.go
+++ b/main.go
@@ -267,15 +267,16 @@ type Article struct {
 }
 
 func (a *Article) ComputeDerivedFields(addr, root string) {
-	a.WebPath = computeWebPath(root, a.OutputPath)
-	a.TemplatePaths = computeTemplatePaths(a.Path, a.Templates)
+	a.computeWebPath(root)
+	a.computeTemplatePaths()
+
 	if a.Thumb != "" {
 		a.OpenGraphImage = addr + root + filepath.Dir(a.Path) + "/" + a.Thumb
 	}
 }
 
-func computeWebPath(root string, outputPath string) string {
-	webPath := root + outputPath
+func (a *Article) computeWebPath(root string) {
+	webPath := root + a.OutputPath
 	if strings.HasSuffix(webPath, "/index.html") {
 		webPath = strings.TrimSuffix(webPath, "index.html")
 	}
@@ -286,20 +287,47 @@ func computeWebPath(root string, outputPath string) string {
 		escaped[i] = url.PathEscape(parts[i])
 	}
 
-	return strings.Join(escaped, "/")
+	a.WebPath = strings.Join(escaped, "/")
 }
 
-func computeTemplatePaths(articlePath string, templates []string) []string {
+func (a *Article) computeTemplatePaths() {
+	var templates []string
+	switch a.PageType {
+	case PTPost:
+		templates = []string{
+			"$base.tmpl",
+			"$includes.tmpl",
+			"$post.tmpl",
+		}
+	case PTHome:
+		templates = []string{
+			"$base.tmpl",
+			"$includes.tmpl",
+			"$home.tmpl",
+		}
+	case PTSeriesIndex:
+		templates = []string{
+			"$base.tmpl",
+			"$includes.tmpl",
+			"$series-index.tmpl",
+		}
+	case PTCustom:
+		templates = a.Templates
+	default:
+		panic(fmt.Sprintf("Invalid PageType: %v", a.PageType))
+	}
+
 	paths := make([]string, len(templates))
 	for i := 0; i < len(paths); i++ {
 		p := templates[i]
 		if strings.HasPrefix(p, "$") {
 			paths[i] = ThemePath + "/" + strings.TrimPrefix(p, "$")
 		} else {
-			paths[i] = filepath.Join(filepath.Dir(articlePath), p)
+			paths[i] = filepath.Join(filepath.Dir(a.Path), p)
 		}
 	}
-	return paths
+
+	a.TemplatePaths = paths
 }
 
 func (a *Article) WriteHtmlFile(
@@ -314,7 +342,7 @@ func (a *Article) WriteHtmlFile(
 	// TODO: should probably reuse the template object for common cases
 	if err != nil {
 		return fmt.Errorf(
-			"Failed to parse templates (%v): %w", a.Templates, err,
+			"Failed to parse templates (%v): %w", a.TemplatePaths, err,
 		)
 	}
 
@@ -343,7 +371,7 @@ func (a *Article) WriteHtmlFile(
 		ThemePath:      site.Root + ThemePath,
 	})
 	if err != nil {
-		return fmt.Errorf("Failed to execute templates (%v): %w", a.Templates, err)
+		return fmt.Errorf("Failed to execute templates (%v): %w", a.TemplatePaths, err)
 	}
 	fullHtml := buf.Bytes()
 
@@ -377,11 +405,7 @@ func findArticles(fsys writablefs.FS, site *SiteMetadata) (map[string]Article, e
 		}
 
 		meta := ArticleMetadata{
-			Templates: []string{
-				"$base.tmpl",
-				"$includes.tmpl",
-				"$post.tmpl",
-			},
+			PageType:   PTPost,
 			ShowInFeed: true,
 		}
 		userErr := UnmarshalMetadata(metaText, &meta)
@@ -390,6 +414,22 @@ func findArticles(fsys writablefs.FS, site *SiteMetadata) (map[string]Article, e
 			return fmt.Errorf("findArticles failed to unmarshall metadata: %w", userErr)
 		}
 
+		if meta.PageType != PTCustom && len(meta.Templates) > 0 {
+			return &errs.UserErr{
+				File:  path,
+				Field: "PageType",
+				Msg:   `you must set "PageType: custom" in order to use custom Templates`,
+			}
+		}
+
+		if meta.PageType == PTCustom && len(meta.Templates) == 0 {
+			return &errs.UserErr{
+				File:  path,
+				Field: "Templates",
+				Msg:   `custom PageType requires a non-empty Templates list`,
+			}
+		}
+
 		article := Article{
 			Fs:              fsys,
 			Path:            path,
diff --git a/makesite.go b/makesite.go
index 11f0725..259491d 100644
--- a/makesite.go
+++ b/makesite.go
@@ -32,7 +32,7 @@ func makeSite(path string, meta SiteMetadata) error {
 	// Write default index page
 	indexData := []byte(`Title: Home
 ShowInFeed: false
-Templates: $base.tmpl, $includes.tmpl, $home.tmpl
+PageType: home
 ---
 `)
 	err = os.WriteFile(filepath.Join(path, "index.dj"), indexData, 0664)
diff --git a/metadata.go b/metadata.go
index bf9e52e..98981dc 100644
--- a/metadata.go
+++ b/metadata.go
@@ -29,11 +29,51 @@ type SiteMetadata struct {
 	AuthorTwitter string
 }
 
+type PageType int
+
+const (
+	PTPost PageType = iota
+	PTHome
+	PTSeriesIndex
+	PTCustom
+)
+
+func (t PageType) String() string {
+	switch t {
+	case PTPost:
+		return "post"
+	case PTHome:
+		return "home"
+	case PTSeriesIndex:
+		return "series-index"
+	case PTCustom:
+		return "custom"
+	default:
+		panic(fmt.Sprintf("unexpected value: %d", t))
+	}
+}
+
+func ParsePageType(name string) (PageType, error) {
+	switch name {
+	case "post":
+		return PTPost, nil
+	case "home":
+		return PTHome, nil
+	case "series-index":
+		return PTSeriesIndex, nil
+	case "custom":
+		return PTCustom, nil
+	default:
+		return -1, fmt.Errorf(`"%s" is not a valid PageType`, name)
+	}
+}
+
 type ArticleMetadata struct {
 	Title       string
 	Description string
 	IsDraft     bool
 	PostedAt    time.Time
+	PageType    PageType
 	Templates   []string
 	ShowInFeed  bool
 	Thumb       string
@@ -149,6 +189,16 @@ func UnmarshalMetadata(data []byte, dest any) *errs.UserErr {
 				}
 				s.Field(i).Set(reflect.ValueOf(trimmed))
 
+			case "main.PageType":
+				pt, err := ParsePageType(val)
+				if err != nil {
+					return &errs.UserErr{
+						Field: fieldName,
+						Msg:   err.Error(),
+					}
+				}
+				s.Field(i).Set(reflect.ValueOf(pt))
+
 			default:
 				panic(fmt.Sprintf(
 					`unsupported metadata field type: "%s"`,