mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-05 18:44:58 +03:00
markdown: Added template support.
This commit is contained in:
parent
0fccd3707d
commit
48a12c605a
3 changed files with 237 additions and 69 deletions
|
@ -1,7 +1,10 @@
|
|||
package markdown
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
@ -79,12 +82,12 @@ func (j *JSONMetadataParser) Metadata() Metadata {
|
|||
|
||||
// Opening returns the opening identifier JSON metadata
|
||||
func (j *JSONMetadataParser) Opening() []byte {
|
||||
return []byte("{")
|
||||
return []byte(":::")
|
||||
}
|
||||
|
||||
// Closing returns the closing identifier JSON metadata
|
||||
func (j *JSONMetadataParser) Closing() []byte {
|
||||
return []byte("}")
|
||||
return []byte(":::")
|
||||
}
|
||||
|
||||
// TOMLMetadataParser is the MetadataParser for TOML
|
||||
|
@ -148,3 +151,65 @@ func (y *YAMLMetadataParser) Opening() []byte {
|
|||
func (y *YAMLMetadataParser) Closing() []byte {
|
||||
return []byte("---")
|
||||
}
|
||||
|
||||
// extractMetadata extracts metadata content from a page.
|
||||
// it returns the metadata, the remaining bytes (markdown),
|
||||
// and an error if any
|
||||
func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) {
|
||||
b = bytes.TrimSpace(b)
|
||||
reader := bytes.NewBuffer(b)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
var parser MetadataParser
|
||||
|
||||
// Read first line
|
||||
if scanner.Scan() {
|
||||
line := scanner.Bytes()
|
||||
parser = findParser(line)
|
||||
// if no parser found,
|
||||
// assume metadata not present
|
||||
if parser == nil {
|
||||
return metadata, b, nil
|
||||
}
|
||||
}
|
||||
|
||||
// buffer for metadata contents
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
// Read remaining lines until closing identifier is found
|
||||
for scanner.Scan() {
|
||||
line := scanner.Bytes()
|
||||
|
||||
// if closing identifier found
|
||||
if bytes.Equal(bytes.TrimSpace(line), parser.Closing()) {
|
||||
// parse the metadata
|
||||
err := parser.Parse(buf.Bytes())
|
||||
if err != nil {
|
||||
return metadata, nil, err
|
||||
}
|
||||
// get the scanner to return remaining bytes
|
||||
scanner.Split(func(data []byte, atEOF bool) (int, []byte, error) {
|
||||
return len(data), data, nil
|
||||
})
|
||||
// scan the remaining bytes
|
||||
scanner.Scan()
|
||||
|
||||
return parser.Metadata(), scanner.Bytes(), nil
|
||||
}
|
||||
buf.Write(line)
|
||||
buf.WriteString("\r\n")
|
||||
}
|
||||
|
||||
// closing identifier not found
|
||||
return metadata, nil, fmt.Errorf("Metadata not closed. '%v' not found", string(parser.Closing()))
|
||||
}
|
||||
|
||||
// findParser finds the parser using line that contains opening identifier
|
||||
func findParser(line []byte) MetadataParser {
|
||||
line = bytes.TrimSpace(line)
|
||||
for _, parser := range parsers {
|
||||
if bytes.Equal(parser.Opening(), line) {
|
||||
return parser
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
166
middleware/markdown/metadata_test.go
Normal file
166
middleware/markdown/metadata_test.go
Normal file
|
@ -0,0 +1,166 @@
|
|||
package markdown
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var TOML = [4]string{`
|
||||
title = "A title"
|
||||
template = "default"
|
||||
[variables]
|
||||
name = "value"
|
||||
`,
|
||||
`+++
|
||||
title = "A title"
|
||||
template = "default"
|
||||
[variables]
|
||||
name = "value"
|
||||
+++
|
||||
`,
|
||||
`+++
|
||||
title = "A title"
|
||||
template = "default"
|
||||
[variables]
|
||||
name = "value"
|
||||
`,
|
||||
`title = "A title" template = "default" [variables] name = "value"`,
|
||||
}
|
||||
|
||||
var YAML = [4]string{`
|
||||
title : A title
|
||||
template : default
|
||||
variables :
|
||||
- name : value
|
||||
`,
|
||||
`---
|
||||
title : A title
|
||||
template : default
|
||||
variables :
|
||||
- name : value
|
||||
---
|
||||
`,
|
||||
`---
|
||||
title : A title
|
||||
template : default
|
||||
variables :
|
||||
- name : value
|
||||
`,
|
||||
`title : A title template : default variables : name : value`,
|
||||
}
|
||||
var JSON = [4]string{`
|
||||
{
|
||||
"title" : "A title",
|
||||
"template" : "default",
|
||||
"variables" : {
|
||||
"name" : "value"
|
||||
}
|
||||
}
|
||||
`,
|
||||
`:::
|
||||
{
|
||||
"title" : "A title",
|
||||
"template" : "default",
|
||||
"variables" : {
|
||||
"name" : "value"
|
||||
}
|
||||
}
|
||||
:::`,
|
||||
`:::
|
||||
{
|
||||
"title" : "A title",
|
||||
"template" : "default",
|
||||
"variables" : {
|
||||
"name" : "value"
|
||||
}
|
||||
}
|
||||
`,
|
||||
`
|
||||
:::
|
||||
{{
|
||||
"title" : "A title",
|
||||
"template" : "default",
|
||||
"variables" : {
|
||||
"name" : "value"
|
||||
}
|
||||
}
|
||||
:::
|
||||
`,
|
||||
}
|
||||
|
||||
func check(t *testing.T, err error) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsers(t *testing.T) {
|
||||
expected := Metadata{
|
||||
Title: "A title",
|
||||
Template: "default",
|
||||
Variables: map[string]interface{}{"name": "value"},
|
||||
}
|
||||
compare := func(m Metadata) bool {
|
||||
if m.Title != expected.Title {
|
||||
return false
|
||||
}
|
||||
if m.Template != expected.Template {
|
||||
return false
|
||||
}
|
||||
for k, v := range m.Variables {
|
||||
if v != expected.Variables[k] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
data := []struct {
|
||||
parser MetadataParser
|
||||
testData [4]string
|
||||
name string
|
||||
}{
|
||||
{&JSONMetadataParser{}, JSON, "json"},
|
||||
{&YAMLMetadataParser{}, YAML, "yaml"},
|
||||
{&TOMLMetadataParser{}, TOML, "toml"},
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
// metadata without identifiers
|
||||
err := v.parser.Parse([]byte(v.testData[0]))
|
||||
check(t, err)
|
||||
if !compare(v.parser.Metadata()) {
|
||||
t.Fatalf("Expected %v, found %v for %v", expected, v.parser.Metadata().Variables, v.name)
|
||||
}
|
||||
|
||||
// metadata with identifiers
|
||||
metadata, _, err := extractMetadata([]byte(v.testData[1]))
|
||||
check(t, err)
|
||||
if !compare(metadata) {
|
||||
t.Fatalf("Expected %v, found %v for %v", expected, metadata.Variables, v.name)
|
||||
}
|
||||
|
||||
var line []byte
|
||||
fmt.Fscanln(bytes.NewReader([]byte(v.testData[1])), &line)
|
||||
if parser := findParser(line); parser == nil {
|
||||
t.Fatalf("Parser must be found for %v", v.name)
|
||||
} else {
|
||||
if reflect.TypeOf(parser) != reflect.TypeOf(v.parser) {
|
||||
t.Fatalf("parsers not equal. %v != %v", reflect.TypeOf(parser), reflect.TypeOf(v.parser))
|
||||
}
|
||||
}
|
||||
|
||||
// metadata without closing identifier
|
||||
if _, _, err := extractMetadata([]byte(v.testData[2])); err == nil {
|
||||
t.Fatalf("Expected error for missing closing identifier for %v", v.name)
|
||||
}
|
||||
|
||||
// invalid metadata
|
||||
if err := v.parser.Parse([]byte(v.testData[3])); err == nil {
|
||||
t.Fatalf("Expected error for invalid metadata for %v", v.name)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,17 +1,16 @@
|
|||
package markdown
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/russross/blackfriday"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -48,73 +47,11 @@ func (md Markdown) process(c Config, requestPath string, b []byte) ([]byte, erro
|
|||
markdown = blackfriday.Markdown(markdown, c.Renderer, 0)
|
||||
|
||||
// set it as body for template
|
||||
metadata.Variables["body"] = string(markdown)
|
||||
metadata.Variables["markdown"] = string(markdown)
|
||||
|
||||
return md.processTemplate(c, requestPath, tmpl, metadata)
|
||||
}
|
||||
|
||||
// extractMetadata extracts metadata content from a page.
|
||||
// it returns the metadata, the remaining bytes (markdown),
|
||||
// and an error if any
|
||||
func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) {
|
||||
b = bytes.TrimSpace(b)
|
||||
reader := bytes.NewBuffer(b)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
var parser MetadataParser
|
||||
|
||||
// Read first line
|
||||
if scanner.Scan() {
|
||||
line := scanner.Bytes()
|
||||
parser = findParser(line)
|
||||
// if no parser found,
|
||||
// assume metadata not present
|
||||
if parser == nil {
|
||||
return metadata, b, nil
|
||||
}
|
||||
}
|
||||
|
||||
// buffer for metadata contents
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
// Read remaining lines until closing identifier is found
|
||||
for scanner.Scan() {
|
||||
line := scanner.Bytes()
|
||||
|
||||
// if closing identifier found
|
||||
if bytes.Equal(bytes.TrimSpace(line), parser.Closing()) {
|
||||
// parse the metadata
|
||||
err := parser.Parse(buf.Bytes())
|
||||
if err != nil {
|
||||
return metadata, nil, err
|
||||
}
|
||||
// get the scanner to return remaining bytes
|
||||
scanner.Split(func(data []byte, atEOF bool) (int, []byte, error) {
|
||||
return len(data), data, nil
|
||||
})
|
||||
// scan the remaining bytes
|
||||
scanner.Scan()
|
||||
|
||||
return parser.Metadata(), scanner.Bytes(), nil
|
||||
}
|
||||
buf.Write(line)
|
||||
buf.WriteString("\r\n")
|
||||
}
|
||||
|
||||
// closing identifier not found
|
||||
return metadata, nil, fmt.Errorf("Metadata not closed. '%v' not found", string(parser.Closing()))
|
||||
}
|
||||
|
||||
// findParser finds the parser using line that contains opening identifier
|
||||
func findParser(line []byte) MetadataParser {
|
||||
line = bytes.TrimSpace(line)
|
||||
for _, parser := range parsers {
|
||||
if bytes.Equal(parser.Opening(), line) {
|
||||
return parser
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// processTemplate processes a template given a requestPath,
|
||||
// template (tmpl) and metadata
|
||||
func (md Markdown) processTemplate(c Config, requestPath string, tmpl []byte, metadata Metadata) ([]byte, error) {
|
||||
|
|
Loading…
Reference in a new issue