mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-07 20:30:36 -05:00
Fix "hugo new" EOF error with an archetype file without the final EOL
This rewrites `extractFrontMatterDelims` function to make it work with an archetype file without the final EOL and adds more detailed error messages and comments. It also removes `matches` and `matches_quick` functions which aren't called anywhere.
This commit is contained in:
parent
b6ab661893
commit
78e9229c52
3 changed files with 70 additions and 81 deletions
|
@ -447,7 +447,7 @@ func TestDegenerateInvalidFrontMatterShortDelim(t *testing.T) {
|
||||||
r string
|
r string
|
||||||
err string
|
err string
|
||||||
}{
|
}{
|
||||||
{INVALID_FRONT_MATTER_SHORT_DELIM_ENDING, "Unable to read frontmatter at filepos 45: EOF"},
|
{INVALID_FRONT_MATTER_SHORT_DELIM_ENDING, "unable to read frontmatter at filepos 45: EOF"},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
|
||||||
|
|
146
parser/page.go
146
parser/page.go
|
@ -159,17 +159,13 @@ func isFrontMatterDelim(data []byte) bool {
|
||||||
|
|
||||||
func determineDelims(firstLine []byte) (left, right []byte) {
|
func determineDelims(firstLine []byte) (left, right []byte) {
|
||||||
switch len(firstLine) {
|
switch len(firstLine) {
|
||||||
|
case 5:
|
||||||
|
fallthrough
|
||||||
case 4:
|
case 4:
|
||||||
if firstLine[0] == YAML_LEAD[0] {
|
if firstLine[0] == YAML_LEAD[0] {
|
||||||
return []byte(YAML_DELIM_UNIX), []byte(YAML_DELIM_UNIX)
|
return []byte(YAML_DELIM), []byte(YAML_DELIM)
|
||||||
}
|
}
|
||||||
return []byte(TOML_DELIM_UNIX), []byte(TOML_DELIM_UNIX)
|
return []byte(TOML_DELIM), []byte(TOML_DELIM)
|
||||||
|
|
||||||
case 5:
|
|
||||||
if firstLine[0] == YAML_LEAD[0] {
|
|
||||||
return []byte(YAML_DELIM_DOS), []byte(YAML_DELIM_DOS)
|
|
||||||
}
|
|
||||||
return []byte(TOML_DELIM_DOS), []byte(TOML_DELIM_DOS)
|
|
||||||
case 3:
|
case 3:
|
||||||
fallthrough
|
fallthrough
|
||||||
case 2:
|
case 2:
|
||||||
|
@ -181,104 +177,98 @@ func determineDelims(firstLine []byte) (left, right []byte) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractFrontMatterDelims takes a frontmatter from the content bufio.Reader.
|
||||||
|
// Begining white spaces of the bufio.Reader must be trimmed before call this
|
||||||
|
// function.
|
||||||
func extractFrontMatterDelims(r *bufio.Reader, left, right []byte) (fm FrontMatter, err error) {
|
func extractFrontMatterDelims(r *bufio.Reader, left, right []byte) (fm FrontMatter, err error) {
|
||||||
var (
|
var (
|
||||||
c byte
|
c byte
|
||||||
level int = 0
|
buf bytes.Buffer
|
||||||
bytesRead int = 0
|
level int = 0
|
||||||
sameDelim = bytes.Equal(left, right)
|
sameDelim bool = bytes.Equal(left, right)
|
||||||
)
|
)
|
||||||
|
|
||||||
wr := new(bytes.Buffer)
|
// Frontmatter must start with a delimiter. To check it first,
|
||||||
|
// pre-reads beginning delimiter length - 1 bytes from Reader
|
||||||
|
for i := 0; i < len(left)-1; i++ {
|
||||||
|
if c, err = r.ReadByte(); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to read frontmatter at filepos %d: %s", buf.Len(), err)
|
||||||
|
}
|
||||||
|
if err = buf.WriteByte(c); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a character from Reader one by one and checks it matches the
|
||||||
|
// last character of one of delemiters to find the last character of
|
||||||
|
// frontmatter. If it matches, makes sure it contains the delimiter
|
||||||
|
// and if so, also checks it is followed by CR+LF or LF when YAML,
|
||||||
|
// TOML case. In JSON case, nested delimiters must be parsed and it
|
||||||
|
// is expected that the delimiter only contains one character.
|
||||||
for {
|
for {
|
||||||
if c, err = r.ReadByte(); err != nil {
|
if c, err = r.ReadByte(); err != nil {
|
||||||
return nil, fmt.Errorf("Unable to read frontmatter at filepos %d: %s", bytesRead, err)
|
return nil, fmt.Errorf("unable to read frontmatter at filepos %d: %s", buf.Len(), err)
|
||||||
|
}
|
||||||
|
if err = buf.WriteByte(c); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
bytesRead += 1
|
|
||||||
|
|
||||||
switch c {
|
switch c {
|
||||||
case left[0]:
|
case left[len(left)-1]:
|
||||||
var (
|
if sameDelim { // YAML, TOML case
|
||||||
buf []byte = []byte{c}
|
if bytes.HasSuffix(buf.Bytes(), left) {
|
||||||
remaining []byte
|
c, err = r.ReadByte()
|
||||||
)
|
if err != nil {
|
||||||
|
// It is ok that the end delimiter ends with EOF
|
||||||
if remaining, err = r.Peek(len(left) - 1); err != nil {
|
if err != io.EOF || level != 1 {
|
||||||
return nil, err
|
return nil, fmt.Errorf("unable to read frontmatter at filepos %d: %s", buf.Len(), err)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
buf = append(buf, remaining...)
|
switch c {
|
||||||
|
case '\n':
|
||||||
if bytes.Equal(buf, left) {
|
// ok
|
||||||
if sameDelim {
|
case '\r':
|
||||||
|
if err = buf.WriteByte(c); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if c, err = r.ReadByte(); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to read frontmatter at filepos %d: %s", buf.Len(), err)
|
||||||
|
}
|
||||||
|
if c != '\n' {
|
||||||
|
return nil, fmt.Errorf("frontmatter delimiter must be followed by CR+LF or LF but those can't be found at filepos %d", buf.Len())
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("frontmatter delimiter must be followed by CR+LF or LF but those can't be found at filepos %d", buf.Len())
|
||||||
|
}
|
||||||
|
if err = buf.WriteByte(c); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
if level == 0 {
|
if level == 0 {
|
||||||
level = 1
|
level = 1
|
||||||
} else {
|
} else {
|
||||||
level = 0
|
level = 0
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
level += 1
|
|
||||||
}
|
}
|
||||||
|
} else { // JSON case
|
||||||
|
level++
|
||||||
}
|
}
|
||||||
|
case right[len(right)-1]: // JSON case only reaches here
|
||||||
if _, err = wr.Write([]byte{c}); err != nil {
|
level--
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if level == 0 {
|
|
||||||
if _, err = r.Read(remaining); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, err = wr.Write(remaining); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case right[0]:
|
|
||||||
match, err := matches(r, wr, []byte{c}, right)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if match {
|
|
||||||
level -= 1
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if err = wr.WriteByte(c); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if level == 0 && !unicode.IsSpace(rune(c)) {
|
if level == 0 {
|
||||||
|
// Consumes white spaces immediately behind frontmatter
|
||||||
if err = chompWhitespace(r); err != nil {
|
if err = chompWhitespace(r); err != nil {
|
||||||
if err != io.EOF {
|
if err != io.EOF {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return wr.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func matches_quick(buf, expected []byte) (ok bool, err error) {
|
|
||||||
return bytes.Equal(expected, buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func matches(r *bufio.Reader, wr io.Writer, c, expected []byte) (ok bool, err error) {
|
|
||||||
if len(expected) == 1 {
|
|
||||||
if _, err = wr.Write(c); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return bytes.Equal(c, expected), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, len(expected)-1)
|
|
||||||
if buf, err = r.Peek(len(expected) - 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = append(c, buf...)
|
|
||||||
return bytes.Equal(expected, buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractContent(r io.Reader) (content Content, err error) {
|
func extractContent(r io.Reader) (content Content, err error) {
|
||||||
wr := new(bytes.Buffer)
|
wr := new(bytes.Buffer)
|
||||||
if _, err = wr.ReadFrom(r); err != nil {
|
if _, err = wr.ReadFrom(r); err != nil {
|
||||||
|
|
|
@ -54,7 +54,6 @@ func TestDegenerateCreatePageFrom(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{CONTENT_MISSING_END_FM_DELIM},
|
{CONTENT_MISSING_END_FM_DELIM},
|
||||||
{CONTENT_INCOMPLETE_END_FM_DELIM},
|
{CONTENT_INCOMPLETE_END_FM_DELIM},
|
||||||
{CONTENT_FM_NO_DOC},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -230,6 +229,7 @@ func TestExtractFrontMatter(t *testing.T) {
|
||||||
{"---\nfoobar\nbarfoo\nfizbaz\n", nil, false},
|
{"---\nfoobar\nbarfoo\nfizbaz\n", nil, false},
|
||||||
{"---\nblar\n-\n", nil, false},
|
{"---\nblar\n-\n", nil, false},
|
||||||
{"---\nralb\n---\n", []byte("---\nralb\n---\n"), true},
|
{"---\nralb\n---\n", []byte("---\nralb\n---\n"), true},
|
||||||
|
{"---\neof\n---", []byte("---\neof\n---"), true},
|
||||||
{"---\nminc\n---\ncontent", []byte("---\nminc\n---\n"), true},
|
{"---\nminc\n---\ncontent", []byte("---\nminc\n---\n"), true},
|
||||||
{"---\ncnim\n---\ncontent\n", []byte("---\ncnim\n---\n"), true},
|
{"---\ncnim\n---\ncontent\n", []byte("---\ncnim\n---\n"), true},
|
||||||
{"---\ntitle: slug doc 2\nslug: slug-doc-2\n---\ncontent\n", []byte("---\ntitle: slug doc 2\nslug: slug-doc-2\n---\n"), true},
|
{"---\ntitle: slug doc 2\nslug: slug-doc-2\n---\ncontent\n", []byte("---\ntitle: slug doc 2\nslug: slug-doc-2\n---\n"), true},
|
||||||
|
@ -274,7 +274,6 @@ func TestExtractFrontMatterDelim(t *testing.T) {
|
||||||
{"", "", errExpected},
|
{"", "", errExpected},
|
||||||
{"{", "", errExpected},
|
{"{", "", errExpected},
|
||||||
{"{}", "{}", noErrExpected},
|
{"{}", "{}", noErrExpected},
|
||||||
{" {}", " {}", noErrExpected},
|
|
||||||
{"{} ", "{}", noErrExpected},
|
{"{} ", "{}", noErrExpected},
|
||||||
{"{ } ", "{ }", noErrExpected},
|
{"{ } ", "{ }", noErrExpected},
|
||||||
{"{ { }", "", errExpected},
|
{"{ { }", "", errExpected},
|
||||||
|
|
Loading…
Reference in a new issue