// // Where.Expression.swift // CoreStore // // Copyright © 2018 John Rommel Estropia // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // import Foundation import CoreData // MARK: - ~ infix operator ~ : AdditionPrecedence // MARK: - WhereExpressionTrait public protocol WhereExpressionTrait {} // MARK: - Where extension Where { // MARK: - Expression public struct Expression: CustomStringConvertible, DynamicKeyPath { public typealias Trait = T // MARK: AnyDynamicKeyPath public let cs_keyPathString: String // MARK: DynamicKeyPath public typealias ObjectType = D public typealias ValueType = V // MARK: CustomStringConvertible public var description: String { return self.cs_keyPathString } // MARK: Internal internal init(_ component: String) { self.cs_keyPathString = component } internal init(_ component1: String, _ component2: String) { self.cs_keyPathString = component1 + "." + component2 } } // MARK: - SingleTarget public enum SingleTarget: WhereExpressionTrait {} // MARK: - CollectionTarget public enum CollectionTarget: WhereExpressionTrait {} } // MARK: - ~ (Where.Expression Creation Operators) // MARK: ~ where D: NSManagedObject public func ~(_ lhs: KeyPath, _ rhs: KeyPath) -> Where.Expression.SingleTarget, V> { return .init(lhs.cs_keyPathString, rhs.cs_keyPathString) } public func ~(_ lhs: KeyPath, _ rhs: KeyPath) -> Where.Expression.SingleTarget, V> { return .init(lhs.cs_keyPathString, rhs.cs_keyPathString) } public func ~(_ lhs: KeyPath, _ rhs: KeyPath) -> Where.Expression.CollectionTarget, V> { return .init(lhs.cs_keyPathString, rhs.cs_keyPathString) } public func ~(_ lhs: KeyPath, _ rhs: KeyPath) -> Where.Expression.CollectionTarget, V> { return .init(lhs.cs_keyPathString, rhs.cs_keyPathString) } public func ~(_ lhs: Where.Expression, _ rhs: KeyPath) -> Where.Expression { return .init(lhs.cs_keyPathString, rhs.cs_keyPathString) } public func ~(_ lhs: Where.Expression, _ rhs: KeyPath) -> Where.Expression.CollectionTarget, V> { return .init(lhs.cs_keyPathString, rhs.cs_keyPathString) } public func ~(_ lhs: Where.Expression, _ rhs: KeyPath) -> Where.Expression { return .init(lhs.cs_keyPathString, rhs.cs_keyPathString) } public func ~(_ lhs: Where.Expression, _ rhs: KeyPath) -> Where.Expression.CollectionTarget, V> { return .init(lhs.cs_keyPathString, rhs.cs_keyPathString) } public func ~(_ lhs: Where.Expression, _ rhs: KeyPath) -> Where.Expression.CollectionTarget, V> { return .init(lhs.cs_keyPathString, rhs.cs_keyPathString) } // MARK: - ~ where D: CoreStoreObject public func ~(_ lhs: KeyPath.ToOne>, _ rhs: KeyPath) -> Where.Expression.SingleTarget, K.ValueType> where K.ObjectType == O { return .init( D.meta[keyPath: lhs].cs_keyPathString, O.meta[keyPath: rhs].cs_keyPathString ) } public func ~(_ lhs: KeyPath.ToOne>, _ rhs: KeyPath) -> Where.Expression.CollectionTarget, K.ValueType> where K.ObjectType == O { return .init( D.meta[keyPath: lhs].cs_keyPathString, O.meta[keyPath: rhs].cs_keyPathString ) } public func ~(_ lhs: Where.Expression, _ rhs: KeyPath) -> Where.Expression where K.ObjectType == O { return .init( lhs.cs_keyPathString, O.meta[keyPath: rhs].cs_keyPathString ) } public func ~(_ lhs: Where.Expression, _ rhs: KeyPath) -> Where.Expression.CollectionTarget, K.ValueType> where K.ObjectType == O { return .init( lhs.cs_keyPathString, O.meta[keyPath: rhs].cs_keyPathString ) } public func ~(_ lhs: Where.Expression, _ rhs: KeyPath) -> Where.Expression where K.ObjectType == O { return .init( lhs.cs_keyPathString, O.meta[keyPath: rhs].cs_keyPathString ) } public func ~(_ lhs: Where.Expression, _ rhs: KeyPath) -> Where.Expression.CollectionTarget, K.ValueType> where K.ObjectType == O { return .init( lhs.cs_keyPathString, O.meta[keyPath: rhs].cs_keyPathString ) } public func ~(_ lhs: Where.Expression, _ rhs: KeyPath) -> Where.Expression.CollectionTarget, KV.ValueType> where KC.ObjectType == D, KV.ObjectType == O { return .init( lhs.cs_keyPathString, O.meta[keyPath: rhs].cs_keyPathString ) } // MARK: - Where.Expression where V: QueryableAttributeType public func == (_ lhs: Where.Expression, _ rhs: V) -> Where { return Where(lhs.cs_keyPathString, isEqualTo: rhs) } public func != (_ lhs: Where.Expression, _ rhs: V) -> Where { return !Where(lhs.cs_keyPathString, isEqualTo: rhs) } public func ~= (_ sequence: S, _ expression: Where.Expression) -> Where where S.Iterator.Element == V { return Where(expression.cs_keyPathString, isMemberOf: sequence) } // MARK: - Where.Expression where V: QueryableAttributeType & Comparable public func < (_ lhs: Where.Expression, _ rhs: V) -> Where { return Where(expression: lhs, function: "<", operand: rhs) } public func <= (_ lhs: Where.Expression, _ rhs: V) -> Where { return Where(expression: lhs, function: "<=", operand: rhs) } public func > (_ lhs: Where.Expression, _ rhs: V) -> Where { return Where(expression: lhs, function: ">", operand: rhs) } public func >= (_ lhs: Where.Expression, _ rhs: V) -> Where { return Where(expression: lhs, function: ">=", operand: rhs) } // MARK: - Where.Expression where V: Optional public func == (_ lhs: Where.Expression, _ rhs: V) -> Where { return Where(lhs.cs_keyPathString, isEqualTo: rhs) } public func == (_ lhs: Where.Expression, _ rhs: V?) -> Where { return Where(lhs.cs_keyPathString, isEqualTo: rhs) } public func != (_ lhs: Where.Expression, _ rhs: V) -> Where { return !Where(lhs.cs_keyPathString, isEqualTo: rhs) } public func != (_ lhs: Where.Expression, _ rhs: V?) -> Where { return !Where(lhs.cs_keyPathString, isEqualTo: rhs) } public func ~= (_ sequence: S, _ expression: Where.Expression) -> Where where S.Iterator.Element == V { return Where(expression.cs_keyPathString, isMemberOf: sequence) } // MARK: - Where.Expression where V: Optional public func < (_ lhs: Where.Expression, _ rhs: V) -> Where { return Where(expression: lhs, function: "<", operand: rhs) } public func <= (_ lhs: Where.Expression, _ rhs: V?) -> Where { return Where(expression: lhs, function: "<=", operand: rhs) } public func > (_ lhs: Where.Expression, _ rhs: V) -> Where { return Where(expression: lhs, function: ">", operand: rhs) } public func >= (_ lhs: Where.Expression, _ rhs: V?) -> Where { return Where(expression: lhs, function: ">=", operand: rhs) } // MARK: - KeyPath where Root: NSManagedObject, Value: AllowedObjectiveCCollectionKeyPathValue extension KeyPath where Root: NSManagedObject, Value: AllowedObjectiveCCollectionKeyPathValue { public func count() -> Where.Expression.CollectionTarget, Int> { return .init(self.cs_keyPathString, "@count") } } // MARK: - Where.Expression where D: NSManagedObject, T == Where.CollectionTarget, V: AllowedObjectiveCCollectionKeyPathValue extension Where.Expression where D: NSManagedObject, T == Where.CollectionTarget, V: AllowedObjectiveCCollectionKeyPathValue { public func count() -> Where.Expression { return .init(self.cs_keyPathString, "@count") } } // MARK: - Where.Expression where D: NSManagedObject, T == Where.CollectionTarget, V: AllowedObjectiveCKeyPathValue extension Where.Expression where D: NSManagedObject, T == Where.CollectionTarget, V: AllowedObjectiveCKeyPathValue { public func any() -> Where.Expression { return .init("ANY " + self.cs_keyPathString) } public func all() -> Where.Expression { return .init("ALL " + self.cs_keyPathString) } public func none() -> Where.Expression { return .init("NONE " + self.cs_keyPathString) } } // MARK: - KeyPath where Root: CoreStoreObject, Value: AllowedObjectiveCCollectionKeyPathValue extension KeyPath where Root: CoreStoreObject, Value: AllowedCoreStoreObjectCollectionKeyPathValue { public func count() -> Where.Expression.CollectionTarget, Int> { return .init(Root.meta[keyPath: self].cs_keyPathString, "@count") } } // MARK: - Where.Expression where D: CoreStoreObject, T == Where.CollectionTarget extension Where.Expression where D: CoreStoreObject, T == Where.CollectionTarget { public func count() -> Where.Expression { return .init(self.cs_keyPathString, "@count") } public func any() -> Where.Expression { return .init("ANY " + self.cs_keyPathString) } public func all() -> Where.Expression { return .init("ALL " + self.cs_keyPathString) } public func none() -> Where.Expression { return .init("NONE " + self.cs_keyPathString) } } // MARK: - Where extension Where { // MARK: FilePrivate fileprivate init(expression: Where.Expression, function: String, operand: V) { self.init("\(expression.cs_keyPathString) \(function) %@", operand.cs_toQueryableNativeType()) } fileprivate init(expression: Where.Expression, function: String, operand: V) { self.init("\(expression.cs_keyPathString) \(function) %@", operand.cs_toQueryableNativeType()) } fileprivate init(expression: Where.Expression, function: String, operand: V?) { if let operand = operand { self.init("\(expression.cs_keyPathString) \(function) %@", operand.cs_toQueryableNativeType()) } else { self.init("\(expression.cs_keyPathString) \(function) nil") } } fileprivate init(expression: Where.Expression, function: String, operand: V?) { if let operand = operand { self.init("\(expression.cs_keyPathString) \(function) %@", operand.cs_toQueryableNativeType()) } else { self.init("\(expression.cs_keyPathString) \(function) nil") } } }