Add support for proper lambdas (#48)

* Add support for proper lambdas

* Get rid of recursion

Remove renderSectionLambda as I can use renderUnescapedLambda for that.
This commit is contained in:
Adam Fowler
2024-09-19 17:17:50 +01:00
committed by GitHub
parent 8fba85e28c
commit 933fa3d60f
9 changed files with 249 additions and 44 deletions

View File

@@ -54,6 +54,8 @@ extension MustacheTemplate {
return template.render(context: context)
} else if let renderable = child as? MustacheCustomRenderable {
return context.contentType.escapeText(renderable.renderText)
} else if let lambda = child as? MustacheLambda {
return self.renderLambda(lambda, parameter: "", context: context)
} else {
return context.contentType.escapeText(String(describing: child))
}
@@ -63,6 +65,8 @@ extension MustacheTemplate {
if let child = getChild(named: variable, transforms: transforms, context: context) {
if let renderable = child as? MustacheCustomRenderable {
return renderable.renderText
} else if let lambda = child as? MustacheLambda {
return self.renderUnescapedLambda(lambda, parameter: "", context: context)
} else {
return String(describing: child)
}
@@ -70,6 +74,9 @@ extension MustacheTemplate {
case .section(let variable, let transforms, let template):
let child = self.getChild(named: variable, transforms: transforms, context: context)
if let lambda = child as? MustacheLambda {
return self.renderUnescapedLambda(lambda, parameter: template.text, context: context)
}
return self.renderSection(child, with: template, context: context)
case .invertedSection(let variable, let transforms, let template):
@@ -144,8 +151,6 @@ extension MustacheTemplate {
return array.renderSection(with: template, context: context)
case let bool as Bool:
return bool ? template.render(context: context) : ""
case let lambda as MustacheLambda:
return lambda.run(context.stack.last!, template)
case let null as MustacheCustomRenderable where null.isNull == true:
return ""
case .some(let value):
@@ -176,19 +181,67 @@ extension MustacheTemplate {
}
}
func renderLambda(_ lambda: MustacheLambda, parameter: String, context: MustacheContext) -> String {
var lambda = lambda
while true {
guard let result = lambda(parameter) else { return "" }
if let string = result as? String {
do {
let newTemplate = try MustacheTemplate(string: context.contentType.escapeText(string))
return self.renderSection(context.stack.last, with: newTemplate, context: context)
} catch {
return ""
}
} else if let lambda2 = result as? MustacheLambda {
lambda = lambda2
continue
} else {
return context.contentType.escapeText(String(describing: result))
}
}
}
func renderUnescapedLambda(_ lambda: MustacheLambda, parameter: String, context: MustacheContext) -> String {
var lambda = lambda
while true {
guard let result = lambda(parameter) else { return "" }
if let string = result as? String {
do {
let newTemplate = try MustacheTemplate(string: string)
return self.renderSection(context.stack.last, with: newTemplate, context: context)
} catch {
return ""
}
} else if let lambda2 = result as? MustacheLambda {
lambda = lambda2
continue
} else {
return String(describing: result)
}
}
}
/// Get child object from variable name
func getChild(named name: String, transforms: [String], context: MustacheContext) -> Any? {
func _getImmediateChild(named name: String, from object: Any) -> Any? {
if let customBox = object as? MustacheParent {
return customBox.child(named: name)
} else {
let mirror = Mirror(reflecting: object)
return mirror.getValue(forKey: name)
}
let object = {
if let customBox = object as? MustacheParent {
return customBox.child(named: name)
} else {
let mirror = Mirror(reflecting: object)
return mirror.getValue(forKey: name)
}
}()
return object
}
func _getChild(named names: ArraySlice<String>, from object: Any) -> Any? {
guard let name = names.first else { return object }
var object = object
if let lambda = object as? MustacheLambda {
guard let result = lambda("") else { return nil }
object = result
}
guard let childObject = _getImmediateChild(named: name, from: object) else { return nil }
let names2 = names.dropFirst()
return _getChild(named: names2, from: childObject)