69 lines
1.8 KiB
Ruby
69 lines
1.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "toml-rb"
|
|
|
|
module Volumen
|
|
# Parses and serialises the TOML frontmatter block of a Markdown file.
|
|
#
|
|
# A post file starts with a `+++` line, contains a TOML document, is closed
|
|
# by another `+++` line, and is followed by the Markdown body:
|
|
#
|
|
# +++
|
|
# title = "Hello"
|
|
# +++
|
|
#
|
|
# Body in **Markdown**.
|
|
module Frontmatter
|
|
DELIMITER = "+++"
|
|
|
|
# Returns `[metadata_hash, body_string]`. If the content has no
|
|
# frontmatter, the metadata hash is empty and the body is the full input.
|
|
def self.parse(content)
|
|
text = normalize(content)
|
|
lines = text.lines
|
|
return [{}, text] unless delimiter?(lines.first)
|
|
|
|
closing = closing_index(lines)
|
|
return [{}, text] if closing.nil?
|
|
|
|
toml = lines[1...closing].join
|
|
body = lines[(closing + 1)..]&.join.to_s
|
|
metadata = toml.strip.empty? ? {} : TomlRB.parse(toml)
|
|
[metadata, body.sub(/\A\r?\n/, "")]
|
|
end
|
|
|
|
# Serialises metadata + body back into a single post file string.
|
|
def self.dump(metadata, body)
|
|
toml = serialize(metadata)
|
|
buffer = "#{DELIMITER}\n"
|
|
buffer << toml
|
|
buffer << "\n" unless toml.empty? || toml.end_with?("\n")
|
|
buffer << "#{DELIMITER}\n\n"
|
|
buffer << body.to_s.sub(/\A\s+/, "")
|
|
buffer.end_with?("\n") ? buffer : "#{buffer}\n"
|
|
end
|
|
|
|
def self.normalize(content)
|
|
content.to_s.gsub("\r\n", "\n")
|
|
end
|
|
|
|
def self.closing_index(lines)
|
|
index = lines[1..].index { |line| delimiter?(line) }
|
|
index.nil? ? nil : index + 1
|
|
end
|
|
|
|
def self.delimiter?(line)
|
|
line&.chomp&.strip == DELIMITER
|
|
end
|
|
|
|
def self.serialize(metadata)
|
|
data = metadata || {}
|
|
return "" if data.empty?
|
|
|
|
TomlRB.dump(data)
|
|
end
|
|
|
|
private_class_method :normalize, :closing_index, :delimiter?, :serialize
|
|
end
|
|
end
|