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 { ...@@ -51,8 +51,10 @@ type MetadataParser interface {
// Closing identifier // Closing identifier
Closing() []byte Closing() []byte
// Parse the metadata // Parse the metadata.
Parse([]byte) error // Returns the remaining page contents (Markdown)
// after extracting metadata
Parse([]byte) ([]byte, error)
// Parsed metadata. // Parsed metadata.
// Should be called after a call to Parse returns no error // Should be called after a call to Parse returns no error
...@@ -65,13 +67,25 @@ type JSONMetadataParser struct { ...@@ -65,13 +67,25 @@ type JSONMetadataParser struct {
} }
// Parse the metadata // Parse the metadata
func (j *JSONMetadataParser) Parse(b []byte) error { func (j *JSONMetadataParser) Parse(b []byte) ([]byte, error) {
m := make(map[string]interface{}) 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) 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. // Parsed metadata.
...@@ -82,12 +96,12 @@ func (j *JSONMetadataParser) Metadata() Metadata { ...@@ -82,12 +96,12 @@ func (j *JSONMetadataParser) Metadata() Metadata {
// Opening returns the opening identifier JSON metadata // Opening returns the opening identifier JSON metadata
func (j *JSONMetadataParser) Opening() []byte { func (j *JSONMetadataParser) Opening() []byte {
return []byte(":::") return []byte("{")
} }
// Closing returns the closing identifier JSON metadata // Closing returns the closing identifier JSON metadata
func (j *JSONMetadataParser) Closing() []byte { func (j *JSONMetadataParser) Closing() []byte {
return []byte(":::") return []byte("}")
} }
// TOMLMetadataParser is the MetadataParser for TOML // TOMLMetadataParser is the MetadataParser for TOML
...@@ -96,13 +110,17 @@ type TOMLMetadataParser struct { ...@@ -96,13 +110,17 @@ type TOMLMetadataParser struct {
} }
// Parse the metadata // 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{}) m := make(map[string]interface{})
if err := toml.Unmarshal(b, &m); err != nil { if err := toml.Unmarshal(b, &m); err != nil {
return err return markdown, err
} }
t.metadata.load(m) t.metadata.load(m)
return nil return markdown, nil
} }
// Parsed metadata. // Parsed metadata.
...@@ -127,13 +145,17 @@ type YAMLMetadataParser struct { ...@@ -127,13 +145,17 @@ type YAMLMetadataParser struct {
} }
// Parse the metadata // 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{}) m := make(map[string]interface{})
if err := yaml.Unmarshal(b, &m); err != nil { if err := yaml.Unmarshal(b, &m); err != nil {
return err return markdown, err
} }
y.metadata.load(m) y.metadata.load(m)
return nil return markdown, nil
} }
// Parsed metadata. // Parsed metadata.
...@@ -154,26 +176,23 @@ func (y *YAMLMetadataParser) Closing() []byte { ...@@ -154,26 +176,23 @@ func (y *YAMLMetadataParser) Closing() []byte {
// extractMetadata extracts metadata content from a page. // extractMetadata extracts metadata content from a page.
// it returns the metadata, the remaining bytes (markdown), // it returns the metadata, the remaining bytes (markdown),
// and an error if any // and an error if any.
func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) { // Useful for MetadataParser with defined identifiers (YAML, TOML)
func extractMetadata(parser MetadataParser, b []byte) (metadata []byte, markdown []byte, err error) {
b = bytes.TrimSpace(b) b = bytes.TrimSpace(b)
reader := bytes.NewBuffer(b) reader := bytes.NewBuffer(b)
scanner := bufio.NewScanner(reader) scanner := bufio.NewScanner(reader)
var parser MetadataParser
// Read first line // Read first line
if !scanner.Scan() { if !scanner.Scan() {
// if no line is read, // if no line is read,
// assume metadata not present // assume metadata not present
return metadata, b, nil return nil, b, nil
} }
line := scanner.Bytes() line := bytes.TrimSpace(scanner.Bytes())
parser = findParser(line) if !bytes.Equal(line, parser.Opening()) {
// if no parser found, return nil, b, fmt.Errorf("Wrong identifier")
// assume metadata not present
if parser == nil {
return metadata, b, nil
} }
// buffer for metadata contents // buffer for metadata contents
...@@ -185,11 +204,7 @@ func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) { ...@@ -185,11 +204,7 @@ func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) {
// if closing identifier found // if closing identifier found
if bytes.Equal(bytes.TrimSpace(line), parser.Closing()) { 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 // get the scanner to return remaining bytes
scanner.Split(func(data []byte, atEOF bool) (int, []byte, error) { scanner.Split(func(data []byte, atEOF bool) (int, []byte, error) {
return len(data), data, nil return len(data), data, nil
...@@ -197,18 +212,23 @@ func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) { ...@@ -197,18 +212,23 @@ func extractMetadata(b []byte) (metadata Metadata, markdown []byte, err error) {
// scan the remaining bytes // scan the remaining bytes
scanner.Scan() scanner.Scan()
return parser.Metadata(), scanner.Bytes(), nil return buf.Bytes(), scanner.Bytes(), nil
} }
buf.Write(line) buf.Write(line)
buf.WriteString("\r\n") buf.WriteString("\r\n")
} }
// closing identifier not found // 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 // 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) line = bytes.TrimSpace(line)
for _, parser := range parsers { for _, parser := range parsers {
if bytes.Equal(parser.Opening(), line) { if bytes.Equal(parser.Opening(), line) {
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"reflect" "reflect"
"strings"
"testing" "testing"
) )
...@@ -19,6 +20,7 @@ template = "default" ...@@ -19,6 +20,7 @@ template = "default"
[variables] [variables]
name = "value" name = "value"
+++ +++
Page content
`, `,
`+++ `+++
title = "A title" title = "A title"
...@@ -41,6 +43,7 @@ template : default ...@@ -41,6 +43,7 @@ template : default
variables : variables :
- name : value - name : value
--- ---
Page content
`, `,
`--- `---
title : A title title : A title
...@@ -51,34 +54,30 @@ variables : ...@@ -51,34 +54,30 @@ variables :
`title : A title template : default variables : name : value`, `title : A title template : default variables : name : value`,
} }
var JSON = [4]string{` var JSON = [4]string{`
{
"title" : "A title", "title" : "A title",
"template" : "default", "template" : "default",
"variables" : { "variables" : {
"name" : "value" "name" : "value"
} }
}
`, `,
`::: `{
{
"title" : "A title", "title" : "A title",
"template" : "default", "template" : "default",
"variables" : { "variables" : {
"name" : "value" "name" : "value"
} }
} }
:::`, Page content
`::: `,
`
{ {
"title" : "A title", "title" : "A title",
"template" : "default", "template" : "default",
"variables" : { "variables" : {
"name" : "value" "name" : "value"
} }
}
`, `,
` `
:::
{{ {{
"title" : "A title", "title" : "A title",
"template" : "default", "template" : "default",
...@@ -86,7 +85,6 @@ var JSON = [4]string{` ...@@ -86,7 +85,6 @@ var JSON = [4]string{`
"name" : "value" "name" : "value"
} }
} }
:::
`, `,
} }
...@@ -129,17 +127,18 @@ func TestParsers(t *testing.T) { ...@@ -129,17 +127,18 @@ func TestParsers(t *testing.T) {
for _, v := range data { for _, v := range data {
// metadata without identifiers // metadata without identifiers
err := v.parser.Parse([]byte(v.testData[0])) if _, err := v.parser.Parse([]byte(v.testData[0])); err == nil {
check(t, err) t.Fatalf("Expected error for invalid metadata for %v", v.name)
if !compare(v.parser.Metadata()) {
t.Fatalf("Expected %v, found %v for %v", expected, v.parser.Metadata().Variables, v.name)
} }
// metadata with identifiers // metadata with identifiers
metadata, _, err := extractMetadata([]byte(v.testData[1])) md, err := v.parser.Parse([]byte(v.testData[1]))
check(t, err) check(t, err)
if !compare(metadata) { if !compare(v.parser.Metadata()) {
t.Fatalf("Expected %v, found %v for %v", expected, metadata.Variables, v.name) 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 var line []byte
...@@ -153,12 +152,12 @@ func TestParsers(t *testing.T) { ...@@ -153,12 +152,12 @@ func TestParsers(t *testing.T) {
} }
// metadata without closing identifier // 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) t.Fatalf("Expected error for missing closing identifier for %v", v.name)
} }
// invalid metadata // 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) t.Fatalf("Expected error for invalid metadata for %v", v.name)
} }
} }
......
...@@ -21,10 +21,22 @@ const ( ...@@ -21,10 +21,22 @@ const (
// process processes the contents of a page. // process processes the contents of a page.
// It parses the metadata (if any) and uses the template (if found) // It parses the metadata (if any) and uses the template (if found)
func (md Markdown) process(c Config, requestPath string, b []byte) ([]byte, error) { func (md Markdown) process(c Config, requestPath string, b []byte) ([]byte, error) {
metadata, markdown, err := extractMetadata(b) var metadata = Metadata{}
if err != nil { var markdown []byte
return nil, err 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 template is not specified, check if Default template is set
if metadata.Template == "" { if metadata.Template == "" {
if _, ok := c.Templates[DefaultTemplate]; ok { 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