From ef112be9bdfa523b6c325b7bdcad73ac039b6e22 Mon Sep 17 00:00:00 2001 From: Naman Sood Date: Mon, 15 Mar 2021 22:47:07 -0400 Subject: [PATCH] rename 'pages' to 'posts' and move functionality into own file Signed-off-by: Naman Sood --- page.go | 125 --------------------------------------- post.go | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ server.go | 86 ++++++++------------------- 3 files changed, 198 insertions(+), 187 deletions(-) delete mode 100644 page.go create mode 100644 post.go diff --git a/page.go b/page.go deleted file mode 100644 index d155ca3..0000000 --- a/page.go +++ /dev/null @@ -1,125 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "os" - "sort" - - "github.com/aymerick/raymond" - "github.com/mitchellh/mapstructure" - "github.com/yuin/goldmark" - highlighting "github.com/yuin/goldmark-highlighting" - meta "github.com/yuin/goldmark-meta" - "github.com/yuin/goldmark/extension" - "github.com/yuin/goldmark/parser" - "github.com/yuin/goldmark/renderer/html" -) - -// Metadata stores the data about a page that needs to be visible -// at the home page. -type Metadata struct { - Title string - Summary string - Time int64 // unix timestamp -} - -// Page stores the contents of a blog post. -type Page struct { - Slug string - Metadata Metadata - Contents string -} - -func newPage(slug string) (*Page, error) { - data, err := os.ReadFile("posts/" + slug + ".md") - if err != nil { - return nil, fmt.Errorf("could not read file: %s", err) - } - - md := goldmark.New( - goldmark.WithExtensions( - extension.Linkify, - extension.Strikethrough, - extension.Typographer, - meta.Meta, - highlighting.Highlighting, - ), - goldmark.WithRendererOptions( - html.WithUnsafe(), - ), - ) - var converted bytes.Buffer - ctx := parser.NewContext() - err = md.Convert(data, &converted, parser.WithContext(ctx)) - if err != nil { - return nil, fmt.Errorf("could not parse markdown: %s", err) - } - mdMap, err := meta.TryGet(ctx) - if err != nil { - return nil, fmt.Errorf("could not parse metadata: %s", err) - } - var metadata Metadata - err = mapstructure.Decode(mdMap, &metadata) - if err != nil { - return nil, fmt.Errorf("could not destructure metadata: %s", err) - } - - page := &Page{ - Slug: slug, - Metadata: metadata, - Contents: converted.String(), - } - - return page, nil -} - -func (p *Page) render(tpl *raymond.Template) (string, error) { - return tpl.Exec(p) -} - -func (p *Page) String() string { - return p.Slug -} - -type pages []*Page - -func insertOrUpdate(ps pages, p *Page) pages { - defer sort.Sort(ps) - for i, pg := range ps { - if pg.Slug == p.Slug { - ps[i] = p - return ps - } - } - ps = append(ps, p) - return ps -} - -func remove(ps pages, slug string) pages { - for i, pg := range ps { - if pg.Slug == slug { - ps = append(ps[:i], ps[i+1:]...) - break - } - } - fmt.Println(ps) - return ps -} - -// Len implements sort.Interface -func (ps pages) Len() int { - return len(ps) -} - -// Less implements sort.Interface -func (ps pages) Less(i, j int) bool { - return ps[i].Metadata.Time > ps[j].Metadata.Time -} - -// Swap implements sort.Interface -func (ps pages) Swap(i, j int) { - temp := ps[i] - ps[i] = ps[j] - ps[j] = temp -} diff --git a/post.go b/post.go new file mode 100644 index 0000000..2e7b285 --- /dev/null +++ b/post.go @@ -0,0 +1,174 @@ +package main + +import ( + "bytes" + "fmt" + "log" + "os" + "sort" + "strings" + + "github.com/aymerick/raymond" + "github.com/mitchellh/mapstructure" + "github.com/yuin/goldmark" + highlighting "github.com/yuin/goldmark-highlighting" + meta "github.com/yuin/goldmark-meta" + "github.com/yuin/goldmark/extension" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/renderer/html" +) + +// Metadata stores the data about a post that needs to be visible +// at the home page. +type Metadata struct { + Title string + Summary string + Time int64 // unix timestamp +} + +// Post stores the contents of a blog post. +type Post struct { + Slug string + Metadata Metadata + Contents string +} + +func newPost(slug string) (*Post, error) { + data, err := os.ReadFile("posts/" + slug + ".md") + if err != nil { + return nil, fmt.Errorf("could not read file: %s", err) + } + + md := goldmark.New( + goldmark.WithExtensions( + extension.Linkify, + extension.Strikethrough, + extension.Typographer, + meta.Meta, + highlighting.Highlighting, + ), + goldmark.WithRendererOptions( + html.WithUnsafe(), + ), + ) + var converted bytes.Buffer + ctx := parser.NewContext() + err = md.Convert(data, &converted, parser.WithContext(ctx)) + if err != nil { + return nil, fmt.Errorf("could not parse markdown: %s", err) + } + mdMap, err := meta.TryGet(ctx) + if err != nil { + return nil, fmt.Errorf("could not parse metadata: %s", err) + } + var metadata Metadata + err = mapstructure.Decode(mdMap, &metadata) + if err != nil { + return nil, fmt.Errorf("could not destructure metadata: %s", err) + } + + post := &Post{ + Slug: slug, + Metadata: metadata, + Contents: converted.String(), + } + + return post, nil +} + +func (p *Post) render(tpl *raymond.Template) (string, error) { + return tpl.Exec(p) +} + +func (p *Post) String() string { + return p.Slug +} + +type postList []*Post + +func newPostList() (postList, error) { + files, err := os.ReadDir("posts/") + if err != nil { + return nil, err + } + + pl := make(postList, 0, len(files)) + for _, f := range files { + filename := f.Name() + + if strings.HasSuffix(filename, ".md") { + post, err := newPost(strings.TrimSuffix(filename, ".md")) + if err != nil { + return nil, fmt.Errorf("could not render %s: %s", filename, err) + } + pl = append(pl, post) + log.Printf("Loaded post %s", filename) + } + } + sort.Sort(pl) + + return pl, nil +} + +func insertOrUpdatePost(pl postList, p *Post) postList { + defer sort.Sort(pl) + for i, post := range pl { + if post.Slug == p.Slug { + pl[i] = p + return pl + } + } + pl = append(pl, p) + return pl +} + +func removePost(pl postList, slug string) postList { + for i, post := range pl { + if post.Slug == slug { + pl = append(pl[:i], pl[i+1:]...) + break + } + } + fmt.Println(pl) + return pl +} + +// Len implements sort.Interface +func (pl postList) Len() int { + return len(pl) +} + +// Less implements sort.Interface +func (pl postList) Less(i, j int) bool { + return pl[i].Metadata.Time > pl[j].Metadata.Time +} + +// Swap implements sort.Interface +func (pl postList) Swap(i, j int) { + temp := pl[i] + pl[i] = pl[j] + pl[j] = temp +} + +func newPostListener(update func(func(postList) postList)) *listener { + ln := &listener{ + folder: "posts/", + update: func(file string) error { + post, err := newPost(strings.TrimSuffix(file, ".md")) + if err != nil { + return err + } + update(func(oldList postList) postList { + return insertOrUpdatePost(oldList, post) + }) + return nil + }, + clean: func(file string) error { + update(func(oldList postList) postList { + return removePost(oldList, strings.TrimSuffix(file, ".md")) + }) + return nil + }, + } + return ln +} diff --git a/server.go b/server.go index 133e2b9..3444f38 100644 --- a/server.go +++ b/server.go @@ -7,7 +7,6 @@ import ( "net/http" "os" "runtime" - "sort" "strconv" "strings" "sync" @@ -24,8 +23,8 @@ type server struct { tplMutex sync.RWMutex templates map[string]*raymond.Template - pgMutex sync.RWMutex - pages + postsMutex sync.RWMutex + postList } func newServer() (*server, error) { @@ -33,10 +32,13 @@ func newServer() (*server, error) { staticHandler: http.FileServer(http.Dir("static/")), } - err := s.refreshPages() + posts, err := newPostList() if err != nil { return nil, err } + s.postsMutex.Lock() + s.postList = posts + s.postsMutex.Unlock() err = s.refreshTemplates() if err != nil { @@ -48,26 +50,12 @@ func newServer() (*server, error) { return nil, err } - pagesLn := &listener{ - folder: "posts/", - update: func(file string) error { - s.pgMutex.Lock() - defer s.pgMutex.Unlock() - page, err := newPage(strings.TrimSuffix(file, ".md")) - if err != nil { - return err - } - s.pages = insertOrUpdate(s.pages, page) - return nil - }, - clean: func(file string) error { - s.pgMutex.Lock() - defer s.pgMutex.Unlock() - s.pages = remove(s.pages, strings.TrimSuffix(file, ".md")) - return nil - }, - } - go pagesLn.listen() + postsLn := newPostListener(func(updateFn func(postList) postList) { + s.postsMutex.Lock() + defer s.postsMutex.Unlock() + s.postList = updateFn(s.postList) + }) + go postsLn.listen() templatesLn := &listener{ folder: "templates/", @@ -119,32 +107,6 @@ func newServer() (*server, error) { return s, nil } -func (s *server) refreshPages() error { - files, err := os.ReadDir("posts/") - if err != nil { - return err - } - - s.pgMutex.Lock() - defer s.pgMutex.Unlock() - s.pages = make(pages, 0, len(files)) - for _, f := range files { - filename := f.Name() - - if strings.HasSuffix(filename, ".md") { - page, err := newPage(strings.TrimSuffix(filename, ".md")) - if err != nil { - return fmt.Errorf("could not render %s: %s", filename, err) - } - s.pages = append(s.pages, page) - log.Printf("Loaded page %s", filename) - } - } - sort.Sort(s.pages) - - return nil -} - func loadTemplate(file string) (*raymond.Template, error) { tpl, err := raymond.ParseFile("templates/" + file + ".html") if err != nil { @@ -340,11 +302,11 @@ func (s *server) router(res http.ResponseWriter, req *http.Request) { return } - s.pgMutex.RLock() - defer s.pgMutex.RUnlock() - for _, p := range s.pages { + s.postsMutex.RLock() + defer s.postsMutex.RUnlock() + for _, p := range s.postList { if p.Slug == slug { - s.renderPage(p, res, req) + s.postPage(p, res, req) return } } @@ -358,7 +320,7 @@ func (s *server) errorInRequest(res http.ResponseWriter, req *http.Request, err log.Printf("ERR %s: %s", req.URL.Path, err) } -func (s *server) createPage(title, contents string) (string, error) { +func (s *server) createWebPage(title, contents string) (string, error) { ctx := map[string]interface{}{ "title": title, "contents": contents, @@ -366,13 +328,13 @@ func (s *server) createPage(title, contents string) (string, error) { return s.templates["page"].Exec(ctx) } -func (s *server) renderPage(p *Page, res http.ResponseWriter, req *http.Request) { +func (s *server) postPage(p *Post, res http.ResponseWriter, req *http.Request) { res.Header().Add("content-type", "text/html") contents, err := p.render(s.templates["fullpost"]) if err != nil { s.errorInRequest(res, req, err) } - page, err := s.createPage(p.Metadata.Title, contents) + page, err := s.createWebPage(p.Metadata.Title, contents) if err != nil { s.errorInRequest(res, req, err) } @@ -384,17 +346,17 @@ func (s *server) homePage(res http.ResponseWriter, req *http.Request) { var posts string - s.pgMutex.RLock() - defer s.pgMutex.RUnlock() - for _, p := range s.pages { + s.postsMutex.RLock() + defer s.postsMutex.RUnlock() + for _, p := range s.postList { summary, err := p.render(s.templates["summary"]) if err != nil { - log.Printf("could not render page summary for %s", p.Slug) + log.Printf("could not render post summary for %s", p.Slug) } posts = posts + summary } - page, err := s.createPage("Home", posts) + page, err := s.createWebPage("Home", posts) if err != nil { s.errorInRequest(res, req, err)