separate adoc pkg, prep dir structure for multiple binaries
This commit is contained in:
parent
40e2a9d93f
commit
149a95b56c
|
@ -8,7 +8,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.sr.ht/~ewintr/shitty-ssg/site"
|
"git.sr.ht/~ewintr/shitty-ssg/cmd/ssg/site"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
|
@ -0,0 +1,75 @@
|
||||||
|
package site
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"html"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.sr.ht/~ewintr/shitty-ssg/pkg/adoc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HTMLPost struct {
|
||||||
|
Slug string
|
||||||
|
Title string
|
||||||
|
DateLong string
|
||||||
|
DateShort string
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTMLSummary struct {
|
||||||
|
Link string
|
||||||
|
Title string
|
||||||
|
Language Language
|
||||||
|
DateShort string
|
||||||
|
DateLong string
|
||||||
|
Summary string
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormatBlock(block adoc.BlockElement) string {
|
||||||
|
switch block.(type) {
|
||||||
|
case adoc.Paragraph:
|
||||||
|
text := ""
|
||||||
|
for _, inline := range block.(adoc.Paragraph) {
|
||||||
|
text += FormatInline(inline)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("<p>%s</p>", text)
|
||||||
|
case adoc.SubTitle:
|
||||||
|
return fmt.Sprintf("<h2>%s</h2>", html.EscapeString(block.Text()))
|
||||||
|
case adoc.SubSubTitle:
|
||||||
|
return fmt.Sprintf("<h3>%s</h3>", html.EscapeString(block.Text()))
|
||||||
|
case adoc.CodeBlock:
|
||||||
|
return fmt.Sprintf("<pre><code>%s</code></pre>", html.EscapeString(block.Text()))
|
||||||
|
case adoc.List:
|
||||||
|
var items []string
|
||||||
|
for _, item := range block.(adoc.List) {
|
||||||
|
itemText := ""
|
||||||
|
for _, inline := range item {
|
||||||
|
itemText += FormatInline(inline)
|
||||||
|
}
|
||||||
|
items = append(items, fmt.Sprintf("<li>%s</li>", itemText))
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("<ul>\n%s\n</ul>", strings.Join(items, "\n"))
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormatInline(src adoc.InlineElement) string {
|
||||||
|
text := html.EscapeString(src.Text())
|
||||||
|
switch src.(type) {
|
||||||
|
case adoc.PlainText:
|
||||||
|
return text
|
||||||
|
case adoc.StrongText:
|
||||||
|
return fmt.Sprintf("<strong>%s</strong>", text)
|
||||||
|
case adoc.EmpText:
|
||||||
|
return fmt.Sprintf("<em>%s</em>", text)
|
||||||
|
case adoc.StrongEmpText:
|
||||||
|
return fmt.Sprintf("<strong><em>%s</em></strong>", text)
|
||||||
|
case adoc.Link:
|
||||||
|
return fmt.Sprintf("<a href=%q>%s</a>", src.(adoc.Link).URL(), text)
|
||||||
|
case adoc.CodeText:
|
||||||
|
return fmt.Sprintf("<code>%s</code>", text)
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package site_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.sr.ht/~ewintr/go-kit/test"
|
||||||
|
"git.sr.ht/~ewintr/shitty-ssg/cmd/ssg/site"
|
||||||
|
"git.sr.ht/~ewintr/shitty-ssg/pkg/adoc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFormatBlock(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
element adoc.BlockElement
|
||||||
|
exp string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "paragraph",
|
||||||
|
element: adoc.Paragraph{
|
||||||
|
adoc.PlainText("one"),
|
||||||
|
adoc.PlainText("two"),
|
||||||
|
adoc.PlainText("three"),
|
||||||
|
},
|
||||||
|
exp: "<p>onetwothree</p>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "subtitle",
|
||||||
|
element: adoc.SubTitle("text"),
|
||||||
|
exp: "<h2>text</h2>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "subsubtitle",
|
||||||
|
element: adoc.SubSubTitle("text"),
|
||||||
|
exp: "<h3>text</h3>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code",
|
||||||
|
element: adoc.CodeBlock("text"),
|
||||||
|
exp: "<pre><code>text</code></pre>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "list",
|
||||||
|
element: adoc.List{
|
||||||
|
{adoc.PlainText("one")},
|
||||||
|
{adoc.PlainText("two")},
|
||||||
|
{adoc.PlainText("three")},
|
||||||
|
},
|
||||||
|
exp: "<ul>\n<li>one</li>\n<li>two</li>\n<li>three</li>\n</ul>",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
test.Equals(t, tc.exp, site.FormatBlock(tc.element))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatInline(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
element adoc.InlineElement
|
||||||
|
exp string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "plain text",
|
||||||
|
element: adoc.PlainText("text"),
|
||||||
|
exp: "text",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strong",
|
||||||
|
element: adoc.StrongText("text"),
|
||||||
|
exp: "<strong>text</strong>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "emphasis",
|
||||||
|
element: adoc.EmpText("text"),
|
||||||
|
exp: "<em>text</em>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strong emphasis",
|
||||||
|
element: adoc.StrongEmpText("text"),
|
||||||
|
exp: "<strong><em>text</em></strong>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "link",
|
||||||
|
element: adoc.NewLink("url", "title"),
|
||||||
|
exp: `<a href="url">title</a>`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code",
|
||||||
|
element: adoc.CodeText("text"),
|
||||||
|
exp: "<code>text</code>",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
test.Equals(t, tc.exp, site.FormatInline(tc.element))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,9 @@
|
||||||
package site
|
package site
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.sr.ht/~ewintr/shitty-ssg/pkg/adoc"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
KIND_NOTE = Kind("note")
|
KIND_NOTE = Kind("note")
|
||||||
KIND_STORY = Kind("story")
|
KIND_STORY = Kind("story")
|
||||||
|
@ -9,19 +13,15 @@ const (
|
||||||
|
|
||||||
type Kind string
|
type Kind string
|
||||||
|
|
||||||
var pluralKind = map[Kind]string{
|
func NewKind(kind adoc.Kind) Kind {
|
||||||
KIND_NOTE: "notes",
|
|
||||||
KIND_STORY: "stories",
|
|
||||||
KIND_ARTICLE: "articles",
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewKind(kind string) Kind {
|
|
||||||
switch kind {
|
switch kind {
|
||||||
case "note":
|
case adoc.KIND_NOTE:
|
||||||
return KIND_NOTE
|
return KIND_NOTE
|
||||||
case "story":
|
case adoc.KIND_VKV:
|
||||||
return KIND_STORY
|
return KIND_STORY
|
||||||
case "article":
|
case adoc.KIND_ESSAY:
|
||||||
|
fallthrough
|
||||||
|
case adoc.KIND_TUTORIAL:
|
||||||
return KIND_ARTICLE
|
return KIND_ARTICLE
|
||||||
default:
|
default:
|
||||||
return KIND_INVALID
|
return KIND_INVALID
|
||||||
|
@ -37,11 +37,11 @@ const (
|
||||||
|
|
||||||
type Language string
|
type Language string
|
||||||
|
|
||||||
func NewLanguage(text string) Language {
|
func NewLanguage(ln adoc.Language) Language {
|
||||||
switch text {
|
switch ln {
|
||||||
case "nl":
|
case adoc.LANGUAGE_NL:
|
||||||
return LANGUAGE_NL
|
return LANGUAGE_NL
|
||||||
case "en":
|
case adoc.LANGUAGE_EN:
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
return LANGUAGE_EN
|
return LANGUAGE_EN
|
|
@ -0,0 +1,79 @@
|
||||||
|
package site_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.sr.ht/~ewintr/go-kit/test"
|
||||||
|
"git.sr.ht/~ewintr/shitty-ssg/cmd/ssg/site"
|
||||||
|
"git.sr.ht/~ewintr/shitty-ssg/pkg/adoc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewKind(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
input adoc.Kind
|
||||||
|
exp site.Kind
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
exp: site.KIND_INVALID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "note",
|
||||||
|
input: adoc.KIND_NOTE,
|
||||||
|
exp: site.KIND_NOTE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "work",
|
||||||
|
input: adoc.KIND_WORK,
|
||||||
|
exp: site.KIND_INVALID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vkv",
|
||||||
|
input: adoc.KIND_VKV,
|
||||||
|
exp: site.KIND_STORY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "essay",
|
||||||
|
input: adoc.KIND_ESSAY,
|
||||||
|
exp: site.KIND_ARTICLE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tutorial",
|
||||||
|
input: adoc.KIND_TUTORIAL,
|
||||||
|
exp: site.KIND_ARTICLE,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
test.Equals(t, tc.exp, site.NewKind(tc.input))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewLanguage(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
input adoc.Language
|
||||||
|
exp site.Language
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nl",
|
||||||
|
input: adoc.LANGUAGE_NL,
|
||||||
|
exp: site.LANGUAGE_NL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "en",
|
||||||
|
input: adoc.LANGUAGE_EN,
|
||||||
|
exp: site.LANGUAGE_EN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown",
|
||||||
|
input: adoc.LANGUAGE_UNKNOWN,
|
||||||
|
exp: site.LANGUAGE_EN,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
test.Equals(t, tc.exp, site.NewLanguage(tc.input))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
package site
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"html"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.sr.ht/~ewintr/go-kit/slugify"
|
||||||
|
"git.sr.ht/~ewintr/shitty-ssg/pkg/adoc"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidPost = errors.New("invalid post")
|
||||||
|
)
|
||||||
|
|
||||||
|
var pluralKind = map[Kind]string{
|
||||||
|
KIND_NOTE: "notes",
|
||||||
|
KIND_STORY: "stories",
|
||||||
|
KIND_ARTICLE: "articles",
|
||||||
|
}
|
||||||
|
|
||||||
|
type Post struct {
|
||||||
|
doc *adoc.ADoc
|
||||||
|
Date time.Time
|
||||||
|
Kind Kind
|
||||||
|
Language Language
|
||||||
|
Tags []Tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPost(doc *adoc.ADoc) *Post {
|
||||||
|
var tags []Tag
|
||||||
|
for _, t := range doc.Tags {
|
||||||
|
tags = append(tags, Tag(t))
|
||||||
|
}
|
||||||
|
return &Post{
|
||||||
|
doc: doc,
|
||||||
|
Date: doc.Date,
|
||||||
|
Kind: NewKind(doc.Kind),
|
||||||
|
Language: Language(doc.Language),
|
||||||
|
Tags: tags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Post) Slug() string {
|
||||||
|
return slugify.Slugify(p.doc.Title)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Post) Year() string {
|
||||||
|
return strconv.Itoa(p.Date.Year())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Post) Link() string {
|
||||||
|
return fmt.Sprintf("%s/", path.Join("/", pluralKind[p.Kind], p.Year(), p.Slug()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Post) FullLink() string {
|
||||||
|
return fmt.Sprintf("https://erikwinter.nl%s", p.Link())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Post) HTMLSummary() *HTMLSummary {
|
||||||
|
summary := ""
|
||||||
|
if len(p.doc.Content) > 0 {
|
||||||
|
summary = fmt.Sprintf("<p>%s...</p>", TruncateOnSpace(p.doc.Content[0].Text(), 150))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &HTMLSummary{
|
||||||
|
Link: p.Link(),
|
||||||
|
Title: p.doc.Title,
|
||||||
|
Language: p.Language,
|
||||||
|
DateLong: FormatDate(p.Date, p.Language, DATE_LONG),
|
||||||
|
DateShort: FormatDate(p.Date, p.Language, DATE_SHORT),
|
||||||
|
Summary: summary,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Post) HTMLPost() *HTMLPost {
|
||||||
|
var content string
|
||||||
|
for _, be := range p.doc.Content {
|
||||||
|
content += fmt.Sprintf("%s\n", FormatBlock(be))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &HTMLPost{
|
||||||
|
Slug: p.Slug(),
|
||||||
|
Title: html.EscapeString(p.doc.Title),
|
||||||
|
DateLong: FormatDate(p.Date, p.Language, DATE_LONG),
|
||||||
|
DateShort: FormatDate(p.Date, p.Language, DATE_SHORT),
|
||||||
|
Content: content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Post) XMLPost() *XMLPost {
|
||||||
|
var content string
|
||||||
|
for _, be := range p.doc.Content {
|
||||||
|
content += fmt.Sprintf("%s\n", FormatBlock(be))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &XMLPost{
|
||||||
|
Link: p.FullLink(),
|
||||||
|
Title: html.EscapeString(p.doc.Title),
|
||||||
|
DateFormal: FormatDate(p.Date, p.Language, DATE_FORMAL),
|
||||||
|
Content: content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormatDate(date time.Time, language Language, format DateFormat) string {
|
||||||
|
switch {
|
||||||
|
case format == DATE_LONG && language == LANGUAGE_NL:
|
||||||
|
nlMonth := [...]string{"januari", "februari", "maart",
|
||||||
|
"april", "mei", "juni", "juli", "augustus", "september",
|
||||||
|
"oktober", "november", "december",
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d %s %d", date.Day(), nlMonth[date.Month()-1], date.Year())
|
||||||
|
case format == DATE_LONG && language == LANGUAGE_EN:
|
||||||
|
return date.Format("January 2, 2006")
|
||||||
|
case format == DATE_FORMAL:
|
||||||
|
return date.Format(time.RFC1123Z)
|
||||||
|
case format == DATE_SHORT:
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
return date.Format("2006-01-02 00:00:00")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TruncateOnSpace(text string, maxChars int) string {
|
||||||
|
if len(text) <= maxChars {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
var keep []string
|
||||||
|
ss := strings.Split(text, " ")
|
||||||
|
for _, s := range ss {
|
||||||
|
if len(strings.Join(keep, " ")+s) > maxChars {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
keep = append(keep, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(keep, " ")
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
package site_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.sr.ht/~ewintr/go-kit/test"
|
||||||
|
"git.sr.ht/~ewintr/shitty-ssg/cmd/ssg/site"
|
||||||
|
"git.sr.ht/~ewintr/shitty-ssg/pkg/adoc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPost(t *testing.T) {
|
||||||
|
title := "title thing"
|
||||||
|
author := "author"
|
||||||
|
kind := adoc.KIND_NOTE
|
||||||
|
language := adoc.LANGUAGE_EN
|
||||||
|
path := "/path"
|
||||||
|
date := time.Date(2020, 12, 28, 7, 23, 45, 0, time.UTC)
|
||||||
|
tag1, tag2 := adoc.Tag("tag1"), adoc.Tag("tag2")
|
||||||
|
par1 := adoc.Paragraph{adoc.PlainText("one")}
|
||||||
|
par2 := adoc.Paragraph{adoc.PlainText("two")}
|
||||||
|
post := site.NewPost(&adoc.ADoc{
|
||||||
|
Title: title,
|
||||||
|
Author: author,
|
||||||
|
Kind: kind,
|
||||||
|
Language: language,
|
||||||
|
Path: path,
|
||||||
|
Date: date,
|
||||||
|
Tags: []adoc.Tag{tag1, tag2},
|
||||||
|
Content: []adoc.BlockElement{par1, par2},
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("new", func(t *testing.T) {
|
||||||
|
test.Equals(t, date, post.Date)
|
||||||
|
test.Equals(t, site.Kind(kind), post.Kind)
|
||||||
|
test.Equals(t, site.Language(language), post.Language)
|
||||||
|
test.Equals(t, []site.Tag{site.Tag(tag1), site.Tag(tag2)}, post.Tags)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("tags", func(t *testing.T) {
|
||||||
|
test.Equals(t, []site.Tag{site.Tag("tag1"), site.Tag("tag2")}, post.Tags)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slug", func(t *testing.T) {
|
||||||
|
test.Equals(t, "title-thing", post.Slug())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("year", func(t *testing.T) {
|
||||||
|
test.Equals(t, "2020", post.Year())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("link", func(t *testing.T) {
|
||||||
|
test.Equals(t, "/notes/2020/title-thing/", post.Link())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("full link", func(t *testing.T) {
|
||||||
|
test.Equals(t, "https://erikwinter.nl/notes/2020/title-thing/", post.FullLink())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("html summary", func(t *testing.T) {
|
||||||
|
exp := &site.HTMLSummary{
|
||||||
|
Link: "/notes/2020/title-thing/",
|
||||||
|
Title: "title thing",
|
||||||
|
Language: site.LANGUAGE_EN,
|
||||||
|
DateShort: "2020-12-28 00:00:00",
|
||||||
|
DateLong: "December 28, 2020",
|
||||||
|
Summary: "<p>one...</p>",
|
||||||
|
}
|
||||||
|
|
||||||
|
test.Equals(t, exp, post.HTMLSummary())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("html post", func(t *testing.T) {
|
||||||
|
exp := &site.HTMLPost{
|
||||||
|
Slug: "title-thing",
|
||||||
|
Title: "title thing",
|
||||||
|
DateLong: "December 28, 2020",
|
||||||
|
DateShort: "2020-12-28 00:00:00",
|
||||||
|
Content: "<p>one</p>\n<p>two</p>\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
test.Equals(t, exp, post.HTMLPost())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("xml post", func(t *testing.T) {
|
||||||
|
exp := &site.XMLPost{
|
||||||
|
Link: "https://erikwinter.nl/notes/2020/title-thing/",
|
||||||
|
Title: "title thing",
|
||||||
|
DateFormal: "Mon, 28 Dec 2020 07:23:45 +0000",
|
||||||
|
Content: "<p>one</p>\n<p>two</p>\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
test.Equals(t, exp, post.XMLPost())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatDate(t *testing.T) {
|
||||||
|
date := time.Date(2020, 12, 28, 7, 23, 45, 0, time.UTC)
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
language site.Language
|
||||||
|
format site.DateFormat
|
||||||
|
exp string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "long nl",
|
||||||
|
language: site.LANGUAGE_NL,
|
||||||
|
format: site.DATE_LONG,
|
||||||
|
exp: "28 december 2020",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "long en",
|
||||||
|
language: site.LANGUAGE_EN,
|
||||||
|
format: site.DATE_LONG,
|
||||||
|
exp: "December 28, 2020",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "formal nl",
|
||||||
|
language: site.LANGUAGE_NL,
|
||||||
|
format: site.DATE_FORMAL,
|
||||||
|
exp: "Mon, 28 Dec 2020 07:23:45 +0000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "formal en",
|
||||||
|
language: site.LANGUAGE_EN,
|
||||||
|
format: site.DATE_FORMAL,
|
||||||
|
exp: "Mon, 28 Dec 2020 07:23:45 +0000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "short nl",
|
||||||
|
language: site.LANGUAGE_NL,
|
||||||
|
format: site.DATE_SHORT,
|
||||||
|
exp: "2020-12-28 00:00:00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "short en",
|
||||||
|
language: site.LANGUAGE_EN,
|
||||||
|
format: site.DATE_SHORT,
|
||||||
|
exp: "2020-12-28 00:00:00",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
test.Equals(t, tc.exp, site.FormatDate(date, tc.language, tc.format))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTruncateOnSpace(t *testing.T) {
|
||||||
|
text := "this is a short text"
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
text string
|
||||||
|
max int
|
||||||
|
exp string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "short text",
|
||||||
|
text: text,
|
||||||
|
max: 150,
|
||||||
|
exp: text,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "truncate on space",
|
||||||
|
text: text,
|
||||||
|
max: 10,
|
||||||
|
exp: "this is a",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "truncate in word",
|
||||||
|
text: text,
|
||||||
|
max: 12,
|
||||||
|
exp: "this is a",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
test.Equals(t, tc.exp, site.TruncateOnSpace(tc.text, tc.max))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,12 +2,13 @@ package site
|
||||||
|
|
||||||
import "sort"
|
import "sort"
|
||||||
|
|
||||||
type Posts []Post
|
type Posts []*Post
|
||||||
|
|
||||||
func (p Posts) Len() int { return len(p) }
|
func (p Posts) Len() int { return len(p) }
|
||||||
func (p Posts) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
func (p Posts) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||||
func (p Posts) Less(i, j int) bool { return p[i].Date.After(p[j].Date) }
|
func (p Posts) Less(i, j int) bool { return p[i].Date.After(p[j].Date) }
|
||||||
|
|
||||||
|
// Sort sorts on reverse chronological order
|
||||||
func (p Posts) Sort() Posts {
|
func (p Posts) Sort() Posts {
|
||||||
sort.Sort(p)
|
sort.Sort(p)
|
||||||
|
|
||||||
|
@ -86,15 +87,6 @@ func (p Posts) TagList() []string {
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Posts) HTMLSummaries() []*HTMLSummary {
|
|
||||||
summaries := []*HTMLSummary{}
|
|
||||||
for _, post := range p {
|
|
||||||
summaries = append(summaries, post.HTMLSummary())
|
|
||||||
}
|
|
||||||
|
|
||||||
return summaries
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeDuplicates(fullList []string) []string {
|
func removeDuplicates(fullList []string) []string {
|
||||||
list := []string{}
|
list := []string{}
|
||||||
for _, item := range fullList {
|
for _, item := range fullList {
|
|
@ -0,0 +1,77 @@
|
||||||
|
package site_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.sr.ht/~ewintr/go-kit/test"
|
||||||
|
"git.sr.ht/~ewintr/shitty-ssg/cmd/ssg/site"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPosts(t *testing.T) {
|
||||||
|
kind1, kind2 := site.Kind("kind1"), site.Kind("kind2")
|
||||||
|
tag1, tag2 := site.Tag("tag1"), site.Tag("tag2")
|
||||||
|
post1 := &site.Post{
|
||||||
|
Date: time.Date(2020, 12, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
Kind: kind1,
|
||||||
|
Tags: []site.Tag{tag1},
|
||||||
|
}
|
||||||
|
post2 := &site.Post{
|
||||||
|
Date: time.Date(2019, 12, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
Kind: kind2,
|
||||||
|
Tags: []site.Tag{tag1, tag2},
|
||||||
|
}
|
||||||
|
post3 := &site.Post{
|
||||||
|
Date: time.Date(2018, 12, 1, 0, 0, 0, 0, time.UTC),
|
||||||
|
Kind: kind1,
|
||||||
|
Tags: []site.Tag{tag2},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("sort", func(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
posts site.Posts
|
||||||
|
exp site.Posts
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "ordered",
|
||||||
|
posts: site.Posts{post1, post2, post3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unordered",
|
||||||
|
posts: site.Posts{post2, post3, post1},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
exp := site.Posts{post1, post2, post3}
|
||||||
|
test.Equals(t, exp, tc.posts.Sort())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
posts := site.Posts{post1, post2, post3}
|
||||||
|
|
||||||
|
t.Run("filter kind", func(t *testing.T) {
|
||||||
|
test.Equals(t, site.Posts{post1, post3}, posts.FilterByKind(kind1))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("filter year", func(t *testing.T) {
|
||||||
|
test.Equals(t, site.Posts{post2}, posts.FilterByYear("2019"))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("tilter tag", func(t *testing.T) {
|
||||||
|
test.Equals(t, site.Posts{post2, post3}, posts.FilterByTag(tag2))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("limit", func(t *testing.T) {
|
||||||
|
test.Equals(t, site.Posts{post1, post2}, posts.Limit(2))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("year list", func(t *testing.T) {
|
||||||
|
test.Equals(t, []string{"2018", "2019", "2020"}, posts.YearList())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("tag list", func(t *testing.T) {
|
||||||
|
test.Equals(t, []string{"tag1", "tag2"}, posts.TagList())
|
||||||
|
})
|
||||||
|
}
|
|
@ -69,12 +69,16 @@ func renderStaticPages(targetPath string, tpl *template.Template, statics []*Sta
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderHome(targetPath string, tpl *template.Template, posts Posts) error {
|
func renderHome(targetPath string, tpl *template.Template, posts Posts) error {
|
||||||
|
var summaries []*HTMLSummary
|
||||||
|
for _, p := range posts {
|
||||||
|
summaries = append(summaries, p.HTMLSummary())
|
||||||
|
}
|
||||||
data := struct {
|
data := struct {
|
||||||
Title string
|
Title string
|
||||||
Summaries []*HTMLSummary
|
Summaries []*HTMLSummary
|
||||||
}{
|
}{
|
||||||
Title: "Recent",
|
Title: "Recent",
|
||||||
Summaries: posts.HTMLSummaries(),
|
Summaries: summaries,
|
||||||
}
|
}
|
||||||
|
|
||||||
hPath := filepath.Join(targetPath, "index.html")
|
hPath := filepath.Join(targetPath, "index.html")
|
||||||
|
@ -145,7 +149,11 @@ func renderListings(targetPath string, tpl *template.Template, posts Posts) erro
|
||||||
for _, kind := range []Kind{KIND_NOTE, KIND_STORY, KIND_ARTICLE} {
|
for _, kind := range []Kind{KIND_NOTE, KIND_STORY, KIND_ARTICLE} {
|
||||||
for _, year := range posts.FilterByKind(kind).YearList() {
|
for _, year := range posts.FilterByKind(kind).YearList() {
|
||||||
title := fmt.Sprintf("%s in %s", strings.Title(pluralKind[kind]), year)
|
title := fmt.Sprintf("%s in %s", strings.Title(pluralKind[kind]), year)
|
||||||
summaries := posts.FilterByKind(kind).FilterByYear(year).HTMLSummaries()
|
kyposts := posts.FilterByKind(kind).FilterByYear(year)
|
||||||
|
var summaries []*HTMLSummary
|
||||||
|
for _, p := range kyposts {
|
||||||
|
summaries = append(summaries, p.HTMLSummary())
|
||||||
|
}
|
||||||
path := filepath.Join(targetPath, pluralKind[kind], year)
|
path := filepath.Join(targetPath, pluralKind[kind], year)
|
||||||
if err := renderListing(path, tpl, title, summaries); err != nil {
|
if err := renderListing(path, tpl, title, summaries); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -155,7 +163,11 @@ func renderListings(targetPath string, tpl *template.Template, posts Posts) erro
|
||||||
|
|
||||||
for _, tag := range posts.TagList() {
|
for _, tag := range posts.TagList() {
|
||||||
title := fmt.Sprintf("Posts Tagged with \"%s\"", tag)
|
title := fmt.Sprintf("Posts Tagged with \"%s\"", tag)
|
||||||
summaries := posts.FilterByTag(Tag(tag)).HTMLSummaries()
|
tposts := posts.FilterByTag(Tag(tag))
|
||||||
|
var summaries []*HTMLSummary
|
||||||
|
for _, p := range tposts {
|
||||||
|
summaries = append(summaries, p.HTMLSummary())
|
||||||
|
}
|
||||||
path := filepath.Join(targetPath, "tags", tag)
|
path := filepath.Join(targetPath, "tags", tag)
|
||||||
if err := renderListing(path, tpl, title, summaries); err != nil {
|
if err := renderListing(path, tpl, title, summaries); err != nil {
|
||||||
return err
|
return err
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
"git.sr.ht/~ewintr/shitty-ssg/pkg/adoc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StaticPage struct {
|
type StaticPage struct {
|
||||||
|
@ -28,7 +30,7 @@ func New(resourcesPath string) (*Site, error) {
|
||||||
return &Site{
|
return &Site{
|
||||||
resourcesPath: resourcesPath,
|
resourcesPath: resourcesPath,
|
||||||
templates: templates,
|
templates: templates,
|
||||||
posts: []Post{},
|
posts: Posts{},
|
||||||
staticPages: []*StaticPage{},
|
staticPages: []*StaticPage{},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -45,7 +47,10 @@ func (s *Site) AddFilePost(fPath string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.posts = append(s.posts, NewPost(string(content)))
|
post := NewPost(adoc.New(string(content)))
|
||||||
|
if post.Kind != KIND_INVALID {
|
||||||
|
s.posts = append(s.posts, post)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
5
go.mod
5
go.mod
|
@ -2,4 +2,7 @@ module git.sr.ht/~ewintr/shitty-ssg
|
||||||
|
|
||||||
go 1.14
|
go 1.14
|
||||||
|
|
||||||
require git.sr.ht/~ewintr/go-kit v0.0.0-20200704112030-36275689b0ea
|
require (
|
||||||
|
git.sr.ht/~ewintr/erikwinternl/shitty-ssg v0.0.0-20201204092507-5528bf33815d // indirect
|
||||||
|
git.sr.ht/~ewintr/go-kit v0.0.0-20200704112030-36275689b0ea
|
||||||
|
)
|
||||||
|
|
3
go.sum
3
go.sum
|
@ -1,5 +1,8 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
git.sr.ht/~ewintr/erikwinternl v0.0.0-20201204092507-5528bf33815d h1:teNzxpdrJw5sUpB/84ojdjVV2q2+3IVwYjztCze9YmU=
|
||||||
|
git.sr.ht/~ewintr/erikwinternl/shitty-ssg v0.0.0-20201204092507-5528bf33815d h1:vyJ0Pgdrvqh28jxBCLGCGt/80p86iSQtKJW7rAbYRk8=
|
||||||
|
git.sr.ht/~ewintr/erikwinternl/shitty-ssg v0.0.0-20201204092507-5528bf33815d/go.mod h1:ka/PzT9A0BNvjcUQpIM0YE1BnOR1/qr6x7XeCUyYDlw=
|
||||||
git.sr.ht/~ewintr/go-kit v0.0.0-20200704112030-36275689b0ea h1:YdHan+/QwjzF05Dlp40BHw5N47nWy2QZCY7aseXf2n0=
|
git.sr.ht/~ewintr/go-kit v0.0.0-20200704112030-36275689b0ea h1:YdHan+/QwjzF05Dlp40BHw5N47nWy2QZCY7aseXf2n0=
|
||||||
git.sr.ht/~ewintr/go-kit v0.0.0-20200704112030-36275689b0ea/go.mod h1:zrpigRr1EHuVGw7GWwvYWwfLgdAwJ110zIRAGtaicvI=
|
git.sr.ht/~ewintr/go-kit v0.0.0-20200704112030-36275689b0ea/go.mod h1:zrpigRr1EHuVGw7GWwvYWwfLgdAwJ110zIRAGtaicvI=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
package adoc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
KIND_NOTE = Kind("note")
|
||||||
|
KIND_VKV = Kind("vkv")
|
||||||
|
KIND_STORY = Kind("story")
|
||||||
|
KIND_SNIPPET = Kind("snippet")
|
||||||
|
KIND_ESSAY = Kind("essay")
|
||||||
|
KIND_WORK = Kind("work")
|
||||||
|
KIND_TUTORIAL = Kind("tutorial")
|
||||||
|
KIND_UNKNOWN = Kind("unknown")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Kind string
|
||||||
|
|
||||||
|
func NewKind(text string) Kind {
|
||||||
|
switch text {
|
||||||
|
case "verhaal":
|
||||||
|
text = "story"
|
||||||
|
case "los":
|
||||||
|
text = "snippet"
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, k := range []string{
|
||||||
|
"note", "vkv", "story", "snippet",
|
||||||
|
"essay", "tutorial", "work",
|
||||||
|
} {
|
||||||
|
if k == text {
|
||||||
|
return Kind(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return KIND_UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
LANGUAGE_EN = Language("en")
|
||||||
|
LANGUAGE_NL = Language("nl")
|
||||||
|
LANGUAGE_UNKNOWN = Language("unknown")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Language string
|
||||||
|
|
||||||
|
func NewLanguage(ln string) Language {
|
||||||
|
switch strings.ToLower(ln) {
|
||||||
|
case "nl":
|
||||||
|
return LANGUAGE_NL
|
||||||
|
case "en":
|
||||||
|
return LANGUAGE_EN
|
||||||
|
default:
|
||||||
|
return LANGUAGE_UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tag string
|
||||||
|
|
||||||
|
type ADoc struct {
|
||||||
|
Title string
|
||||||
|
Author string
|
||||||
|
Kind Kind
|
||||||
|
Language Language
|
||||||
|
Path string
|
||||||
|
Date time.Time
|
||||||
|
Tags []Tag
|
||||||
|
Content []BlockElement
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
package adoc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.sr.ht/~ewintr/go-kit/test"
|
||||||
|
"git.sr.ht/~ewintr/shitty-ssg/pkg/adoc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewKind(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
exp adoc.Kind
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
exp: adoc.KIND_UNKNOWN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown",
|
||||||
|
input: "something",
|
||||||
|
exp: adoc.KIND_UNKNOWN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "note",
|
||||||
|
input: "note",
|
||||||
|
exp: adoc.KIND_NOTE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vkv",
|
||||||
|
input: "vkv",
|
||||||
|
exp: adoc.KIND_VKV,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "story",
|
||||||
|
input: "verhaal",
|
||||||
|
exp: adoc.KIND_STORY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "snippet",
|
||||||
|
input: "los",
|
||||||
|
exp: adoc.KIND_SNIPPET,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "essay",
|
||||||
|
input: "essay",
|
||||||
|
exp: adoc.KIND_ESSAY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tutorial",
|
||||||
|
input: "tutorial",
|
||||||
|
exp: adoc.KIND_TUTORIAL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "work note",
|
||||||
|
input: "work",
|
||||||
|
exp: adoc.KIND_WORK,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
act := adoc.NewKind(tc.input)
|
||||||
|
test.Equals(t, tc.exp, act)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewLanguage(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
exp adoc.Language
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
exp: adoc.LANGUAGE_UNKNOWN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dutch lower",
|
||||||
|
input: "nl",
|
||||||
|
exp: adoc.LANGUAGE_NL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dutch upper",
|
||||||
|
input: "NL",
|
||||||
|
exp: adoc.LANGUAGE_NL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "english lower",
|
||||||
|
input: "en",
|
||||||
|
exp: adoc.LANGUAGE_EN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "english upper",
|
||||||
|
input: "EN",
|
||||||
|
exp: adoc.LANGUAGE_EN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown",
|
||||||
|
input: "something",
|
||||||
|
exp: adoc.LANGUAGE_UNKNOWN,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
act := adoc.NewLanguage(tc.input)
|
||||||
|
test.Equals(t, tc.exp, act)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,12 @@
|
||||||
package site
|
package adoc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BlockElement interface {
|
type BlockElement interface {
|
||||||
Text() string
|
Text() string
|
||||||
BlockHTML() string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Paragraph []InlineElement
|
type Paragraph []InlineElement
|
||||||
|
@ -22,35 +20,17 @@ func (p Paragraph) Text() string {
|
||||||
return strings.Join(text, " ")
|
return strings.Join(text, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Paragraph) BlockHTML() string {
|
|
||||||
var body string
|
|
||||||
for _, ie := range p {
|
|
||||||
body += ie.InlineHTML()
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("<p>%s</p>", body)
|
|
||||||
}
|
|
||||||
|
|
||||||
type SubTitle string
|
type SubTitle string
|
||||||
|
|
||||||
func (st SubTitle) Text() string { return string(st) }
|
func (st SubTitle) Text() string { return string(st) }
|
||||||
func (st SubTitle) BlockHTML() string {
|
|
||||||
return fmt.Sprintf("<h2>%s</h2>", st)
|
|
||||||
}
|
|
||||||
|
|
||||||
type SubSubTitle string
|
type SubSubTitle string
|
||||||
|
|
||||||
func (st SubSubTitle) Text() string { return string(st) }
|
func (st SubSubTitle) Text() string { return string(st) }
|
||||||
func (st SubSubTitle) BlockHTML() string {
|
|
||||||
return fmt.Sprintf("<h3>%s</h3>", st)
|
|
||||||
}
|
|
||||||
|
|
||||||
type CodeBlock string
|
type CodeBlock string
|
||||||
|
|
||||||
func (cb CodeBlock) Text() string { return string(cb) }
|
func (cb CodeBlock) Text() string { return string(cb) }
|
||||||
func (cb CodeBlock) BlockHTML() string {
|
|
||||||
return fmt.Sprintf("<pre><code>%s</code></pre>", html.EscapeString(string(cb)))
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListItem []InlineElement
|
type ListItem []InlineElement
|
||||||
|
|
||||||
|
@ -60,16 +40,7 @@ func (li ListItem) Text() string {
|
||||||
text = append(text, ie.Text())
|
text = append(text, ie.Text())
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%s%s", LISTITEM_PREFIX, strings.Join(text, " "))
|
return fmt.Sprintf("%s%s", LISTITEM_PREFIX, strings.Join(text, ""))
|
||||||
}
|
|
||||||
|
|
||||||
func (li ListItem) HTML() string {
|
|
||||||
var body string
|
|
||||||
for _, ie := range li {
|
|
||||||
body += ie.InlineHTML()
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("<li>%s</li>", body)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type List []ListItem
|
type List []ListItem
|
||||||
|
@ -82,12 +53,3 @@ func (l List) Text() string {
|
||||||
|
|
||||||
return strings.Join(items, "\n")
|
return strings.Join(items, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l List) BlockHTML() string {
|
|
||||||
var items []string
|
|
||||||
for _, item := range l {
|
|
||||||
items = append(items, item.HTML())
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("<ul>\n%s\n</ul>", strings.Join(items, "\n"))
|
|
||||||
}
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
package adoc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.sr.ht/~ewintr/go-kit/test"
|
||||||
|
"git.sr.ht/~ewintr/shitty-ssg/pkg/adoc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParagraph(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
elements []adoc.InlineElement
|
||||||
|
exp string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
elements: []adoc.InlineElement{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one",
|
||||||
|
elements: []adoc.InlineElement{
|
||||||
|
adoc.PlainText("one"),
|
||||||
|
},
|
||||||
|
exp: "one",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "many",
|
||||||
|
elements: []adoc.InlineElement{
|
||||||
|
adoc.PlainText("one"),
|
||||||
|
adoc.PlainText("two"),
|
||||||
|
adoc.PlainText("three"),
|
||||||
|
},
|
||||||
|
exp: "one two three",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
p := adoc.Paragraph(tc.elements)
|
||||||
|
test.Equals(t, tc.exp, p.Text())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBlockSimple(t *testing.T) {
|
||||||
|
text := "text"
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
element adoc.BlockElement
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "subtitle",
|
||||||
|
element: adoc.SubTitle(text),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "subsubtitle",
|
||||||
|
element: adoc.SubSubTitle(text),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code block",
|
||||||
|
element: adoc.CodeBlock(text),
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
test.Equals(t, text, tc.element.Text())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListItem(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
elements []adoc.InlineElement
|
||||||
|
exp string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
exp: "* ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one",
|
||||||
|
elements: []adoc.InlineElement{
|
||||||
|
adoc.PlainText("one"),
|
||||||
|
},
|
||||||
|
exp: "* one",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "many",
|
||||||
|
elements: []adoc.InlineElement{
|
||||||
|
adoc.PlainText("one"),
|
||||||
|
adoc.PlainText("two"),
|
||||||
|
adoc.PlainText("three"),
|
||||||
|
},
|
||||||
|
exp: "* onetwothree",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
li := adoc.ListItem(tc.elements)
|
||||||
|
test.Equals(t, tc.exp, li.Text())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestList(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
elements []adoc.ListItem
|
||||||
|
exp string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one",
|
||||||
|
elements: []adoc.ListItem{
|
||||||
|
{adoc.PlainText("one")},
|
||||||
|
},
|
||||||
|
exp: "* one",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "many",
|
||||||
|
elements: []adoc.ListItem{
|
||||||
|
{adoc.PlainText("one")},
|
||||||
|
{adoc.PlainText("two")},
|
||||||
|
{adoc.PlainText("three")},
|
||||||
|
},
|
||||||
|
exp: "* one\n* two\n* three",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
l := adoc.List(tc.elements)
|
||||||
|
test.Equals(t, tc.exp, l.Text())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package adoc
|
||||||
|
|
||||||
|
type InlineElement interface {
|
||||||
|
Text() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlainText string
|
||||||
|
|
||||||
|
func (pt PlainText) Text() string { return string(pt) }
|
||||||
|
|
||||||
|
type StrongText string
|
||||||
|
|
||||||
|
func (st StrongText) Text() string { return string(st) }
|
||||||
|
|
||||||
|
type EmpText string
|
||||||
|
|
||||||
|
func (et EmpText) Text() string { return string(et) }
|
||||||
|
|
||||||
|
type StrongEmpText string
|
||||||
|
|
||||||
|
func (set StrongEmpText) Text() string { return string(set) }
|
||||||
|
|
||||||
|
type Link struct {
|
||||||
|
url string
|
||||||
|
title string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLink(url, title string) Link {
|
||||||
|
return Link{
|
||||||
|
url: url,
|
||||||
|
title: title,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Link) URL() string { return l.url }
|
||||||
|
func (l Link) Text() string { return l.title }
|
||||||
|
|
||||||
|
type CodeText string
|
||||||
|
|
||||||
|
func (ct CodeText) Text() string { return string(ct) }
|
|
@ -0,0 +1,50 @@
|
||||||
|
package adoc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.sr.ht/~ewintr/go-kit/test"
|
||||||
|
"git.sr.ht/~ewintr/shitty-ssg/pkg/adoc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInlineSimple(t *testing.T) {
|
||||||
|
text := "text"
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
element adoc.InlineElement
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "plain text",
|
||||||
|
element: adoc.PlainText(text),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strong",
|
||||||
|
element: adoc.StrongText(text),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "emphasis",
|
||||||
|
element: adoc.EmpText(text),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strong emphasis",
|
||||||
|
element: adoc.StrongEmpText(text),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code",
|
||||||
|
element: adoc.CodeText(text),
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
test.Equals(t, text, tc.element.Text())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TextLink(t *testing.T) {
|
||||||
|
url := "url"
|
||||||
|
title := "title"
|
||||||
|
l := adoc.NewLink(url, title)
|
||||||
|
|
||||||
|
test.Equals(t, url, l.URL())
|
||||||
|
test.Equals(t, title, l.Text())
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package site
|
package adoc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -6,19 +6,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TITLE_PREFIX = "= "
|
TITLE_PREFIX = "= "
|
||||||
SUBTITLE_PREFIX = "== "
|
SUBTITLE_PREFIX = "== "
|
||||||
SUBSUBTITLE_PREFIX = "=== "
|
SUBSUBTITLE_PREFIX = "=== "
|
||||||
PARAGRAPH_SEPARATOR = "\n\n"
|
PARAGRAPH_SEPARATOR = "\n\n"
|
||||||
LINE_SEPARATOR = "\n"
|
LINE_SEPARATOR = "\n"
|
||||||
CODE_PREFIX = "----\n"
|
CODE_PREFIX = "----\n"
|
||||||
CODE_SUFFIX = "\n----"
|
CODE_SUFFIX = "\n----"
|
||||||
LISTITEM_PREFIX = "* "
|
LISTITEM_PREFIX = "* "
|
||||||
PARAGRAPH_CONTINUATION = "\n+\n"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewPost(text string) Post {
|
func New(text string) *ADoc {
|
||||||
post := Post{
|
doc := &ADoc{
|
||||||
Language: LANGUAGE_EN,
|
Language: LANGUAGE_EN,
|
||||||
Tags: []Tag{},
|
Tags: []Tag{},
|
||||||
}
|
}
|
||||||
|
@ -27,6 +26,7 @@ func NewPost(text string) Post {
|
||||||
var pars []string
|
var pars []string
|
||||||
for _, s := range strings.Split(text, PARAGRAPH_SEPARATOR) {
|
for _, s := range strings.Split(text, PARAGRAPH_SEPARATOR) {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ func NewPost(text string) Post {
|
||||||
for i, p := range blocks {
|
for i, p := range blocks {
|
||||||
switch {
|
switch {
|
||||||
case i == 0 && strings.HasPrefix(p, TITLE_PREFIX):
|
case i == 0 && strings.HasPrefix(p, TITLE_PREFIX):
|
||||||
ParseHeader(p, &post)
|
ParseHeader(p, doc)
|
||||||
case strings.HasPrefix(p, SUBTITLE_PREFIX):
|
case strings.HasPrefix(p, SUBTITLE_PREFIX):
|
||||||
p = strings.TrimSpace(p)
|
p = strings.TrimSpace(p)
|
||||||
s := strings.Split(p, SUBTITLE_PREFIX)
|
s := strings.Split(p, SUBTITLE_PREFIX)
|
||||||
|
@ -68,7 +68,7 @@ func NewPost(text string) Post {
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
post.Content = append(post.Content, SubTitle(s[1]))
|
doc.Content = append(doc.Content, SubTitle(s[1]))
|
||||||
case strings.HasPrefix(p, SUBSUBTITLE_PREFIX):
|
case strings.HasPrefix(p, SUBSUBTITLE_PREFIX):
|
||||||
p = strings.TrimSpace(p)
|
p = strings.TrimSpace(p)
|
||||||
s := strings.Split(p, SUBSUBTITLE_PREFIX)
|
s := strings.Split(p, SUBSUBTITLE_PREFIX)
|
||||||
|
@ -76,9 +76,9 @@ func NewPost(text string) Post {
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
post.Content = append(post.Content, SubSubTitle(s[1]))
|
doc.Content = append(doc.Content, SubSubTitle(s[1]))
|
||||||
case isCodeBlock(p):
|
case isCodeBlock(p):
|
||||||
post.Content = append(post.Content, parseCodeBlock(p))
|
doc.Content = append(doc.Content, parseCodeBlock(p))
|
||||||
case strings.HasPrefix(p, LISTITEM_PREFIX):
|
case strings.HasPrefix(p, LISTITEM_PREFIX):
|
||||||
p = strings.TrimSpace(p)
|
p = strings.TrimSpace(p)
|
||||||
var items []ListItem
|
var items []ListItem
|
||||||
|
@ -88,15 +88,15 @@ func NewPost(text string) Post {
|
||||||
items = append(items, ListItem(inline))
|
items = append(items, ListItem(inline))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
post.Content = append(post.Content, List(items))
|
doc.Content = append(doc.Content, List(items))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
p = strings.TrimSpace(p)
|
p = strings.TrimSpace(p)
|
||||||
post.Content = append(post.Content, Paragraph(ParseInline(p)))
|
doc.Content = append(doc.Content, Paragraph(ParseInline(p)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return post
|
return doc
|
||||||
}
|
}
|
||||||
|
|
||||||
func isCodeBlock(par string) bool {
|
func isCodeBlock(par string) bool {
|
||||||
|
@ -111,31 +111,31 @@ func parseCodeBlock(par string) CodeBlock {
|
||||||
return CodeBlock(content)
|
return CodeBlock(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseHeader(text string, post *Post) {
|
func ParseHeader(text string, doc *ADoc) {
|
||||||
text = strings.TrimSpace(text)
|
text = strings.TrimSpace(text)
|
||||||
lines := strings.Split(text, LINE_SEPARATOR)
|
lines := strings.Split(text, LINE_SEPARATOR)
|
||||||
for i, l := range lines {
|
for i, l := range lines {
|
||||||
switch {
|
switch {
|
||||||
case i == 0:
|
case i == 0:
|
||||||
s := strings.Split(l, TITLE_PREFIX)
|
s := strings.Split(l, TITLE_PREFIX)
|
||||||
post.Title = s[1]
|
doc.Title = s[1]
|
||||||
case isDate(l):
|
case isDate(l):
|
||||||
date, _ := time.Parse("2006-01-02", l)
|
date, _ := time.Parse("2006-01-02", l)
|
||||||
post.Date = date
|
doc.Date = date
|
||||||
case strings.HasPrefix(l, ":kind:"):
|
case strings.HasPrefix(l, ":kind:"):
|
||||||
s := strings.Split(l, ":")
|
s := strings.Split(l, ":")
|
||||||
post.Kind = NewKind(strings.TrimSpace(s[2]))
|
doc.Kind = NewKind(strings.TrimSpace(s[2]))
|
||||||
case strings.HasPrefix(l, ":language:"):
|
case strings.HasPrefix(l, ":language:"):
|
||||||
s := strings.Split(l, ":")
|
s := strings.Split(l, ":")
|
||||||
post.Language = NewLanguage(strings.TrimSpace(s[2]))
|
doc.Language = NewLanguage(strings.TrimSpace(s[2]))
|
||||||
case strings.HasPrefix(l, ":tags:"):
|
case strings.HasPrefix(l, ":tags:"):
|
||||||
s := strings.Split(l, ":")
|
s := strings.Split(l, ":")
|
||||||
t := strings.Split(s[2], ",")
|
t := strings.Split(s[2], ",")
|
||||||
for _, tag := range t {
|
for _, tag := range t {
|
||||||
post.Tags = append(post.Tags, Tag(strings.TrimSpace(tag)))
|
doc.Tags = append(doc.Tags, Tag(strings.TrimSpace(tag)))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
post.Author = l
|
doc.Author = l
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,219 @@
|
||||||
|
package adoc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.sr.ht/~ewintr/go-kit/test"
|
||||||
|
"git.sr.ht/~ewintr/shitty-ssg/pkg/adoc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
one := "one"
|
||||||
|
two := "two"
|
||||||
|
three := "three"
|
||||||
|
ptOne := adoc.PlainText(one)
|
||||||
|
ptTwo := adoc.PlainText(two)
|
||||||
|
ptThree := adoc.PlainText(three)
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
exp *adoc.ADoc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
exp: &adoc.ADoc{
|
||||||
|
Tags: []adoc.Tag{},
|
||||||
|
Language: adoc.LANGUAGE_EN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "title",
|
||||||
|
input: "= Title",
|
||||||
|
exp: &adoc.ADoc{
|
||||||
|
Title: "Title",
|
||||||
|
Tags: []adoc.Tag{},
|
||||||
|
Language: adoc.LANGUAGE_EN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "header",
|
||||||
|
input: "= Title\nT. Test\n2020-10-27\n:tags:\ttag1, tag2\n:kind:\tnote\n:language:\tnl",
|
||||||
|
exp: &adoc.ADoc{
|
||||||
|
Title: "Title",
|
||||||
|
Author: "T. Test",
|
||||||
|
Kind: adoc.KIND_NOTE,
|
||||||
|
Language: adoc.LANGUAGE_NL,
|
||||||
|
Tags: []adoc.Tag{
|
||||||
|
adoc.Tag("tag1"),
|
||||||
|
adoc.Tag("tag2"),
|
||||||
|
},
|
||||||
|
Date: time.Date(2020, time.October, 27, 0, 0, 0, 0, time.UTC),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "paragraphs",
|
||||||
|
input: fmt.Sprintf("%s\n\n%s\n\n%s", one, two, three),
|
||||||
|
exp: &adoc.ADoc{
|
||||||
|
Tags: []adoc.Tag{},
|
||||||
|
Language: adoc.LANGUAGE_EN,
|
||||||
|
Content: []adoc.BlockElement{
|
||||||
|
adoc.Paragraph([]adoc.InlineElement{ptOne}),
|
||||||
|
adoc.Paragraph([]adoc.InlineElement{ptTwo}),
|
||||||
|
adoc.Paragraph([]adoc.InlineElement{ptThree}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "subtitle",
|
||||||
|
input: "== Subtitle",
|
||||||
|
exp: &adoc.ADoc{
|
||||||
|
Tags: []adoc.Tag{},
|
||||||
|
Language: adoc.LANGUAGE_EN,
|
||||||
|
Content: []adoc.BlockElement{
|
||||||
|
adoc.SubTitle("Subtitle"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code block",
|
||||||
|
input: "----\nsome code\nmore code\n----",
|
||||||
|
exp: &adoc.ADoc{
|
||||||
|
Tags: []adoc.Tag{},
|
||||||
|
Language: adoc.LANGUAGE_EN,
|
||||||
|
Content: []adoc.BlockElement{
|
||||||
|
adoc.CodeBlock("some code\nmore code"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code block with empty lines",
|
||||||
|
input: "----\nsome code\n\nmore code\n----",
|
||||||
|
exp: &adoc.ADoc{
|
||||||
|
Tags: []adoc.Tag{},
|
||||||
|
Language: adoc.LANGUAGE_EN,
|
||||||
|
Content: []adoc.BlockElement{
|
||||||
|
adoc.CodeBlock("some code\n\nmore code"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "list",
|
||||||
|
input: "* item 1\n* item 2\n* *item 3*\n",
|
||||||
|
exp: &adoc.ADoc{
|
||||||
|
Tags: []adoc.Tag{},
|
||||||
|
Language: adoc.LANGUAGE_EN,
|
||||||
|
Content: []adoc.BlockElement{
|
||||||
|
adoc.List{
|
||||||
|
adoc.ListItem([]adoc.InlineElement{adoc.PlainText("item 1")}),
|
||||||
|
adoc.ListItem([]adoc.InlineElement{adoc.PlainText("item 2")}),
|
||||||
|
adoc.ListItem([]adoc.InlineElement{adoc.StrongText("item 3")}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
act := adoc.New(tc.input)
|
||||||
|
|
||||||
|
test.Equals(t, tc.exp, act)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseInline(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
exp []adoc.InlineElement
|
||||||
|
}{{
|
||||||
|
name: "empty",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "plain",
|
||||||
|
input: "some test text",
|
||||||
|
exp: []adoc.InlineElement{
|
||||||
|
adoc.PlainText("some test text")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strong",
|
||||||
|
input: "*some strong text*",
|
||||||
|
exp: []adoc.InlineElement{
|
||||||
|
adoc.StrongText("some strong text"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strong in plain",
|
||||||
|
input: "some *strong* text",
|
||||||
|
exp: []adoc.InlineElement{
|
||||||
|
adoc.PlainText("some "),
|
||||||
|
adoc.StrongText("strong"),
|
||||||
|
adoc.PlainText(" text"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "emphasis",
|
||||||
|
input: "_some emphasized text_",
|
||||||
|
exp: []adoc.InlineElement{
|
||||||
|
adoc.EmpText("some emphasized text"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "emphasis in plain",
|
||||||
|
input: "some _emphasized_ text",
|
||||||
|
exp: []adoc.InlineElement{
|
||||||
|
adoc.PlainText("some "),
|
||||||
|
adoc.EmpText("emphasized"),
|
||||||
|
adoc.PlainText(" text"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "emp and strong in plain",
|
||||||
|
input: "some _*special*_ text",
|
||||||
|
exp: []adoc.InlineElement{
|
||||||
|
adoc.PlainText("some "),
|
||||||
|
adoc.StrongEmpText("special"),
|
||||||
|
adoc.PlainText(" text"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "link",
|
||||||
|
input: "a link[title] somewhere",
|
||||||
|
exp: []adoc.InlineElement{
|
||||||
|
adoc.PlainText("a "),
|
||||||
|
adoc.NewLink("link", "title"),
|
||||||
|
adoc.PlainText(" somewhere"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code",
|
||||||
|
input: "`command`",
|
||||||
|
exp: []adoc.InlineElement{
|
||||||
|
adoc.CodeText("command"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "code in plain",
|
||||||
|
input: "some `code` in text",
|
||||||
|
exp: []adoc.InlineElement{
|
||||||
|
adoc.PlainText("some "),
|
||||||
|
adoc.CodeText("code"),
|
||||||
|
adoc.PlainText(" in text"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "link with underscore",
|
||||||
|
input: "https://example.com/some_url[some url]",
|
||||||
|
exp: []adoc.InlineElement{
|
||||||
|
adoc.NewLink("https://example.com/some_url", "some url"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
act := adoc.ParseInline(tc.input)
|
||||||
|
|
||||||
|
test.Equals(t, tc.exp, act)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +0,0 @@
|
||||||
package site
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"git.sr.ht/~ewintr/go-kit/test"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParagraph(t *testing.T) {
|
|
||||||
p := Paragraph([]InlineElement{
|
|
||||||
PlainText("one "),
|
|
||||||
PlainText("two "),
|
|
||||||
PlainText("three"),
|
|
||||||
})
|
|
||||||
|
|
||||||
exp := "<p>one two three</p>"
|
|
||||||
test.Equals(t, exp, p.BlockHTML())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubTitle(t *testing.T) {
|
|
||||||
text := "text"
|
|
||||||
st := SubTitle(text)
|
|
||||||
|
|
||||||
exp := fmt.Sprintf("<h2>%s</h2>", text)
|
|
||||||
test.Equals(t, exp, st.BlockHTML())
|
|
||||||
}
|
|
18
site/html.go
18
site/html.go
|
@ -1,18 +0,0 @@
|
||||||
package site
|
|
||||||
|
|
||||||
type HTMLPost struct {
|
|
||||||
Slug string
|
|
||||||
Title string
|
|
||||||
DateLong string
|
|
||||||
DateShort string
|
|
||||||
Content string
|
|
||||||
}
|
|
||||||
|
|
||||||
type HTMLSummary struct {
|
|
||||||
Link string
|
|
||||||
Title string
|
|
||||||
Language Language
|
|
||||||
DateShort string
|
|
||||||
DateLong string
|
|
||||||
Summary string
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
package site
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"html"
|
|
||||||
)
|
|
||||||
|
|
||||||
type InlineType int
|
|
||||||
|
|
||||||
type InlineElement interface {
|
|
||||||
Text() string
|
|
||||||
InlineHTML() string
|
|
||||||
}
|
|
||||||
|
|
||||||
type PlainText string
|
|
||||||
|
|
||||||
func (pt PlainText) Text() string { return string(pt) }
|
|
||||||
|
|
||||||
func (pt PlainText) InlineHTML() string {
|
|
||||||
return html.EscapeString(string(pt))
|
|
||||||
}
|
|
||||||
|
|
||||||
type StrongText string
|
|
||||||
|
|
||||||
func (st StrongText) Text() string { return string(st) }
|
|
||||||
|
|
||||||
func (st StrongText) InlineHTML() string {
|
|
||||||
return fmt.Sprintf("<strong>%s</strong>", html.EscapeString(string(st)))
|
|
||||||
}
|
|
||||||
|
|
||||||
type EmpText string
|
|
||||||
|
|
||||||
func (et EmpText) Text() string { return string(et) }
|
|
||||||
|
|
||||||
func (et EmpText) InlineHTML() string {
|
|
||||||
return fmt.Sprintf("<em>%s</em>", html.EscapeString(string(et)))
|
|
||||||
}
|
|
||||||
|
|
||||||
type StrongEmpText string
|
|
||||||
|
|
||||||
func (set StrongEmpText) Text() string { return string(set) }
|
|
||||||
|
|
||||||
func (set StrongEmpText) InlineHTML() string {
|
|
||||||
return fmt.Sprintf("<strong><em>%s</em></strong>", html.EscapeString(string(set)))
|
|
||||||
}
|
|
||||||
|
|
||||||
type Link struct {
|
|
||||||
url string
|
|
||||||
title string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLink(url, title string) Link {
|
|
||||||
return Link{
|
|
||||||
url: url,
|
|
||||||
title: title,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l Link) Text() string { return l.title }
|
|
||||||
|
|
||||||
func (l Link) InlineHTML() string {
|
|
||||||
return fmt.Sprintf("<a href=%q>%s</a>", l.url, html.EscapeString(l.title))
|
|
||||||
}
|
|
||||||
|
|
||||||
type CodeText string
|
|
||||||
|
|
||||||
func (ct CodeText) Text() string { return string(ct) }
|
|
||||||
|
|
||||||
func (ct CodeText) InlineHTML() string {
|
|
||||||
return fmt.Sprintf("<code>%s</code>", html.EscapeString(string(ct)))
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
package site_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"git.sr.ht/~ewintr/go-kit/test"
|
|
||||||
"git.sr.ht/~ewintr/shitty-ssg/site"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPlainText(t *testing.T) {
|
|
||||||
text := "text"
|
|
||||||
pt := site.PlainText(text)
|
|
||||||
|
|
||||||
test.Equals(t, text, pt.InlineHTML())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStrongText(t *testing.T) {
|
|
||||||
text := "text"
|
|
||||||
st := site.StrongText(text)
|
|
||||||
exp := fmt.Sprintf("<strong>%s</strong>", text)
|
|
||||||
|
|
||||||
test.Equals(t, exp, st.InlineHTML())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmpText(t *testing.T) {
|
|
||||||
text := "text"
|
|
||||||
et := site.EmpText(text)
|
|
||||||
exp := fmt.Sprintf("<em>%s</em>", text)
|
|
||||||
|
|
||||||
test.Equals(t, exp, et.InlineHTML())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLink(t *testing.T) {
|
|
||||||
url := "http://example.com"
|
|
||||||
title := "link title"
|
|
||||||
link := site.NewLink(url, title)
|
|
||||||
exp := fmt.Sprintf("<a href=%q>%s</a>", url, title)
|
|
||||||
|
|
||||||
test.Equals(t, exp, link.InlineHTML())
|
|
||||||
}
|
|
|
@ -1,247 +0,0 @@
|
||||||
package site_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.sr.ht/~ewintr/go-kit/test"
|
|
||||||
"git.sr.ht/~ewintr/shitty-ssg/site"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewPost(t *testing.T) {
|
|
||||||
one := "one"
|
|
||||||
two := "two"
|
|
||||||
three := "three"
|
|
||||||
ptOne := site.PlainText(one)
|
|
||||||
ptTwo := site.PlainText(two)
|
|
||||||
ptThree := site.PlainText(three)
|
|
||||||
for _, tc := range []struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
exp site.Post
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty",
|
|
||||||
exp: site.Post{
|
|
||||||
Tags: []site.Tag{},
|
|
||||||
Language: site.LANGUAGE_EN,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "title",
|
|
||||||
input: "= Title",
|
|
||||||
exp: site.Post{
|
|
||||||
Title: "Title",
|
|
||||||
Tags: []site.Tag{},
|
|
||||||
Language: site.LANGUAGE_EN,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "header",
|
|
||||||
input: "= Title\nT. Test\n2020-10-27\n:tags:\ttag1, tag2\n:kind:\tnote\n:language:\tnl",
|
|
||||||
exp: site.Post{
|
|
||||||
Title: "Title",
|
|
||||||
Author: "T. Test",
|
|
||||||
Kind: site.KIND_NOTE,
|
|
||||||
Language: site.LANGUAGE_NL,
|
|
||||||
Tags: []site.Tag{
|
|
||||||
site.Tag("tag1"),
|
|
||||||
site.Tag("tag2"),
|
|
||||||
},
|
|
||||||
Date: time.Date(2020, time.October, 27, 0, 0, 0, 0, time.UTC),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "paragraphs",
|
|
||||||
input: fmt.Sprintf("%s\n\n%s\n\n%s", one, two, three),
|
|
||||||
exp: site.Post{
|
|
||||||
Tags: []site.Tag{},
|
|
||||||
Language: site.LANGUAGE_EN,
|
|
||||||
Content: []site.BlockElement{
|
|
||||||
site.Paragraph([]site.InlineElement{ptOne}),
|
|
||||||
site.Paragraph([]site.InlineElement{ptTwo}),
|
|
||||||
site.Paragraph([]site.InlineElement{ptThree}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "subtitle",
|
|
||||||
input: "== Subtitle",
|
|
||||||
exp: site.Post{
|
|
||||||
Tags: []site.Tag{},
|
|
||||||
Language: site.LANGUAGE_EN,
|
|
||||||
Content: []site.BlockElement{
|
|
||||||
site.SubTitle("Subtitle"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "code block",
|
|
||||||
input: "----\nsome code\nmore code\n----",
|
|
||||||
exp: site.Post{
|
|
||||||
Tags: []site.Tag{},
|
|
||||||
Language: site.LANGUAGE_EN,
|
|
||||||
Content: []site.BlockElement{
|
|
||||||
site.CodeBlock("some code\nmore code"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "code block with empty lines",
|
|
||||||
input: "----\nsome code\n\nmore code\n----",
|
|
||||||
exp: site.Post{
|
|
||||||
Tags: []site.Tag{},
|
|
||||||
Language: site.LANGUAGE_EN,
|
|
||||||
Content: []site.BlockElement{
|
|
||||||
site.CodeBlock("some code\n\nmore code"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "list",
|
|
||||||
input: "* item 1\n* item 2\n* *item 3*\n",
|
|
||||||
exp: site.Post{
|
|
||||||
Tags: []site.Tag{},
|
|
||||||
Language: site.LANGUAGE_EN,
|
|
||||||
Content: []site.BlockElement{
|
|
||||||
site.List{
|
|
||||||
site.ListItem([]site.InlineElement{site.PlainText("item 1")}),
|
|
||||||
site.ListItem([]site.InlineElement{site.PlainText("item 2")}),
|
|
||||||
site.ListItem([]site.InlineElement{site.StrongText("item 3")}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
act := site.NewPost(tc.input)
|
|
||||||
|
|
||||||
test.Equals(t, tc.exp, act)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostBodyHTML(t *testing.T) {
|
|
||||||
text := `= Title
|
|
||||||
|
|
||||||
Some text. With some *strong*. And a http://example.com[link].
|
|
||||||
|
|
||||||
== A Sub Title
|
|
||||||
|
|
||||||
And some more text.
|
|
||||||
`
|
|
||||||
post := site.NewPost(text)
|
|
||||||
act := post.HTMLPost()
|
|
||||||
|
|
||||||
exp := &site.HTMLPost{
|
|
||||||
Slug: "title",
|
|
||||||
Title: "Title",
|
|
||||||
DateLong: "January 1, 0001",
|
|
||||||
DateShort: "0001-01-01 00:00:00",
|
|
||||||
Content: `<p>Some text. With some <strong>strong</strong>. And a <a href="http://example.com">link</a>.</p>
|
|
||||||
<h2>A Sub Title</h2>
|
|
||||||
<p>And some more text.</p>
|
|
||||||
`,
|
|
||||||
}
|
|
||||||
|
|
||||||
test.Equals(t, exp, act)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseInline(t *testing.T) {
|
|
||||||
for _, tc := range []struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
exp []site.InlineElement
|
|
||||||
}{{
|
|
||||||
name: "empty",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "plain",
|
|
||||||
input: "some test text",
|
|
||||||
exp: []site.InlineElement{
|
|
||||||
site.PlainText("some test text")},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "strong",
|
|
||||||
input: "*some strong text*",
|
|
||||||
exp: []site.InlineElement{
|
|
||||||
site.StrongText("some strong text"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "strong in plain",
|
|
||||||
input: "some *strong* text",
|
|
||||||
exp: []site.InlineElement{
|
|
||||||
site.PlainText("some "),
|
|
||||||
site.StrongText("strong"),
|
|
||||||
site.PlainText(" text"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "emphasis",
|
|
||||||
input: "_some emphasized text_",
|
|
||||||
exp: []site.InlineElement{
|
|
||||||
site.EmpText("some emphasized text"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "emphasis in plain",
|
|
||||||
input: "some _emphasized_ text",
|
|
||||||
exp: []site.InlineElement{
|
|
||||||
site.PlainText("some "),
|
|
||||||
site.EmpText("emphasized"),
|
|
||||||
site.PlainText(" text"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "emp and strong in plain",
|
|
||||||
input: "some _*special*_ text",
|
|
||||||
exp: []site.InlineElement{
|
|
||||||
site.PlainText("some "),
|
|
||||||
site.StrongEmpText("special"),
|
|
||||||
site.PlainText(" text"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "link",
|
|
||||||
input: "a link[title] somewhere",
|
|
||||||
exp: []site.InlineElement{
|
|
||||||
site.PlainText("a "),
|
|
||||||
site.NewLink("link", "title"),
|
|
||||||
site.PlainText(" somewhere"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "code",
|
|
||||||
input: "`command`",
|
|
||||||
exp: []site.InlineElement{
|
|
||||||
site.CodeText("command"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "code in plain",
|
|
||||||
input: "some `code` in text",
|
|
||||||
exp: []site.InlineElement{
|
|
||||||
site.PlainText("some "),
|
|
||||||
site.CodeText("code"),
|
|
||||||
site.PlainText(" in text"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "link with underscore",
|
|
||||||
input: "https://example.com/some_url[some url]",
|
|
||||||
exp: []site.InlineElement{
|
|
||||||
site.NewLink("https://example.com/some_url", "some url"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
act := site.ParseInline(tc.input)
|
|
||||||
|
|
||||||
test.Equals(t, tc.exp, act)
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
125
site/post.go
125
site/post.go
|
@ -1,125 +0,0 @@
|
||||||
package site
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"html"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.sr.ht/~ewintr/go-kit/slugify"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidPost = errors.New("invalid post")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Post struct {
|
|
||||||
Title string
|
|
||||||
Author string
|
|
||||||
Kind Kind
|
|
||||||
Language Language
|
|
||||||
Path string
|
|
||||||
Date time.Time
|
|
||||||
Tags []Tag
|
|
||||||
Content []BlockElement
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Post) Slug() string {
|
|
||||||
return slugify.Slugify(p.Title)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Post) Year() string {
|
|
||||||
return strconv.Itoa(p.Date.Year())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Post) Link() string {
|
|
||||||
return fmt.Sprintf("%s/", path.Join("/", pluralKind[p.Kind], p.Year(), p.Slug()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Post) FullLink() string {
|
|
||||||
return fmt.Sprintf("https://erikwinter.nl%s", p.Link())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Post) HTMLSummary() *HTMLSummary {
|
|
||||||
summary := ""
|
|
||||||
if len(p.Content) > 0 {
|
|
||||||
summary = fmt.Sprintf("<p>%s...</p>", truncateOnSpace(p.Content[0].Text(), 150))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &HTMLSummary{
|
|
||||||
Link: p.Link(),
|
|
||||||
Title: p.Title,
|
|
||||||
Language: p.Language,
|
|
||||||
DateLong: p.FormattedDate(DATE_LONG),
|
|
||||||
DateShort: p.FormattedDate(DATE_SHORT),
|
|
||||||
Summary: summary,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Post) HTMLPost() *HTMLPost {
|
|
||||||
var content string
|
|
||||||
for _, be := range p.Content {
|
|
||||||
content += fmt.Sprintf("%s\n", be.BlockHTML())
|
|
||||||
}
|
|
||||||
|
|
||||||
return &HTMLPost{
|
|
||||||
Slug: p.Slug(),
|
|
||||||
Title: html.EscapeString(p.Title),
|
|
||||||
DateLong: p.FormattedDate(DATE_LONG),
|
|
||||||
DateShort: p.FormattedDate(DATE_SHORT),
|
|
||||||
Content: content,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Post) XMLPost() *XMLPost {
|
|
||||||
var content string
|
|
||||||
for _, be := range p.Content {
|
|
||||||
content += fmt.Sprintf("%s\n", html.EscapeString(be.BlockHTML()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &XMLPost{
|
|
||||||
Link: p.FullLink(),
|
|
||||||
Title: html.EscapeString(p.Title),
|
|
||||||
DateFormal: p.FormattedDate(DATE_FORMAL),
|
|
||||||
Content: content,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Post) FormattedDate(format DateFormat) string {
|
|
||||||
switch {
|
|
||||||
case format == DATE_LONG && p.Language == LANGUAGE_NL:
|
|
||||||
nlMonth := [...]string{"januari", "februari", "maart",
|
|
||||||
"april", "mei", "juni", "juli", "augustus", "september",
|
|
||||||
"oktober", "november", "december",
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%d %s %d", p.Date.Day(), nlMonth[p.Date.Month()-1], p.Date.Year())
|
|
||||||
case format == DATE_LONG && p.Language == LANGUAGE_EN:
|
|
||||||
return p.Date.Format("January 2, 2006")
|
|
||||||
case format == DATE_FORMAL:
|
|
||||||
return p.Date.Format(time.RFC1123Z)
|
|
||||||
case format == DATE_SHORT:
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
return p.Date.Format("2006-01-02 00:00:00")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func truncateOnSpace(text string, maxChars int) string {
|
|
||||||
if len(text) <= maxChars {
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
shortText := ""
|
|
||||||
ss := strings.Split(text, " ")
|
|
||||||
for _, s := range ss {
|
|
||||||
if len(shortText+" "+s) > maxChars {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
shortText += " " + s
|
|
||||||
}
|
|
||||||
|
|
||||||
return shortText
|
|
||||||
}
|
|
Loading…
Reference in New Issue