entry.go (3076B)
1 package entry 2 3 import ( 4 "embed" 5 "encoding/json" 6 "fmt" 7 "html/template" 8 "io/fs" 9 "path/filepath" 10 "sort" 11 "time" 12 13 "code.dwrz.net/src/pkg/log" 14 ) 15 16 //go:embed static/* 17 var static embed.FS 18 19 const ( 20 YearFormat = "2006" 21 DateFormat = "2006-01-02" 22 metadataFile = "metadata.json" 23 ) 24 25 type Entry struct { 26 Cover string `json:"cover"` 27 Content template.HTML `json:"content"` 28 Date time.Time `json:"date"` 29 Link string `json:"link"` 30 Next *Entry `json:"next"` 31 Previous *Entry `json:"previous"` 32 Published bool `json:"published"` 33 Title string `json:"title"` 34 } 35 36 type Year struct { 37 Entries []*Entry 38 Text string 39 } 40 41 type LoadParams struct { 42 Log *log.Logger 43 } 44 45 func Load(p LoadParams) ([]*Entry, error) { 46 var entries []*Entry 47 48 parseFS := func(path string, d fs.DirEntry, err error) error { 49 if err != nil { 50 return err 51 } 52 if !d.IsDir() || d.Name() == "static" { 53 return nil 54 } 55 56 // Open the entry metadata file. 57 data, err := static.ReadFile( 58 filepath.Join("static", d.Name(), metadataFile), 59 ) 60 if err != nil { 61 p.Log.Error.Printf("ignoring %s: %v", d.Name(), err) 62 return nil 63 } 64 65 var entry = &Entry{} 66 if err := json.Unmarshal(data, entry); err != nil { 67 p.Log.Error.Printf("ignoring %s: %v", d.Name(), err) 68 return nil 69 } 70 71 // Ignore unpublished entries. 72 if !entry.Published { 73 p.Log.Debug.Printf( 74 "ignoring %s: not published", d.Name(), 75 ) 76 return nil 77 } 78 79 // Get the entry content. 80 entryFile := d.Name() + ".html" 81 content, err := static.ReadFile( 82 filepath.Join("static", d.Name(), entryFile), 83 ) 84 if err != nil { 85 p.Log.Error.Printf("ignoring %s: %v", d.Name(), err) 86 return nil 87 } 88 89 // Set entry values. 90 entry.Content = template.HTML(string(content)) 91 entry.Link = fmt.Sprintf("%s", entry.Date.Format(DateFormat)) 92 93 entries = append(entries, entry) 94 95 return nil 96 } 97 98 if err := fs.WalkDir(static, "static", parseFS); err != nil { 99 return nil, fmt.Errorf("failed to parse templates: %v", err) 100 } 101 102 // Sort the entries. 103 sort.Slice(entries, func(i, j int) bool { 104 return entries[i].Date.Before(entries[j].Date) 105 }) 106 107 // Set the previous and next entry. 108 for i, e := range entries { 109 if i-1 >= 0 { 110 e.Previous = entries[i-1] 111 } 112 if i+1 < len(entries) { 113 e.Next = entries[i+1] 114 } 115 } 116 117 return entries, nil 118 } 119 120 func SortYear(entries []*Entry) []Year { 121 var yearEntries = map[string]*Year{} 122 for _, e := range entries { 123 year := e.Date.Format(YearFormat) 124 if _, exists := yearEntries[year]; !exists { 125 yearEntries[year] = &Year{ 126 Entries: []*Entry{e}, 127 Text: year, 128 } 129 continue 130 } 131 132 yearEntries[year].Entries = append(yearEntries[year].Entries, e) 133 } 134 135 // Sort each year's entries, then sort the years. 136 var years = []Year{} 137 for _, year := range yearEntries { 138 sort.Slice(year.Entries, func(i, j int) bool { 139 return year.Entries[i].Date.After(year.Entries[j].Date) 140 }) 141 142 years = append(years, *year) 143 } 144 sort.Slice(years, func(i, j int) bool { 145 return years[i].Text > years[j].Text 146 }) 147 148 return years 149 }