Comments
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
|
||||
extension HBMustacheLibrary {
|
||||
/// Load templates from a folder
|
||||
func loadTemplates(from directory: String, withExtension extension: String = "mustache") {
|
||||
var directory = directory
|
||||
if !directory.hasSuffix("/") {
|
||||
|
||||
@@ -1,23 +1,47 @@
|
||||
|
||||
/// Class holding a collection of mustache templates.
|
||||
///
|
||||
/// Each template can reference the others via a partial using the name the template is registered under
|
||||
/// ```
|
||||
/// {{#sequence}}{{>entry}}{{/sequence}}
|
||||
/// ```
|
||||
public class HBMustacheLibrary {
|
||||
/// Initialize empty library
|
||||
public init() {
|
||||
self.templates = [:]
|
||||
}
|
||||
|
||||
public init(directory: String) {
|
||||
/// Initialize library with contents of folder.
|
||||
///
|
||||
/// Each template is registered with the name of the file minus its extension. The search through
|
||||
/// the folder is recursive and templates in subfolders will be registered with the name `subfolder/template`.
|
||||
/// - Parameter directory: Directory to look for mustache templates
|
||||
/// - Parameter extension: Extension of files to look for
|
||||
public init(directory: String, withExtension extension: String = "mustache") {
|
||||
self.templates = [:]
|
||||
self.loadTemplates(from: directory)
|
||||
}
|
||||
|
||||
/// Register template under name
|
||||
/// - Parameters:
|
||||
/// - template: Template
|
||||
/// - name: Name of template
|
||||
public func register(_ template: HBMustacheTemplate, named name: String) {
|
||||
template.setLibrary(self)
|
||||
templates[name] = template
|
||||
}
|
||||
|
||||
/// Return template registed with name
|
||||
/// - Parameter name: name to search for
|
||||
/// - Returns: Template
|
||||
public func getTemplate(named name: String) -> HBMustacheTemplate? {
|
||||
templates[name]
|
||||
}
|
||||
|
||||
/// Render object using templated with name
|
||||
/// - Parameters:
|
||||
/// - object: Object to render
|
||||
/// - name: Name of template
|
||||
/// - Returns: Rendered text
|
||||
public func render(_ object: Any, withTemplateNamed name: String) -> String? {
|
||||
guard let template = templates[name] else { return nil }
|
||||
return template.render(object)
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
|
||||
|
||||
func unwrapOptional(_ object: Any) -> Any? {
|
||||
let mirror = Mirror(reflecting: object)
|
||||
guard mirror.displayStyle == .optional else { return object }
|
||||
guard let first = mirror.children.first else { return nil }
|
||||
return first.value
|
||||
}
|
||||
|
||||
extension Mirror {
|
||||
/// Return value from Mirror given name
|
||||
func getValue(forKey key: String) -> Any? {
|
||||
guard let matched = children.filter({ $0.label == key }).first else {
|
||||
return nil
|
||||
@@ -15,3 +9,12 @@ extension Mirror {
|
||||
return unwrapOptional(matched.value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return object and if it is an Optional return object Optional holds
|
||||
private func unwrapOptional(_ object: Any) -> Any? {
|
||||
let mirror = Mirror(reflecting: object)
|
||||
guard mirror.displayStyle == .optional else { return object }
|
||||
guard let first = mirror.children.first else { return nil }
|
||||
return first.value
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
|
||||
/// Protocol for object that has a custom method for accessing their children, instead
|
||||
/// of using Mirror
|
||||
protocol HBMustacheParent {
|
||||
func child(named: String) -> Any?
|
||||
}
|
||||
|
||||
extension HBMustacheParent {
|
||||
// default child to nil
|
||||
func child(named: String) -> Any? { return nil }
|
||||
}
|
||||
|
||||
/// Extend dictionary where the key is a string so that it uses the key values to access
|
||||
/// it values
|
||||
extension Dictionary: HBMustacheParent where Key == String {
|
||||
func child(named: String) -> Any? { return self[named] }
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Half inspired by Reader class from John Sundell's Ink project
|
||||
// https://github.com/JohnSundell/Ink/blob/master/Sources/Ink/Internal/Reader.swift
|
||||
// with optimisation working ie removing String and doing my own UTF8 processing inspired by Fabian Fett's work in
|
||||
// https://github.com/fabianfett/pure-swift-json/blob/master/Sources/PureSwiftJSONParsing/DocumentReader.swift
|
||||
// https://github.com/swift-extras/swift-extras-json/blob/main/Sources/ExtrasJSON/Parsing/DocumentReader.swift
|
||||
//
|
||||
// This is a copy of the parser from Hummingbird. I am not using the version in Hummingbird to avoid the dependency
|
||||
import Foundation
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
|
||||
protocol HBMustacheSequence {
|
||||
/// Protocol for objects that can be rendered as a sequence in Mustache
|
||||
public protocol HBMustacheSequence {
|
||||
/// Render section using template
|
||||
func renderSection(with template: HBMustacheTemplate) -> String
|
||||
/// Render inverted section using template
|
||||
func renderInvertedSection(with template: HBMustacheTemplate) -> String
|
||||
}
|
||||
|
||||
extension Sequence {
|
||||
func renderSection(with template: HBMustacheTemplate) -> String {
|
||||
/// Render section using template
|
||||
public func renderSection(with template: HBMustacheTemplate) -> String {
|
||||
var string = ""
|
||||
var context = HBMustacheContext(first: true)
|
||||
var iterator = self.makeIterator()
|
||||
@@ -24,7 +28,8 @@ extension Sequence {
|
||||
return string
|
||||
}
|
||||
|
||||
func renderInvertedSection(with template: HBMustacheTemplate) -> String {
|
||||
/// Render inverted section using template
|
||||
public func renderInvertedSection(with template: HBMustacheTemplate) -> String {
|
||||
var iterator = makeIterator()
|
||||
if iterator.next() == nil {
|
||||
return template.render(self)
|
||||
@@ -35,4 +40,5 @@ extension Sequence {
|
||||
}
|
||||
|
||||
extension Array: HBMustacheSequence {}
|
||||
extension Set: HBMustacheSequence {}
|
||||
extension ReversedCollection: HBMustacheSequence {}
|
||||
|
||||
@@ -6,11 +6,13 @@ extension HBMustacheTemplate {
|
||||
case expectedSectionEnd
|
||||
}
|
||||
|
||||
/// parse mustache text to generate a list of tokens
|
||||
static func parse(_ string: String) throws -> [Token] {
|
||||
var parser = HBParser(string)
|
||||
return try parse(&parser, sectionName: nil)
|
||||
}
|
||||
|
||||
/// parse section in mustache text
|
||||
static func parse(_ parser: inout HBParser, sectionName: String?) throws -> [Token] {
|
||||
var tokens: [Token] = []
|
||||
while !parser.reachedEnd() {
|
||||
@@ -78,6 +80,7 @@ extension HBMustacheTemplate {
|
||||
return tokens
|
||||
}
|
||||
|
||||
/// parse variable name
|
||||
static func parseName(_ parser: inout HBParser) throws -> (String, String?) {
|
||||
parser.read(while: \.isWhitespace)
|
||||
var text = parser.read(while: sectionNameChars )
|
||||
@@ -88,6 +91,7 @@ extension HBMustacheTemplate {
|
||||
if text.reachedEnd() {
|
||||
return (text.string, nil)
|
||||
} else {
|
||||
// parse function parameter, as we have just parsed a function name
|
||||
guard text.current() == "(" else { throw Error.unfinishedName }
|
||||
text.unsafeAdvance()
|
||||
let string2 = text.read(while: sectionNameCharsWithoutBrackets)
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
|
||||
/// Class holding Mustache template
|
||||
public class HBMustacheTemplate {
|
||||
/// Initialize template
|
||||
/// - Parameter string: Template text
|
||||
/// - Throws: HBMustacheTemplate.Error
|
||||
public init(string: String) throws {
|
||||
self.tokens = try Self.parse(string)
|
||||
}
|
||||
|
||||
internal init(_ tokens: [Token]) {
|
||||
self.tokens = tokens
|
||||
}
|
||||
|
||||
/// Render object using this template
|
||||
/// - Parameter object: Object to render
|
||||
/// - Returns: Rendered text
|
||||
public func render(_ object: Any) -> String {
|
||||
self.render(object, context: nil)
|
||||
}
|
||||
|
||||
internal init(_ tokens: [Token]) {
|
||||
self.tokens = tokens
|
||||
}
|
||||
|
||||
internal func setLibrary(_ library: HBMustacheLibrary) {
|
||||
self.library = library
|
||||
for token in tokens {
|
||||
|
||||
Reference in New Issue
Block a user