private string ParseMetadataHeaderFromContent(string content)
{
var startOfLine = 0;
var endOfLine = -1;
var preambleOpened = false;
var preambleSkipped = false;
while ((endOfLine = content.IndexOf("\n", startOfLine)) > 0)
{
var line = content.Substring(startOfLine, endOfLine - startOfLine + 1).TrimEnd();
// Eat any blank lines or comments at the top of the document or in the header.
//
if (String.IsNullOrEmpty(line) || line.StartsWith(";") || line.StartsWith("//"))
{
startOfLine = endOfLine + 1;
continue;
}
// If start or end of header.
if (line.StartsWith("---"))
{
startOfLine = endOfLine + 1;
if (preambleOpened || preambleSkipped)
{
// Eat any blank lines after the preamble.
while ((endOfLine = content.IndexOf("\n", startOfLine)) > 0)
{
line = content.Substring(startOfLine, endOfLine - startOfLine + 1).TrimEnd();
if (!String.IsNullOrEmpty(line))
{
break;
}
startOfLine = endOfLine + 1;
}
break;
}
preambleOpened = true;
}
else // try to parse for metadata.
{
var match = MetadataKeyValue.Match(line);
if (match.Success)
{
if (!preambleOpened)
{
preambleSkipped = true;
}
dynamic complexValue;
var key = match.Groups[1].Value.ToLowerInvariant();
var value = match.Groups[2].Value.Trim();
switch (key)
{
case "date":
this.Date = ParseDateTimeSmarter(value);
break;
case "draft":
case "ignore":
case "ignored":
this.Draft = ParseBoolen(value);
break;
case "tag":
case "tags":
var tags = ParseArray(value);
this.Metadata.Add("tags", tags);
break;
default:
if (TryParseComplexValue(value, content, startOfLine + match.Groups[2].Index, ref endOfLine, out complexValue))
{
this.Metadata.Add(key, complexValue);
}
else
{
this.HandleDefaultMetdata(key, value);
}
break;
}
}
else if (!preambleOpened) // no preamble and not metadata means we're done with the header.
{
break;
}
startOfLine = endOfLine + 1;
}
}
return content.Substring(startOfLine).TrimEnd();
}