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"
|
||||
"strings"
|
||||
|
||||
"git.sr.ht/~ewintr/shitty-ssg/site"
|
||||
"git.sr.ht/~ewintr/shitty-ssg/cmd/ssg/site"
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"git.sr.ht/~ewintr/shitty-ssg/pkg/adoc"
|
||||
)
|
||||
|
||||
const (
|
||||
KIND_NOTE = Kind("note")
|
||||
KIND_STORY = Kind("story")
|
||||
|
@ -9,19 +13,15 @@ const (
|
|||
|
||||
type Kind string
|
||||
|
||||
var pluralKind = map[Kind]string{
|
||||
KIND_NOTE: "notes",
|
||||
KIND_STORY: "stories",
|
||||
KIND_ARTICLE: "articles",
|
||||
}
|
||||
|
||||
func NewKind(kind string) Kind {
|
||||
func NewKind(kind adoc.Kind) Kind {
|
||||
switch kind {
|
||||
case "note":
|
||||
case adoc.KIND_NOTE:
|
||||
return KIND_NOTE
|
||||
case "story":
|
||||
case adoc.KIND_VKV:
|
||||
return KIND_STORY
|
||||
case "article":
|
||||
case adoc.KIND_ESSAY:
|
||||
fallthrough
|
||||
case adoc.KIND_TUTORIAL:
|
||||
return KIND_ARTICLE
|
||||
default:
|
||||
return KIND_INVALID
|
||||
|
@ -37,11 +37,11 @@ const (
|
|||
|
||||
type Language string
|
||||
|
||||
func NewLanguage(text string) Language {
|
||||
switch text {
|
||||
case "nl":
|
||||
func NewLanguage(ln adoc.Language) Language {
|
||||
switch ln {
|
||||
case adoc.LANGUAGE_NL:
|
||||
return LANGUAGE_NL
|
||||
case "en":
|
||||
case adoc.LANGUAGE_EN:
|
||||
fallthrough
|
||||
default:
|
||||
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"
|
||||
|
||||
type Posts []Post
|
||||
type Posts []*Post
|
||||
|
||||
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) Less(i, j int) bool { return p[i].Date.After(p[j].Date) }
|
||||
|
||||
// Sort sorts on reverse chronological order
|
||||
func (p Posts) Sort() Posts {
|
||||
sort.Sort(p)
|
||||
|
||||
|
@ -86,15 +87,6 @@ func (p Posts) TagList() []string {
|
|||
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 {
|
||||
list := []string{}
|
||||
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 {
|
||||
var summaries []*HTMLSummary
|
||||
for _, p := range posts {
|
||||
summaries = append(summaries, p.HTMLSummary())
|
||||
}
|
||||
data := struct {
|
||||
Title string
|
||||
Summaries []*HTMLSummary
|
||||
}{
|
||||
Title: "Recent",
|
||||
Summaries: posts.HTMLSummaries(),
|
||||
Summaries: summaries,
|
||||
}
|
||||
|
||||
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 _, year := range posts.FilterByKind(kind).YearList() {
|
||||
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)
|
||||
if err := renderListing(path, tpl, title, summaries); err != nil {
|
||||
return err
|
||||
|
@ -155,7 +163,11 @@ func renderListings(targetPath string, tpl *template.Template, posts Posts) erro
|
|||
|
||||
for _, tag := range posts.TagList() {
|
||||
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)
|
||||
if err := renderListing(path, tpl, title, summaries); err != nil {
|
||||
return err
|
|
@ -5,6 +5,8 @@ import (
|
|||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
|
||||
"git.sr.ht/~ewintr/shitty-ssg/pkg/adoc"
|
||||
)
|
||||
|
||||
type StaticPage struct {
|
||||
|
@ -28,7 +30,7 @@ func New(resourcesPath string) (*Site, error) {
|
|||
return &Site{
|
||||
resourcesPath: resourcesPath,
|
||||
templates: templates,
|
||||
posts: []Post{},
|
||||
posts: Posts{},
|
||||
staticPages: []*StaticPage{},
|
||||
}, nil
|
||||
}
|
||||
|
@ -45,7 +47,10 @@ func (s *Site) AddFilePost(fPath string) error {
|
|||
if err != nil {
|
||||
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
|
||||
}
|
5
go.mod
5
go.mod
|
@ -2,4 +2,7 @@ module git.sr.ht/~ewintr/shitty-ssg
|
|||
|
||||
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.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/go.mod h1:zrpigRr1EHuVGw7GWwvYWwfLgdAwJ110zIRAGtaicvI=
|
||||
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 (
|
||||
"fmt"
|
||||
"html"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type BlockElement interface {
|
||||
Text() string
|
||||
BlockHTML() string
|
||||
}
|
||||
|
||||
type Paragraph []InlineElement
|
||||
|
@ -22,35 +20,17 @@ func (p Paragraph) Text() string {
|
|||
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
|
||||
|
||||
func (st SubTitle) Text() string { return string(st) }
|
||||
func (st SubTitle) BlockHTML() string {
|
||||
return fmt.Sprintf("<h2>%s</h2>", st)
|
||||
}
|
||||
|
||||
type SubSubTitle string
|
||||
|
||||
func (st SubSubTitle) Text() string { return string(st) }
|
||||
func (st SubSubTitle) BlockHTML() string {
|
||||
return fmt.Sprintf("<h3>%s</h3>", st)
|
||||
}
|
||||
|
||||
type CodeBlock string
|
||||
|
||||
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
|
||||
|
||||
|
@ -63,15 +43,6 @@ func (li ListItem) Text() string {
|
|||
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
|
||||
|
||||
func (l List) Text() string {
|
||||
|
@ -82,12 +53,3 @@ func (l List) Text() string {
|
|||
|
||||
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 (
|
||||
"strings"
|
||||
|
@ -14,11 +14,10 @@ const (
|
|||
CODE_PREFIX = "----\n"
|
||||
CODE_SUFFIX = "\n----"
|
||||
LISTITEM_PREFIX = "* "
|
||||
PARAGRAPH_CONTINUATION = "\n+\n"
|
||||
)
|
||||
|
||||
func NewPost(text string) Post {
|
||||
post := Post{
|
||||
func New(text string) *ADoc {
|
||||
doc := &ADoc{
|
||||
Language: LANGUAGE_EN,
|
||||
Tags: []Tag{},
|
||||
}
|
||||
|
@ -27,6 +26,7 @@ func NewPost(text string) Post {
|
|||
var pars []string
|
||||
for _, s := range strings.Split(text, PARAGRAPH_SEPARATOR) {
|
||||
if s == "" {
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ func NewPost(text string) Post {
|
|||
for i, p := range blocks {
|
||||
switch {
|
||||
case i == 0 && strings.HasPrefix(p, TITLE_PREFIX):
|
||||
ParseHeader(p, &post)
|
||||
ParseHeader(p, doc)
|
||||
case strings.HasPrefix(p, SUBTITLE_PREFIX):
|
||||
p = strings.TrimSpace(p)
|
||||
s := strings.Split(p, SUBTITLE_PREFIX)
|
||||
|
@ -68,7 +68,7 @@ func NewPost(text string) Post {
|
|||
|
||||
continue
|
||||
}
|
||||
post.Content = append(post.Content, SubTitle(s[1]))
|
||||
doc.Content = append(doc.Content, SubTitle(s[1]))
|
||||
case strings.HasPrefix(p, SUBSUBTITLE_PREFIX):
|
||||
p = strings.TrimSpace(p)
|
||||
s := strings.Split(p, SUBSUBTITLE_PREFIX)
|
||||
|
@ -76,9 +76,9 @@ func NewPost(text string) Post {
|
|||
|
||||
continue
|
||||
}
|
||||
post.Content = append(post.Content, SubSubTitle(s[1]))
|
||||
doc.Content = append(doc.Content, SubSubTitle(s[1]))
|
||||
case isCodeBlock(p):
|
||||
post.Content = append(post.Content, parseCodeBlock(p))
|
||||
doc.Content = append(doc.Content, parseCodeBlock(p))
|
||||
case strings.HasPrefix(p, LISTITEM_PREFIX):
|
||||
p = strings.TrimSpace(p)
|
||||
var items []ListItem
|
||||
|
@ -88,15 +88,15 @@ func NewPost(text string) Post {
|
|||
items = append(items, ListItem(inline))
|
||||
}
|
||||
}
|
||||
post.Content = append(post.Content, List(items))
|
||||
doc.Content = append(doc.Content, List(items))
|
||||
|
||||
default:
|
||||
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 {
|
||||
|
@ -111,31 +111,31 @@ func parseCodeBlock(par string) CodeBlock {
|
|||
return CodeBlock(content)
|
||||
}
|
||||
|
||||
func ParseHeader(text string, post *Post) {
|
||||
func ParseHeader(text string, doc *ADoc) {
|
||||
text = strings.TrimSpace(text)
|
||||
lines := strings.Split(text, LINE_SEPARATOR)
|
||||
for i, l := range lines {
|
||||
switch {
|
||||
case i == 0:
|
||||
s := strings.Split(l, TITLE_PREFIX)
|
||||
post.Title = s[1]
|
||||
doc.Title = s[1]
|
||||
case isDate(l):
|
||||
date, _ := time.Parse("2006-01-02", l)
|
||||
post.Date = date
|
||||
doc.Date = date
|
||||
case strings.HasPrefix(l, ":kind:"):
|
||||
s := strings.Split(l, ":")
|
||||
post.Kind = NewKind(strings.TrimSpace(s[2]))
|
||||
doc.Kind = NewKind(strings.TrimSpace(s[2]))
|
||||
case strings.HasPrefix(l, ":language:"):
|
||||
s := strings.Split(l, ":")
|
||||
post.Language = NewLanguage(strings.TrimSpace(s[2]))
|
||||
doc.Language = NewLanguage(strings.TrimSpace(s[2]))
|
||||
case strings.HasPrefix(l, ":tags:"):
|
||||
s := strings.Split(l, ":")
|
||||
t := strings.Split(s[2], ",")
|
||||
for _, tag := range t {
|
||||
post.Tags = append(post.Tags, Tag(strings.TrimSpace(tag)))
|
||||
doc.Tags = append(doc.Tags, Tag(strings.TrimSpace(tag)))
|
||||
}
|
||||
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