commit 805c20bce337bb03c110d03129675a29335759b1
parent 694d6018c5f98e84e7a393fa5721b72a54228fcb
Author: gearsix <gearsix@tuta.io>
Date: Mon, 21 Jun 2021 02:07:51 +0100
gofmt; bugfix to pathing in LoadContentsDir & TestLoadContentsDir
Diffstat:
M | config.go | | | 60 | ++++++++++++++++++++++++++++++------------------------------ |
M | config_test.go | | | 34 | +++++++++++++++++----------------- |
M | content.go | | | 341 | ++++++++++++++++++++++++++++++++++++++++--------------------------------------- |
M | content_test.go | | | 127 | +++++++++++++++++++++++++++++++++++++++++++++---------------------------------- |
M | pagr.go | | | 40 | +++++++++++++++++++--------------------- |
5 files changed, 312 insertions(+), 290 deletions(-)
diff --git a/config.go b/config.go
@@ -1,37 +1,37 @@
package main
import (
- "path/filepath"
- "os"
- "notabug.org/gearsix/suti"
+ "notabug.org/gearsix/suti"
+ "os"
+ "path/filepath"
)
type Config struct {
- Contents string
- Templates string
- Output string
+ Contents string
+ Templates string
+ Output string
}
func (cfg *Config) relPaths(dir string) {
- var paths = []string{cfg.Contents, cfg.Templates, cfg.Output}
- for i, path := range paths {
- if !filepath.IsAbs(path) {
- paths[i] = filepath.Join(dir, path)
- }
- }
- cfg.Contents = paths[0]
- cfg.Templates = paths[1]
- cfg.Output = paths[2]
- return
+ var paths = []string{cfg.Contents, cfg.Templates, cfg.Output}
+ for i, path := range paths {
+ if !filepath.IsAbs(path) {
+ paths[i] = filepath.Join(dir, path)
+ }
+ }
+ cfg.Contents = paths[0]
+ cfg.Templates = paths[1]
+ cfg.Output = paths[2]
+ return
}
// NewConfig returns a Config with default values
func NewConfig() Config {
- return Config {
- Contents: "./content",
- Templates: "./templates",
- Output: "./out",
- }
+ return Config{
+ Contents: "./content",
+ Templates: "./templates",
+ Output: "./out",
+ }
}
// NewConfigFromFile returns a Config with values read from the config file found at `fpath`.
@@ -39,16 +39,16 @@ func NewConfig() Config {
// suti.LoadDataFile() is called to load the file (see notabug.org/gearsix/suti).
// Any relative filepaths in the returned Config are set relative to the parent directory of `fpath`.
func NewConfigFromFile(fpath string) (cfg Config, err error) {
- cfg = NewConfig()
+ cfg = NewConfig()
- if _, err = os.Stat(fpath); err != nil {
- return
- }
+ if _, err = os.Stat(fpath); err != nil {
+ return
+ }
- if err = suti.LoadDataFile(fpath, &cfg); err != nil {
- return
- }
+ if err = suti.LoadDataFile(fpath, &cfg); err != nil {
+ return
+ }
- cfg.relPaths(filepath.Dir(fpath))
- return
+ cfg.relPaths(filepath.Dir(fpath))
+ return
}
diff --git a/config_test.go b/config_test.go
@@ -1,25 +1,25 @@
package main
import (
- "fmt"
- "os"
- "testing"
+ "fmt"
+ "os"
+ "testing"
)
func TestNewConfigFromFile(test *testing.T) {
- test.Parallel()
- tdir := test.TempDir()
- cfgp := fmt.Sprintf("%s/%s.toml", tdir, Name)
- if f, err := os.Create(cfgp); err != nil {
- test.Skipf("failed to create config file: '%s'", cfgp)
- } else {
- f.WriteString(`Output = "./test"`)
- f.Close()
- }
+ test.Parallel()
+ tdir := test.TempDir()
+ cfgp := fmt.Sprintf("%s/%s.toml", tdir, Name)
+ if f, err := os.Create(cfgp); err != nil {
+ test.Skipf("failed to create config file: '%s'", cfgp)
+ } else {
+ f.WriteString(`Output = "./test"`)
+ f.Close()
+ }
- if cfg, err := NewConfigFromFile(cfgp); err != nil {
- test.Fatal(err)
- } else if cfg.Output != tdir+"/test" {
- test.Fatalf(".Output invalid: '%s'", cfg.Output)
- }
+ if cfg, err := NewConfigFromFile(cfgp); err != nil {
+ test.Fatal(err)
+ } else if cfg.Output != tdir+"/test" {
+ test.Fatalf(".Output invalid: '%s'", cfg.Output)
+ }
}
diff --git a/content.go b/content.go
@@ -1,158 +1,164 @@
package main
import (
- "bytes"
- "bufio"
- "path/filepath"
- "io"
- "io/fs"
- "strings"
- "os"
- "notabug.org/gearsix/suti"
+ "bufio"
+ "bytes"
"github.com/yuin/goldmark"
goldmarkext "github.com/yuin/goldmark/extension"
goldmarkparse "github.com/yuin/goldmark/parser"
goldmarkhtml "github.com/yuin/goldmark/renderer/html"
+ "io"
+ "io/fs"
+ "notabug.org/gearsix/suti"
+ "os"
+ "path/filepath"
+ "strings"
)
var ContentBodyExts = [5]string{
- ".txt", // plain-text
- ".html", // HTML
- ".md", // commonmark with non-intrusive extensions: linkify, auto heading id, unsafe HTML
- ".gfm", // github-flavoured markdown
- ".cm", // commonmark
+ ".txt", // plain-text
+ ".html", // HTML
+ ".md", // commonmark with non-intrusive extensions: linkify, auto heading id, unsafe HTML
+ ".gfm", // github-flavoured markdown
+ ".cm", // commonmark
}
type Content []Page
-func LoadContentDir(path string) (c Content, e error) {
- pages := make(map[string]Page)
- defaults := make(map[string]Meta)
- e = filepath.Walk(path, func(fpath string, info fs.FileInfo, err error) error {
- if err != nil {
- return nil
- }
-
- if info.IsDir() {
- path := strings.TrimPrefix(fpath, path)
- p := NewPage(path)
- for _, dir := range strings.Split(fpath, "/") {
- if _, ok := defaults[dir]; ok {
- p.Meta.MergeMeta(defaults[dir], true)
- }
- }
- pages[path] = p
- return nil
- }
-
- pdir := filepath.Dir(fpath)
- page := pages[pdir]
- if strings.Contains(fpath, ".page") || strings.Contains(fpath, ".defaults") {
- var m Meta
- if err = suti.LoadDataFile(fpath, &m); err != nil {
- return err
- }
- if strings.Contains(fpath, ".page") {
- page.Meta.MergeMeta(m, true)
- } else if strings.Contains(fpath, ".defaults") {
- defaults[pdir] = m
- }
- } else if isContentBodyExt(filepath.Ext(fpath)) > -1 {
- page.NewBodyFromFile(fpath)
- } else {
- page.Assets = append(page.Assets, strings.TrimPrefix(fpath, path))
- }
-
- pages[pdir] = page
- return nil
- })
-
- for _, page := range pages {
- c = append(c, page)
- }
-
- return c, e
+func LoadContentDir(dir string) (c Content, e error) {
+ pages := make(map[string]Page)
+ defaults := make(map[string]Meta)
+ if dir[len(dir)-1] != '/' {
+ dir += "/"
+ }
+ e = filepath.Walk(dir, func(fpath string, info fs.FileInfo, err error) error {
+ if err != nil {
+ return nil
+ }
+
+ var path string
+ if info.IsDir() {
+ path = "/" + strings.TrimPrefix(fpath, dir)
+ p := NewPage(path)
+ for _, d := range strings.Split(fpath, "/") {
+ if _, ok := defaults[d]; ok {
+ p.Meta.MergeMeta(defaults[d], true)
+ }
+ }
+ pages[path] = p
+ return nil
+ }
+
+ path, _ = filepath.Split(fpath)
+ path = strings.TrimPrefix(path, dir)
+ path = "/" + strings.TrimSuffix(path, "/")
+ page := pages[path]
+ if strings.Contains(fpath, ".page") || strings.Contains(fpath, ".default") {
+ var m Meta
+ if err = suti.LoadDataFile(fpath, &m); err != nil {
+ return err
+ }
+ if strings.Contains(fpath, ".page") {
+ page.Meta.MergeMeta(m, true)
+ } else if strings.Contains(fpath, ".defaults") {
+ defaults[path] = m
+ }
+ } else if isContentBodyExt(filepath.Ext(fpath)) > -1 {
+ page.NewBodyFromFile(fpath)
+ } else {
+ page.Assets = append(page.Assets, strings.TrimPrefix(fpath, path))
+ }
+
+ pages[path] = page
+ return nil
+ })
+
+ for _, page := range pages {
+ c = append(c, page)
+ }
+
+ return c, e
}
func isContentBodyExt(ext string) int {
- for i, supported := range ContentBodyExts {
- if ext == supported {
- return i
- }
- }
- return -1
+ for i, supported := range ContentBodyExts {
+ if ext == supported {
+ return i
+ }
+ }
+ return -1
}
type Meta map[string]interface{}
func (m Meta) MergeMeta(meta Meta, overwrite bool) {
- for k, v := range meta {
- if _, ok := m[k]; ok && overwrite {
- m[k] = v
- } else if !ok {
- m[k] = v
- }
- }
+ for k, v := range meta {
+ if _, ok := m[k]; ok && overwrite {
+ m[k] = v
+ } else if !ok {
+ m[k] = v
+ }
+ }
}
type Page struct {
- Path string
- Meta Meta
- Body []string
- Assets []string
+ Path string
+ Meta Meta
+ Body []string
+ Assets []string
}
func NewPage(path string) Page {
- return Page {
- Path: path,
- Meta: make(Meta),
- Body: make([]string, 0),
- Assets: make([]string, 0),
- }
+ return Page{
+ Path: path,
+ Meta: make(Meta),
+ Body: make([]string, 0),
+ Assets: make([]string, 0),
+ }
}
func (p *Page) NewBodyFromFile(fpath string) (err error) {
- var buf []byte
- if f, err := os.Open(fpath); err == nil {
- buf, err = io.ReadAll(f)
- f.Close()
- }
- if err != nil {
- return
- }
-
- var body string
- for _, lang := range ContentBodyExts {
- if filepath.Ext(fpath) == lang {
- switch (lang) {
- case ".txt":
- body = txt2html(bytes.NewReader(buf))
- case ".md":
- fallthrough
- case ".gfm":
- fallthrough
- case ".cm":
- markdown := getMarkdown(lang)
- var out bytes.Buffer
- if err = markdown.Convert(buf, &out); err == nil {
- body = out.String()
- } else {
- return err
- }
- case ".html":
- body = string(buf)
- default:
- continue
- }
- }
- }
-
- if len(body) == 0 {
- panic("invalid filetype (" + filepath.Ext(fpath) + ") passed to NewBodyFromFile")
- }
- p.Body = append(p.Body, body)
-
- return err
+ var buf []byte
+ if f, err := os.Open(fpath); err == nil {
+ buf, err = io.ReadAll(f)
+ f.Close()
+ }
+ if err != nil {
+ return
+ }
+
+ var body string
+ for _, lang := range ContentBodyExts {
+ if filepath.Ext(fpath) == lang {
+ switch lang {
+ case ".txt":
+ body = txt2html(bytes.NewReader(buf))
+ case ".md":
+ fallthrough
+ case ".gfm":
+ fallthrough
+ case ".cm":
+ markdown := getMarkdown(lang)
+ var out bytes.Buffer
+ if err = markdown.Convert(buf, &out); err == nil {
+ body = out.String()
+ } else {
+ return err
+ }
+ case ".html":
+ body = string(buf)
+ default:
+ continue
+ }
+ }
+ }
+
+ if len(body) == 0 {
+ panic("invalid filetype (" + filepath.Ext(fpath) + ") passed to NewBodyFromFile")
+ }
+ p.Body = append(p.Body, body)
+
+ return err
}
// txt2html parses textual data from `in` and line-by-line converts
@@ -163,7 +169,7 @@ func (p *Page) NewBodyFromFile(fpath string) (err error) {
func txt2html(in io.Reader) (html string) {
var tag int
const p = 1
- const pre = 2
+ const pre = 2
fscan := bufio.NewScanner(in)
for fscan.Scan() {
@@ -178,17 +184,17 @@ func txt2html(in io.Reader) (html string) {
tag = 0
} else if tag == 0 && line[0] == '\t' {
tag = pre
- html += "<pre>" + line[1:] + "\n"
+ html += "<pre>" + line[1:] + "\n"
} else if tag == 0 || (tag == pre && line[0] != '\t') {
if tag == pre {
- html += "</pre>\n"
- }
- tag = p
- html += "<p>" + line
+ html += "</pre>\n"
+ }
+ tag = p
+ html += "<p>" + line
} else if tag == p {
html += " " + line
} else if tag == pre {
- html += line[1:] + "\n"
+ html += line[1:] + "\n"
}
}
if tag == p {
@@ -201,41 +207,40 @@ func txt2html(in io.Reader) (html string) {
}
func getMarkdown(lang string) (markdown goldmark.Markdown) {
- switch lang {
- case ".gfm":
- markdown = goldmark.New(
- goldmark.WithExtensions(
- goldmarkext.GFM,
- goldmarkext.Table,
- goldmarkext.Strikethrough,
- goldmarkext.Linkify,
- goldmarkext.TaskList,
- ),
- goldmark.WithParserOptions(
- goldmarkparse.WithAutoHeadingID(),
- ),
- goldmark.WithRendererOptions(
- goldmarkhtml.WithUnsafe(),
- goldmarkhtml.WithHardWraps(),
- ),
- )
- case ".cm":
- markdown = goldmark.New()
- case ".md":
- fallthrough
- default:
- markdown = goldmark.New(
- goldmark.WithExtensions(
- goldmarkext.Linkify,
- ),
- goldmark.WithParserOptions(
- goldmarkparse.WithAutoHeadingID(),
- ),
- goldmark.WithRendererOptions(
- goldmarkhtml.WithUnsafe(),
- ),
- )
- }
- return
+ switch lang {
+ case ".gfm":
+ markdown = goldmark.New(
+ goldmark.WithExtensions(
+ goldmarkext.GFM,
+ goldmarkext.Table,
+ goldmarkext.Strikethrough,
+ goldmarkext.Linkify,
+ goldmarkext.TaskList,
+ ),
+ goldmark.WithParserOptions(
+ goldmarkparse.WithAutoHeadingID(),
+ ),
+ goldmark.WithRendererOptions(
+ goldmarkhtml.WithUnsafe(),
+ goldmarkhtml.WithHardWraps(),
+ ),
+ )
+ case ".cm":
+ markdown = goldmark.New()
+ case ".md":
+ fallthrough
+ default:
+ markdown = goldmark.New(
+ goldmark.WithExtensions(
+ goldmarkext.Linkify,
+ ),
+ goldmark.WithParserOptions(
+ goldmarkparse.WithAutoHeadingID(),
+ ),
+ goldmark.WithRendererOptions(
+ goldmarkhtml.WithUnsafe(),
+ ),
+ )
+ }
+ return
}
-
diff --git a/content_test.go b/content_test.go
@@ -1,35 +1,41 @@
package main
import (
- "fmt"
- "testing"
- "os"
- "strings"
+ "fmt"
+ "os"
+ "testing"
)
func TestLoadContentDir(t *testing.T) {
- var err error
+ var err error
tdir := t.TempDir()
- cdir := tdir + "/contents"
- if err = createProjectContents(cdir); err != nil {
- t.Errorf("failed to create project: %s", err)
- }
-
- var c Content
- if c, err = LoadContentDir(tdir); err != nil {
- t.Fatalf("LoadContentDir failed: %s", err)
- }
- for _, p := range c {
- fmt.Println("--------------")
- fmt.Println("Path:", p.Path)
- if p.Path != strings.TrimPrefix(p.Path, cdir) {
+ if err = createProjectContents(tdir); err != nil {
+ t.Errorf("failed to create project: %s", err)
+ }
+
+ var c Content
+ if c, err = LoadContentDir(tdir); err != nil {
+ t.Fatalf("LoadContentDir failed: %s", err)
+ }
+
+ if len(c) != len(contentBody) {
+ t.Fatalf("invalid number of pages returned (%d should be %d)",
+ len(c), len(contentBody))
+ }
+ for _, p := range c {
+ if len(p.Path) == 0 {
+ t.Fatalf("empty Path for page:\n%s\n", p)
}
- fmt.Println("Meta:", p.Meta)
- for i, b := range p.Body {
- fmt.Printf("Body%d: `%s`\n", i, b)
- }
- fmt.Println("Assets:", p.Assets)
- }
+ if _, ok := p.Meta["test"]; !ok || len(p.Meta) == 0 {
+ t.Fatalf("empty Meta for page:\n%s\n", p)
+ }
+ if len(p.Body) == 0 {
+ t.Fatalf("empty Body for page:\n%s\n", p)
+ }
+ if len(p.Assets) == 0 {
+ t.Fatalf("empty Assets for page:\n%s\n", p)
+ }
+ }
}
var contentBody = map[string]string{
@@ -42,14 +48,14 @@ p3
p4
`,
- ".html": `<p>p1<br>
+ ".html": `<p>p1<br>
p2</p>
<pre>pre1
pre2
</pre>
<p>p3</p>
<p>p4</p>`,
- ".md": `p1
+ ".md": `p1
p2
pre1
@@ -57,14 +63,14 @@ p2
p3
`,
- ".gfm": `p1
+ ".gfm": `p1
p2
pre1
pre2
p3`,
- ".cm": `p1
+ ".cm": `p1
p2
pre1
@@ -74,39 +80,53 @@ p3`,
}
var contentAsset = []byte{ // 5x5 black png
- 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00,
- 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
- 0x00, 0x05, 0x01, 0x03, 0x00, 0x00, 0x00, 0xb7, 0xa1, 0xb4, 0xa6,
- 0x00, 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xa5, 0x67, 0xb9, 0xcf, 0x00, 0x00, 0x00, 0x02,
- 0x74, 0x52, 0x4e, 0x53, 0xff, 0x00, 0xe5, 0xb7, 0x30, 0x4a, 0x00,
- 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0e, 0xc4,
- 0x00, 0x00, 0x0e, 0xc4, 0x01, 0x95, 0x2b, 0x0e, 0x1b, 0x00, 0x00,
- 0x00, 0x10, 0x49, 0x44, 0x41, 0x54, 0x08, 0x99, 0x63, 0x60, 0x66,
- 0x60, 0x66, 0x60, 0x00, 0x62, 0x76, 0x00, 0x00, 0x4a, 0x00, 0x11,
- 0x3a, 0x34, 0x8c, 0xad, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e,
- 0x44, 0xae, 0x42, 0x60, 0x82,
+ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00,
+ 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+ 0x00, 0x05, 0x01, 0x03, 0x00, 0x00, 0x00, 0xb7, 0xa1, 0xb4, 0xa6,
+ 0x00, 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xa5, 0x67, 0xb9, 0xcf, 0x00, 0x00, 0x00, 0x02,
+ 0x74, 0x52, 0x4e, 0x53, 0xff, 0x00, 0xe5, 0xb7, 0x30, 0x4a, 0x00,
+ 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0e, 0xc4,
+ 0x00, 0x00, 0x0e, 0xc4, 0x01, 0x95, 0x2b, 0x0e, 0x1b, 0x00, 0x00,
+ 0x00, 0x10, 0x49, 0x44, 0x41, 0x54, 0x08, 0x99, 0x63, 0x60, 0x66,
+ 0x60, 0x66, 0x60, 0x00, 0x62, 0x76, 0x00, 0x00, 0x4a, 0x00, 0x11,
+ 0x3a, 0x34, 0x8c, 0xad, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e,
+ 0x44, 0xae, 0x42, 0x60, 0x82,
}
func createProjectContents(dir string) (err error) {
- if err = os.Mkdir(dir, 0755); err != nil {
- return
- }
+ if _, err := os.Stat(dir); err != nil {
+ if err = os.Mkdir(dir, 0755); err != nil {
+ return err
+ }
+ }
- var f *os.File
+ var f *os.File
var path string
- for l, lang := range ContentBodyExts {
+ for l, lang := range ContentBodyExts {
if l > 0 {
path, err = os.MkdirTemp(dir, "page")
} else {
path = dir
+ /*
+ if f, err = os.Create(fmt.Sprintf("%s/.defaults.json", path)); err == nil {
+ return
+ }
+ f.WriteString("{ test: \"data\" }")
+ f.Close()
+ if f, err = os.Create(fmt.Sprintf("%s/.page.toml", path)); err == nil {
+ return
+ }
+ f.WriteString("{ test = \"data\" }")
+ f.Close()
+ */
}
- f, err = os.Create(fmt.Sprintf("%s/body%d%s", path, l, lang))
- if err != nil {
- return
- }
- f.WriteString(contentBody[lang])
- f.Close()
+ f, err = os.Create(fmt.Sprintf("%s/body%d%s", path, l, lang))
+ if err != nil {
+ return
+ }
+ f.WriteString(contentBody[lang])
+ f.Close()
if f, err = os.Create(fmt.Sprintf("%s/asset.png", path)); err != nil {
return
@@ -115,8 +135,7 @@ func createProjectContents(dir string) (err error) {
return
}
f.Close()
- }
-
+ }
+
return
}
-
diff --git a/pagr.go b/pagr.go
@@ -1,38 +1,36 @@
package main
import (
- "flag"
- "log"
+ "flag"
+ "log"
)
const Name = "pagr"
const Version = "0.0.0"
func main() {
- cfg := flag.String("cfg", "", "path to pagr project configuration file")
- verbose := flag.Bool("verbose", false, "print verbose logs")
- flag.Parse()
+ cfg := flag.String("cfg", "", "path to pagr project configuration file")
+ //verbose := flag.Bool("verbose", false, "print verbose logs")
+ flag.Parse()
- var err error
+ var err error
- var config Config
- if len(*cfg) > 0 {
- config, err = NewConfigFromFile(*cfg)
- check(err)
- } else {
- config = NewConfig()
- }
+ var config Config
+ if len(*cfg) > 0 {
+ config, err = NewConfigFromFile(*cfg)
+ check(err)
+ } else {
+ config = NewConfig()
+ }
- var content Content
- content, err = LoadContentDir(config.Contents)
- check(err)
+ _, err = LoadContentDir(config.Contents)
+ check(err)
- return
+ return
}
func check(err error) {
- if err != nil {
- log.Fatalf("ERROR! %s\n", err)
- }
+ if err != nil {
+ log.Fatalf("ERROR! %s\n", err)
+ }
}
-