aboutsummaryrefslogtreecommitdiff
path: root/page
diff options
context:
space:
mode:
Diffstat (limited to 'page')
-rw-r--r--page/checkup.go8
-rw-r--r--page/index.go23
-rw-r--r--page/misc.go8
-rw-r--r--page/page.go59
-rw-r--r--page/pagelist.go12
-rw-r--r--page/render.go33
-rw-r--r--page/time.go14
7 files changed, 102 insertions, 55 deletions
diff --git a/page/checkup.go b/page/checkup.go
index c4501c2..9f721b7 100644
--- a/page/checkup.go
+++ b/page/checkup.go
@@ -9,25 +9,25 @@ import (
)
// Checkup will return a map[string]PageList of all the pages broken down by
-// the status of their fields. For instance, whehter or not they have a date
+// the status of their fields. For instance, whehter or not they have a date.
func (p *Page) Checkup() (map[string]PageList, error) {
Logger.Println("Checking up on all files...")
out := make(map[string]PageList)
- filepath.Walk(filepath.Dir("."),
+ _ = filepath.Walk(filepath.Dir("."),
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && strings.HasSuffix(info.Name(), Suffix) {
-
p2 := NewPage(strings.ReplaceAll(path, Suffix, ""))
err = p2.Read()
if err != nil {
Logger.Println("Error encountered: ", err)
+
return err
}
@@ -73,9 +73,7 @@ func (p *Page) Checkup() (map[string]PageList, error) {
} else {
out[key] = append(out[key], p2)
}
-
}
-
}
return nil
diff --git a/page/index.go b/page/index.go
index f9f2df5..425bf04 100644
--- a/page/index.go
+++ b/page/index.go
@@ -8,27 +8,32 @@ import (
"time"
)
-var index map[string]PageList
-var indexMu sync.RWMutex
+var (
+ index map[string]PageList
+ indexMu sync.RWMutex
+)
// RebuildIndex can be called in order to rebuild the entire website
-// index
+// index.
func (p *Page) RebuildIndex() error {
indexMu.Lock()
index = nil
indexMu.Unlock()
+
_, err := p.Index()
+
return err
}
-// Index returns a map of all pages in the current directory seperated into
+// Index returns a map of all pages in the current directory separated into
// their respective tags If a Page has multiple tags it will be listed under
// each.
-// Pages are located by their Suffix, default being ".md"
+// Pages are located by their Suffix, default being ".md".
func (p *Page) Index() (map[string]PageList, error) {
indexMu.RLock()
if index != nil && CacheIndex {
indexMu.RUnlock()
+
return index, nil
}
indexMu.RUnlock()
@@ -36,30 +41,29 @@ func (p *Page) Index() (map[string]PageList, error) {
out := make(map[string]PageList)
- filepath.Walk(filepath.Dir("."),
+ _ = filepath.Walk(filepath.Dir("."),
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && strings.HasSuffix(info.Name(), Suffix) {
-
p2 := NewPage(strings.ReplaceAll(path, Suffix, ""))
err = p2.Read()
if err != nil {
Logger.Println("Error encountered: ", err)
+
return err
}
- for tag, _ := range p2.Tags {
+ for tag := range p2.Tags {
if _, ok := out[tag]; !ok {
out[tag] = []*Page{p2}
} else {
out[tag] = append(out[tag], p2)
}
}
-
}
return nil
@@ -72,6 +76,7 @@ func (p *Page) Index() (map[string]PageList, error) {
return out, nil
}
+// Time fetches the time.Time from the Date field.
func (p *Page) Time() time.Time {
return p.Date.Time
}
diff --git a/page/misc.go b/page/misc.go
index 0dbd059..e8bdfb7 100644
--- a/page/misc.go
+++ b/page/misc.go
@@ -6,6 +6,8 @@ import (
"gopkg.in/yaml.v3"
)
+// EncodeYaml is meant to be used in templating functions to encode
+// arbitrary information as a yaml string.
func (p Page) EncodeYaml(data interface{}) string {
if data == nil {
data = p
@@ -15,10 +17,13 @@ func (p Page) EncodeYaml(data interface{}) string {
if err != nil {
Logger.Println("Encountered error in EncodeYaml: ", err)
}
+
return string(b)
}
-func (p Page) EncodeJson(data interface{}) string {
+// EncodeJSON is meant to be used in templating functions to encode
+// arbitrary information as a JSON string.
+func (p Page) EncodeJSON(data interface{}) string {
if data == nil {
data = p
}
@@ -27,5 +32,6 @@ func (p Page) EncodeJson(data interface{}) string {
if err != nil {
Logger.Println("Encountered error in EncodeJson: ", err)
}
+
return string(b)
}
diff --git a/page/page.go b/page/page.go
index c926bed..fa4ce6c 100644
--- a/page/page.go
+++ b/page/page.go
@@ -1,4 +1,4 @@
-// page implements the website backed by a local filesystem.
+// Package page implements the website backed by a local filesystem.
//
// Reading the base template off the disk, then any markdown files which are
// split into two sections by the DocumentSplit global variable. The first
@@ -25,6 +25,7 @@ package page
import (
"bufio"
"bytes"
+ "errors"
"fmt"
"io"
"log"
@@ -56,27 +57,28 @@ type Page struct {
}
// Global is meant to be supplied by external users of this package to populate
-// globally accessable information across all of the templates accessiable via
-// .Global care must be taken when utilizing this functionality
+// globally accessible information across all of the templates accessiable via
+// .Global care must be taken when utilizing this functionality.
var Global interface{}
// CacheIndex determines whether or not the index will be cached in memory
-// or rebuilt on each call
+// or rebuilt on each call.
var CacheIndex = true
-// BaseTemplate can be adjusted to change the base template used in rendering
+// BaseTemplate can be adjusted to change the base template used in rendering.
var BaseTemplate = "inc/base.html"
-// Suffix is applied to all pages for reading off of the disk
+// Suffix is applied to all pages for reading off of the disk.
var Suffix = ".md"
-// DocumentSplit is used to split the .md files into yaml and markdown
+// DocumentSplit is used to split the .md files into yaml and markdown.
var DocumentSplit = "|---\n"
-// Default logger
-var Logger = log.New(os.Stderr, "", log.LstdFlags)
+// Logger is the default logger used throughout this package, feel
+// free to override.
+var Logger = log.New(os.Stderr, "PAGE: ", log.LstdFlags)
-// NewPage returns a page struct with the path populated
+// NewPage returns a page struct with the path populated.
func NewPage(pth string) *Page {
return &Page{path: filepath.FromSlash(filepath.Clean(pth))}
}
@@ -95,12 +97,12 @@ func (p Page) Path() string {
}
// Global is specifically for use inside of a page markdown file or
-// in a base template. This simply returns the package Global variable
+// in a base template. This simply returns the package Global variable.
func (p *Page) Global() interface{} {
return Global
}
-// Renders a page
+// Render a page.
func (p *Page) Render(wr io.Writer) error {
if err := p.Read(); err != nil {
return err
@@ -108,38 +110,46 @@ func (p *Page) Render(wr io.Writer) error {
t, err := template.ParseFiles(BaseTemplate)
if err != nil {
- return err
+ return fmt.Errorf("rendering: %w", err)
}
return t.Execute(wr, p)
}
-// Reads in the special markdown file format for the website off of the disk
+// Read in the special markdown file format for the website off of the disk.
func (p *Page) Read() error {
yamlBuf := bytes.NewBuffer(nil)
markdownBuf := bytes.NewBuffer(nil)
fh, err := os.Open(p.path + Suffix)
if err != nil {
- return err
+ return fmt.Errorf("opening markdown: %w", err)
}
- defer fh.Close()
+
+ defer func() {
+ err := fh.Close()
+ if err != nil {
+ Logger.Println(err)
+ }
+ }()
+
rdr := bufio.NewReader(fh)
// Read in the file and split between markdown and yaml buffers
yamlDone := false
- for {
+ for {
bytes, err := rdr.ReadBytes('\n')
- if err == io.EOF {
+ if errors.Is(err, io.EOF) {
break
} else if err != nil {
- return err
+ return fmt.Errorf("reading markdown: %w", err)
}
// Is this the line where we stop reading the yaml and start reading markdown?
if DocumentSplit == string(bytes) && !yamlDone {
yamlDone = true
+
continue
}
@@ -152,23 +162,28 @@ func (p *Page) Read() error {
err = yaml.Unmarshal(yamlBuf.Bytes(), p)
if err != nil {
- return err
+ return fmt.Errorf("reading yaml: %w", err)
}
p.markdown = markdownBuf.Bytes()
+
return nil
}
+// RenderBody renders and executes a template from the body of the
+// markdown file, then runs it through the markdown parser.
func (p *Page) RenderBody() (string, error) {
buf := &bytes.Buffer{}
+
t, err := template.New("Body").Parse(string(p.markdown))
if err != nil {
- return "", err
+ return "", fmt.Errorf("render body: %w", err)
}
err = t.Execute(buf, p)
+
if err != nil {
- return "", err
+ return "", fmt.Errorf("template execute; %w", err)
}
return string(blackfriday.Run(buf.Bytes())), nil
diff --git a/page/pagelist.go b/page/pagelist.go
index f2140f9..298acf9 100644
--- a/page/pagelist.go
+++ b/page/pagelist.go
@@ -5,15 +5,16 @@ import (
)
// PageList is a slice of pages, providing a couple of methods to sort
-// by the date, or date reversed
+// by the date, or date reversed.
type PageList []*Page
// RemoveDateless returns two PageLists, the first with valid dates,
// and the second without. This is useful if you need a PageList which
-// will run SortDate and SortDateReverse without issue
+// will run SortDate and SortDateReverse without issue.
func (p PageList) RemoveDateless() (PageList, PageList) {
with := PageList{}
without := PageList{}
+
for _, p := range p {
if p.Date != nil {
with = append(with, p)
@@ -21,19 +22,26 @@ func (p PageList) RemoveDateless() (PageList, PageList) {
without = append(without, p)
}
}
+
return with, without
}
+// SortDate returns the pagelist sorted by date, may panic if pages do
+// not all have dates.
func (p PageList) SortDate() PageList {
sort.Slice(p, func(i, j int) bool {
return p[i].Time().After(p[j].Time())
})
+
return p
}
+// SortDateReverse returns the pagelist sorted by date in reverse, may panic if
+// pages do not all have dates.
func (p PageList) SortDateReverse() PageList {
sort.Slice(p, func(i, j int) bool {
return p[i].Time().Before(p[j].Time())
})
+
return p
}
diff --git a/page/render.go b/page/render.go
index 07b1b88..6cbabf9 100644
--- a/page/render.go
+++ b/page/render.go
@@ -7,14 +7,14 @@ import (
)
// Render is a lower level option, allowing you to specify local
-// variables and the status code in which to return
+// variables and the status code in which to return.
func Render(w http.ResponseWriter, r *http.Request,
path string, vars map[string]interface{}, statusCode int) {
-
u := r.URL.Path
if u == "/" {
u = "/index"
}
+
u = filepath.Join(".", u)
// Sepcifically use the specified path for the page
@@ -32,8 +32,11 @@ func Render(w http.ResponseWriter, r *http.Request,
r.Method,
http.StatusNotFound,
u)
+
p = NewPage("404")
+
w.WriteHeader(http.StatusNotFound)
+
err := p.Render(w)
if err != nil {
Logger.Printf("%s %s path: %s while trying 404: %s",
@@ -43,19 +46,22 @@ func Render(w http.ResponseWriter, r *http.Request,
err)
http.Error(w, "Internal server error",
http.StatusInternalServerError)
+
return
}
- return
- } else {
- Logger.Printf("%s %s path: %s encountered: %s",
- r.RemoteAddr,
- r.Method,
- u,
- err)
- http.Error(w, "Internal server error",
- http.StatusInternalServerError)
+
return
}
+
+ Logger.Printf("%s %s path: %s encountered: %s",
+ r.RemoteAddr,
+ r.Method,
+ u,
+ err)
+ http.Error(w, "Internal server error",
+ http.StatusInternalServerError)
+
+ return
}
Logger.Printf("%s %s %d %s", r.RemoteAddr, r.Method, statusCode, u)
@@ -63,15 +69,14 @@ func Render(w http.ResponseWriter, r *http.Request,
// RenderWithVars allows you to specify a specific page and whether or not
// you wish to override vars. If left nil they will not be overridden.
-// Also see RenderForPath if you don't need to override them
+// Also see RenderForPath if you don't need to override them.
func RenderWithVars(w http.ResponseWriter, r *http.Request,
path string, vars map[string]interface{}) {
-
Render(w, r, path, vars, http.StatusOK)
}
// RenderForPath takes the path to a page and finish up the rendering
-// Allowing you to place logic on what page is rendered by your handlers
+// Allowing you to place logic on what page is rendered by your handlers.
func RenderForPath(w http.ResponseWriter, r *http.Request, path string) {
RenderWithVars(w, r, path, nil)
}
diff --git a/page/time.go b/page/time.go
index 0374490..cd419c8 100644
--- a/page/time.go
+++ b/page/time.go
@@ -1,10 +1,14 @@
package page
import (
- "gopkg.in/yaml.v3"
+ "fmt"
"time"
+
+ "gopkg.in/yaml.v3"
)
+// PageTime allows us to slip in a different time format for loading/unloading
+// the yaml from disk than the default.
type PageTime struct {
time.Time
}
@@ -13,16 +17,22 @@ type PageTime struct {
// from the yaml information.
var TimeFormat = "01.02.2006 15:04:05 MST"
+// UnmarshalYAML override the default and parse our time with the global time
+// format.
func (pt *PageTime) UnmarshalYAML(n *yaml.Node) error {
t, err := time.Parse(TimeFormat, n.Value)
if err != nil {
- return err
+ return fmt.Errorf("pagetime: %w", err)
}
+
pt.Time = t
+
return nil
}
+// MarshalYAML override the default with our own time format.
func (pt PageTime) MarshalYAML() (interface{}, error) {
s := pt.Time.Format(TimeFormat)
+
return s, nil
}