Commit 2f5e2f39 authored by Abiola Ibrahim's avatar Abiola Ibrahim

markdown: remove identifier from Json metadata

parent 6ce83aad
......@@ -51,8 +51,10 @@ type MetadataParser interface {
// Closing identifier
Closing() []byte
// Parse the metadata
Parse([]byte) error
// Parse the metadata.
// Returns the remaining page contents (Markdown)
// after extracting metadata
Parse([]byte) ([]byte, error)
// Parsed metadata.
// Should be called after a call to Parse returns no error
......@@ -65,13 +67,25 @@ type JSONMetadataParser struct {
}
// Parse the metadata
func (j *JSONMetadataParser) Parse(b []byte) error {
func (j *JSONMetadataParser) Parse(b []byte) ([]byte, error) {
m := make(map[string]interface{})
if err := json.Unmarshal(b, &m); err != nil {
return err
// Read the preceding JSON object
decoder := json.NewDecoder(bytes.NewReader(b))
if err := decoder.Decode(&m); err != nil {
return b, err
}
j.metadata.load(m)
return nil
// Retrieve remaining bytes after decoding
buf := make([]byte, len(b))
n, err := decoder.Buffered().Read(buf)
if err != nil {
return b, err
}
return buf[:n], nil
}
// Parsed metadata.
......@@ -82,12 +96,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
......@@ -96,13 +110,17 @@ type TOMLMetadataParser struct {
}
// Parse the metadata
func (t *TOMLMetadataParser) Parse(b []byte) error {
func (t *TOMLMetadataParser) Parse(b []byte) ([]byte, error) {
b, markdown, err := extractMetadata(t, b)
if err != nil {
return markdown, err
}
m := make(map[string]interface{})
if err := toml.Unmarshal(b, &m); err != nil {
return err
return markdown, err
}
t.metadata.load(m)
return nil
return markdown, nil
}
// Parsed metadata.
......@@ -127,13 +145,17 @@ type YAMLMetadataParser struct {
}
// Parse the metadata
func (y *YAMLMetadataParser) Parse(b []byte) error {
func (y *YAMLMetadataParser) Parse(b []byte) ([]byte, error) {
b, markdown, err := extractMetadata(y, b)
if err != nil {
return markdown, err
}
m := make(map[string]interface{})
if err := yaml.Unmarshal(b, &m); err != nil {
return err
return markdown, err
}
y.metadata.load(m)
return nil
return markdown, nil
}
// Parsed metadata.
......@@ -154,26 +176,23 @@ func (y *YAMLMetadataParser) Closing() []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) {
// and an error if any.
// Useful for MetadataParser with defined identifiers (YAML, TOML)
func extractMetadata(parser MetadataParser, b []byte) (metadata []byte, 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() {
// if no line is read,
// assume metadata not present
return metadata, b, nil
return nil, b, nil
}
line := scanner.Bytes()
parser = findParser(line)
// if no parser found,
// assume metadata not present
if parser == nil {
return metadata, b, nil
line := bytes.TrimSpace(scanner.Bytes())
if !bytes.Equal(line, parser.Opening()) {
return nil, b, fmt.Errorf("Wrong identifier")
}
// buffer for metadata contents
......@@ -185,11 +204,7 @@ func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) {
// 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
......@@ -197,18 +212,23 @@ func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) {
// scan the remaining bytes
scanner.Scan()
return parser.Metadata(), scanner.Bytes(), nil
return buf.Bytes(), 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()))
return buf.Bytes(), 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 {
func findParser(b []byte) MetadataParser {
var line []byte
// Read first line
if _, err := fmt.Fscanln(bytes.NewReader(b), &line); err != nil {
return nil
}
line = bytes.TrimSpace(line)
for _, parser := range parsers {
if bytes.Equal(parser.Opening(), line) {
......
......@@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"reflect"
"strings"
"testing"
)
......@@ -19,6 +20,7 @@ template = "default"
[variables]
name = "value"
+++
Page content
`,
`+++
title = "A title"
......@@ -41,6 +43,7 @@ template : default
variables :
- name : value
---
Page content
`,
`---
title : A title
......@@ -51,34 +54,30 @@ variables :
`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"
}
}
:::`,
`:::
Page content
`,
`
{
"title" : "A title",
"template" : "default",
"variables" : {
"name" : "value"
}
}
`,
`
:::
{{
"title" : "A title",
"template" : "default",
......@@ -86,7 +85,6 @@ var JSON = [4]string{`
"name" : "value"
}
}
:::
`,
}
......@@ -129,17 +127,18 @@ func TestParsers(t *testing.T) {
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)
if _, err := v.parser.Parse([]byte(v.testData[0])); err == nil {
t.Fatalf("Expected error for invalid metadata for %v", v.name)
}
// metadata with identifiers
metadata, _, err := extractMetadata([]byte(v.testData[1]))
md, err := v.parser.Parse([]byte(v.testData[1]))
check(t, err)
if !compare(metadata) {
t.Fatalf("Expected %v, found %v for %v", expected, metadata.Variables, v.name)
if !compare(v.parser.Metadata()) {
t.Fatalf("Expected %v, found %v for %v", expected, v.parser.Metadata().Variables, v.name)
}
if "Page content" != strings.TrimSpace(string(md)) {
t.Fatalf("Expected %v, found %v for %v", "Page content", string(md), v.name)
}
var line []byte
......@@ -153,12 +152,12 @@ func TestParsers(t *testing.T) {
}
// metadata without closing identifier
if _, _, err := extractMetadata([]byte(v.testData[2])); err == nil {
if _, err := v.parser.Parse([]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 {
if md, err = v.parser.Parse([]byte(v.testData[3])); err == nil {
t.Fatalf("Expected error for invalid metadata for %v", v.name)
}
}
......
......@@ -21,10 +21,22 @@ const (
// process processes the contents of a page.
// It parses the metadata (if any) and uses the template (if found)
func (md Markdown) process(c Config, requestPath string, b []byte) ([]byte, error) {
metadata, markdown, err := extractMetadata(b)
if err != nil {
return nil, err
var metadata = Metadata{}
var markdown []byte
var err error
// find parser compatible with page contents
parser := findParser(b)
// if found, assume metadata present and parse.
if parser != nil {
markdown, err = parser.Parse(b)
if err != nil {
return nil, err
}
metadata = parser.Metadata()
}
// if template is not specified, check if Default template is set
if metadata.Template == "" {
if _, ok := c.Templates[DefaultTemplate]; ok {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment