2.x.x Template struct (#22)
* Start of turning template into a struct * Everything is Sendable now, just doesnt work * Add library to context * Make sure render is initialized with library * comment about inheritance spec * Add register back in * Re-instate register functions * Re-instate commented out print * Fix tabbing in Partial tests * Make HBMustacheLibrary.loadTemplates async * Update platforms, swift version
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Protocol for content types
|
||||
public protocol HBMustacheContentType {
|
||||
public protocol HBMustacheContentType: Sendable {
|
||||
/// escape text for this content type eg for HTML replace "<" with "<"
|
||||
func escapeText(_ text: String) -> String
|
||||
}
|
||||
|
||||
@@ -19,14 +19,16 @@ struct HBMustacheContext {
|
||||
let indentation: String?
|
||||
let inherited: [String: HBMustacheTemplate]?
|
||||
let contentType: HBMustacheContentType
|
||||
let library: HBMustacheLibrary?
|
||||
|
||||
/// initialize context with a single objectt
|
||||
init(_ object: Any) {
|
||||
init(_ object: Any, library: HBMustacheLibrary? = nil) {
|
||||
self.stack = [object]
|
||||
self.sequenceContext = nil
|
||||
self.indentation = nil
|
||||
self.inherited = nil
|
||||
self.contentType = HBHTMLContentType()
|
||||
self.library = library
|
||||
}
|
||||
|
||||
private init(
|
||||
@@ -34,13 +36,15 @@ struct HBMustacheContext {
|
||||
sequenceContext: HBMustacheSequenceContext?,
|
||||
indentation: String?,
|
||||
inherited: [String: HBMustacheTemplate]?,
|
||||
contentType: HBMustacheContentType
|
||||
contentType: HBMustacheContentType,
|
||||
library: HBMustacheLibrary? = nil
|
||||
) {
|
||||
self.stack = stack
|
||||
self.sequenceContext = sequenceContext
|
||||
self.indentation = indentation
|
||||
self.inherited = inherited
|
||||
self.contentType = contentType
|
||||
self.library = library
|
||||
}
|
||||
|
||||
/// return context with object add to stack
|
||||
@@ -52,7 +56,8 @@ struct HBMustacheContext {
|
||||
sequenceContext: nil,
|
||||
indentation: self.indentation,
|
||||
inherited: self.inherited,
|
||||
contentType: self.contentType
|
||||
contentType: self.contentType,
|
||||
library: self.library
|
||||
)
|
||||
}
|
||||
|
||||
@@ -79,7 +84,8 @@ struct HBMustacheContext {
|
||||
sequenceContext: nil,
|
||||
indentation: indentation,
|
||||
inherited: inherits,
|
||||
contentType: HBHTMLContentType()
|
||||
contentType: HBHTMLContentType(),
|
||||
library: self.library
|
||||
)
|
||||
}
|
||||
|
||||
@@ -92,7 +98,8 @@ struct HBMustacheContext {
|
||||
sequenceContext: sequenceContext,
|
||||
indentation: self.indentation,
|
||||
inherited: self.inherited,
|
||||
contentType: self.contentType
|
||||
contentType: self.contentType,
|
||||
library: self.library
|
||||
)
|
||||
}
|
||||
|
||||
@@ -103,7 +110,8 @@ struct HBMustacheContext {
|
||||
sequenceContext: self.sequenceContext,
|
||||
indentation: self.indentation,
|
||||
inherited: self.inherited,
|
||||
contentType: contentType
|
||||
contentType: contentType,
|
||||
library: self.library
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,19 +16,20 @@ import Foundation
|
||||
|
||||
extension HBMustacheLibrary {
|
||||
/// Load templates from a folder
|
||||
func loadTemplates(from directory: String, withExtension extension: String = "mustache") throws {
|
||||
static func loadTemplates(from directory: String, withExtension extension: String = "mustache") async throws -> [String: HBMustacheTemplate] {
|
||||
var directory = directory
|
||||
if !directory.hasSuffix("/") {
|
||||
directory += "/"
|
||||
}
|
||||
let extWithDot = ".\(`extension`)"
|
||||
let fs = FileManager()
|
||||
guard let enumerator = fs.enumerator(atPath: directory) else { return }
|
||||
guard let enumerator = fs.enumerator(atPath: directory) else { return [:] }
|
||||
var templates: [String: HBMustacheTemplate] = [:]
|
||||
for case let path as String in enumerator {
|
||||
guard path.hasSuffix(extWithDot) else { continue }
|
||||
guard let data = fs.contents(atPath: directory + path) else { continue }
|
||||
let string = String(decoding: data, as: Unicode.UTF8.self)
|
||||
let template: HBMustacheTemplate
|
||||
var template: HBMustacheTemplate
|
||||
do {
|
||||
template = try HBMustacheTemplate(string: string)
|
||||
} catch let error as HBMustacheTemplate.ParserError {
|
||||
@@ -36,7 +37,8 @@ extension HBMustacheLibrary {
|
||||
}
|
||||
// drop ".mustache" from path to get name
|
||||
let name = String(path.dropLast(extWithDot.count))
|
||||
register(template, named: name)
|
||||
templates[name] = template
|
||||
}
|
||||
return templates
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
/// ```
|
||||
/// {{#sequence}}{{>entry}}{{/sequence}}
|
||||
/// ```
|
||||
public final class HBMustacheLibrary {
|
||||
public struct HBMustacheLibrary: Sendable {
|
||||
/// Initialize empty library
|
||||
public init() {
|
||||
self.templates = [:]
|
||||
@@ -30,17 +30,25 @@ public final class HBMustacheLibrary {
|
||||
/// 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") throws {
|
||||
self.templates = [:]
|
||||
try loadTemplates(from: directory, withExtension: `extension`)
|
||||
public init(templates: [String: HBMustacheTemplate]) {
|
||||
self.templates = templates
|
||||
}
|
||||
|
||||
/// 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") async throws {
|
||||
self.templates = try await Self.loadTemplates(from: directory, withExtension: `extension`)
|
||||
}
|
||||
|
||||
/// Register template under name
|
||||
/// - Parameters:
|
||||
/// - template: Template
|
||||
/// - name: Name of template
|
||||
public func register(_ template: HBMustacheTemplate, named name: String) {
|
||||
template.setLibrary(self)
|
||||
public mutating func register(_ template: HBMustacheTemplate, named name: String) {
|
||||
self.templates[name] = template
|
||||
}
|
||||
|
||||
@@ -48,9 +56,8 @@ public final class HBMustacheLibrary {
|
||||
/// - Parameters:
|
||||
/// - mustache: Mustache text
|
||||
/// - name: Name of template
|
||||
public func register(_ mustache: String, named name: String) throws {
|
||||
public mutating func register(_ mustache: String, named name: String) throws {
|
||||
let template = try HBMustacheTemplate(string: mustache)
|
||||
template.setLibrary(self)
|
||||
self.templates[name] = template
|
||||
}
|
||||
|
||||
@@ -68,7 +75,7 @@ public final class HBMustacheLibrary {
|
||||
/// - Returns: Rendered text
|
||||
public func render(_ object: Any, withTemplate name: String) -> String? {
|
||||
guard let template = templates[name] else { return nil }
|
||||
return template.render(object)
|
||||
return template.render(object, library: self)
|
||||
}
|
||||
|
||||
/// Error returned by init() when parser fails
|
||||
|
||||
@@ -80,7 +80,7 @@ extension HBMustacheTemplate {
|
||||
}
|
||||
|
||||
case .partial(let name, let indentation, let overrides):
|
||||
if let template = library?.getTemplate(named: name) {
|
||||
if let template = context.library?.getTemplate(named: name) {
|
||||
return template.render(context: context.withPartial(indented: indentation, inheriting: overrides))
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Class holding Mustache template
|
||||
public final class HBMustacheTemplate {
|
||||
public struct HBMustacheTemplate: Sendable {
|
||||
/// Initialize template
|
||||
/// - Parameter string: Template text
|
||||
/// - Throws: HBMustacheTemplate.Error
|
||||
@@ -24,29 +24,15 @@ public final class HBMustacheTemplate {
|
||||
/// Render object using this template
|
||||
/// - Parameter object: Object to render
|
||||
/// - Returns: Rendered text
|
||||
public func render(_ object: Any) -> String {
|
||||
self.render(context: .init(object))
|
||||
public func render(_ object: Any, library: HBMustacheLibrary? = nil) -> String {
|
||||
self.render(context: .init(object, library: library))
|
||||
}
|
||||
|
||||
internal init(_ tokens: [Token]) {
|
||||
self.tokens = tokens
|
||||
}
|
||||
|
||||
internal func setLibrary(_ library: HBMustacheLibrary) {
|
||||
self.library = library
|
||||
for token in self.tokens {
|
||||
switch token {
|
||||
case .section(_, _, let template), .invertedSection(_, _, let template), .inheritedSection(_, let template):
|
||||
template.setLibrary(library)
|
||||
case .partial(_, _, let templates):
|
||||
templates?.forEach { $1.setLibrary(library) }
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Token {
|
||||
enum Token: Sendable {
|
||||
case text(String)
|
||||
case variable(name: String, transform: String? = nil)
|
||||
case unescapedVariable(name: String, transform: String? = nil)
|
||||
@@ -57,6 +43,5 @@ public final class HBMustacheTemplate {
|
||||
case contentType(HBMustacheContentType)
|
||||
}
|
||||
|
||||
let tokens: [Token]
|
||||
var library: HBMustacheLibrary?
|
||||
var tokens: [Token]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user