Remove HB prefix, make Parser private (#26)

This commit is contained in:
Adam Fowler
2024-03-11 22:07:05 +00:00
committed by GitHub
parent f029081b61
commit bdfa05391a
25 changed files with 300 additions and 262 deletions

View File

@@ -8,7 +8,7 @@ While Hummingbird Mustache has been designed to be used with the Hummingbird ser
Load your templates from the filesystem Load your templates from the filesystem
```swift ```swift
let library = HBMustacheLibrary("folder/my/templates/are/in") let library = MustacheLibrary("folder/my/templates/are/in")
``` ```
This will look for all the files with the extension ".mustache" in the specified folder and subfolders and attempt to load them. Each file is registed with the name of the file (with subfolder, if inside a subfolder) minus the "mustache" extension. This will look for all the files with the extension ".mustache" in the specified folder and subfolders and attempt to load them. Each file is registed with the name of the file (with subfolder, if inside a subfolder) minus the "mustache" extension.
@@ -20,23 +20,23 @@ let output = library.render(object, withTemplate: "myTemplate")
### Using with Hummingbird ### Using with Hummingbird
HummingbirdMustache doesn't have any integration with Hummingbird as I wanted to keep the library dependency free. But if you are going to use the library with Hummingbird it is recommended you extend `HBApplication` to store an instance of your library. HummingbirdMustache doesn't have any integration with Hummingbird as I wanted to keep the library dependency free. But if you are going to use the library with Hummingbird it is recommended you extend `Application` to store an instance of your library.
```swift ```swift
extension HBApplication { extension Application {
var mustache: HBMustacheLibrary { var mustache: MustacheLibrary {
get { self.extensions.get(\.mustache) } get { self.extensions.get(\.mustache) }
set { self.extensions.set(\.mustache, value: newValue) } set { self.extensions.set(\.mustache, value: newValue) }
} }
} }
extension HBRequest { extension Request {
var mustache: HBMustacheLibrary { self.application.mustache } var mustache: MustacheLibrary { self.application.mustache }
} }
// load mustache templates from templates folder // load mustache templates from templates folder
application.mustache = try .init(directory: "templates") application.mustache = try .init(directory: "templates")
``` ```
You can now access your mustache templates via `HBRequest` eg `HBRequest.mustache.render(obj, withTemplate: "myTemplate")` You can now access your mustache templates via `Request` eg `Request.mustache.render(obj, withTemplate: "myTemplate")`
## Support ## Support

View File

@@ -13,20 +13,20 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// Protocol for content types /// Protocol for content types
public protocol HBMustacheContentType: Sendable { public protocol MustacheContentType: Sendable {
/// escape text for this content type eg for HTML replace "<" with "&lt;" /// escape text for this content type eg for HTML replace "<" with "&lt;"
func escapeText(_ text: String) -> String func escapeText(_ text: String) -> String
} }
/// Text content type where no character is escaped /// Text content type where no character is escaped
struct HBTextContentType: HBMustacheContentType { struct TextContentType: MustacheContentType {
func escapeText(_ text: String) -> String { func escapeText(_ text: String) -> String {
return text return text
} }
} }
/// HTML content where text is escaped for HTML output /// HTML content where text is escaped for HTML output
struct HBHTMLContentType: HBMustacheContentType { struct HTMLContentType: MustacheContentType {
func escapeText(_ text: String) -> String { func escapeText(_ text: String) -> String {
return text.htmlEscape() return text.htmlEscape()
} }
@@ -36,9 +36,9 @@ struct HBHTMLContentType: HBMustacheContentType {
/// ///
/// The string is read from the "CONTENT_TYPE" pragma `{{% CONTENT_TYPE: type}}`. Replace type with /// The string is read from the "CONTENT_TYPE" pragma `{{% CONTENT_TYPE: type}}`. Replace type with
/// the content type required. The default available types are `TEXT` and `HTML`. You can register your own /// the content type required. The default available types are `TEXT` and `HTML`. You can register your own
/// with `HBMustacheContentTypes.register`. /// with `MustacheContentTypes.register`.
public enum HBMustacheContentTypes { public enum MustacheContentTypes {
static func get(_ name: String) -> HBMustacheContentType? { static func get(_ name: String) -> MustacheContentType? {
return self.types[name] return self.types[name]
} }
@@ -46,12 +46,12 @@ public enum HBMustacheContentTypes {
/// - Parameters: /// - Parameters:
/// - contentType: Content type /// - contentType: Content type
/// - name: String to identify it /// - name: String to identify it
public static func register(_ contentType: HBMustacheContentType, named name: String) { public static func register(_ contentType: MustacheContentType, named name: String) {
self.types[name] = contentType self.types[name] = contentType
} }
static var types: [String: HBMustacheContentType] = [ static var types: [String: MustacheContentType] = [
"HTML": HBHTMLContentType(), "HTML": HTMLContentType(),
"TEXT": HBTextContentType(), "TEXT": TextContentType(),
] ]
} }

View File

@@ -13,31 +13,31 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// Context while rendering mustache tokens /// Context while rendering mustache tokens
struct HBMustacheContext { struct MustacheContext {
let stack: [Any] let stack: [Any]
let sequenceContext: HBMustacheSequenceContext? let sequenceContext: MustacheSequenceContext?
let indentation: String? let indentation: String?
let inherited: [String: HBMustacheTemplate]? let inherited: [String: MustacheTemplate]?
let contentType: HBMustacheContentType let contentType: MustacheContentType
let library: HBMustacheLibrary? let library: MustacheLibrary?
/// initialize context with a single objectt /// initialize context with a single objectt
init(_ object: Any, library: HBMustacheLibrary? = nil) { init(_ object: Any, library: MustacheLibrary? = nil) {
self.stack = [object] self.stack = [object]
self.sequenceContext = nil self.sequenceContext = nil
self.indentation = nil self.indentation = nil
self.inherited = nil self.inherited = nil
self.contentType = HBHTMLContentType() self.contentType = HTMLContentType()
self.library = library self.library = library
} }
private init( private init(
stack: [Any], stack: [Any],
sequenceContext: HBMustacheSequenceContext?, sequenceContext: MustacheSequenceContext?,
indentation: String?, indentation: String?,
inherited: [String: HBMustacheTemplate]?, inherited: [String: MustacheTemplate]?,
contentType: HBMustacheContentType, contentType: MustacheContentType,
library: HBMustacheLibrary? = nil library: MustacheLibrary? = nil
) { ) {
self.stack = stack self.stack = stack
self.sequenceContext = sequenceContext self.sequenceContext = sequenceContext
@@ -48,7 +48,7 @@ struct HBMustacheContext {
} }
/// return context with object add to stack /// return context with object add to stack
func withObject(_ object: Any) -> HBMustacheContext { func withObject(_ object: Any) -> MustacheContext {
var stack = self.stack var stack = self.stack
stack.append(object) stack.append(object)
return .init( return .init(
@@ -62,13 +62,13 @@ struct HBMustacheContext {
} }
/// return context with indent and parameter information for invoking a partial /// return context with indent and parameter information for invoking a partial
func withPartial(indented: String?, inheriting: [String: HBMustacheTemplate]?) -> HBMustacheContext { func withPartial(indented: String?, inheriting: [String: MustacheTemplate]?) -> MustacheContext {
let indentation: String? = if let indented { let indentation: String? = if let indented {
(self.indentation ?? "") + indented (self.indentation ?? "") + indented
} else { } else {
self.indentation self.indentation
} }
let inherits: [String: HBMustacheTemplate]? = if let inheriting { let inherits: [String: MustacheTemplate]? = if let inheriting {
if let originalInherits = self.inherited { if let originalInherits = self.inherited {
originalInherits.merging(inheriting) { value, _ in value } originalInherits.merging(inheriting) { value, _ in value }
} else { } else {
@@ -82,13 +82,13 @@ struct HBMustacheContext {
sequenceContext: nil, sequenceContext: nil,
indentation: indentation, indentation: indentation,
inherited: inherits, inherited: inherits,
contentType: HBHTMLContentType(), contentType: HTMLContentType(),
library: self.library library: self.library
) )
} }
/// return context with sequence info and sequence element added to stack /// return context with sequence info and sequence element added to stack
func withSequence(_ object: Any, sequenceContext: HBMustacheSequenceContext) -> HBMustacheContext { func withSequence(_ object: Any, sequenceContext: MustacheSequenceContext) -> MustacheContext {
var stack = self.stack var stack = self.stack
stack.append(object) stack.append(object)
return .init( return .init(
@@ -102,7 +102,7 @@ struct HBMustacheContext {
} }
/// return context with sequence info and sequence element added to stack /// return context with sequence info and sequence element added to stack
func withContentType(_ contentType: HBMustacheContentType) -> HBMustacheContext { func withContentType(_ contentType: MustacheContentType) -> MustacheContext {
return .init( return .init(
stack: self.stack, stack: self.stack,
sequenceContext: self.sequenceContext, sequenceContext: self.sequenceContext,

View File

@@ -16,23 +16,23 @@ import Foundation
/// Allow object to override standard hummingbird type rendering which uses /// Allow object to override standard hummingbird type rendering which uses
/// `String(describing)`. /// `String(describing)`.
public protocol HBMustacheCustomRenderable { public protocol MustacheCustomRenderable {
/// Custom rendered version of object /// Custom rendered version of object
var renderText: String { get } var renderText: String { get }
/// Whether the object is a null object. Used when scoping sections /// Whether the object is a null object. Used when scoping sections
var isNull: Bool { get } var isNull: Bool { get }
} }
extension HBMustacheCustomRenderable { extension MustacheCustomRenderable {
/// default version returning the standard rendering /// default version returning the standard rendering
var renderText: String { String(describing: self) } var renderText: String { String(describing: self) }
/// default version returning false /// default version returning false
var isNull: Bool { false } var isNull: Bool { false }
} }
/// Extend NSNull to conform to `HBMustacheCustomRenderable` to avoid outputting `<null>` and returning /// Extend NSNull to conform to `MustacheCustomRenderable` to avoid outputting `<null>` and returning
/// a valid response for `isNull` /// a valid response for `isNull`
extension NSNull: HBMustacheCustomRenderable { extension NSNull: MustacheCustomRenderable {
public var renderText: String { "" } public var renderText: String { "" }
public var isNull: Bool { true } public var isNull: Bool { true }
} }

View File

@@ -0,0 +1,38 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Hummingbird server framework project
//
// Copyright (c) 2024 the Hummingbird authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// Below is a list of deprecated symbols with the "HB" prefix. These are available
// temporarily to ease transition from the old symbols that included the "HB"
// prefix to the new ones.
//
// This file will be removed before we do a 2.0 release
@_documentation(visibility: internal) @available(*, deprecated, renamed: "MustacheContentType")
public typealias HBMustacheContentType = MustacheContentType
@_documentation(visibility: internal) @available(*, deprecated, renamed: "MustacheContentTypes")
public typealias HBMustacheContentTypes = MustacheContentTypes
@_documentation(visibility: internal) @available(*, deprecated, renamed: "MustacheCustomRenderable")
public typealias HBMustacheCustomRenderable = MustacheCustomRenderable
@_documentation(visibility: internal) @available(*, deprecated, renamed: "MustacheLambda")
public typealias HBMustacheLambda = MustacheLambda
@_documentation(visibility: internal) @available(*, deprecated, renamed: "MustacheLibrary")
public typealias HBMustacheLibrary = MustacheLibrary
@_documentation(visibility: internal) @available(*, deprecated, renamed: "MustacheParent")
public typealias HBMustacheParent = MustacheParent
@_documentation(visibility: internal) @available(*, deprecated, renamed: "MustacheParserContext")
public typealias HBMustacheParserContext = MustacheParserContext
@_documentation(visibility: internal) @available(*, deprecated, renamed: "MustacheTemplate")
public typealias HBMustacheTemplate = MustacheTemplate
@_documentation(visibility: internal) @available(*, deprecated, renamed: "MustacheTransformable")
public typealias HBMustacheTransformable = MustacheTransformable

View File

@@ -21,30 +21,30 @@
/// ``` /// ```
/// struct Object { /// struct Object {
/// let name: String /// let name: String
/// let wrapped: HBMustacheLambda /// let wrapped: MustacheLambda
/// } /// }
/// let willy = Object(name: "Willy", wrapped: .init({ object, template in /// let willy = Object(name: "Willy", wrapped: .init({ object, template in
/// return "<b>\(template.render(object))</b>" /// return "<b>\(template.render(object))</b>"
/// })) /// }))
/// let mustache = "{{#wrapped}}{{name}} is awesome.{{/wrapped}}" /// let mustache = "{{#wrapped}}{{name}} is awesome.{{/wrapped}}"
/// let template = try HBMustacheTemplate(string: mustache) /// let template = try MustacheTemplate(string: mustache)
/// let output = template.render(willy) /// let output = template.render(willy)
/// print(output) // <b>Willy is awesome</b> /// print(output) // <b>Willy is awesome</b>
/// ``` /// ```
/// ///
public struct HBMustacheLambda { public struct MustacheLambda {
/// lambda callback /// lambda callback
public typealias Callback = (Any, HBMustacheTemplate) -> String public typealias Callback = (Any, MustacheTemplate) -> String
let callback: Callback let callback: Callback
/// Initialize `HBMustacheLambda` /// Initialize `MustacheLambda`
/// - Parameter cb: function to be called by lambda /// - Parameter cb: function to be called by lambda
public init(_ cb: @escaping Callback) { public init(_ cb: @escaping Callback) {
self.callback = cb self.callback = cb
} }
internal func run(_ object: Any, _ template: HBMustacheTemplate) -> String { internal func run(_ object: Any, _ template: MustacheTemplate) -> String {
return self.callback(object, template) return self.callback(object, template)
} }
} }

View File

@@ -14,9 +14,9 @@
import Foundation import Foundation
extension HBMustacheLibrary { extension MustacheLibrary {
/// Load templates from a folder /// Load templates from a folder
static func loadTemplates(from directory: String, withExtension extension: String = "mustache") async throws -> [String: HBMustacheTemplate] { static func loadTemplates(from directory: String, withExtension extension: String = "mustache") async throws -> [String: MustacheTemplate] {
var directory = directory var directory = directory
if !directory.hasSuffix("/") { if !directory.hasSuffix("/") {
directory += "/" directory += "/"
@@ -24,15 +24,15 @@ extension HBMustacheLibrary {
let extWithDot = ".\(`extension`)" let extWithDot = ".\(`extension`)"
let fs = FileManager() 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] = [:] var templates: [String: MustacheTemplate] = [:]
for case let path as String in enumerator { for case let path as String in enumerator {
guard path.hasSuffix(extWithDot) else { continue } guard path.hasSuffix(extWithDot) else { continue }
guard let data = fs.contents(atPath: directory + path) else { continue } guard let data = fs.contents(atPath: directory + path) else { continue }
let string = String(decoding: data, as: Unicode.UTF8.self) let string = String(decoding: data, as: Unicode.UTF8.self)
var template: HBMustacheTemplate var template: MustacheTemplate
do { do {
template = try HBMustacheTemplate(string: string) template = try MustacheTemplate(string: string)
} catch let error as HBMustacheTemplate.ParserError { } catch let error as MustacheTemplate.ParserError {
throw ParserError(filename: path, context: error.context, error: error.error) throw ParserError(filename: path, context: error.context, error: error.error)
} }
// drop ".mustache" from path to get name // drop ".mustache" from path to get name

View File

@@ -18,7 +18,7 @@
/// ``` /// ```
/// {{#sequence}}{{>entry}}{{/sequence}} /// {{#sequence}}{{>entry}}{{/sequence}}
/// ``` /// ```
public struct HBMustacheLibrary: Sendable { public struct MustacheLibrary: Sendable {
/// Initialize empty library /// Initialize empty library
public init() { public init() {
self.templates = [:] self.templates = [:]
@@ -30,7 +30,7 @@ public struct HBMustacheLibrary: Sendable {
/// the folder is recursive and templates in subfolders will be registered with the name `subfolder/template`. /// 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 directory: Directory to look for mustache templates
/// - Parameter extension: Extension of files to look for /// - Parameter extension: Extension of files to look for
public init(templates: [String: HBMustacheTemplate]) { public init(templates: [String: MustacheTemplate]) {
self.templates = templates self.templates = templates
} }
@@ -48,7 +48,7 @@ public struct HBMustacheLibrary: Sendable {
/// - Parameters: /// - Parameters:
/// - template: Template /// - template: Template
/// - name: Name of template /// - name: Name of template
public mutating func register(_ template: HBMustacheTemplate, named name: String) { public mutating func register(_ template: MustacheTemplate, named name: String) {
self.templates[name] = template self.templates[name] = template
} }
@@ -57,14 +57,14 @@ public struct HBMustacheLibrary: Sendable {
/// - mustache: Mustache text /// - mustache: Mustache text
/// - name: Name of template /// - name: Name of template
public mutating func register(_ mustache: String, named name: String) throws { public mutating func register(_ mustache: String, named name: String) throws {
let template = try HBMustacheTemplate(string: mustache) let template = try MustacheTemplate(string: mustache)
self.templates[name] = template self.templates[name] = template
} }
/// Return template registed with name /// Return template registed with name
/// - Parameter name: name to search for /// - Parameter name: name to search for
/// - Returns: Template /// - Returns: Template
public func getTemplate(named name: String) -> HBMustacheTemplate? { public func getTemplate(named name: String) -> MustacheTemplate? {
self.templates[name] self.templates[name]
} }
@@ -83,10 +83,10 @@ public struct HBMustacheLibrary: Sendable {
/// File error occurred in /// File error occurred in
public let filename: String public let filename: String
/// Context (line, linenumber and column number) /// Context (line, linenumber and column number)
public let context: HBParser.Context public let context: MustacheParserContext
/// Actual error that occurred /// Actual error that occurred
public let error: Error public let error: Error
} }
private var templates: [String: HBMustacheTemplate] private var templates: [String: MustacheTemplate]
} }

View File

@@ -14,12 +14,12 @@
/// Protocol for object that has a custom method for accessing their children, instead /// Protocol for object that has a custom method for accessing their children, instead
/// of using Mirror /// of using Mirror
public protocol HBMustacheParent { public protocol MustacheParent {
func child(named: String) -> Any? func child(named: String) -> Any?
} }
/// Extend dictionary where the key is a string so that it uses the key values to access /// Extend dictionary where the key is a string so that it uses the key values to access
/// it values /// it values
extension Dictionary: HBMustacheParent where Key == String { extension Dictionary: MustacheParent where Key == String {
public func child(named: String) -> Any? { return self[named] } public func child(named: String) -> Any? { return self[named] }
} }

View File

@@ -15,7 +15,7 @@
import Foundation import Foundation
/// Reader object for parsing String buffers /// Reader object for parsing String buffers
public struct HBParser { struct Parser {
enum Error: Swift.Error { enum Error: Swift.Error {
case overflow case overflow
} }
@@ -42,12 +42,12 @@ public struct HBParser {
private(set) var position: String.Index private(set) var position: String.Index
} }
extension HBParser { extension Parser {
/// Return current character /// Return current character
/// - Throws: .overflow /// - Throws: .overflow
/// - Returns: Current character /// - Returns: Current character
mutating func character() throws -> Character { mutating func character() throws -> Character {
guard !self.reachedEnd() else { throw HBParser.Error.overflow } guard !self.reachedEnd() else { throw Parser.Error.overflow }
let c = unsafeCurrent() let c = unsafeCurrent()
unsafeAdvance() unsafeAdvance()
return c return c
@@ -93,7 +93,7 @@ extension HBParser {
/// - Throws: .overflow /// - Throws: .overflow
/// - Returns: The string read from the buffer /// - Returns: The string read from the buffer
mutating func read(count: Int) throws -> Substring { mutating func read(count: Int) throws -> Substring {
guard self.buffer.distance(from: self.position, to: self.buffer.endIndex) >= count else { throw HBParser.Error.overflow } guard self.buffer.distance(from: self.position, to: self.buffer.endIndex) >= count else { throw Parser.Error.overflow }
let end = self.buffer.index(self.position, offsetBy: count) let end = self.buffer.index(self.position, offsetBy: count)
let subString = self.buffer[self.position..<end] let subString = self.buffer[self.position..<end]
unsafeAdvance(by: count) unsafeAdvance(by: count)
@@ -114,7 +114,7 @@ extension HBParser {
} }
if throwOnOverflow { if throwOnOverflow {
unsafeSetPosition(startIndex) unsafeSetPosition(startIndex)
throw HBParser.Error.overflow throw Parser.Error.overflow
} }
return self.buffer[startIndex..<self.position] return self.buffer[startIndex..<self.position]
} }
@@ -170,7 +170,7 @@ extension HBParser {
} }
if throwOnOverflow { if throwOnOverflow {
unsafeSetPosition(startIndex) unsafeSetPosition(startIndex)
throw HBParser.Error.overflow throw Parser.Error.overflow
} }
return self.buffer[startIndex..<self.position] return self.buffer[startIndex..<self.position]
} }
@@ -287,16 +287,16 @@ extension HBParser {
} }
} }
extension HBParser { /// context used in parser error
/// context used in parser error public struct MustacheParserContext {
public struct Context { public let line: String
public let line: String public let lineNumber: Int
public let lineNumber: Int public let columnNumber: Int
public let columnNumber: Int }
}
extension Parser {
/// Return context of current position (line, lineNumber, columnNumber) /// Return context of current position (line, lineNumber, columnNumber)
func getContext() -> Context { func getContext() -> MustacheParserContext {
var parser = self var parser = self
var columnNumber = 0 var columnNumber = 0
while !parser.atStart() { while !parser.atStart() {
@@ -316,12 +316,12 @@ extension HBParser {
let textBefore = buffer[buffer.startIndex..<self.position] let textBefore = buffer[buffer.startIndex..<self.position]
let lineNumber = textBefore.filter(\.isNewline).count let lineNumber = textBefore.filter(\.isNewline).count
return Context(line: String(line), lineNumber: lineNumber + 1, columnNumber: columnNumber + 1) return MustacheParserContext(line: String(line), lineNumber: lineNumber + 1, columnNumber: columnNumber + 1)
} }
} }
/// Public versions of internal functions which include tests for overflow /// versions of internal functions which include tests for overflow
extension HBParser { extension Parser {
/// Return the character at the current position /// Return the character at the current position
/// - Throws: .overflow /// - Throws: .overflow
/// - Returns: Character /// - Returns: Character
@@ -333,14 +333,14 @@ extension HBParser {
/// Move forward one character /// Move forward one character
/// - Throws: .overflow /// - Throws: .overflow
mutating func advance() throws { mutating func advance() throws {
guard !self.reachedEnd() else { throw HBParser.Error.overflow } guard !self.reachedEnd() else { throw Parser.Error.overflow }
return unsafeAdvance() return unsafeAdvance()
} }
/// Move back one character /// Move back one character
/// - Throws: .overflow /// - Throws: .overflow
mutating func retreat() throws { mutating func retreat() throws {
guard self.position != self.buffer.startIndex else { throw HBParser.Error.overflow } guard self.position != self.buffer.startIndex else { throw Parser.Error.overflow }
return unsafeRetreat() return unsafeRetreat()
} }
@@ -348,7 +348,7 @@ extension HBParser {
/// - Parameter amount: number of characters to move forward /// - Parameter amount: number of characters to move forward
/// - Throws: .overflow /// - Throws: .overflow
mutating func advance(by amount: Int) throws { mutating func advance(by amount: Int) throws {
guard self.buffer.distance(from: self.position, to: self.buffer.endIndex) >= amount else { throw HBParser.Error.overflow } guard self.buffer.distance(from: self.position, to: self.buffer.endIndex) >= amount else { throw Parser.Error.overflow }
return unsafeAdvance(by: amount) return unsafeAdvance(by: amount)
} }
@@ -356,18 +356,18 @@ extension HBParser {
/// - Parameter amount: number of characters to move back /// - Parameter amount: number of characters to move back
/// - Throws: .overflow /// - Throws: .overflow
mutating func retreat(by amount: Int) throws { mutating func retreat(by amount: Int) throws {
guard self.buffer.distance(from: self.buffer.startIndex, to: self.position) >= amount else { throw HBParser.Error.overflow } guard self.buffer.distance(from: self.buffer.startIndex, to: self.position) >= amount else { throw Parser.Error.overflow }
return unsafeRetreat(by: amount) return unsafeRetreat(by: amount)
} }
mutating func setPosition(_ position: String.Index) throws { mutating func setPosition(_ position: String.Index) throws {
guard position <= self.buffer.endIndex else { throw HBParser.Error.overflow } guard position <= self.buffer.endIndex else { throw Parser.Error.overflow }
unsafeSetPosition(position) unsafeSetPosition(position)
} }
} }
// unsafe versions without checks // unsafe versions without checks
extension HBParser { extension Parser {
func unsafeCurrent() -> Character { func unsafeCurrent() -> Character {
return self.buffer[self.position] return self.buffer[self.position]
} }

View File

@@ -13,18 +13,18 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// Protocol for objects that can be rendered as a sequence in Mustache /// Protocol for objects that can be rendered as a sequence in Mustache
protocol HBMustacheSequence { protocol MustacheSequence {
/// Render section using template /// Render section using template
func renderSection(with template: HBMustacheTemplate, context: HBMustacheContext) -> String func renderSection(with template: MustacheTemplate, context: MustacheContext) -> String
/// Render inverted section using template /// Render inverted section using template
func renderInvertedSection(with template: HBMustacheTemplate, context: HBMustacheContext) -> String func renderInvertedSection(with template: MustacheTemplate, context: MustacheContext) -> String
} }
extension Sequence { extension Sequence {
/// Render section using template /// Render section using template
func renderSection(with template: HBMustacheTemplate, context: HBMustacheContext) -> String { func renderSection(with template: MustacheTemplate, context: MustacheContext) -> String {
var string = "" var string = ""
var sequenceContext = HBMustacheSequenceContext(first: true) var sequenceContext = MustacheSequenceContext(first: true)
var iterator = makeIterator() var iterator = makeIterator()
guard var currentObject = iterator.next() else { return "" } guard var currentObject = iterator.next() else { return "" }
@@ -43,7 +43,7 @@ extension Sequence {
} }
/// Render inverted section using template /// Render inverted section using template
func renderInvertedSection(with template: HBMustacheTemplate, context: HBMustacheContext) -> String { func renderInvertedSection(with template: MustacheTemplate, context: MustacheContext) -> String {
var iterator = makeIterator() var iterator = makeIterator()
if iterator.next() == nil { if iterator.next() == nil {
return template.render(context: context.withObject(self)) return template.render(context: context.withObject(self))
@@ -52,6 +52,6 @@ extension Sequence {
} }
} }
extension Array: HBMustacheSequence {} extension Array: MustacheSequence {}
extension Set: HBMustacheSequence {} extension Set: MustacheSequence {}
extension ReversedCollection: HBMustacheSequence {} extension ReversedCollection: MustacheSequence {}

View File

@@ -13,7 +13,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// Context that current object inside a sequence is being rendered in. Only relevant when rendering a sequence /// Context that current object inside a sequence is being rendered in. Only relevant when rendering a sequence
struct HBMustacheSequenceContext: HBMustacheTransformable { struct MustacheSequenceContext: MustacheTransformable {
var first: Bool var first: Bool
var last: Bool var last: Bool
var index: Int var index: Int
@@ -24,7 +24,7 @@ struct HBMustacheSequenceContext: HBMustacheTransformable {
self.index = 0 self.index = 0
} }
/// Transform `HBMustacheContext`. These are available when processing elements /// Transform `MustacheContext`. These are available when processing elements
/// of a sequence. /// of a sequence.
/// ///
/// Format your mustache as follows to accept them. They look like a function without any arguments /// Format your mustache as follows to accept them. They look like a function without any arguments

View File

@@ -12,14 +12,14 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
extension HBMustacheTemplate { extension MustacheTemplate {
/// Error return by `HBMustacheTemplate.parse`. Includes information about where error occurred /// Error return by `MustacheTemplate.parse`. Includes information about where error occurred
public struct ParserError: Swift.Error { public struct ParserError: Swift.Error {
public let context: HBParser.Context public let context: MustacheParserContext
public let error: Swift.Error public let error: Swift.Error
} }
/// Error generated by `HBMustacheTemplate.parse` /// Error generated by `MustacheTemplate.parse`
public enum Error: Swift.Error { public enum Error: Swift.Error {
/// the end section does not match the name of the start section /// the end section does not match the name of the start section
case sectionCloseNameIncorrect case sectionCloseNameIncorrect
@@ -72,7 +72,7 @@ extension HBMustacheTemplate {
/// parse mustache text to generate a list of tokens /// parse mustache text to generate a list of tokens
static func parse(_ string: String) throws -> [Token] { static func parse(_ string: String) throws -> [Token] {
var parser = HBParser(string) var parser = Parser(string)
do { do {
return try self.parse(&parser, state: .init()) return try self.parse(&parser, state: .init())
} catch { } catch {
@@ -81,7 +81,7 @@ extension HBMustacheTemplate {
} }
/// parse section in mustache text /// parse section in mustache text
static func parse(_ parser: inout HBParser, state: ParserState) throws -> [Token] { static func parse(_ parser: inout Parser, state: ParserState) throws -> [Token] {
var tokens: [Token] = [] var tokens: [Token] = []
var state = state var state = state
var whiteSpaceBefore: Substring = "" var whiteSpaceBefore: Substring = ""
@@ -122,7 +122,7 @@ extension HBMustacheTemplate {
whiteSpaceBefore = "" whiteSpaceBefore = ""
} }
let sectionTokens = try parse(&parser, state: state.withSectionName(name, transform: transform)) let sectionTokens = try parse(&parser, state: state.withSectionName(name, transform: transform))
tokens.append(.section(name: name, transform: transform, template: HBMustacheTemplate(sectionTokens))) tokens.append(.section(name: name, transform: transform, template: MustacheTemplate(sectionTokens)))
case "^": case "^":
// inverted section // inverted section
@@ -135,7 +135,7 @@ extension HBMustacheTemplate {
whiteSpaceBefore = "" whiteSpaceBefore = ""
} }
let sectionTokens = try parse(&parser, state: state.withSectionName(name, transform: transform)) let sectionTokens = try parse(&parser, state: state.withSectionName(name, transform: transform))
tokens.append(.invertedSection(name: name, transform: transform, template: HBMustacheTemplate(sectionTokens))) tokens.append(.invertedSection(name: name, transform: transform, template: MustacheTemplate(sectionTokens)))
case "$": case "$":
// inherited section // inherited section
@@ -150,7 +150,7 @@ extension HBMustacheTemplate {
whiteSpaceBefore = "" whiteSpaceBefore = ""
} }
let sectionTokens = try parse(&parser, state: state.withSectionName(name, transform: transform)) let sectionTokens = try parse(&parser, state: state.withSectionName(name, transform: transform))
tokens.append(.inheritedSection(name: name, template: HBMustacheTemplate(sectionTokens))) tokens.append(.inheritedSection(name: name, template: MustacheTemplate(sectionTokens)))
case "/": case "/":
// end of section // end of section
@@ -224,7 +224,7 @@ extension HBMustacheTemplate {
whiteSpaceBefore = "" whiteSpaceBefore = ""
} }
let sectionTokens = try parse(&parser, state: state.withSectionName(name)) let sectionTokens = try parse(&parser, state: state.withSectionName(name))
var inherit: [String: HBMustacheTemplate] = [:] var inherit: [String: MustacheTemplate] = [:]
// parse tokens in section to extract inherited sections // parse tokens in section to extract inherited sections
for token in sectionTokens { for token in sectionTokens {
switch token { switch token {
@@ -271,7 +271,7 @@ extension HBMustacheTemplate {
} }
/// read until we hit either the start delimiter of a tag or a newline /// read until we hit either the start delimiter of a tag or a newline
static func readUntilDelimiterOrNewline(_ parser: inout HBParser, state: ParserState) throws -> String { static func readUntilDelimiterOrNewline(_ parser: inout Parser, state: ParserState) throws -> String {
var untilSet: Set<Character> = ["\n", "\r\n"] var untilSet: Set<Character> = ["\n", "\r\n"]
guard let delimiterFirstChar = state.startDelimiter.first else { return "" } guard let delimiterFirstChar = state.startDelimiter.first else { return "" }
var totalText = "" var totalText = ""
@@ -296,14 +296,14 @@ extension HBMustacheTemplate {
} }
/// parse variable name /// parse variable name
static func parseName(_ parser: inout HBParser, state: ParserState) throws -> (String, String?) { static func parseName(_ parser: inout Parser, state: ParserState) throws -> (String, String?) {
parser.read(while: \.isWhitespace) parser.read(while: \.isWhitespace)
let text = String(parser.read(while: self.sectionNameChars)) let text = String(parser.read(while: self.sectionNameChars))
parser.read(while: \.isWhitespace) parser.read(while: \.isWhitespace)
guard try parser.read(string: state.endDelimiter) else { throw Error.unfinishedName } guard try parser.read(string: state.endDelimiter) else { throw Error.unfinishedName }
// does the name include brackets. If so this is a transform call // does the name include brackets. If so this is a transform call
var nameParser = HBParser(String(text)) var nameParser = Parser(String(text))
let string = nameParser.read(while: self.sectionNameCharsWithoutBrackets) let string = nameParser.read(while: self.sectionNameCharsWithoutBrackets)
if nameParser.reachedEnd() { if nameParser.reachedEnd() {
return (text, nil) return (text, nil)
@@ -320,7 +320,7 @@ extension HBMustacheTemplate {
} }
/// parse partial name /// parse partial name
static func parsePartialName(_ parser: inout HBParser, state: ParserState) throws -> String { static func parsePartialName(_ parser: inout Parser, state: ParserState) throws -> String {
parser.read(while: \.isWhitespace) parser.read(while: \.isWhitespace)
let text = String(parser.read(while: self.sectionNameChars)) let text = String(parser.read(while: self.sectionNameChars))
parser.read(while: \.isWhitespace) parser.read(while: \.isWhitespace)
@@ -328,12 +328,12 @@ extension HBMustacheTemplate {
return text return text
} }
static func parseComment(_ parser: inout HBParser, state: ParserState) throws -> String { static func parseComment(_ parser: inout Parser, state: ParserState) throws -> String {
let text = try parser.read(untilString: state.endDelimiter, throwOnOverflow: true, skipToEnd: true) let text = try parser.read(untilString: state.endDelimiter, throwOnOverflow: true, skipToEnd: true)
return String(text) return String(text)
} }
static func parserSetDelimiter(_ parser: inout HBParser, state: ParserState) throws -> ParserState { static func parserSetDelimiter(_ parser: inout Parser, state: ParserState) throws -> ParserState {
let startDelimiter: Substring let startDelimiter: Substring
let endDelimiter: Substring let endDelimiter: Substring
@@ -352,7 +352,7 @@ extension HBMustacheTemplate {
return state.withDelimiters(start: String(startDelimiter), end: String(endDelimiter)) return state.withDelimiters(start: String(startDelimiter), end: String(endDelimiter))
} }
static func readConfigVariable(_ parser: inout HBParser, state: ParserState) throws -> Token? { static func readConfigVariable(_ parser: inout Parser, state: ParserState) throws -> Token? {
let variable: Substring let variable: Substring
let value: Substring let value: Substring
@@ -374,14 +374,14 @@ extension HBMustacheTemplate {
switch variable { switch variable {
case "CONTENT_TYPE": case "CONTENT_TYPE":
guard let contentType = HBMustacheContentTypes.get(String(value)) else { throw Error.unrecognisedConfigVariable } guard let contentType = MustacheContentTypes.get(String(value)) else { throw Error.unrecognisedConfigVariable }
return .contentType(contentType) return .contentType(contentType)
default: default:
throw Error.unrecognisedConfigVariable throw Error.unrecognisedConfigVariable
} }
} }
static func hasLineFinished(_ parser: inout HBParser) -> Bool { static func hasLineFinished(_ parser: inout Parser) -> Bool {
var parser2 = parser var parser2 = parser
if parser.reachedEnd() { return true } if parser.reachedEnd() { return true }
parser2.read(while: Set(" \t")) parser2.read(while: Set(" \t"))
@@ -393,7 +393,7 @@ extension HBMustacheTemplate {
return false return false
} }
static func isStandalone(_ parser: inout HBParser, state: ParserState) -> Bool { static func isStandalone(_ parser: inout Parser, state: ParserState) -> Bool {
return state.newLine && self.hasLineFinished(&parser) return state.newLine && self.hasLineFinished(&parser)
} }

View File

@@ -14,14 +14,14 @@
import Foundation import Foundation
extension HBMustacheTemplate { extension MustacheTemplate {
/// Render template using object /// Render template using object
/// - Parameters: /// - Parameters:
/// - stack: Object /// - stack: Object
/// - context: Context that render is occurring in. Contains information about position in sequence /// - context: Context that render is occurring in. Contains information about position in sequence
/// - indentation: indentation of partial /// - indentation: indentation of partial
/// - Returns: Rendered text /// - Returns: Rendered text
func render(context: HBMustacheContext) -> String { func render(context: MustacheContext) -> String {
var string = "" var string = ""
var context = context var context = context
@@ -42,15 +42,15 @@ extension HBMustacheTemplate {
return string return string
} }
func renderToken(_ token: Token, context: inout HBMustacheContext) -> String { func renderToken(_ token: Token, context: inout MustacheContext) -> String {
switch token { switch token {
case .text(let text): case .text(let text):
return text return text
case .variable(let variable, let transform): case .variable(let variable, let transform):
if let child = getChild(named: variable, transform: transform, context: context) { if let child = getChild(named: variable, transform: transform, context: context) {
if let template = child as? HBMustacheTemplate { if let template = child as? MustacheTemplate {
return template.render(context: context) return template.render(context: context)
} else if let renderable = child as? HBMustacheCustomRenderable { } else if let renderable = child as? MustacheCustomRenderable {
return context.contentType.escapeText(renderable.renderText) return context.contentType.escapeText(renderable.renderText)
} else { } else {
return context.contentType.escapeText(String(describing: child)) return context.contentType.escapeText(String(describing: child))
@@ -58,7 +58,7 @@ extension HBMustacheTemplate {
} }
case .unescapedVariable(let variable, let transform): case .unescapedVariable(let variable, let transform):
if let child = getChild(named: variable, transform: transform, context: context) { if let child = getChild(named: variable, transform: transform, context: context) {
if let renderable = child as? HBMustacheCustomRenderable { if let renderable = child as? MustacheCustomRenderable {
return renderable.renderText return renderable.renderText
} else { } else {
return String(describing: child) return String(describing: child)
@@ -96,15 +96,15 @@ extension HBMustacheTemplate {
/// - parent: Current object being rendered /// - parent: Current object being rendered
/// - template: Template to render with /// - template: Template to render with
/// - Returns: Rendered text /// - Returns: Rendered text
func renderSection(_ child: Any?, with template: HBMustacheTemplate, context: HBMustacheContext) -> String { func renderSection(_ child: Any?, with template: MustacheTemplate, context: MustacheContext) -> String {
switch child { switch child {
case let array as HBMustacheSequence: case let array as MustacheSequence:
return array.renderSection(with: template, context: context) return array.renderSection(with: template, context: context)
case let bool as Bool: case let bool as Bool:
return bool ? template.render(context: context) : "" return bool ? template.render(context: context) : ""
case let lambda as HBMustacheLambda: case let lambda as MustacheLambda:
return lambda.run(context.stack.last!, template) return lambda.run(context.stack.last!, template)
case let null as HBMustacheCustomRenderable where null.isNull == true: case let null as MustacheCustomRenderable where null.isNull == true:
return "" return ""
case .some(let value): case .some(let value):
return template.render(context: context.withObject(value)) return template.render(context: context.withObject(value))
@@ -119,13 +119,13 @@ extension HBMustacheTemplate {
/// - parent: Current object being rendered /// - parent: Current object being rendered
/// - template: Template to render with /// - template: Template to render with
/// - Returns: Rendered text /// - Returns: Rendered text
func renderInvertedSection(_ child: Any?, with template: HBMustacheTemplate, context: HBMustacheContext) -> String { func renderInvertedSection(_ child: Any?, with template: MustacheTemplate, context: MustacheContext) -> String {
switch child { switch child {
case let array as HBMustacheSequence: case let array as MustacheSequence:
return array.renderInvertedSection(with: template, context: context) return array.renderInvertedSection(with: template, context: context)
case let bool as Bool: case let bool as Bool:
return bool ? "" : template.render(context: context) return bool ? "" : template.render(context: context)
case let null as HBMustacheCustomRenderable where null.isNull == true: case let null as MustacheCustomRenderable where null.isNull == true:
return template.render(context: context) return template.render(context: context)
case .some: case .some:
return "" return ""
@@ -135,9 +135,9 @@ extension HBMustacheTemplate {
} }
/// Get child object from variable name /// Get child object from variable name
func getChild(named name: String, transform: String?, context: HBMustacheContext) -> Any? { func getChild(named name: String, transform: String?, context: MustacheContext) -> Any? {
func _getImmediateChild(named name: String, from object: Any) -> Any? { func _getImmediateChild(named name: String, from object: Any) -> Any? {
if let customBox = object as? HBMustacheParent { if let customBox = object as? MustacheParent {
return customBox.child(named: name) return customBox.child(named: name)
} else { } else {
let mirror = Mirror(reflecting: object) let mirror = Mirror(reflecting: object)
@@ -183,7 +183,7 @@ extension HBMustacheTemplate {
// if we want to run a transform and the current child can have transforms applied to it then // if we want to run a transform and the current child can have transforms applied to it then
// run transform on the current child // run transform on the current child
if let transform { if let transform {
if let runnable = child as? HBMustacheTransformable { if let runnable = child as? MustacheTransformable {
return runnable.transform(transform) return runnable.transform(transform)
} }
return nil return nil

View File

@@ -13,10 +13,10 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// Class holding Mustache template /// Class holding Mustache template
public struct HBMustacheTemplate: Sendable { public struct MustacheTemplate: Sendable {
/// Initialize template /// Initialize template
/// - Parameter string: Template text /// - Parameter string: Template text
/// - Throws: HBMustacheTemplate.Error /// - Throws: MustacheTemplate.Error
public init(string: String) throws { public init(string: String) throws {
self.tokens = try Self.parse(string) self.tokens = try Self.parse(string)
} }
@@ -24,7 +24,7 @@ public struct HBMustacheTemplate: Sendable {
/// Render object using this template /// Render object using this template
/// - Parameter object: Object to render /// - Parameter object: Object to render
/// - Returns: Rendered text /// - Returns: Rendered text
public func render(_ object: Any, library: HBMustacheLibrary? = nil) -> String { public func render(_ object: Any, library: MustacheLibrary? = nil) -> String {
self.render(context: .init(object, library: library)) self.render(context: .init(object, library: library))
} }
@@ -36,11 +36,11 @@ public struct HBMustacheTemplate: Sendable {
case text(String) case text(String)
case variable(name: String, transform: String? = nil) case variable(name: String, transform: String? = nil)
case unescapedVariable(name: String, transform: String? = nil) case unescapedVariable(name: String, transform: String? = nil)
case section(name: String, transform: String? = nil, template: HBMustacheTemplate) case section(name: String, transform: String? = nil, template: MustacheTemplate)
case invertedSection(name: String, transform: String? = nil, template: HBMustacheTemplate) case invertedSection(name: String, transform: String? = nil, template: MustacheTemplate)
case inheritedSection(name: String, template: HBMustacheTemplate) case inheritedSection(name: String, template: MustacheTemplate)
case partial(String, indentation: String?, inherits: [String: HBMustacheTemplate]?) case partial(String, indentation: String?, inherits: [String: MustacheTemplate]?)
case contentType(HBMustacheContentType) case contentType(MustacheContentType)
} }
var tokens: [Token] var tokens: [Token]

View File

@@ -27,7 +27,7 @@
/// ``` /// ```
/// {{#reversed(sequence)}}{{.}}{{\reversed(sequence)}} /// {{#reversed(sequence)}}{{.}}{{\reversed(sequence)}}
/// ``` /// ```
public protocol HBMustacheTransformable { public protocol MustacheTransformable {
func transform(_ name: String) -> Any? func transform(_ name: String) -> Any?
} }
@@ -55,15 +55,15 @@ public extension StringProtocol {
} }
} }
extension String: HBMustacheTransformable {} extension String: MustacheTransformable {}
extension Substring: HBMustacheTransformable {} extension Substring: MustacheTransformable {}
/// Protocol for sequence that can be sorted /// Protocol for sequence that can be sorted
private protocol HBComparableSequence { private protocol ComparableSequence {
func comparableTransform(_ name: String) -> Any? func comparableTransform(_ name: String) -> Any?
} }
extension Array: HBMustacheTransformable { extension Array: MustacheTransformable {
/// Transform Array. /// Transform Array.
/// ///
/// Transforms available are `first`, `last`, `reversed`, `count` and for arrays /// Transforms available are `first`, `last`, `reversed`, `count` and for arrays
@@ -83,7 +83,7 @@ extension Array: HBMustacheTransformable {
case "empty": case "empty":
return isEmpty return isEmpty
default: default:
if let comparableSeq = self as? HBComparableSequence { if let comparableSeq = self as? ComparableSequence {
return comparableSeq.comparableTransform(name) return comparableSeq.comparableTransform(name)
} }
return nil return nil
@@ -91,7 +91,7 @@ extension Array: HBMustacheTransformable {
} }
} }
extension Array: HBComparableSequence where Element: Comparable { extension Array: ComparableSequence where Element: Comparable {
func comparableTransform(_ name: String) -> Any? { func comparableTransform(_ name: String) -> Any? {
switch name { switch name {
case "sorted": case "sorted":
@@ -102,7 +102,7 @@ extension Array: HBComparableSequence where Element: Comparable {
} }
} }
extension Dictionary: HBMustacheTransformable { extension Dictionary: MustacheTransformable {
/// Transform Dictionary /// Transform Dictionary
/// ///
/// Transforms available are `count`, `enumerated` and for dictionaries /// Transforms available are `count`, `enumerated` and for dictionaries
@@ -118,7 +118,7 @@ extension Dictionary: HBMustacheTransformable {
case "enumerated": case "enumerated":
return map { (key: $0.key, value: $0.value) } return map { (key: $0.key, value: $0.value) }
default: default:
if let comparableSeq = self as? HBComparableSequence { if let comparableSeq = self as? ComparableSequence {
return comparableSeq.comparableTransform(name) return comparableSeq.comparableTransform(name)
} }
return nil return nil
@@ -126,7 +126,7 @@ extension Dictionary: HBMustacheTransformable {
} }
} }
extension Dictionary: HBComparableSequence where Key: Comparable { extension Dictionary: ComparableSequence where Key: Comparable {
func comparableTransform(_ name: String) -> Any? { func comparableTransform(_ name: String) -> Any? {
switch name { switch name {
case "sorted": case "sorted":
@@ -161,13 +161,13 @@ public extension FixedWidthInteger {
} }
} }
extension Int: HBMustacheTransformable {} extension Int: MustacheTransformable {}
extension Int8: HBMustacheTransformable {} extension Int8: MustacheTransformable {}
extension Int16: HBMustacheTransformable {} extension Int16: MustacheTransformable {}
extension Int32: HBMustacheTransformable {} extension Int32: MustacheTransformable {}
extension Int64: HBMustacheTransformable {} extension Int64: MustacheTransformable {}
extension UInt: HBMustacheTransformable {} extension UInt: MustacheTransformable {}
extension UInt8: HBMustacheTransformable {} extension UInt8: MustacheTransformable {}
extension UInt16: HBMustacheTransformable {} extension UInt16: MustacheTransformable {}
extension UInt32: HBMustacheTransformable {} extension UInt32: MustacheTransformable {}
extension UInt64: HBMustacheTransformable {} extension UInt64: MustacheTransformable {}

View File

@@ -17,14 +17,14 @@ import XCTest
final class ErrorTests: XCTestCase { final class ErrorTests: XCTestCase {
func testSectionCloseNameIncorrect() { func testSectionCloseNameIncorrect() {
XCTAssertThrowsError(try HBMustacheTemplate(string: """ XCTAssertThrowsError(try MustacheTemplate(string: """
{{#test}} {{#test}}
{{.}} {{.}}
{{/test2}} {{/test2}}
""")) { error in """)) { error in
switch error { switch error {
case let error as HBMustacheTemplate.ParserError: case let error as MustacheTemplate.ParserError:
XCTAssertEqual(error.error as? HBMustacheTemplate.Error, .sectionCloseNameIncorrect) XCTAssertEqual(error.error as? MustacheTemplate.Error, .sectionCloseNameIncorrect)
XCTAssertEqual(error.context.line, "{{/test2}}") XCTAssertEqual(error.context.line, "{{/test2}}")
XCTAssertEqual(error.context.lineNumber, 3) XCTAssertEqual(error.context.lineNumber, 3)
XCTAssertEqual(error.context.columnNumber, 4) XCTAssertEqual(error.context.columnNumber, 4)
@@ -36,14 +36,14 @@ final class ErrorTests: XCTestCase {
} }
func testUnfinishedName() { func testUnfinishedName() {
XCTAssertThrowsError(try HBMustacheTemplate(string: """ XCTAssertThrowsError(try MustacheTemplate(string: """
{{#test}} {{#test}}
{{name} {{name}
{{/test2}} {{/test2}}
""")) { error in """)) { error in
switch error { switch error {
case let error as HBMustacheTemplate.ParserError: case let error as MustacheTemplate.ParserError:
XCTAssertEqual(error.error as? HBMustacheTemplate.Error, .unfinishedName) XCTAssertEqual(error.error as? MustacheTemplate.Error, .unfinishedName)
XCTAssertEqual(error.context.line, "{{name}") XCTAssertEqual(error.context.line, "{{name}")
XCTAssertEqual(error.context.lineNumber, 2) XCTAssertEqual(error.context.lineNumber, 2)
XCTAssertEqual(error.context.columnNumber, 7) XCTAssertEqual(error.context.columnNumber, 7)
@@ -55,13 +55,13 @@ final class ErrorTests: XCTestCase {
} }
func testExpectedSectionEnd() { func testExpectedSectionEnd() {
XCTAssertThrowsError(try HBMustacheTemplate(string: """ XCTAssertThrowsError(try MustacheTemplate(string: """
{{#test}} {{#test}}
{{.}} {{.}}
""")) { error in """)) { error in
switch error { switch error {
case let error as HBMustacheTemplate.ParserError: case let error as MustacheTemplate.ParserError:
XCTAssertEqual(error.error as? HBMustacheTemplate.Error, .expectedSectionEnd) XCTAssertEqual(error.error as? MustacheTemplate.Error, .expectedSectionEnd)
XCTAssertEqual(error.context.line, "{{.}}") XCTAssertEqual(error.context.line, "{{.}}")
XCTAssertEqual(error.context.lineNumber, 2) XCTAssertEqual(error.context.lineNumber, 2)
XCTAssertEqual(error.context.columnNumber, 6) XCTAssertEqual(error.context.columnNumber, 6)
@@ -73,14 +73,14 @@ final class ErrorTests: XCTestCase {
} }
func testInvalidSetDelimiter() { func testInvalidSetDelimiter() {
XCTAssertThrowsError(try HBMustacheTemplate(string: """ XCTAssertThrowsError(try MustacheTemplate(string: """
{{=<% %>=}} {{=<% %>=}}
<%.%> <%.%>
<%={{}}=%> <%={{}}=%>
""")) { error in """)) { error in
switch error { switch error {
case let error as HBMustacheTemplate.ParserError: case let error as MustacheTemplate.ParserError:
XCTAssertEqual(error.error as? HBMustacheTemplate.Error, .invalidSetDelimiter) XCTAssertEqual(error.error as? MustacheTemplate.Error, .invalidSetDelimiter)
XCTAssertEqual(error.context.line, "<%={{}}=%>") XCTAssertEqual(error.context.line, "<%={{}}=%>")
XCTAssertEqual(error.context.lineNumber, 3) XCTAssertEqual(error.context.lineNumber, 3)
XCTAssertEqual(error.context.columnNumber, 4) XCTAssertEqual(error.context.columnNumber, 4)

View File

@@ -24,7 +24,7 @@ final class LibraryTests: XCTestCase {
try mustache.write(to: URL(fileURLWithPath: "templates/test.mustache")) try mustache.write(to: URL(fileURLWithPath: "templates/test.mustache"))
defer { XCTAssertNoThrow(try fs.removeItem(atPath: "templates/test.mustache")) } defer { XCTAssertNoThrow(try fs.removeItem(atPath: "templates/test.mustache")) }
let library = try await HBMustacheLibrary(directory: "./templates") let library = try await MustacheLibrary(directory: "./templates")
let object = ["value": ["value1", "value2"]] let object = ["value": ["value1", "value2"]]
XCTAssertEqual(library.render(object, withTemplate: "test"), "<test><value>value1</value><value>value2</value></test>") XCTAssertEqual(library.render(object, withTemplate: "test"), "<test><value>value1</value><value>value2</value></test>")
} }
@@ -42,7 +42,7 @@ final class LibraryTests: XCTestCase {
XCTAssertNoThrow(try fs.removeItem(atPath: "templates")) XCTAssertNoThrow(try fs.removeItem(atPath: "templates"))
} }
let library = try await HBMustacheLibrary(directory: "./templates") let library = try await MustacheLibrary(directory: "./templates")
let object = ["value": ["value1", "value2"]] let object = ["value": ["value1", "value2"]]
XCTAssertEqual(library.render(object, withTemplate: "test"), "<test><value>value1</value><value>value2</value></test>") XCTAssertEqual(library.render(object, withTemplate: "test"), "<test><value>value1</value><value>value2</value></test>")
} }
@@ -63,8 +63,8 @@ final class LibraryTests: XCTestCase {
defer { XCTAssertNoThrow(try fs.removeItem(atPath: "templates/error.mustache")) } defer { XCTAssertNoThrow(try fs.removeItem(atPath: "templates/error.mustache")) }
do { do {
_ = try await HBMustacheLibrary(directory: "./templates") _ = try await MustacheLibrary(directory: "./templates")
} catch let parserError as HBMustacheLibrary.ParserError { } catch let parserError as MustacheLibrary.ParserError {
XCTAssertEqual(parserError.filename, "error.mustache") XCTAssertEqual(parserError.filename, "error.mustache")
XCTAssertEqual(parserError.context.line, "{{{name}}") XCTAssertEqual(parserError.context.line, "{{{name}}")
XCTAssertEqual(parserError.context.lineNumber, 2) XCTAssertEqual(parserError.context.lineNumber, 2)

View File

@@ -18,17 +18,17 @@ import XCTest
final class PartialTests: XCTestCase { final class PartialTests: XCTestCase {
/// Testing partials /// Testing partials
func testMustacheManualExample9() throws { func testMustacheManualExample9() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
<h2>Names</h2> <h2>Names</h2>
{{#names}} {{#names}}
{{> user}} {{> user}}
{{/names}} {{/names}}
""") """)
let template2 = try HBMustacheTemplate(string: """ let template2 = try MustacheTemplate(string: """
<strong>{{.}}</strong> <strong>{{.}}</strong>
""") """)
let library = HBMustacheLibrary(templates: ["base": template, "user": template2]) let library = MustacheLibrary(templates: ["base": template, "user": template2])
let object: [String: Any] = ["names": ["john", "adam", "claire"]] let object: [String: Any] = ["names": ["john", "adam", "claire"]]
XCTAssertEqual(library.render(object, withTemplate: "base"), """ XCTAssertEqual(library.render(object, withTemplate: "base"), """
@@ -43,7 +43,7 @@ final class PartialTests: XCTestCase {
/// Test where last line of partial generates no content. It should not add a /// Test where last line of partial generates no content. It should not add a
/// tab either /// tab either
func testPartialEmptyLineTabbing() throws { func testPartialEmptyLineTabbing() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
<h2>Names</h2> <h2>Names</h2>
{{#names}} {{#names}}
{{> user}} {{> user}}
@@ -51,7 +51,7 @@ final class PartialTests: XCTestCase {
Text after Text after
""") """)
let template2 = try HBMustacheTemplate(string: """ let template2 = try MustacheTemplate(string: """
{{^empty(.)}} {{^empty(.)}}
<strong>{{.}}</strong> <strong>{{.}}</strong>
{{/empty(.)}} {{/empty(.)}}
@@ -60,9 +60,9 @@ final class PartialTests: XCTestCase {
{{/empty(.)}} {{/empty(.)}}
""") """)
var library = HBMustacheLibrary() var library = MustacheLibrary()
library.register(template, named: "base") library.register(template, named: "base")
library.register(template2, named: "user") // , withTemplate: String)// = HBMustacheLibrary(templates: ["base": template, "user": template2]) library.register(template2, named: "user") // , withTemplate: String)// = MustacheLibrary(templates: ["base": template, "user": template2])
let object: [String: Any] = ["names": ["john", "adam", "claire"]] let object: [String: Any] = ["names": ["john", "adam", "claire"]]
XCTAssertEqual(library.render(object, withTemplate: "base"), """ XCTAssertEqual(library.render(object, withTemplate: "base"), """
@@ -77,16 +77,16 @@ final class PartialTests: XCTestCase {
/// Testing dynamic partials /// Testing dynamic partials
func testDynamicPartials() throws { func testDynamicPartials() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
<h2>Names</h2> <h2>Names</h2>
{{partial}} {{partial}}
""") """)
let template2 = try HBMustacheTemplate(string: """ let template2 = try MustacheTemplate(string: """
{{#names}} {{#names}}
<strong>{{.}}</strong> <strong>{{.}}</strong>
{{/names}} {{/names}}
""") """)
let library = HBMustacheLibrary(templates: ["base": template]) let library = MustacheLibrary(templates: ["base": template])
let object: [String: Any] = ["names": ["john", "adam", "claire"], "partial": template2] let object: [String: Any] = ["names": ["john", "adam", "claire"], "partial": template2]
XCTAssertEqual(library.render(object, withTemplate: "base"), """ XCTAssertEqual(library.render(object, withTemplate: "base"), """
@@ -100,7 +100,7 @@ final class PartialTests: XCTestCase {
/// test inheritance /// test inheritance
func testInheritance() throws { func testInheritance() throws {
var library = HBMustacheLibrary() var library = MustacheLibrary()
try library.register( try library.register(
""" """
<head> <head>

View File

@@ -68,17 +68,17 @@ final class MustacheSpecTests: XCTestCase {
func run() throws { func run() throws {
// print("Test: \(self.name)") // print("Test: \(self.name)")
if let partials = self.partials { if let partials = self.partials {
let template = try HBMustacheTemplate(string: self.template) let template = try MustacheTemplate(string: self.template)
var templates: [String: HBMustacheTemplate] = ["__test__": template] var templates: [String: MustacheTemplate] = ["__test__": template]
for (key, value) in partials { for (key, value) in partials {
let template = try HBMustacheTemplate(string: value) let template = try MustacheTemplate(string: value)
templates[key] = template templates[key] = template
} }
let library = HBMustacheLibrary(templates: templates) let library = MustacheLibrary(templates: templates)
let result = library.render(self.data.value, withTemplate: "__test__") let result = library.render(self.data.value, withTemplate: "__test__")
self.XCTAssertSpecEqual(result, self) self.XCTAssertSpecEqual(result, self)
} else { } else {
let template = try HBMustacheTemplate(string: self.template) let template = try MustacheTemplate(string: self.template)
let result = template.render(self.data.value) let result = template.render(self.data.value)
self.XCTAssertSpecEqual(result, self) self.XCTAssertSpecEqual(result, self)
} }

View File

@@ -17,55 +17,55 @@ import XCTest
final class TemplateParserTests: XCTestCase { final class TemplateParserTests: XCTestCase {
func testText() throws { func testText() throws {
let template = try HBMustacheTemplate(string: "test template") let template = try MustacheTemplate(string: "test template")
XCTAssertEqual(template.tokens, [.text("test template")]) XCTAssertEqual(template.tokens, [.text("test template")])
} }
func testVariable() throws { func testVariable() throws {
let template = try HBMustacheTemplate(string: "test {{variable}}") let template = try MustacheTemplate(string: "test {{variable}}")
XCTAssertEqual(template.tokens, [.text("test "), .variable(name: "variable")]) XCTAssertEqual(template.tokens, [.text("test "), .variable(name: "variable")])
} }
func testSection() throws { func testSection() throws {
let template = try HBMustacheTemplate(string: "test {{#section}}text{{/section}}") let template = try MustacheTemplate(string: "test {{#section}}text{{/section}}")
XCTAssertEqual(template.tokens, [.text("test "), .section(name: "section", template: .init([.text("text")]))]) XCTAssertEqual(template.tokens, [.text("test "), .section(name: "section", template: .init([.text("text")]))])
} }
func testInvertedSection() throws { func testInvertedSection() throws {
let template = try HBMustacheTemplate(string: "test {{^section}}text{{/section}}") let template = try MustacheTemplate(string: "test {{^section}}text{{/section}}")
XCTAssertEqual(template.tokens, [.text("test "), .invertedSection(name: "section", template: .init([.text("text")]))]) XCTAssertEqual(template.tokens, [.text("test "), .invertedSection(name: "section", template: .init([.text("text")]))])
} }
func testComment() throws { func testComment() throws {
let template = try HBMustacheTemplate(string: "test {{!section}}") let template = try MustacheTemplate(string: "test {{!section}}")
XCTAssertEqual(template.tokens, [.text("test ")]) XCTAssertEqual(template.tokens, [.text("test ")])
} }
func testWhitespace() throws { func testWhitespace() throws {
let template = try HBMustacheTemplate(string: "{{ section }}") let template = try MustacheTemplate(string: "{{ section }}")
XCTAssertEqual(template.tokens, [.variable(name: "section")]) XCTAssertEqual(template.tokens, [.variable(name: "section")])
} }
func testContentType() throws { func testContentType() throws {
let template = try HBMustacheTemplate(string: "{{% CONTENT_TYPE:TEXT}}") let template = try MustacheTemplate(string: "{{% CONTENT_TYPE:TEXT}}")
let template1 = try HBMustacheTemplate(string: "{{% CONTENT_TYPE:TEXT }}") let template1 = try MustacheTemplate(string: "{{% CONTENT_TYPE:TEXT }}")
let template2 = try HBMustacheTemplate(string: "{{% CONTENT_TYPE: TEXT}}") let template2 = try MustacheTemplate(string: "{{% CONTENT_TYPE: TEXT}}")
let template3 = try HBMustacheTemplate(string: "{{%CONTENT_TYPE:TEXT}}") let template3 = try MustacheTemplate(string: "{{%CONTENT_TYPE:TEXT}}")
XCTAssertEqual(template.tokens, [.contentType(HBTextContentType())]) XCTAssertEqual(template.tokens, [.contentType(TextContentType())])
XCTAssertEqual(template1.tokens, [.contentType(HBTextContentType())]) XCTAssertEqual(template1.tokens, [.contentType(TextContentType())])
XCTAssertEqual(template2.tokens, [.contentType(HBTextContentType())]) XCTAssertEqual(template2.tokens, [.contentType(TextContentType())])
XCTAssertEqual(template3.tokens, [.contentType(HBTextContentType())]) XCTAssertEqual(template3.tokens, [.contentType(TextContentType())])
} }
} }
extension HBMustacheTemplate: Equatable { extension MustacheTemplate: Equatable {
public static func == (lhs: HBMustacheTemplate, rhs: HBMustacheTemplate) -> Bool { public static func == (lhs: MustacheTemplate, rhs: MustacheTemplate) -> Bool {
lhs.tokens == rhs.tokens lhs.tokens == rhs.tokens
} }
} }
extension HBMustacheTemplate.Token: Equatable { extension MustacheTemplate.Token: Equatable {
public static func == (lhs: HBMustacheTemplate.Token, rhs: HBMustacheTemplate.Token) -> Bool { public static func == (lhs: MustacheTemplate.Token, rhs: MustacheTemplate.Token) -> Bool {
switch (lhs, rhs) { switch (lhs, rhs) {
case (.text(let lhs), .text(let rhs)): case (.text(let lhs), .text(let rhs)):
return lhs == rhs return lhs == rhs

View File

@@ -17,50 +17,50 @@ import XCTest
final class TemplateRendererTests: XCTestCase { final class TemplateRendererTests: XCTestCase {
func testText() throws { func testText() throws {
let template = try HBMustacheTemplate(string: "test text") let template = try MustacheTemplate(string: "test text")
XCTAssertEqual(template.render("test"), "test text") XCTAssertEqual(template.render("test"), "test text")
} }
func testStringVariable() throws { func testStringVariable() throws {
let template = try HBMustacheTemplate(string: "test {{.}}") let template = try MustacheTemplate(string: "test {{.}}")
XCTAssertEqual(template.render("text"), "test text") XCTAssertEqual(template.render("text"), "test text")
} }
func testIntegerVariable() throws { func testIntegerVariable() throws {
let template = try HBMustacheTemplate(string: "test {{.}}") let template = try MustacheTemplate(string: "test {{.}}")
XCTAssertEqual(template.render(101), "test 101") XCTAssertEqual(template.render(101), "test 101")
} }
func testDictionary() throws { func testDictionary() throws {
let template = try HBMustacheTemplate(string: "test {{value}} {{bool}}") let template = try MustacheTemplate(string: "test {{value}} {{bool}}")
XCTAssertEqual(template.render(["value": "test2", "bool": true]), "test test2 true") XCTAssertEqual(template.render(["value": "test2", "bool": true]), "test test2 true")
} }
func testArraySection() throws { func testArraySection() throws {
let template = try HBMustacheTemplate(string: "test {{#value}}*{{.}}{{/value}}") let template = try MustacheTemplate(string: "test {{#value}}*{{.}}{{/value}}")
XCTAssertEqual(template.render(["value": ["test2", "bool"]]), "test *test2*bool") XCTAssertEqual(template.render(["value": ["test2", "bool"]]), "test *test2*bool")
XCTAssertEqual(template.render(["value": ["test2"]]), "test *test2") XCTAssertEqual(template.render(["value": ["test2"]]), "test *test2")
XCTAssertEqual(template.render(["value": []]), "test ") XCTAssertEqual(template.render(["value": []]), "test ")
} }
func testBooleanSection() throws { func testBooleanSection() throws {
let template = try HBMustacheTemplate(string: "test {{#.}}Yep{{/.}}") let template = try MustacheTemplate(string: "test {{#.}}Yep{{/.}}")
XCTAssertEqual(template.render(true), "test Yep") XCTAssertEqual(template.render(true), "test Yep")
XCTAssertEqual(template.render(false), "test ") XCTAssertEqual(template.render(false), "test ")
} }
func testIntegerSection() throws { func testIntegerSection() throws {
let template = try HBMustacheTemplate(string: "test {{#.}}{{.}}{{/.}}") let template = try MustacheTemplate(string: "test {{#.}}{{.}}{{/.}}")
XCTAssertEqual(template.render(23), "test 23") XCTAssertEqual(template.render(23), "test 23")
} }
func testStringSection() throws { func testStringSection() throws {
let template = try HBMustacheTemplate(string: "test {{#.}}{{.}}{{/.}}") let template = try MustacheTemplate(string: "test {{#.}}{{.}}{{/.}}")
XCTAssertEqual(template.render("Hello"), "test Hello") XCTAssertEqual(template.render("Hello"), "test Hello")
} }
func testInvertedSection() throws { func testInvertedSection() throws {
let template = try HBMustacheTemplate(string: "test {{^.}}Inverted{{/.}}") let template = try MustacheTemplate(string: "test {{^.}}Inverted{{/.}}")
XCTAssertEqual(template.render(true), "test ") XCTAssertEqual(template.render(true), "test ")
XCTAssertEqual(template.render(false), "test Inverted") XCTAssertEqual(template.render(false), "test Inverted")
} }
@@ -69,7 +69,7 @@ final class TemplateRendererTests: XCTestCase {
struct Test { struct Test {
let string: String let string: String
} }
let template = try HBMustacheTemplate(string: "test {{string}}") let template = try MustacheTemplate(string: "test {{string}}")
XCTAssertEqual(template.render(Test(string: "string")), "test string") XCTAssertEqual(template.render(Test(string: "string")), "test string")
} }
@@ -77,7 +77,7 @@ final class TemplateRendererTests: XCTestCase {
struct Test { struct Test {
let string: String? let string: String?
} }
let template = try HBMustacheTemplate(string: "test {{string}}") let template = try MustacheTemplate(string: "test {{string}}")
XCTAssertEqual(template.render(Test(string: "string")), "test string") XCTAssertEqual(template.render(Test(string: "string")), "test string")
XCTAssertEqual(template.render(Test(string: nil)), "test ") XCTAssertEqual(template.render(Test(string: nil)), "test ")
} }
@@ -86,10 +86,10 @@ final class TemplateRendererTests: XCTestCase {
struct Test { struct Test {
let string: String? let string: String?
} }
let template = try HBMustacheTemplate(string: "test {{#string}}*{{.}}{{/string}}") let template = try MustacheTemplate(string: "test {{#string}}*{{.}}{{/string}}")
XCTAssertEqual(template.render(Test(string: "string")), "test *string") XCTAssertEqual(template.render(Test(string: "string")), "test *string")
XCTAssertEqual(template.render(Test(string: nil)), "test ") XCTAssertEqual(template.render(Test(string: nil)), "test ")
let template2 = try HBMustacheTemplate(string: "test {{^string}}*{{/string}}") let template2 = try MustacheTemplate(string: "test {{^string}}*{{/string}}")
XCTAssertEqual(template2.render(Test(string: "string")), "test ") XCTAssertEqual(template2.render(Test(string: "string")), "test ")
XCTAssertEqual(template2.render(Test(string: nil)), "test *") XCTAssertEqual(template2.render(Test(string: nil)), "test *")
} }
@@ -98,7 +98,7 @@ final class TemplateRendererTests: XCTestCase {
struct Test { struct Test {
let string: String? let string: String?
} }
let template = try HBMustacheTemplate(string: "test {{#.}}{{string}}{{/.}}") let template = try MustacheTemplate(string: "test {{#.}}{{string}}{{/.}}")
XCTAssertEqual(template.render([Test(string: "string")]), "test string") XCTAssertEqual(template.render([Test(string: "string")]), "test string")
} }
@@ -106,7 +106,7 @@ final class TemplateRendererTests: XCTestCase {
struct Test { struct Test {
let string: String? let string: String?
} }
let template = try HBMustacheTemplate(string: "test {{#.}}{{#string}}*{{.}}{{/string}}{{/.}}") let template = try MustacheTemplate(string: "test {{#.}}{{#string}}*{{.}}{{/string}}{{/.}}")
XCTAssertEqual(template.render([Test(string: "string")]), "test *string") XCTAssertEqual(template.render([Test(string: "string")]), "test *string")
} }
@@ -118,20 +118,20 @@ final class TemplateRendererTests: XCTestCase {
let test: SubTest let test: SubTest
} }
let template = try HBMustacheTemplate(string: "test {{test.string}}") let template = try MustacheTemplate(string: "test {{test.string}}")
XCTAssertEqual(template.render(Test(test: .init(string: "sub"))), "test sub") XCTAssertEqual(template.render(Test(test: .init(string: "sub"))), "test sub")
} }
func testTextEscaping() throws { func testTextEscaping() throws {
let template1 = try HBMustacheTemplate(string: "{{% CONTENT_TYPE:TEXT}}{{.}}") let template1 = try MustacheTemplate(string: "{{% CONTENT_TYPE:TEXT}}{{.}}")
XCTAssertEqual(template1.render("<>"), "<>") XCTAssertEqual(template1.render("<>"), "<>")
let template2 = try HBMustacheTemplate(string: "{{% CONTENT_TYPE:HTML}}{{.}}") let template2 = try MustacheTemplate(string: "{{% CONTENT_TYPE:HTML}}{{.}}")
XCTAssertEqual(template2.render("<>"), "&lt;&gt;") XCTAssertEqual(template2.render("<>"), "&lt;&gt;")
} }
func testStopClimbingStack() throws { func testStopClimbingStack() throws {
let template1 = try HBMustacheTemplate(string: "{{#test}}{{name}}{{/test}}") let template1 = try MustacheTemplate(string: "{{#test}}{{name}}{{/test}}")
let template2 = try HBMustacheTemplate(string: "{{#test}}{{.name}}{{/test}}") let template2 = try MustacheTemplate(string: "{{#test}}{{.name}}{{/test}}")
let object: [String: Any] = ["test": [:], "name": "John"] let object: [String: Any] = ["test": [:], "name": "John"]
let object2: [String: Any] = ["test": ["name": "Jane"], "name": "John"] let object2: [String: Any] = ["test": ["name": "Jane"], "name": "John"]
XCTAssertEqual(template1.render(object), "John") XCTAssertEqual(template1.render(object), "John")
@@ -141,7 +141,7 @@ final class TemplateRendererTests: XCTestCase {
/// variables /// variables
func testMustacheManualExample1() throws { func testMustacheManualExample1() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
Hello {{name}} Hello {{name}}
You have just won {{value}} dollars! You have just won {{value}} dollars!
{{#in_ca}} {{#in_ca}}
@@ -159,7 +159,7 @@ final class TemplateRendererTests: XCTestCase {
/// test esacped and unescaped text /// test esacped and unescaped text
func testMustacheManualExample2() throws { func testMustacheManualExample2() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
*{{name}} *{{name}}
*{{age}} *{{age}}
*{{company}} *{{company}}
@@ -176,7 +176,7 @@ final class TemplateRendererTests: XCTestCase {
/// test boolean /// test boolean
func testMustacheManualExample3() throws { func testMustacheManualExample3() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
Shown. Shown.
{{#person}} {{#person}}
Never shown! Never shown!
@@ -191,7 +191,7 @@ final class TemplateRendererTests: XCTestCase {
/// test non-empty lists /// test non-empty lists
func testMustacheManualExample4() throws { func testMustacheManualExample4() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{#repo}} {{#repo}}
<b>{{name}}</b> <b>{{name}}</b>
{{/repo}} {{/repo}}
@@ -207,13 +207,13 @@ final class TemplateRendererTests: XCTestCase {
/// test lambdas /// test lambdas
func testMustacheManualExample5() throws { func testMustacheManualExample5() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{#wrapped}}{{name}} is awesome.{{/wrapped}} {{#wrapped}}{{name}} is awesome.{{/wrapped}}
""") """)
func wrapped(object: Any, template: HBMustacheTemplate) -> String { func wrapped(object: Any, template: MustacheTemplate) -> String {
return "<b>\(template.render(object))</b>" return "<b>\(template.render(object))</b>"
} }
let object: [String: Any] = ["name": "Willy", "wrapped": HBMustacheLambda(wrapped)] let object: [String: Any] = ["name": "Willy", "wrapped": MustacheLambda(wrapped)]
XCTAssertEqual(template.render(object), """ XCTAssertEqual(template.render(object), """
<b>Willy is awesome.</b> <b>Willy is awesome.</b>
""") """)
@@ -221,7 +221,7 @@ final class TemplateRendererTests: XCTestCase {
/// test setting context object /// test setting context object
func testMustacheManualExample6() throws { func testMustacheManualExample6() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{#person?}} {{#person?}}
Hi {{name}}! Hi {{name}}!
{{/person?}} {{/person?}}
@@ -235,7 +235,7 @@ final class TemplateRendererTests: XCTestCase {
/// test inverted sections /// test inverted sections
func testMustacheManualExample7() throws { func testMustacheManualExample7() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{#repo}} {{#repo}}
<b>{{name}}</b> <b>{{name}}</b>
{{/repo}} {{/repo}}
@@ -252,7 +252,7 @@ final class TemplateRendererTests: XCTestCase {
/// test comments /// test comments
func testMustacheManualExample8() throws { func testMustacheManualExample8() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
<h1>Today{{! ignore me }}.</h1> <h1>Today{{! ignore me }}.</h1>
""") """)
let object: [String: Any] = ["repo": []] let object: [String: Any] = ["repo": []]
@@ -261,12 +261,12 @@ final class TemplateRendererTests: XCTestCase {
""") """)
} }
/// test HBMustacheCustomRenderable /// test MustacheCustomRenderable
func testCustomRenderable() throws { func testCustomRenderable() throws {
let template = try HBMustacheTemplate(string: "{{.}}") let template = try MustacheTemplate(string: "{{.}}")
let template1 = try HBMustacheTemplate(string: "{{#.}}not null{{/.}}") let template1 = try MustacheTemplate(string: "{{#.}}not null{{/.}}")
let template2 = try HBMustacheTemplate(string: "{{^.}}null{{/.}}") let template2 = try MustacheTemplate(string: "{{^.}}null{{/.}}")
struct Object: HBMustacheCustomRenderable { struct Object: MustacheCustomRenderable {
let value: String let value: String
var renderText: String { self.value.uppercased() } var renderText: String { self.value.uppercased() }
@@ -282,7 +282,7 @@ final class TemplateRendererTests: XCTestCase {
} }
func testPerformance() throws { func testPerformance() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{#repo}} {{#repo}}
<b>{{name}}</b> <b>{{name}}</b>
{{/repo}} {{/repo}}

View File

@@ -17,7 +17,7 @@ import XCTest
final class TransformTests: XCTestCase { final class TransformTests: XCTestCase {
func testLowercased() throws { func testLowercased() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{ lowercased(name) }} {{ lowercased(name) }}
""") """)
let object: [String: Any] = ["name": "Test"] let object: [String: Any] = ["name": "Test"]
@@ -25,7 +25,7 @@ final class TransformTests: XCTestCase {
} }
func testUppercased() throws { func testUppercased() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{ uppercased(name) }} {{ uppercased(name) }}
""") """)
let object: [String: Any] = ["name": "Test"] let object: [String: Any] = ["name": "Test"]
@@ -33,7 +33,7 @@ final class TransformTests: XCTestCase {
} }
func testNewline() throws { func testNewline() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{#repo}} {{#repo}}
<b>{{name}}</b> <b>{{name}}</b>
{{/repo}} {{/repo}}
@@ -49,7 +49,7 @@ final class TransformTests: XCTestCase {
} }
func testFirstLast() throws { func testFirstLast() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{#repo}} {{#repo}}
<b>{{#first()}}first: {{/first()}}{{#last()}}last: {{/last()}}{{ name }}</b> <b>{{#first()}}first: {{/first()}}{{#last()}}last: {{/last()}}{{ name }}</b>
{{/repo}} {{/repo}}
@@ -65,7 +65,7 @@ final class TransformTests: XCTestCase {
} }
func testIndex() throws { func testIndex() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{#repo}} {{#repo}}
<b>{{#index()}}{{plusone(.)}}{{/index()}}) {{ name }}</b> <b>{{#index()}}{{plusone(.)}}{{/index()}}) {{ name }}</b>
{{/repo}} {{/repo}}
@@ -81,7 +81,7 @@ final class TransformTests: XCTestCase {
} }
func testEvenOdd() throws { func testEvenOdd() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{#repo}} {{#repo}}
<b>{{index()}}) {{#even()}}even {{/even()}}{{#odd()}}odd {{/odd()}}{{ name }}</b> <b>{{index()}}) {{#even()}}even {{/even()}}{{#odd()}}odd {{/odd()}}{{ name }}</b>
{{/repo}} {{/repo}}
@@ -97,7 +97,7 @@ final class TransformTests: XCTestCase {
} }
func testReversed() throws { func testReversed() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{#reversed(repo)}} {{#reversed(repo)}}
<b>{{ name }}</b> <b>{{ name }}</b>
{{/reversed(repo)}} {{/reversed(repo)}}
@@ -113,7 +113,7 @@ final class TransformTests: XCTestCase {
} }
func testArrayIndex() throws { func testArrayIndex() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{#repo}} {{#repo}}
<b>{{ index() }}) {{ name }}</b> <b>{{ index() }}) {{ name }}</b>
{{/repo}} {{/repo}}
@@ -128,7 +128,7 @@ final class TransformTests: XCTestCase {
} }
func testArraySorted() throws { func testArraySorted() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{#sorted(repo)}} {{#sorted(repo)}}
<b>{{ index() }}) {{ . }}</b> <b>{{ index() }}) {{ . }}</b>
{{/sorted(repo)}} {{/sorted(repo)}}
@@ -143,7 +143,7 @@ final class TransformTests: XCTestCase {
} }
func testDictionaryEmpty() throws { func testDictionaryEmpty() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{#empty(array)}}Array{{/empty(array)}}{{#empty(dictionary)}}Dictionary{{/empty(dictionary)}} {{#empty(array)}}Array{{/empty(array)}}{{#empty(dictionary)}}Dictionary{{/empty(dictionary)}}
""") """)
let object: [String: Any] = ["array": [], "dictionary": [:]] let object: [String: Any] = ["array": [], "dictionary": [:]]
@@ -152,12 +152,12 @@ final class TransformTests: XCTestCase {
func testListOutput() throws { func testListOutput() throws {
let object = [1, 2, 3, 4] let object = [1, 2, 3, 4]
let template = try HBMustacheTemplate(string: "{{#.}}{{.}}{{^last()}}, {{/last()}}{{/.}}") let template = try MustacheTemplate(string: "{{#.}}{{.}}{{^last()}}, {{/last()}}{{/.}}")
XCTAssertEqual(template.render(object), "1, 2, 3, 4") XCTAssertEqual(template.render(object), "1, 2, 3, 4")
} }
func testDictionaryEnumerated() throws { func testDictionaryEnumerated() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{#enumerated(.)}}<b>{{ key }} = {{ value }}</b>{{/enumerated(.)}} {{#enumerated(.)}}<b>{{ key }} = {{ value }}</b>{{/enumerated(.)}}
""") """)
let object: [String: Any] = ["one": 1, "two": 2] let object: [String: Any] = ["one": 1, "two": 2]
@@ -166,7 +166,7 @@ final class TransformTests: XCTestCase {
} }
func testDictionarySortedByKey() throws { func testDictionarySortedByKey() throws {
let template = try HBMustacheTemplate(string: """ let template = try MustacheTemplate(string: """
{{#sorted(.)}}<b>{{ key }} = {{ value }}</b>{{/sorted(.)}} {{#sorted(.)}}<b>{{ key }} = {{ value }}</b>{{/sorted(.)}}
""") """)
let object: [String: Any] = ["one": 1, "two": 2, "three": 3] let object: [String: Any] = ["one": 1, "two": 2, "three": 3]

View File

@@ -2,17 +2,17 @@
The library doesn't provide a lambda implementation but it does provide something akin to the lambda feature. The library doesn't provide a lambda implementation but it does provide something akin to the lambda feature.
Add a `HBMustacheLambda` to the object you want to be rendered and it can be used in a similar way to lambdas are used in Mustache. When you create a section referencing the lambda the contents of the section are passed as a template along with the current object to the lamdba function. This is slightly different from the standard implementation where the unprocessed text is passed to the lambda. Add a `MustacheLambda` to the object you want to be rendered and it can be used in a similar way to lambdas are used in Mustache. When you create a section referencing the lambda the contents of the section are passed as a template along with the current object to the lamdba function. This is slightly different from the standard implementation where the unprocessed text is passed to the lambda.
Given the object `person` defined below Given the object `person` defined below
```swift ```swift
struct Person { struct Person {
let name: String let name: String
let wrapped: HBMustacheLambda let wrapped: MustacheLambda
} }
let person = Person( let person = Person(
name: "John", name: "John",
wrapped: HBMustacheLambda { object, template in wrapped: MustacheLambda { object, template in
return "<b>\(template.render(object))</b>" return "<b>\(template.render(object))</b>"
} }
) )
@@ -21,7 +21,7 @@ let person = Person(
and the following mustache template and the following mustache template
```swift ```swift
let mustache = "{{#wrapped}}{{name}} is awesome.{{/wrapped}}" let mustache = "{{#wrapped}}{{name}} is awesome.{{/wrapped}}"
let template = try HBMustacheTemplate(string: mustache) let template = try MustacheTemplate(string: mustache)
``` ```
Then `template.render(person)` will output Then `template.render(person)` will output
``` ```

View File

@@ -52,9 +52,9 @@ The following sequence context transforms are available
## Custom transforms ## Custom transforms
You can add transforms to your own objects. Conform the object to `HBMustacheTransformable` and provide an implementation of the function `transform`. eg You can add transforms to your own objects. Conform the object to `MustacheTransformable` and provide an implementation of the function `transform`. eg
```swift ```swift
struct Object: HBMustacheTransformable { struct Object: MustacheTransformable {
let either: Bool let either: Bool
let or: Bool let or: Bool