diff options
Diffstat (limited to 'page')
| -rw-r--r-- | page/checkup.go | 8 | ||||
| -rw-r--r-- | page/index.go | 23 | ||||
| -rw-r--r-- | page/misc.go | 8 | ||||
| -rw-r--r-- | page/page.go | 59 | ||||
| -rw-r--r-- | page/pagelist.go | 12 | ||||
| -rw-r--r-- | page/render.go | 33 | ||||
| -rw-r--r-- | page/time.go | 14 |
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 } |
