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
```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.
@@ -20,23 +20,23 @@ let output = library.render(object, withTemplate: "myTemplate")
### 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
extension HBApplication {
var mustache: HBMustacheLibrary {
extension Application {
var mustache: MustacheLibrary {
get { self.extensions.get(\.mustache) }
set { self.extensions.set(\.mustache, value: newValue) }
}
}
extension HBRequest {
var mustache: HBMustacheLibrary { self.application.mustache }
extension Request {
var mustache: MustacheLibrary { self.application.mustache }
}
// load mustache templates from templates folder
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

View File

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

View File

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

View File

@@ -16,23 +16,23 @@ import Foundation
/// Allow object to override standard hummingbird type rendering which uses
/// `String(describing)`.
public protocol HBMustacheCustomRenderable {
public protocol MustacheCustomRenderable {
/// Custom rendered version of object
var renderText: String { get }
/// Whether the object is a null object. Used when scoping sections
var isNull: Bool { get }
}
extension HBMustacheCustomRenderable {
extension MustacheCustomRenderable {
/// default version returning the standard rendering
var renderText: String { String(describing: self) }
/// default version returning 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`
extension NSNull: HBMustacheCustomRenderable {
extension NSNull: MustacheCustomRenderable {
public var renderText: String { "" }
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 {
/// let name: String
/// let wrapped: HBMustacheLambda
/// let wrapped: MustacheLambda
/// }
/// let willy = Object(name: "Willy", wrapped: .init({ object, template in
/// return "<b>\(template.render(object))</b>"
/// }))
/// let mustache = "{{#wrapped}}{{name}} is awesome.{{/wrapped}}"
/// let template = try HBMustacheTemplate(string: mustache)
/// let template = try MustacheTemplate(string: mustache)
/// let output = template.render(willy)
/// print(output) // <b>Willy is awesome</b>
/// ```
///
public struct HBMustacheLambda {
public struct MustacheLambda {
/// lambda callback
public typealias Callback = (Any, HBMustacheTemplate) -> String
public typealias Callback = (Any, MustacheTemplate) -> String
let callback: Callback
/// Initialize `HBMustacheLambda`
/// Initialize `MustacheLambda`
/// - Parameter cb: function to be called by lambda
public init(_ cb: @escaping Callback) {
self.callback = cb
}
internal func run(_ object: Any, _ template: HBMustacheTemplate) -> String {
internal func run(_ object: Any, _ template: MustacheTemplate) -> String {
return self.callback(object, template)
}
}

View File

@@ -14,9 +14,9 @@
import Foundation
extension HBMustacheLibrary {
extension MustacheLibrary {
/// 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
if !directory.hasSuffix("/") {
directory += "/"
@@ -24,15 +24,15 @@ extension HBMustacheLibrary {
let extWithDot = ".\(`extension`)"
let fs = FileManager()
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 {
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)
var template: HBMustacheTemplate
var template: MustacheTemplate
do {
template = try HBMustacheTemplate(string: string)
} catch let error as HBMustacheTemplate.ParserError {
template = try MustacheTemplate(string: string)
} catch let error as MustacheTemplate.ParserError {
throw ParserError(filename: path, context: error.context, error: error.error)
}
// drop ".mustache" from path to get name

View File

@@ -18,7 +18,7 @@
/// ```
/// {{#sequence}}{{>entry}}{{/sequence}}
/// ```
public struct HBMustacheLibrary: Sendable {
public struct MustacheLibrary: Sendable {
/// Initialize empty library
public init() {
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`.
/// - Parameter directory: Directory to look for mustache templates
/// - Parameter extension: Extension of files to look for
public init(templates: [String: HBMustacheTemplate]) {
public init(templates: [String: MustacheTemplate]) {
self.templates = templates
}
@@ -48,7 +48,7 @@ public struct HBMustacheLibrary: Sendable {
/// - Parameters:
/// - template: 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
}
@@ -57,14 +57,14 @@ public struct HBMustacheLibrary: Sendable {
/// - mustache: Mustache text
/// - name: Name of template
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
}
/// Return template registed with name
/// - Parameter name: name to search for
/// - Returns: Template
public func getTemplate(named name: String) -> HBMustacheTemplate? {
public func getTemplate(named name: String) -> MustacheTemplate? {
self.templates[name]
}
@@ -83,10 +83,10 @@ public struct HBMustacheLibrary: Sendable {
/// File error occurred in
public let filename: String
/// Context (line, linenumber and column number)
public let context: HBParser.Context
public let context: MustacheParserContext
/// Actual error that occurred
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
/// of using Mirror
public protocol HBMustacheParent {
public protocol MustacheParent {
func child(named: String) -> Any?
}
/// Extend dictionary where the key is a string so that it uses the key values to access
/// it values
extension Dictionary: HBMustacheParent where Key == String {
extension Dictionary: MustacheParent where Key == String {
public func child(named: String) -> Any? { return self[named] }
}

View File

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

View File

@@ -13,18 +13,18 @@
//===----------------------------------------------------------------------===//
/// Protocol for objects that can be rendered as a sequence in Mustache
protocol HBMustacheSequence {
protocol MustacheSequence {
/// 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
func renderInvertedSection(with template: HBMustacheTemplate, context: HBMustacheContext) -> String
func renderInvertedSection(with template: MustacheTemplate, context: MustacheContext) -> String
}
extension Sequence {
/// Render section using template
func renderSection(with template: HBMustacheTemplate, context: HBMustacheContext) -> String {
func renderSection(with template: MustacheTemplate, context: MustacheContext) -> String {
var string = ""
var sequenceContext = HBMustacheSequenceContext(first: true)
var sequenceContext = MustacheSequenceContext(first: true)
var iterator = makeIterator()
guard var currentObject = iterator.next() else { return "" }
@@ -43,7 +43,7 @@ extension Sequence {
}
/// Render inverted section using template
func renderInvertedSection(with template: HBMustacheTemplate, context: HBMustacheContext) -> String {
func renderInvertedSection(with template: MustacheTemplate, context: MustacheContext) -> String {
var iterator = makeIterator()
if iterator.next() == nil {
return template.render(context: context.withObject(self))
@@ -52,6 +52,6 @@ extension Sequence {
}
}
extension Array: HBMustacheSequence {}
extension Set: HBMustacheSequence {}
extension ReversedCollection: HBMustacheSequence {}
extension Array: MustacheSequence {}
extension Set: MustacheSequence {}
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
struct HBMustacheSequenceContext: HBMustacheTransformable {
struct MustacheSequenceContext: MustacheTransformable {
var first: Bool
var last: Bool
var index: Int
@@ -24,7 +24,7 @@ struct HBMustacheSequenceContext: HBMustacheTransformable {
self.index = 0
}
/// Transform `HBMustacheContext`. These are available when processing elements
/// Transform `MustacheContext`. These are available when processing elements
/// of a sequence.
///
/// Format your mustache as follows to accept them. They look like a function without any arguments

View File

@@ -12,14 +12,14 @@
//
//===----------------------------------------------------------------------===//
extension HBMustacheTemplate {
/// Error return by `HBMustacheTemplate.parse`. Includes information about where error occurred
extension MustacheTemplate {
/// Error return by `MustacheTemplate.parse`. Includes information about where error occurred
public struct ParserError: Swift.Error {
public let context: HBParser.Context
public let context: MustacheParserContext
public let error: Swift.Error
}
/// Error generated by `HBMustacheTemplate.parse`
/// Error generated by `MustacheTemplate.parse`
public enum Error: Swift.Error {
/// the end section does not match the name of the start section
case sectionCloseNameIncorrect
@@ -72,7 +72,7 @@ extension HBMustacheTemplate {
/// parse mustache text to generate a list of tokens
static func parse(_ string: String) throws -> [Token] {
var parser = HBParser(string)
var parser = Parser(string)
do {
return try self.parse(&parser, state: .init())
} catch {
@@ -81,7 +81,7 @@ extension HBMustacheTemplate {
}
/// 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 state = state
var whiteSpaceBefore: Substring = ""
@@ -122,7 +122,7 @@ extension HBMustacheTemplate {
whiteSpaceBefore = ""
}
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 "^":
// inverted section
@@ -135,7 +135,7 @@ extension HBMustacheTemplate {
whiteSpaceBefore = ""
}
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 "$":
// inherited section
@@ -150,7 +150,7 @@ extension HBMustacheTemplate {
whiteSpaceBefore = ""
}
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 "/":
// end of section
@@ -224,7 +224,7 @@ extension HBMustacheTemplate {
whiteSpaceBefore = ""
}
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
for token in sectionTokens {
switch token {
@@ -271,7 +271,7 @@ extension HBMustacheTemplate {
}
/// 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"]
guard let delimiterFirstChar = state.startDelimiter.first else { return "" }
var totalText = ""
@@ -296,14 +296,14 @@ extension HBMustacheTemplate {
}
/// 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)
let text = String(parser.read(while: self.sectionNameChars))
parser.read(while: \.isWhitespace)
guard try parser.read(string: state.endDelimiter) else { throw Error.unfinishedName }
// 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)
if nameParser.reachedEnd() {
return (text, nil)
@@ -320,7 +320,7 @@ extension HBMustacheTemplate {
}
/// 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)
let text = String(parser.read(while: self.sectionNameChars))
parser.read(while: \.isWhitespace)
@@ -328,12 +328,12 @@ extension HBMustacheTemplate {
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)
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 endDelimiter: Substring
@@ -352,7 +352,7 @@ extension HBMustacheTemplate {
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 value: Substring
@@ -374,14 +374,14 @@ extension HBMustacheTemplate {
switch variable {
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)
default:
throw Error.unrecognisedConfigVariable
}
}
static func hasLineFinished(_ parser: inout HBParser) -> Bool {
static func hasLineFinished(_ parser: inout Parser) -> Bool {
var parser2 = parser
if parser.reachedEnd() { return true }
parser2.read(while: Set(" \t"))
@@ -393,7 +393,7 @@ extension HBMustacheTemplate {
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)
}

View File

@@ -14,14 +14,14 @@
import Foundation
extension HBMustacheTemplate {
extension MustacheTemplate {
/// Render template using object
/// - Parameters:
/// - stack: Object
/// - context: Context that render is occurring in. Contains information about position in sequence
/// - indentation: indentation of partial
/// - Returns: Rendered text
func render(context: HBMustacheContext) -> String {
func render(context: MustacheContext) -> String {
var string = ""
var context = context
@@ -42,15 +42,15 @@ extension HBMustacheTemplate {
return string
}
func renderToken(_ token: Token, context: inout HBMustacheContext) -> String {
func renderToken(_ token: Token, context: inout MustacheContext) -> String {
switch token {
case .text(let text):
return text
case .variable(let variable, let transform):
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)
} else if let renderable = child as? HBMustacheCustomRenderable {
} else if let renderable = child as? MustacheCustomRenderable {
return context.contentType.escapeText(renderable.renderText)
} else {
return context.contentType.escapeText(String(describing: child))
@@ -58,7 +58,7 @@ extension HBMustacheTemplate {
}
case .unescapedVariable(let variable, let transform):
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
} else {
return String(describing: child)
@@ -96,15 +96,15 @@ extension HBMustacheTemplate {
/// - parent: Current object being rendered
/// - template: Template to render with
/// - 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 {
case let array as HBMustacheSequence:
case let array as MustacheSequence:
return array.renderSection(with: template, context: context)
case let bool as Bool:
return bool ? template.render(context: context) : ""
case let lambda as HBMustacheLambda:
case let lambda as MustacheLambda:
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 ""
case .some(let value):
return template.render(context: context.withObject(value))
@@ -119,13 +119,13 @@ extension HBMustacheTemplate {
/// - parent: Current object being rendered
/// - template: Template to render with
/// - 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 {
case let array as HBMustacheSequence:
case let array as MustacheSequence:
return array.renderInvertedSection(with: template, context: context)
case let bool as Bool:
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)
case .some:
return ""
@@ -135,9 +135,9 @@ extension HBMustacheTemplate {
}
/// 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? {
if let customBox = object as? HBMustacheParent {
if let customBox = object as? MustacheParent {
return customBox.child(named: name)
} else {
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
// run transform on the current child
if let transform {
if let runnable = child as? HBMustacheTransformable {
if let runnable = child as? MustacheTransformable {
return runnable.transform(transform)
}
return nil

View File

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

View File

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

View File

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

View File

@@ -24,7 +24,7 @@ final class LibraryTests: XCTestCase {
try mustache.write(to: URL(fileURLWithPath: "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"]]
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"))
}
let library = try await HBMustacheLibrary(directory: "./templates")
let library = try await MustacheLibrary(directory: "./templates")
let object = ["value": ["value1", "value2"]]
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")) }
do {
_ = try await HBMustacheLibrary(directory: "./templates")
} catch let parserError as HBMustacheLibrary.ParserError {
_ = try await MustacheLibrary(directory: "./templates")
} catch let parserError as MustacheLibrary.ParserError {
XCTAssertEqual(parserError.filename, "error.mustache")
XCTAssertEqual(parserError.context.line, "{{{name}}")
XCTAssertEqual(parserError.context.lineNumber, 2)

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,7 +17,7 @@ import XCTest
final class TransformTests: XCTestCase {
func testLowercased() throws {
let template = try HBMustacheTemplate(string: """
let template = try MustacheTemplate(string: """
{{ lowercased(name) }}
""")
let object: [String: Any] = ["name": "Test"]
@@ -25,7 +25,7 @@ final class TransformTests: XCTestCase {
}
func testUppercased() throws {
let template = try HBMustacheTemplate(string: """
let template = try MustacheTemplate(string: """
{{ uppercased(name) }}
""")
let object: [String: Any] = ["name": "Test"]
@@ -33,7 +33,7 @@ final class TransformTests: XCTestCase {
}
func testNewline() throws {
let template = try HBMustacheTemplate(string: """
let template = try MustacheTemplate(string: """
{{#repo}}
<b>{{name}}</b>
{{/repo}}
@@ -49,7 +49,7 @@ final class TransformTests: XCTestCase {
}
func testFirstLast() throws {
let template = try HBMustacheTemplate(string: """
let template = try MustacheTemplate(string: """
{{#repo}}
<b>{{#first()}}first: {{/first()}}{{#last()}}last: {{/last()}}{{ name }}</b>
{{/repo}}
@@ -65,7 +65,7 @@ final class TransformTests: XCTestCase {
}
func testIndex() throws {
let template = try HBMustacheTemplate(string: """
let template = try MustacheTemplate(string: """
{{#repo}}
<b>{{#index()}}{{plusone(.)}}{{/index()}}) {{ name }}</b>
{{/repo}}
@@ -81,7 +81,7 @@ final class TransformTests: XCTestCase {
}
func testEvenOdd() throws {
let template = try HBMustacheTemplate(string: """
let template = try MustacheTemplate(string: """
{{#repo}}
<b>{{index()}}) {{#even()}}even {{/even()}}{{#odd()}}odd {{/odd()}}{{ name }}</b>
{{/repo}}
@@ -97,7 +97,7 @@ final class TransformTests: XCTestCase {
}
func testReversed() throws {
let template = try HBMustacheTemplate(string: """
let template = try MustacheTemplate(string: """
{{#reversed(repo)}}
<b>{{ name }}</b>
{{/reversed(repo)}}
@@ -113,7 +113,7 @@ final class TransformTests: XCTestCase {
}
func testArrayIndex() throws {
let template = try HBMustacheTemplate(string: """
let template = try MustacheTemplate(string: """
{{#repo}}
<b>{{ index() }}) {{ name }}</b>
{{/repo}}
@@ -128,7 +128,7 @@ final class TransformTests: XCTestCase {
}
func testArraySorted() throws {
let template = try HBMustacheTemplate(string: """
let template = try MustacheTemplate(string: """
{{#sorted(repo)}}
<b>{{ index() }}) {{ . }}</b>
{{/sorted(repo)}}
@@ -143,7 +143,7 @@ final class TransformTests: XCTestCase {
}
func testDictionaryEmpty() throws {
let template = try HBMustacheTemplate(string: """
let template = try MustacheTemplate(string: """
{{#empty(array)}}Array{{/empty(array)}}{{#empty(dictionary)}}Dictionary{{/empty(dictionary)}}
""")
let object: [String: Any] = ["array": [], "dictionary": [:]]
@@ -152,12 +152,12 @@ final class TransformTests: XCTestCase {
func testListOutput() throws {
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")
}
func testDictionaryEnumerated() throws {
let template = try HBMustacheTemplate(string: """
let template = try MustacheTemplate(string: """
{{#enumerated(.)}}<b>{{ key }} = {{ value }}</b>{{/enumerated(.)}}
""")
let object: [String: Any] = ["one": 1, "two": 2]
@@ -166,7 +166,7 @@ final class TransformTests: XCTestCase {
}
func testDictionarySortedByKey() throws {
let template = try HBMustacheTemplate(string: """
let template = try MustacheTemplate(string: """
{{#sorted(.)}}<b>{{ key }} = {{ value }}</b>{{/sorted(.)}}
""")
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.
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
```swift
struct Person {
let name: String
let wrapped: HBMustacheLambda
let wrapped: MustacheLambda
}
let person = Person(
name: "John",
wrapped: HBMustacheLambda { object, template in
wrapped: MustacheLambda { object, template in
return "<b>\(template.render(object))</b>"
}
)
@@ -21,7 +21,7 @@ let person = Person(
and the following mustache template
```swift
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
```

View File

@@ -52,9 +52,9 @@ The following sequence context transforms are available
## 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
struct Object: HBMustacheTransformable {
struct Object: MustacheTransformable {
let either: Bool
let or: Bool