Fix issues in Sources

Sources

sources
This commit is contained in:
David Jennes
2018-09-20 05:10:18 +02:00
parent 799490198f
commit 3f4622f54f
21 changed files with 346 additions and 350 deletions

View File

@@ -1,15 +1,13 @@
import Foundation
typealias Number = Float
class FilterExpression : Resolvable {
class FilterExpression: Resolvable {
let filters: [(FilterType, [Variable])]
let variable: Variable
init(token: String, environment: Environment) throws {
let bits = token.smartSplit(separator: "|").map({ String($0).trim(character: " ") })
let bits = token.smartSplit(separator: "|").map { String($0).trim(character: " ") }
if bits.isEmpty {
throw TemplateSyntaxError("Variable tags must include at least 1 argument")
}
@@ -32,15 +30,15 @@ class FilterExpression : Resolvable {
func resolve(_ context: Context) throws -> Any? {
let result = try variable.resolve(context)
return try filters.reduce(result) { x, y in
let arguments = try y.1.map { try $0.resolve(context) }
return try y.0.invoke(value: x, arguments: arguments, context: context)
return try filters.reduce(result) { value, filter in
let arguments = try filter.1.map { try $0.resolve(context) }
return try filter.0.invoke(value: value, arguments: arguments, context: context)
}
}
}
/// A structure used to represent a template variable, and to resolve it in a given context.
public struct Variable : Equatable, Resolvable {
public struct Variable: Equatable, Resolvable {
public let variable: String
/// Create a variable with a string representing the variable
@@ -48,16 +46,8 @@ public struct Variable : Equatable, Resolvable {
self.variable = variable
}
// Split the lookup string and resolve references if possible
fileprivate func lookup(_ context: Context) throws -> [String] {
let keyPath = KeyPath(variable, in: context)
return try keyPath.parse()
}
/// Resolve the variable in the given context
public func resolve(_ context: Context) throws -> Any? {
var current: Any? = context
if (variable.hasPrefix("'") && variable.hasSuffix("'")) || (variable.hasPrefix("\"") && variable.hasSuffix("\"")) {
// String literal
return String(variable[variable.index(after: variable.startIndex) ..< variable.index(before: variable.endIndex)])
@@ -75,35 +65,11 @@ public struct Variable : Equatable, Resolvable {
return bool
}
var current: Any? = context
for bit in try lookup(context) {
current = normalize(current)
current = resolve(bit: bit, context: current)
if let context = current as? Context {
current = context[bit]
} else if let dictionary = current as? [String: Any] {
if bit == "count" {
current = dictionary.count
} else {
current = dictionary[bit]
}
} else if let array = current as? [Any] {
current = resolveCollection(array, bit: bit)
} else if let string = current as? String {
current = resolveCollection(string, bit: bit)
} else if let object = current as? NSObject { // NSKeyValueCoding
#if os(Linux)
return nil
#else
if object.responds(to: Selector(bit)) {
current = object.value(forKey: bit)
}
#endif
} else if let value = current {
current = Mirror(reflecting: value).getValue(for: bit)
if current == nil {
return nil
}
} else {
if current == nil {
return nil
}
}
@@ -116,23 +82,66 @@ public struct Variable : Equatable, Resolvable {
return normalize(current)
}
}
private func resolveCollection<T: Collection>(_ collection: T, bit: String) -> Any? {
if let index = Int(bit) {
if index >= 0 && index < collection.count {
return collection[collection.index(collection.startIndex, offsetBy: index)]
// Split the lookup string and resolve references if possible
private func lookup(_ context: Context) throws -> [String] {
let keyPath = KeyPath(variable, in: context)
return try keyPath.parse()
}
// Try to resolve a partial keypath for the given context
private func resolve(bit: String, context: Any?) -> Any? {
let context = normalize(context)
if let context = context as? Context {
return context[bit]
} else if let dictionary = context as? [String: Any] {
return resolve(bit: bit, dictionary: dictionary)
} else if let array = context as? [Any] {
return resolve(bit: bit, collection: array)
} else if let string = context as? String {
return resolve(bit: bit, collection: string)
} else if let object = context as? NSObject { // NSKeyValueCoding
#if os(Linux)
return nil
#else
if object.responds(to: Selector(bit)) {
return object.value(forKey: bit)
}
#endif
} else if let value = context {
return Mirror(reflecting: value).getValue(for: bit)
}
return nil
}
// Try to resolve a partial keypath for the given dictionary
private func resolve(bit: String, dictionary: [String: Any]) -> Any? {
if bit == "count" {
return dictionary.count
} else {
return dictionary[bit]
}
}
// Try to resolve a partial keypath for the given collection
private func resolve<T: Collection>(bit: String, collection: T) -> Any? {
if let index = Int(bit) {
if index >= 0 && index < collection.count {
return collection[collection.index(collection.startIndex, offsetBy: index)]
} else {
return nil
}
} else if bit == "first" {
return collection.first
} else if bit == "last" {
return collection[collection.index(collection.endIndex, offsetBy: -1)]
} else if bit == "count" {
return collection.count
} else {
return nil
}
} else if bit == "first" {
return collection.first
} else if bit == "last" {
return collection[collection.index(collection.endIndex, offsetBy: -1)]
} else if bit == "count" {
return collection.count
} else {
return nil
}
}
@@ -142,6 +151,7 @@ private func resolveCollection<T: Collection>(_ collection: T, bit: String) -> A
/// If `from` is more than `to` array will contain values of reversed range.
public struct RangeVariable: Resolvable {
public let from: Resolvable
// swiftlint:disable:next identifier_name
public let to: Resolvable
public init?(_ token: String, environment: Environment) throws {
@@ -165,24 +175,23 @@ public struct RangeVariable: Resolvable {
}
public func resolve(_ context: Context) throws -> Any? {
let fromResolved = try from.resolve(context)
let toResolved = try to.resolve(context)
let lowerResolved = try from.resolve(context)
let upperResolved = try to.resolve(context)
guard let from = fromResolved.flatMap(toNumber(value:)).flatMap(Int.init) else {
throw TemplateSyntaxError("'from' value is not an Integer (\(fromResolved ?? "nil"))")
guard let lower = lowerResolved.flatMap(toNumber(value:)).flatMap(Int.init) else {
throw TemplateSyntaxError("'from' value is not an Integer (\(lowerResolved ?? "nil"))")
}
guard let to = toResolved.flatMap(toNumber(value:)).flatMap(Int.init) else {
throw TemplateSyntaxError("'to' value is not an Integer (\(toResolved ?? "nil") )")
guard let upper = upperResolved.flatMap(toNumber(value:)).flatMap(Int.init) else {
throw TemplateSyntaxError("'to' value is not an Integer (\(upperResolved ?? "nil") )")
}
let range = min(from, to)...max(from, to)
return from > to ? Array(range.reversed()) : Array(range)
let range = min(lower, upper)...max(lower, upper)
return lower > upper ? Array(range.reversed()) : Array(range)
}
}
func normalize(_ current: Any?) -> Any? {
if let current = current as? Normalizable {
return current.normalize()
@@ -195,19 +204,19 @@ protocol Normalizable {
func normalize() -> Any?
}
extension Array : Normalizable {
extension Array: Normalizable {
func normalize() -> Any? {
return map { $0 as Any }
}
}
extension NSArray : Normalizable {
extension NSArray: Normalizable {
func normalize() -> Any? {
return map { $0 as Any }
}
}
extension Dictionary : Normalizable {
extension Dictionary: Normalizable {
func normalize() -> Any? {
var dictionary: [String: Any] = [:]
@@ -235,7 +244,7 @@ func parseFilterComponents(token: String) -> (String, [Variable]) {
extension Mirror {
func getValue(for key: String) -> Any? {
let result = descendant(key) ?? Int(key).flatMap({ descendant($0) })
let result = descendant(key) ?? Int(key).flatMap { descendant($0) }
if result == nil {
// go through inheritance chain to reach superclass properties
return superclassMirror?.getValue(for: key)
@@ -267,5 +276,3 @@ extension Optional: AnyOptional {
}
}
}