swift format

This commit is contained in:
Adam Fowler
2021-03-15 18:24:06 +00:00
parent 955e1eb9e4
commit 66edcba185
18 changed files with 306 additions and 313 deletions

View File

@@ -4,11 +4,11 @@ struct HBMustacheContext: HBMustacheMethods {
var first: Bool var first: Bool
var last: Bool var last: Bool
var index: Int var index: Int
init(first: Bool = false, last: Bool = false) { init(first: Bool = false, last: Bool = false) {
self.first = first self.first = first
self.last = last self.last = last
self.index = 0 index = 0
} }
/// Apply method to `HBMustacheContext`. These are available when processing elements /// Apply method to `HBMustacheContext`. These are available when processing elements
@@ -25,18 +25,17 @@ struct HBMustacheContext: HBMustacheMethods {
func runMethod(_ name: String) -> Any? { func runMethod(_ name: String) -> Any? {
switch name { switch name {
case "first": case "first":
return self.first return first
case "last": case "last":
return self.last return last
case "index": case "index":
return self.index return index
case "even": case "even":
return (self.index & 1) == 0 return (index & 1) == 0
case "odd": case "odd":
return (self.index & 1) == 1 return (index & 1) == 1
default: default:
return nil return nil
} }
} }
} }

View File

@@ -27,7 +27,7 @@ public struct HBMustacheLambda {
/// Initialize `HBMustacheLambda` /// Initialize `HBMustacheLambda`
/// - 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 callback = cb
} }
internal func run(_ object: Any, _ template: HBMustacheTemplate) -> String { internal func run(_ object: Any, _ template: HBMustacheTemplate) -> String {

View File

@@ -13,7 +13,7 @@ extension HBMustacheLibrary {
guard let enumerator = fs.enumerator(atPath: directory) else { return } guard let enumerator = fs.enumerator(atPath: directory) else { return }
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)
guard let template = try? HBMustacheTemplate(string: string) else { guard let template = try? HBMustacheTemplate(string: string) else {
logger?.error("Failed to load \(path)") logger?.error("Failed to load \(path)")

View File

@@ -9,7 +9,7 @@ import Logging
public class HBMustacheLibrary { public class HBMustacheLibrary {
/// Initialize empty library /// Initialize empty library
public init() { public init() {
self.templates = [:] templates = [:]
} }
/// Initialize library with contents of folder. /// Initialize library with contents of folder.
@@ -19,8 +19,8 @@ public class HBMustacheLibrary {
/// - 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(directory: String, withExtension extension: String = "mustache", logger: Logger? = nil) { public init(directory: String, withExtension extension: String = "mustache", logger: Logger? = nil) {
self.templates = [:] templates = [:]
self.loadTemplates(from: directory, withExtension: `extension`, logger: logger) loadTemplates(from: directory, withExtension: `extension`, logger: logger)
} }
/// Register template under name /// Register template under name
@@ -48,6 +48,6 @@ public class HBMustacheLibrary {
guard let template = templates[name] else { return nil } guard let template = templates[name] else { return nil }
return template.render(object) return template.render(object)
} }
private var templates: [String: HBMustacheTemplate] private var templates: [String: HBMustacheTemplate]
} }

View File

@@ -17,22 +17,22 @@ public protocol HBMustacheMethods {
func runMethod(_ name: String) -> Any? func runMethod(_ name: String) -> Any?
} }
extension StringProtocol { public extension StringProtocol {
/// Apply method to String/Substring /// Apply method to String/Substring
/// ///
/// Methods available are `capitalized`, `lowercased`, `uppercased` and `reversed` /// Methods available are `capitalized`, `lowercased`, `uppercased` and `reversed`
/// - Parameter name: Method name /// - Parameter name: Method name
/// - Returns: Result /// - Returns: Result
public func runMethod(_ name: String) -> Any? { func runMethod(_ name: String) -> Any? {
switch name { switch name {
case "capitalized": case "capitalized":
return self.capitalized return capitalized
case "lowercased": case "lowercased":
return self.lowercased() return lowercased()
case "uppercased": case "uppercased":
return self.uppercased() return uppercased()
case "reversed": case "reversed":
return self.reversed() return reversed()
default: default:
return nil return nil
} }
@@ -57,13 +57,13 @@ extension Array: HBMustacheMethods {
public func runMethod(_ name: String) -> Any? { public func runMethod(_ name: String) -> Any? {
switch name { switch name {
case "first": case "first":
return self.first return first
case "last": case "last":
return self.last return last
case "reversed": case "reversed":
return self.reversed() return reversed()
case "count": case "count":
return self.count return count
default: default:
if let comparableSeq = self as? HBComparableSequence { if let comparableSeq = self as? HBComparableSequence {
return comparableSeq.runComparableMethod(name) return comparableSeq.runComparableMethod(name)
@@ -77,7 +77,7 @@ extension Array: HBComparableSequence where Element: Comparable {
func runComparableMethod(_ name: String) -> Any? { func runComparableMethod(_ name: String) -> Any? {
switch name { switch name {
case "sorted": case "sorted":
return self.sorted() return sorted()
default: default:
return nil return nil
} }
@@ -94,9 +94,9 @@ extension Dictionary: HBMustacheMethods {
public func runMethod(_ name: String) -> Any? { public func runMethod(_ name: String) -> Any? {
switch name { switch name {
case "count": case "count":
return self.count return count
case "enumerated": case "enumerated":
return self.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? HBComparableSequence {
return comparableSeq.runComparableMethod(name) return comparableSeq.runComparableMethod(name)
@@ -110,20 +110,20 @@ extension Dictionary: HBComparableSequence where Key: Comparable {
func runComparableMethod(_ name: String) -> Any? { func runComparableMethod(_ name: String) -> Any? {
switch name { switch name {
case "sorted": case "sorted":
return self.map { (key: $0.key, value: $0.value) }.sorted { $0.key < $1.key } return map { (key: $0.key, value: $0.value) }.sorted { $0.key < $1.key }
default: default:
return nil return nil
} }
} }
} }
extension FixedWidthInteger { public extension FixedWidthInteger {
/// Apply method to FixedWidthInteger /// Apply method to FixedWidthInteger
/// ///
/// Methods available are `plusone`, `minusone`, `odd`, `even` /// Methods available are `plusone`, `minusone`, `odd`, `even`
/// - Parameter name: method name /// - Parameter name: method name
/// - Returns: Result /// - Returns: Result
public func runMethod(_ name: String) -> Any? { func runMethod(_ name: String) -> Any? {
switch name { switch name {
case "plusone": case "plusone":
return self + 1 return self + 1

View File

@@ -17,4 +17,3 @@ private func unwrapOptional(_ object: Any) -> Any? {
guard let first = mirror.children.first else { return nil } guard let first = mirror.children.first else { return nil }
return first.value return first.value
} }

View File

@@ -10,4 +10,3 @@ protocol HBMustacheParent {
extension Dictionary: HBMustacheParent where Key == String { extension Dictionary: HBMustacheParent where Key == String {
func child(named: String) -> Any? { return self[named] } func child(named: String) -> Any? { return self[named] }
} }

View File

@@ -23,10 +23,10 @@ struct HBParser {
if let buffer = utf8Data as? [UInt8] { if let buffer = utf8Data as? [UInt8] {
self.buffer = buffer self.buffer = buffer
} else { } else {
self.buffer = Array(utf8Data) buffer = Array(utf8Data)
} }
self.index = 0 index = 0
self.range = 0..<self.buffer.endIndex range = 0 ..< buffer.endIndex
// should check that the data is valid utf8 // should check that the data is valid utf8
if validateUTF8 == true, self.validateUTF8() == false { if validateUTF8 == true, self.validateUTF8() == false {
@@ -35,19 +35,19 @@ struct HBParser {
} }
init(_ string: String) { init(_ string: String) {
self.buffer = Array(string.utf8) buffer = Array(string.utf8)
self.index = 0 index = 0
self.range = 0..<self.buffer.endIndex range = 0 ..< buffer.endIndex
} }
/// Return contents of parser as a string /// Return contents of parser as a string
var count: Int { var count: Int {
return self.range.count return range.count
} }
/// Return contents of parser as a string /// Return contents of parser as a string
var string: String { var string: String {
return makeString(self.buffer[self.range]) return makeString(buffer[range])
} }
private var buffer: [UInt8] private var buffer: [UInt8]
@@ -60,12 +60,12 @@ struct HBParser {
extension HBParser { extension HBParser {
/// initialise a parser that parses a section of the buffer attached to another parser /// initialise a parser that parses a section of the buffer attached to another parser
init(_ parser: HBParser, range: Range<Int>) { init(_ parser: HBParser, range: Range<Int>) {
self.buffer = parser.buffer buffer = parser.buffer
self.index = range.startIndex index = range.startIndex
self.range = range self.range = range
precondition(range.startIndex >= 0 && range.endIndex <= self.buffer.endIndex) precondition(range.startIndex >= 0 && range.endIndex <= buffer.endIndex)
precondition(self.buffer[range.startIndex] & 0xC0 != 0x80) // check we arent in the middle of a UTF8 character precondition(buffer[range.startIndex] & 0xC0 != 0x80) // check we arent in the middle of a UTF8 character
} }
/// initialise a parser that parses a section of the buffer attached to this parser /// initialise a parser that parses a section of the buffer attached to this parser
@@ -79,7 +79,7 @@ extension HBParser {
/// - Throws: .overflow /// - Throws: .overflow
/// - Returns: Current character /// - Returns: Current character
mutating func character() throws -> Unicode.Scalar { mutating func character() throws -> Unicode.Scalar {
guard !self.reachedEnd() else { throw Error.overflow } guard !reachedEnd() else { throw Error.overflow }
return unsafeCurrentAndAdvance() return unsafeCurrentAndAdvance()
} }
@@ -88,9 +88,9 @@ extension HBParser {
/// - Throws: .overflow /// - Throws: .overflow
/// - Returns: If current character was the one we expected /// - Returns: If current character was the one we expected
mutating func read(_ char: Unicode.Scalar) throws -> Bool { mutating func read(_ char: Unicode.Scalar) throws -> Bool {
let initialIndex = self.index let initialIndex = index
let c = try character() let c = try character()
guard c == char else { self.index = initialIndex; return false } guard c == char else { index = initialIndex; return false }
return true return true
} }
@@ -99,9 +99,9 @@ extension HBParser {
/// - Throws: .overflow /// - Throws: .overflow
/// - Returns: If current character is in character set /// - Returns: If current character is in character set
mutating func read(_ characterSet: Set<Unicode.Scalar>) throws -> Bool { mutating func read(_ characterSet: Set<Unicode.Scalar>) throws -> Bool {
let initialIndex = self.index let initialIndex = index
let c = try character() let c = try character()
guard characterSet.contains(c) else { self.index = initialIndex; return false } guard characterSet.contains(c) else { index = initialIndex; return false }
return true return true
} }
@@ -110,10 +110,10 @@ extension HBParser {
/// - Throws: .overflow, .emptyString /// - Throws: .overflow, .emptyString
/// - Returns: If characters at current position equal string /// - Returns: If characters at current position equal string
mutating func read(_ string: String) throws -> Bool { mutating func read(_ string: String) throws -> Bool {
let initialIndex = self.index let initialIndex = index
guard string.count > 0 else { throw Error.emptyString } guard string.count > 0 else { throw Error.emptyString }
let subString = try read(count: string.count) let subString = try read(count: string.count)
guard subString.string == string else { self.index = initialIndex; return false } guard subString.string == string else { index = initialIndex; return false }
return true return true
} }
@@ -123,14 +123,14 @@ extension HBParser {
/// - Returns: The string read from the buffer /// - Returns: The string read from the buffer
mutating func read(count: Int) throws -> HBParser { mutating func read(count: Int) throws -> HBParser {
var count = count var count = count
var readEndIndex = self.index var readEndIndex = index
while count > 0 { while count > 0 {
guard readEndIndex != self.range.endIndex else { throw Error.overflow } guard readEndIndex != range.endIndex else { throw Error.overflow }
readEndIndex = skipUTF8Character(at: readEndIndex) readEndIndex = skipUTF8Character(at: readEndIndex)
count -= 1 count -= 1
} }
let result = self.subParser(self.index..<readEndIndex) let result = subParser(index ..< readEndIndex)
self.index = readEndIndex index = readEndIndex
return result return result
} }
@@ -139,10 +139,10 @@ extension HBParser {
/// - Throws: .overflow if we hit the end of the buffer before reading character /// - Throws: .overflow if we hit the end of the buffer before reading character
/// - Returns: String read from buffer /// - Returns: String read from buffer
@discardableResult mutating func read(until: Unicode.Scalar, throwOnOverflow: Bool = true) throws -> HBParser { @discardableResult mutating func read(until: Unicode.Scalar, throwOnOverflow: Bool = true) throws -> HBParser {
let startIndex = self.index let startIndex = index
while !self.reachedEnd() { while !reachedEnd() {
if unsafeCurrent() == until { if unsafeCurrent() == until {
return self.subParser(startIndex..<self.index) return subParser(startIndex ..< index)
} }
unsafeAdvance() unsafeAdvance()
} }
@@ -150,7 +150,7 @@ extension HBParser {
_setPosition(startIndex) _setPosition(startIndex)
throw Error.overflow throw Error.overflow
} }
return self.subParser(startIndex..<self.index) return subParser(startIndex ..< index)
} }
/// Read from buffer until we hit a character in supplied set. Position after this is of the character we were checking for /// Read from buffer until we hit a character in supplied set. Position after this is of the character we were checking for
@@ -158,10 +158,10 @@ extension HBParser {
/// - Throws: .overflow /// - Throws: .overflow
/// - Returns: String read from buffer /// - Returns: String read from buffer
@discardableResult mutating func read(until characterSet: Set<Unicode.Scalar>, throwOnOverflow: Bool = true) throws -> HBParser { @discardableResult mutating func read(until characterSet: Set<Unicode.Scalar>, throwOnOverflow: Bool = true) throws -> HBParser {
let startIndex = self.index let startIndex = index
while !self.reachedEnd() { while !reachedEnd() {
if characterSet.contains(unsafeCurrent()) { if characterSet.contains(unsafeCurrent()) {
return self.subParser(startIndex..<self.index) return subParser(startIndex ..< index)
} }
unsafeAdvance() unsafeAdvance()
} }
@@ -169,7 +169,7 @@ extension HBParser {
_setPosition(startIndex) _setPosition(startIndex)
throw Error.overflow throw Error.overflow
} }
return self.subParser(startIndex..<self.index) return subParser(startIndex ..< index)
} }
/// Read from buffer until we hit a character that returns true for supplied closure. Position after this is of the character we were checking for /// Read from buffer until we hit a character that returns true for supplied closure. Position after this is of the character we were checking for
@@ -177,10 +177,10 @@ extension HBParser {
/// - Throws: .overflow /// - Throws: .overflow
/// - Returns: String read from buffer /// - Returns: String read from buffer
@discardableResult mutating func read(until: (Unicode.Scalar) -> Bool, throwOnOverflow: Bool = true) throws -> HBParser { @discardableResult mutating func read(until: (Unicode.Scalar) -> Bool, throwOnOverflow: Bool = true) throws -> HBParser {
let startIndex = self.index let startIndex = index
while !self.reachedEnd() { while !reachedEnd() {
if until(unsafeCurrent()) { if until(unsafeCurrent()) {
return self.subParser(startIndex..<self.index) return subParser(startIndex ..< index)
} }
unsafeAdvance() unsafeAdvance()
} }
@@ -188,7 +188,7 @@ extension HBParser {
_setPosition(startIndex) _setPosition(startIndex)
throw Error.overflow throw Error.overflow
} }
return self.subParser(startIndex..<self.index) return subParser(startIndex ..< index)
} }
/// Read from buffer until we hit a character where supplied KeyPath is true. Position after this is of the character we were checking for /// Read from buffer until we hit a character where supplied KeyPath is true. Position after this is of the character we were checking for
@@ -196,10 +196,10 @@ extension HBParser {
/// - Throws: .overflow /// - Throws: .overflow
/// - Returns: String read from buffer /// - Returns: String read from buffer
@discardableResult mutating func read(until keyPath: KeyPath<Unicode.Scalar, Bool>, throwOnOverflow: Bool = true) throws -> HBParser { @discardableResult mutating func read(until keyPath: KeyPath<Unicode.Scalar, Bool>, throwOnOverflow: Bool = true) throws -> HBParser {
let startIndex = self.index let startIndex = index
while !self.reachedEnd() { while !reachedEnd() {
if unsafeCurrent()[keyPath: keyPath] { if unsafeCurrent()[keyPath: keyPath] {
return self.subParser(startIndex..<self.index) return subParser(startIndex ..< index)
} }
unsafeAdvance() unsafeAdvance()
} }
@@ -207,7 +207,7 @@ extension HBParser {
_setPosition(startIndex) _setPosition(startIndex)
throw Error.overflow throw Error.overflow
} }
return self.subParser(startIndex..<self.index) return subParser(startIndex ..< index)
} }
/// Read from buffer until we hit a string. By default the position after this is of the beginning of the string we were checking for /// Read from buffer until we hit a string. By default the position after this is of the beginning of the string we were checking for
@@ -234,7 +234,7 @@ extension HBParser {
if skipToEnd == false { if skipToEnd == false {
index = foundIndex index = foundIndex
} }
let result = subParser(startIndex..<foundIndex) let result = subParser(startIndex ..< foundIndex)
return result return result
} }
} else { } else {
@@ -246,16 +246,16 @@ extension HBParser {
_setPosition(startIndex) _setPosition(startIndex)
throw Error.overflow throw Error.overflow
} }
return subParser(startIndex..<index) return subParser(startIndex ..< index)
} }
} }
/// Read from buffer from current position until the end of the buffer /// Read from buffer from current position until the end of the buffer
/// - Returns: String read from buffer /// - Returns: String read from buffer
@discardableResult mutating func readUntilTheEnd() -> HBParser { @discardableResult mutating func readUntilTheEnd() -> HBParser {
let startIndex = self.index let startIndex = index
self.index = self.range.endIndex index = range.endIndex
return self.subParser(startIndex..<self.index) return subParser(startIndex ..< index)
} }
/// Read while character at current position is the one supplied /// Read while character at current position is the one supplied
@@ -263,7 +263,7 @@ extension HBParser {
/// - Returns: String read from buffer /// - Returns: String read from buffer
@discardableResult mutating func read(while: Unicode.Scalar) -> Int { @discardableResult mutating func read(while: Unicode.Scalar) -> Int {
var count = 0 var count = 0
while !self.reachedEnd(), while !reachedEnd(),
unsafeCurrent() == `while` unsafeCurrent() == `while`
{ {
unsafeAdvance() unsafeAdvance()
@@ -276,39 +276,39 @@ extension HBParser {
/// - Parameter while: character set to check /// - Parameter while: character set to check
/// - Returns: String read from buffer /// - Returns: String read from buffer
@discardableResult mutating func read(while characterSet: Set<Unicode.Scalar>) -> HBParser { @discardableResult mutating func read(while characterSet: Set<Unicode.Scalar>) -> HBParser {
let startIndex = self.index let startIndex = index
while !self.reachedEnd(), while !reachedEnd(),
characterSet.contains(unsafeCurrent()) characterSet.contains(unsafeCurrent())
{ {
unsafeAdvance() unsafeAdvance()
} }
return self.subParser(startIndex..<self.index) return subParser(startIndex ..< index)
} }
/// Read while character returns true for supplied closure /// Read while character returns true for supplied closure
/// - Parameter while: character set to check /// - Parameter while: character set to check
/// - Returns: String read from buffer /// - Returns: String read from buffer
@discardableResult mutating func read(while: (Unicode.Scalar) -> Bool) -> HBParser { @discardableResult mutating func read(while: (Unicode.Scalar) -> Bool) -> HBParser {
let startIndex = self.index let startIndex = index
while !self.reachedEnd(), while !reachedEnd(),
`while`(unsafeCurrent()) `while`(unsafeCurrent())
{ {
unsafeAdvance() unsafeAdvance()
} }
return self.subParser(startIndex..<self.index) return subParser(startIndex ..< index)
} }
/// Read while character returns true for supplied KeyPath /// Read while character returns true for supplied KeyPath
/// - Parameter while: character set to check /// - Parameter while: character set to check
/// - Returns: String read from buffer /// - Returns: String read from buffer
@discardableResult mutating func read(while keyPath: KeyPath<Unicode.Scalar, Bool>) -> HBParser { @discardableResult mutating func read(while keyPath: KeyPath<Unicode.Scalar, Bool>) -> HBParser {
let startIndex = self.index let startIndex = index
while !self.reachedEnd(), while !reachedEnd(),
unsafeCurrent()[keyPath: keyPath] unsafeCurrent()[keyPath: keyPath]
{ {
unsafeAdvance() unsafeAdvance()
} }
return self.subParser(startIndex..<self.index) return subParser(startIndex ..< index)
} }
/// Split parser into sections separated by character /// Split parser into sections separated by character
@@ -316,14 +316,14 @@ extension HBParser {
/// - Returns: arrays of sub parsers /// - Returns: arrays of sub parsers
mutating func split(separator: Unicode.Scalar) -> [HBParser] { mutating func split(separator: Unicode.Scalar) -> [HBParser] {
var subParsers: [HBParser] = [] var subParsers: [HBParser] = []
while !self.reachedEnd() { while !reachedEnd() {
do { do {
let section = try read(until: separator) let section = try read(until: separator)
subParsers.append(section) subParsers.append(section)
unsafeAdvance() unsafeAdvance()
} catch { } catch {
if !self.reachedEnd() { if !reachedEnd() {
subParsers.append(self.readUntilTheEnd()) subParsers.append(readUntilTheEnd())
} }
} }
} }
@@ -333,7 +333,7 @@ extension HBParser {
/// Return whether we have reached the end of the buffer /// Return whether we have reached the end of the buffer
/// - Returns: Have we reached the end /// - Returns: Have we reached the end
func reachedEnd() -> Bool { func reachedEnd() -> Bool {
return self.index == self.range.endIndex return index == range.endIndex
} }
} }
@@ -343,15 +343,15 @@ extension HBParser {
/// - Throws: .overflow /// - Throws: .overflow
/// - Returns: Unicode.Scalar /// - Returns: Unicode.Scalar
func current() -> Unicode.Scalar { func current() -> Unicode.Scalar {
guard !self.reachedEnd() else { return Unicode.Scalar(0) } guard !reachedEnd() else { return Unicode.Scalar(0) }
return unsafeCurrent() return unsafeCurrent()
} }
/// 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 Error.overflow } guard !reachedEnd() else { throw Error.overflow }
return self.unsafeAdvance() return unsafeAdvance()
} }
/// Move forward so many character /// Move forward so many character
@@ -360,8 +360,8 @@ extension HBParser {
mutating func advance(by amount: Int) throws { mutating func advance(by amount: Int) throws {
var amount = amount var amount = amount
while amount > 0 { while amount > 0 {
guard !self.reachedEnd() else { throw Error.overflow } guard !reachedEnd() else { throw Error.overflow }
self.index = skipUTF8Character(at: self.index) index = skipUTF8Character(at: index)
amount -= 1 amount -= 1
} }
} }
@@ -369,8 +369,8 @@ extension HBParser {
/// Move backwards one character /// Move backwards one character
/// - Throws: .overflow /// - Throws: .overflow
mutating func retreat() throws { mutating func retreat() throws {
guard self.index > self.range.startIndex else { throw Error.overflow } guard index > range.startIndex else { throw Error.overflow }
self.index = backOneUTF8Character(at: self.index) index = backOneUTF8Character(at: index)
} }
/// Move back so many characters /// Move back so many characters
@@ -379,20 +379,20 @@ extension HBParser {
mutating func retreat(by amount: Int) throws { mutating func retreat(by amount: Int) throws {
var amount = amount var amount = amount
while amount > 0 { while amount > 0 {
guard self.index > self.range.startIndex else { throw Error.overflow } guard index > range.startIndex else { throw Error.overflow }
self.index = backOneUTF8Character(at: self.index) index = backOneUTF8Character(at: index)
amount -= 1 amount -= 1
} }
} }
mutating func unsafeAdvance() { mutating func unsafeAdvance() {
self.index = skipUTF8Character(at: self.index) index = skipUTF8Character(at: index)
} }
mutating func unsafeAdvance(by amount: Int) { mutating func unsafeAdvance(by amount: Int) {
var amount = amount var amount = amount
while amount > 0 { while amount > 0 {
self.index = skipUTF8Character(at: self.index) index = skipUTF8Character(at: index)
amount -= 1 amount -= 1
} }
} }
@@ -416,8 +416,8 @@ extension HBParser: Sequence {
} }
mutating func next() -> Unicode.Scalar? { mutating func next() -> Unicode.Scalar? {
guard !self.parser.reachedEnd() else { return nil } guard !parser.reachedEnd() else { return nil }
return self.parser.unsafeCurrentAndAdvance() return parser.unsafeCurrentAndAdvance()
} }
} }
} }
@@ -425,7 +425,7 @@ extension HBParser: Sequence {
// internal versions without checks // internal versions without checks
private extension HBParser { private extension HBParser {
func unsafeCurrent() -> Unicode.Scalar { func unsafeCurrent() -> Unicode.Scalar {
return decodeUTF8Character(at: self.index).0 return decodeUTF8Character(at: index).0
} }
mutating func unsafeCurrentAndAdvance() -> Unicode.Scalar { mutating func unsafeCurrentAndAdvance() -> Unicode.Scalar {
@@ -477,16 +477,16 @@ extension HBParser {
} }
func skipUTF8Character(at index: Int) -> Int { func skipUTF8Character(at index: Int) -> Int {
if self.buffer[index] & 0x80 != 0x80 { return index + 1 } if buffer[index] & 0x80 != 0x80 { return index + 1 }
if self.buffer[index + 1] & 0xC0 == 0x80 { return index + 2 } if buffer[index + 1] & 0xC0 == 0x80 { return index + 2 }
if self.buffer[index + 2] & 0xC0 == 0x80 { return index + 3 } if buffer[index + 2] & 0xC0 == 0x80 { return index + 3 }
return index + 4 return index + 4
} }
func backOneUTF8Character(at index: Int) -> Int { func backOneUTF8Character(at index: Int) -> Int {
if self.buffer[index - 1] & 0xC0 != 0x80 { return index - 1 } if buffer[index - 1] & 0xC0 != 0x80 { return index - 1 }
if self.buffer[index - 2] & 0xC0 != 0x80 { return index - 2 } if buffer[index - 2] & 0xC0 != 0x80 { return index - 2 }
if self.buffer[index - 3] & 0xC0 != 0x80 { return index - 3 } if buffer[index - 3] & 0xC0 != 0x80 { return index - 3 }
return index - 4 return index - 4
} }
@@ -526,9 +526,9 @@ extension HBParser {
/// return if the buffer is valid UTF8 /// return if the buffer is valid UTF8
func validateUTF8() -> Bool { func validateUTF8() -> Bool {
var index = self.range.startIndex var index = range.startIndex
while index < self.range.endIndex { while index < range.endIndex {
let (scalar, newIndex) = self.validateUTF8Character(at: index) let (scalar, newIndex) = validateUTF8Character(at: index)
guard scalar != nil else { return false } guard scalar != nil else { return false }
index = newIndex index = newIndex
} }
@@ -598,17 +598,17 @@ extension HBParser {
return newIndex return newIndex
} }
guard self.index != self.range.endIndex else { return "" } guard index != range.endIndex else { return "" }
do { do {
if #available(macOS 11, *) { if #available(macOS 11, *) {
return try String(unsafeUninitializedCapacity: range.endIndex - index) { bytes -> Int in return try String(unsafeUninitializedCapacity: range.endIndex - index) { bytes -> Int in
return try _percentDecode(self.buffer[self.index..<range.endIndex], bytes) try _percentDecode(self.buffer[self.index ..< range.endIndex], bytes)
} }
} else { } else {
let newBuffer = try [UInt8].init(unsafeUninitializedCapacity: self.range.endIndex - self.index) { bytes, count in let newBuffer = try [UInt8].init(unsafeUninitializedCapacity: range.endIndex - index) { bytes, count in
try count = _percentDecode(self.buffer[self.index..<range.endIndex], bytes) try count = _percentDecode(self.buffer[self.index ..< range.endIndex], bytes)
} }
return self.makeString(newBuffer) return makeString(newBuffer)
} }
} catch { } catch {
return nil return nil
@@ -622,8 +622,8 @@ extension Unicode.Scalar {
} }
var isNewline: Bool { var isNewline: Bool {
switch self.value { switch value {
case 0x000A...0x000D /* LF ... CR */: return true case 0x000A ... 0x000D /* LF ... CR */: return true
case 0x0085 /* NEXT LINE (NEL) */: return true case 0x0085 /* NEXT LINE (NEL) */: return true
case 0x2028 /* LINE SEPARATOR */: return true case 0x2028 /* LINE SEPARATOR */: return true
case 0x2029 /* PARAGRAPH SEPARATOR */: return true case 0x2029 /* PARAGRAPH SEPARATOR */: return true
@@ -640,7 +640,7 @@ extension Unicode.Scalar {
} }
var isLetterOrNumber: Bool { var isLetterOrNumber: Bool {
return self.isLetter || self.isNumber return isLetter || isNumber
} }
} }

View File

@@ -7,12 +7,12 @@ public protocol HBMustacheSequence {
func renderInvertedSection(with template: HBMustacheTemplate) -> String func renderInvertedSection(with template: HBMustacheTemplate) -> String
} }
extension Sequence { public extension Sequence {
/// Render section using template /// Render section using template
public func renderSection(with template: HBMustacheTemplate) -> String { func renderSection(with template: HBMustacheTemplate) -> String {
var string = "" var string = ""
var context = HBMustacheContext(first: true) var context = HBMustacheContext(first: true)
var iterator = self.makeIterator() var iterator = makeIterator()
guard var currentObject = iterator.next() else { return "" } guard var currentObject = iterator.next() else { return "" }
while let object = iterator.next() { while let object = iterator.next() {
@@ -24,19 +24,18 @@ extension Sequence {
context.last = true context.last = true
string += template.render(currentObject, context: context) string += template.render(currentObject, context: context)
return string return string
} }
/// Render inverted section using template /// Render inverted section using template
public func renderInvertedSection(with template: HBMustacheTemplate) -> String { func renderInvertedSection(with template: HBMustacheTemplate) -> String {
var iterator = makeIterator() var iterator = makeIterator()
if iterator.next() == nil { if iterator.next() == nil {
return template.render(self) return template.render(self)
} }
return "" return ""
} }
} }
extension Array: HBMustacheSequence {} extension Array: HBMustacheSequence {}

View File

@@ -10,7 +10,7 @@ extension String {
/// HTML escape string. Replace '<', '>' and '&' with HTML escaped versions /// HTML escape string. Replace '<', '>' and '&' with HTML escaped versions
func htmlEscape() -> String { func htmlEscape() -> String {
var newString = "" var newString = ""
newString.reserveCapacity(self.count) newString.reserveCapacity(count)
// currently doing this by going through each character could speed // currently doing this by going through each character could speed
// this us by treating as an array of UInt8's // this us by treating as an array of UInt8's
for c in self { for c in self {

View File

@@ -83,7 +83,7 @@ extension HBMustacheTemplate {
/// parse variable name /// parse variable name
static func parseName(_ parser: inout HBParser) throws -> (String, String?) { static func parseName(_ parser: inout HBParser) throws -> (String, String?) {
parser.read(while: \.isWhitespace) parser.read(while: \.isWhitespace)
var text = parser.read(while: sectionNameChars ) var text = parser.read(while: sectionNameChars)
parser.read(while: \.isWhitespace) parser.read(while: \.isWhitespace)
guard try parser.read("}"), try parser.read("}") else { throw Error.unfinishedName } guard try parser.read("}"), try parser.read("}") else { throw Error.unfinishedName }
// does the name include brackets. If so this is a method call // does the name include brackets. If so this is a method call
@@ -106,7 +106,7 @@ extension HBMustacheTemplate {
let text = try parser.read(untilString: "}}", throwOnOverflow: true, skipToEnd: true) let text = try parser.read(untilString: "}}", throwOnOverflow: true, skipToEnd: true)
return text.string return text.string
} }
private static let sectionNameCharsWithoutBrackets = Set<Unicode.Scalar>("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._?") private static let sectionNameCharsWithoutBrackets = Set<Unicode.Scalar>("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._?")
private static let sectionNameChars = Set<Unicode.Scalar>("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._?()") private static let sectionNameChars = Set<Unicode.Scalar>("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._?()")
} }

View File

@@ -9,9 +9,9 @@ extension HBMustacheTemplate {
var string = "" var string = ""
for token in tokens { for token in tokens {
switch token { switch token {
case .text(let text): case let .text(text):
string += text string += text
case .variable(let variable, let method): case let .variable(variable, method):
if let child = getChild(named: variable, from: object, method: method, context: context) { if let child = getChild(named: variable, from: object, method: method, context: context) {
if let template = child as? HBMustacheTemplate { if let template = child as? HBMustacheTemplate {
string += template.render(object) string += template.render(object)
@@ -19,19 +19,19 @@ extension HBMustacheTemplate {
string += String(describing: child).htmlEscape() string += String(describing: child).htmlEscape()
} }
} }
case .unescapedVariable(let variable, let method): case let .unescapedVariable(variable, method):
if let child = getChild(named: variable, from: object, method: method, context: context) { if let child = getChild(named: variable, from: object, method: method, context: context) {
string += String(describing: child) string += String(describing: child)
} }
case .section(let variable, let method, let template): case let .section(variable, method, template):
let child = getChild(named: variable, from: object, method: method, context: context) let child = getChild(named: variable, from: object, method: method, context: context)
string += renderSection(child, parent: object, with: template) string += renderSection(child, parent: object, with: template)
case .invertedSection(let variable, let method, let template): case let .invertedSection(variable, method, template):
let child = getChild(named: variable, from: object, method: method, context: context) let child = getChild(named: variable, from: object, method: method, context: context)
string += renderInvertedSection(child, parent: object, with: template) string += renderInvertedSection(child, parent: object, with: template)
case .partial(let name): case let .partial(name):
if let text = library?.render(object, withTemplate: name) { if let text = library?.render(object, withTemplate: name) {
string += text string += text
} }
@@ -54,13 +54,13 @@ extension HBMustacheTemplate {
return bool ? template.render(parent) : "" return bool ? template.render(parent) : ""
case let lambda as HBMustacheLambda: case let lambda as HBMustacheLambda:
return lambda.run(parent, template) return lambda.run(parent, template)
case .some(let value): case let .some(value):
return template.render(value) return template.render(value)
case .none: case .none:
return "" return ""
} }
} }
/// Render an inverted section /// Render an inverted section
/// - Parameters: /// - Parameters:
/// - child: Object to render section for /// - child: Object to render section for
@@ -111,7 +111,8 @@ extension HBMustacheTemplate {
// if we want to run a method and the current child can have methods applied to it then // if we want to run a method and the current child can have methods applied to it then
// run method on the current child // run method on the current child
if let method = method, if let method = method,
let runnable = child as? HBMustacheMethods { let runnable = child as? HBMustacheMethods
{
if let result = runnable.runMethod(method) { if let result = runnable.runMethod(method) {
return result return result
} }
@@ -119,4 +120,3 @@ extension HBMustacheTemplate {
return child return child
} }
} }

View File

@@ -4,16 +4,16 @@ public class HBMustacheTemplate {
/// - Parameter string: Template text /// - Parameter string: Template text
/// - Throws: HBMustacheTemplate.Error /// - Throws: HBMustacheTemplate.Error
public init(string: String) throws { public init(string: String) throws {
self.tokens = try Self.parse(string) tokens = try Self.parse(string)
} }
/// 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) -> String { public func render(_ object: Any) -> String {
self.render(object, context: nil) render(object, context: nil)
} }
internal init(_ tokens: [Token]) { internal init(_ tokens: [Token]) {
self.tokens = tokens self.tokens = tokens
} }
@@ -22,14 +22,14 @@ public class HBMustacheTemplate {
self.library = library self.library = library
for token in tokens { for token in tokens {
switch token { switch token {
case .section(_, _, let template), .invertedSection(_, _, let template): case let .section(_, _, template), let .invertedSection(_, _, template):
template.setLibrary(library) template.setLibrary(library)
default: default:
break break
} }
} }
} }
enum Token { enum Token {
case text(String) case text(String)
case variable(name: String, method: String? = nil) case variable(name: String, method: String? = nil)
@@ -42,4 +42,3 @@ public class HBMustacheTemplate {
let tokens: [Token] let tokens: [Token]
var library: HBMustacheLibrary? var library: HBMustacheLibrary?
} }

View File

@@ -1,5 +1,5 @@
import XCTest
@testable import HummingbirdMustache @testable import HummingbirdMustache
import XCTest
final class LibraryTests: XCTestCase { final class LibraryTests: XCTestCase {
func testDirectoryLoad() throws { func testDirectoryLoad() throws {
@@ -10,7 +10,7 @@ final class LibraryTests: XCTestCase {
defer { XCTAssertNoThrow(try fs.removeItem(atPath: "templates")) } defer { XCTAssertNoThrow(try fs.removeItem(atPath: "templates")) }
try data.write(to: URL(fileURLWithPath: "templates/test.mustache")) try data.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 = HBMustacheLibrary(directory: "./templates") let library = HBMustacheLibrary(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>")

View File

@@ -1,117 +1,117 @@
import XCTest
import HummingbirdMustache import HummingbirdMustache
import XCTest
final class MethodTests: XCTestCase { final class MethodTests: XCTestCase {
func testLowercased() throws { func testLowercased() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{ lowercased(name) }} {{ lowercased(name) }}
""") """)
let object: [String: Any] = ["name": "Test"] let object: [String: Any] = ["name": "Test"]
XCTAssertEqual(template.render(object), "test") XCTAssertEqual(template.render(object), "test")
} }
func testUppercased() throws { func testUppercased() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{ uppercased(name) }} {{ uppercased(name) }}
""") """)
let object: [String: Any] = ["name": "Test"] let object: [String: Any] = ["name": "Test"]
XCTAssertEqual(template.render(object), "TEST") XCTAssertEqual(template.render(object), "TEST")
} }
func testFirstLast() throws { func testFirstLast() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{#repo}} {{#repo}}
<b>{{#first()}}first: {{/}}{{#last()}}last: {{/}}{{ name }}</b> <b>{{#first()}}first: {{/}}{{#last()}}last: {{/}}{{ name }}</b>
{{/repo}} {{/repo}}
""") """)
let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]] let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]]
XCTAssertEqual(template.render(object), """ XCTAssertEqual(template.render(object), """
<b>first: resque</b> <b>first: resque</b>
<b>hub</b> <b>hub</b>
<b>last: rip</b> <b>last: rip</b>
""") """)
} }
func testIndex() throws { func testIndex() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{#repo}} {{#repo}}
<b>{{#index()}}{{plusone(.)}}{{/}}) {{ name }}</b> <b>{{#index()}}{{plusone(.)}}{{/}}) {{ name }}</b>
{{/repo}} {{/repo}}
""") """)
let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]] let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]]
XCTAssertEqual(template.render(object), """ XCTAssertEqual(template.render(object), """
<b>1) resque</b> <b>1) resque</b>
<b>2) hub</b> <b>2) hub</b>
<b>3) rip</b> <b>3) rip</b>
""") """)
} }
func testEvenOdd() throws { func testEvenOdd() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{#repo}} {{#repo}}
<b>{{index()}}) {{#even()}}even {{/}}{{#odd()}}odd {{/}}{{ name }}</b> <b>{{index()}}) {{#even()}}even {{/}}{{#odd()}}odd {{/}}{{ name }}</b>
{{/repo}} {{/repo}}
""") """)
let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]] let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]]
XCTAssertEqual(template.render(object), """ XCTAssertEqual(template.render(object), """
<b>0) even resque</b> <b>0) even resque</b>
<b>1) odd hub</b> <b>1) odd hub</b>
<b>2) even rip</b> <b>2) even rip</b>
""") """)
} }
func testReversed() throws { func testReversed() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{#reversed(repo)}} {{#reversed(repo)}}
<b>{{ name }}</b> <b>{{ name }}</b>
{{/repo}} {{/repo}}
""") """)
let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]] let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]]
XCTAssertEqual(template.render(object), """ XCTAssertEqual(template.render(object), """
<b>rip</b> <b>rip</b>
<b>hub</b> <b>hub</b>
<b>resque</b> <b>resque</b>
""") """)
} }
func testArrayIndex() throws { func testArrayIndex() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{#repo}} {{#repo}}
<b>{{ index() }}) {{ name }}</b> <b>{{ index() }}) {{ name }}</b>
{{/repo}} {{/repo}}
""") """)
let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]] let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]]
XCTAssertEqual(template.render(object), """ XCTAssertEqual(template.render(object), """
<b>0) resque</b> <b>0) resque</b>
<b>1) hub</b> <b>1) hub</b>
<b>2) rip</b> <b>2) rip</b>
""") """)
} }
func testArraySorted() throws { func testArraySorted() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{#sorted(repo)}} {{#sorted(repo)}}
<b>{{ index() }}) {{ . }}</b> <b>{{ index() }}) {{ . }}</b>
{{/repo}} {{/repo}}
""") """)
let object: [String: Any] = ["repo": ["resque", "hub", "rip"]] let object: [String: Any] = ["repo": ["resque", "hub", "rip"]]
XCTAssertEqual(template.render(object), """ XCTAssertEqual(template.render(object), """
<b>0) hub</b> <b>0) hub</b>
<b>1) resque</b> <b>1) resque</b>
<b>2) rip</b> <b>2) rip</b>
""") """)
} }
func testDictionaryEnumerated() throws { func testDictionaryEnumerated() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{#enumerated(.)}}<b>{{ key }} = {{ value }}</b>{{/.}} {{#enumerated(.)}}<b>{{ key }} = {{ value }}</b>{{/.}}
""") """)
let object: [String: Any] = ["one": 1, "two": 2] let object: [String: Any] = ["one": 1, "two": 2]
let result = template.render(object) let result = template.render(object)
XCTAssertTrue(result == "<b>one = 1</b><b>two = 2</b>" || result == "<b>two = 2</b><b>one = 1</b>") XCTAssertTrue(result == "<b>one = 1</b><b>two = 2</b>" || result == "<b>two = 2</b><b>one = 1</b>")
@@ -119,11 +119,10 @@ final class MethodTests: XCTestCase {
func testDictionarySortedByKey() throws { func testDictionarySortedByKey() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{#sorted(.)}}<b>{{ key }} = {{ value }}</b>{{/.}} {{#sorted(.)}}<b>{{ key }} = {{ value }}</b>{{/.}}
""") """)
let object: [String: Any] = ["one": 1, "two": 2, "three": 3] let object: [String: Any] = ["one": 1, "two": 2, "three": 3]
let result = template.render(object) let result = template.render(object)
XCTAssertEqual(result, "<b>one = 1</b><b>three = 3</b><b>two = 2</b>") XCTAssertEqual(result, "<b>one = 1</b><b>three = 3</b><b>two = 2</b>")
} }
} }

View File

@@ -1,54 +1,53 @@
import XCTest
@testable import HummingbirdMustache @testable import HummingbirdMustache
import XCTest
final class PartialTests: XCTestCase { final class PartialTests: XCTestCase {
/// Testing partials /// Testing partials
func testMustacheManualExample9() throws { func testMustacheManualExample9() throws {
let library = HBMustacheLibrary() let library = HBMustacheLibrary()
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
<h2>Names</h2> <h2>Names</h2>
{{#names}} {{#names}}
{{> user}} {{> user}}
{{/names}} {{/names}}
""") """)
let template2 = try HBMustacheTemplate(string: """ let template2 = try HBMustacheTemplate(string: """
<strong>{{.}}</strong> <strong>{{.}}</strong>
""") """)
library.register(template, named: "base") library.register(template, named: "base")
library.register(template2, named: "user") library.register(template2, named: "user")
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"), """
<h2>Names</h2> <h2>Names</h2>
<strong>john</strong> <strong>john</strong>
<strong>adam</strong> <strong>adam</strong>
<strong>claire</strong> <strong>claire</strong>
""") """)
} }
/// Testing dynamic partials /// Testing dynamic partials
func testDynamicPartials() throws { func testDynamicPartials() throws {
let library = HBMustacheLibrary() let library = HBMustacheLibrary()
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
<h2>Names</h2> <h2>Names</h2>
{{partial}} {{partial}}
""") """)
let template2 = try HBMustacheTemplate(string: """ let template2 = try HBMustacheTemplate(string: """
{{#names}} {{#names}}
<strong>{{.}}</strong> <strong>{{.}}</strong>
{{/names}} {{/names}}
""") """)
library.register(template, named: "base") library.register(template, named: "base")
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"), """
<h2>Names</h2> <h2>Names</h2>
<strong>john</strong> <strong>john</strong>
<strong>adam</strong> <strong>adam</strong>
<strong>claire</strong> <strong>claire</strong>
""") """)
} }
} }

View File

@@ -1,5 +1,5 @@
import XCTest
@testable import HummingbirdMustache @testable import HummingbirdMustache
import XCTest
final class TemplateParserTests: XCTestCase { final class TemplateParserTests: XCTestCase {
func testText() throws { func testText() throws {
@@ -75,15 +75,15 @@ extension HBMustacheTemplate: Equatable {
extension HBMustacheTemplate.Token: Equatable { extension HBMustacheTemplate.Token: Equatable {
public static func == (lhs: HBMustacheTemplate.Token, rhs: HBMustacheTemplate.Token) -> Bool { public static func == (lhs: HBMustacheTemplate.Token, rhs: HBMustacheTemplate.Token) -> Bool {
switch (lhs, rhs) { switch (lhs, rhs) {
case (.text(let lhs), .text(let rhs)): case let (.text(lhs), .text(rhs)):
return lhs == rhs return lhs == rhs
case (.variable(let lhs, let lhs2), .variable(let rhs, let rhs2)): case let (.variable(lhs, lhs2), .variable(rhs, rhs2)):
return lhs == rhs && lhs2 == rhs2 return lhs == rhs && lhs2 == rhs2
case (.section(let lhs1, let lhs2, let lhs3), .section(let rhs1, let rhs2, let rhs3)): case let (.section(lhs1, lhs2, lhs3), .section(rhs1, rhs2, rhs3)):
return lhs1 == rhs1 && lhs2 == rhs2 && lhs3 == rhs3 return lhs1 == rhs1 && lhs2 == rhs2 && lhs3 == rhs3
case (.invertedSection(let lhs1, let lhs2, let lhs3), .invertedSection(let rhs1, let rhs2, let rhs3)): case let (.invertedSection(lhs1, lhs2, lhs3), .invertedSection(rhs1, rhs2, rhs3)):
return lhs1 == rhs1 && lhs2 == rhs2 && lhs3 == rhs3 return lhs1 == rhs1 && lhs2 == rhs2 && lhs3 == rhs3
case (.partial(let name1), .partial(let name2)): case let (.partial(name1), .partial(name2)):
return name1 == name2 return name1 == name2
default: default:
return false return false

View File

@@ -1,5 +1,5 @@
import XCTest
@testable import HummingbirdMustache @testable import HummingbirdMustache
import XCTest
final class TemplateRendererTests: XCTestCase { final class TemplateRendererTests: XCTestCase {
func testText() throws { func testText() throws {
@@ -67,7 +67,7 @@ final class TemplateRendererTests: XCTestCase {
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 ")
} }
func testOptionalSequence() throws { func testOptionalSequence() throws {
struct Test { struct Test {
let string: String? let string: String?
@@ -79,7 +79,7 @@ final class TemplateRendererTests: XCTestCase {
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 *")
} }
func testStructureInStructure() throws { func testStructureInStructure() throws {
struct SubTest { struct SubTest {
let string: String? let string: String?
@@ -95,134 +95,134 @@ final class TemplateRendererTests: XCTestCase {
/// variables /// variables
func testMustacheManualExample1() throws { func testMustacheManualExample1() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
Hello {{name}} Hello {{name}}
You have just won {{value}} dollars! You have just won {{value}} dollars!
{{#in_ca}} {{#in_ca}}
Well, {{taxed_value}} dollars, after taxes. Well, {{taxed_value}} dollars, after taxes.
{{/in_ca}} {{/in_ca}}
""") """)
let object: [String: Any] = ["name": "Chris", "value": 10000, "taxed_value": 10000 - (10000 * 0.4), "in_ca": true] let object: [String: Any] = ["name": "Chris", "value": 10000, "taxed_value": 10000 - (10000 * 0.4), "in_ca": true]
XCTAssertEqual(template.render(object), """ XCTAssertEqual(template.render(object), """
Hello Chris Hello Chris
You have just won 10000 dollars! You have just won 10000 dollars!
Well, 6000.0 dollars, after taxes. Well, 6000.0 dollars, after taxes.
""") """)
} }
/// test esacped and unescaped text /// test esacped and unescaped text
func testMustacheManualExample2() throws { func testMustacheManualExample2() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
*{{name}} *{{name}}
*{{age}} *{{age}}
*{{company}} *{{company}}
*{{{company}}} *{{{company}}}
""") """)
let object: [String: Any] = ["name": "Chris", "company": "<b>GitHub</b>"] let object: [String: Any] = ["name": "Chris", "company": "<b>GitHub</b>"]
XCTAssertEqual(template.render(object), """ XCTAssertEqual(template.render(object), """
*Chris *Chris
* *
*&lt;b&gt;GitHub&lt;/b&gt; *&lt;b&gt;GitHub&lt;/b&gt;
*<b>GitHub</b> *<b>GitHub</b>
""") """)
} }
/// test boolean /// test boolean
func testMustacheManualExample3() throws { func testMustacheManualExample3() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
Shown. Shown.
{{#person}} {{#person}}
Never shown! Never shown!
{{/person}} {{/person}}
""") """)
let object: [String: Any] = ["person": false] let object: [String: Any] = ["person": false]
XCTAssertEqual(template.render(object), """ XCTAssertEqual(template.render(object), """
Shown. Shown.
""") """)
} }
/// test non-empty lists /// test non-empty lists
func testMustacheManualExample4() throws { func testMustacheManualExample4() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{#repo}} {{#repo}}
<b>{{name}}</b> <b>{{name}}</b>
{{/repo}} {{/repo}}
""") """)
let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]] let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]]
XCTAssertEqual(template.render(object), """ XCTAssertEqual(template.render(object), """
<b>resque</b> <b>resque</b>
<b>hub</b> <b>hub</b>
<b>rip</b> <b>rip</b>
""") """)
} }
/// test lambdas /// test lambdas
func testMustacheManualExample5() throws { func testMustacheManualExample5() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{#wrapped}}{{name}} is awesome.{{/wrapped}} {{#wrapped}}{{name}} is awesome.{{/wrapped}}
""") """)
func wrapped(object: Any, template: HBMustacheTemplate) -> String { func wrapped(object: Any, template: HBMustacheTemplate) -> 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": HBMustacheLambda(wrapped)]
XCTAssertEqual(template.render(object), """ XCTAssertEqual(template.render(object), """
<b>Willy is awesome.</b> <b>Willy is awesome.</b>
""") """)
} }
/// test setting context object /// test setting context object
func testMustacheManualExample6() throws { func testMustacheManualExample6() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{#person?}} {{#person?}}
Hi {{name}}! Hi {{name}}!
{{/person?}} {{/person?}}
""") """)
let object: [String: Any] = ["person?": ["name": "Jon"]] let object: [String: Any] = ["person?": ["name": "Jon"]]
XCTAssertEqual(template.render(object), """ XCTAssertEqual(template.render(object), """
Hi Jon! Hi Jon!
""") """)
} }
/// test inverted sections /// test inverted sections
func testMustacheManualExample7() throws { func testMustacheManualExample7() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{#repo}} {{#repo}}
<b>{{name}}</b> <b>{{name}}</b>
{{/repo}} {{/repo}}
{{^repo}} {{^repo}}
No repos :( No repos :(
{{/repo}} {{/repo}}
""") """)
let object: [String: Any] = ["repo": []] let object: [String: Any] = ["repo": []]
XCTAssertEqual(template.render(object), """ XCTAssertEqual(template.render(object), """
No repos :( No repos :(
""") """)
} }
/// test comments /// test comments
func testMustacheManualExample8() throws { func testMustacheManualExample8() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
<h1>Today{{! ignore me }}.</h1> <h1>Today{{! ignore me }}.</h1>
""") """)
let object: [String: Any] = ["repo": []] let object: [String: Any] = ["repo": []]
XCTAssertEqual(template.render(object), """ XCTAssertEqual(template.render(object), """
<h1>Today.</h1> <h1>Today.</h1>
""") """)
} }
func testPerformance() throws { func testPerformance() throws {
let template = try HBMustacheTemplate(string: """ let template = try HBMustacheTemplate(string: """
{{#repo}} {{#repo}}
<b>{{name}}</b> <b>{{name}}</b>
{{/repo}} {{/repo}}
""") """)
let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]] let object: [String: Any] = ["repo": [["name": "resque"], ["name": "hub"], ["name": "rip"]]]
let date = Date() let date = Date()
for _ in 1...10000 { for _ in 1 ... 10000 {
_ = template.render(object) _ = template.render(object)
} }
print(-date.timeIntervalSinceNow) print(-date.timeIntervalSinceNow)