LabCodeHub öffentliche Ansicht
Anmelden
Küpper / Quildrop öffentlich
Branch: main
Quildrop / internal / content / post.go
Verlauf Rohdaten
R Rüdiger Küpper fix: first commit
06fa7c25 vor 6 Monaten
internal/content/post.go 104 Zeilen · 2.5 KB · Go
1
package content
2
3
import (
4
	"fmt"
5
	"html/template"
6
	"strings"
7
	"time"
8
9
	"gopkg.in/yaml.v3"
10
)
11
12
// FlexTime handles both "2025-01-02 15:04:05" and "2025-01-02" date formats.
13
type FlexTime struct {
14
	time.Time
15
}
16
17
func (ft *FlexTime) UnmarshalYAML(value *yaml.Node) error {
18
	layouts := []string{
19
		"2006-01-02 15:04:05 -0700",
20
		"2006-01-02 15:04:05",
21
		"2006-01-02",
22
	}
23
	for _, layout := range layouts {
24
		t, err := time.Parse(layout, value.Value)
25
		if err == nil {
26
			ft.Time = t
27
			return nil
28
		}
29
	}
30
	return fmt.Errorf("cannot parse date: %s", value.Value)
31
}
32
33
type Post struct {
34
	// Frontmatter
35
	Title        string   `yaml:"title"`
36
	Date         FlexTime `yaml:"date"`
37
	Update       FlexTime `yaml:"update"`
38
	Author       string   `yaml:"author"`
39
	Cover        string   `yaml:"cover"`
40
	FeatureImage string   `yaml:"featureImage"`
41
	Tags         []string `yaml:"tags"`
42
	Categories   []string `yaml:"categories"`
43
	Preview      string   `yaml:"preview"`
44
	Draft        bool     `yaml:"draft"`
45
	Top          bool     `yaml:"top"`
46
	Type         string   `yaml:"type"`
47
	Hide         bool     `yaml:"hide"`
48
	TOC          bool     `yaml:"toc"`
49
50
	// Derived
51
	Slug        string
52
	Content     string
53
	HTMLContent template.HTML
54
	Filename    string
55
}
56
57
// GetCover returns the cover image path. Falls back to featureImage if cover is empty.
58
func (p *Post) GetCover() string {
59
	if p.Cover != "" {
60
		return p.Cover
61
	}
62
	return p.FeatureImage
63
}
64
65
// GetPreview returns the preview text, falling back to the first paragraph of content.
66
func (p *Post) GetPreview() string {
67
	if p.Preview != "" {
68
		return p.Preview
69
	}
70
	lines := strings.Split(p.Content, "\n")
71
	for _, line := range lines {
72
		line = strings.TrimSpace(line)
73
		if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, "!") || strings.HasPrefix(line, "{{") || strings.HasPrefix(line, "```") {
74
			continue
75
		}
76
		if len(line) > 200 {
77
			return line[:200] + "..."
78
		}
79
		return line
80
	}
81
	return ""
82
}
83
84
// CollectTags builds a map of tag name -> posts for that tag.
85
func CollectTags(posts []*Post) map[string][]*Post {
86
	tags := make(map[string][]*Post)
87
	for _, p := range posts {
88
		for _, tag := range p.Tags {
89
			tags[tag] = append(tags[tag], p)
90
		}
91
	}
92
	return tags
93
}
94
95
// CollectCategories builds a map of category name -> posts for that category.
96
func CollectCategories(posts []*Post) map[string][]*Post {
97
	cats := make(map[string][]*Post)
98
	for _, p := range posts {
99
		for _, cat := range p.Categories {
100
			cats[cat] = append(cats[cat], p)
101
		}
102
	}
103
	return cats
104
}