// // FetchCondition.swift // CoreStore // // Copyright © 2017 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 public protocol FetchChainableBuilderType { associatedtype ObjectType: DynamicObject var from: From { get set } var fetchClauses: [FetchClause] { get set } } public protocol QueryChainableBuilderType { associatedtype ObjectType: DynamicObject associatedtype ResultType: SelectResultType var from: From { get set } var select: Select { get set } var queryClauses: [QueryClause] { get set } } @available(OSX 10.12, *) public protocol SectionMonitorBuilderType { associatedtype ObjectType: DynamicObject var from: From { get set } var sectionBy: SectionBy { get set } var fetchClauses: [FetchClause] { get set } } // MARK: - FetchChainBuilder public struct FetchChainBuilder: FetchChainableBuilderType { // MARK: FetchChainableBuilderType public typealias ObjectType = D public var from: From public var fetchClauses: [FetchClause] = [] } // MARK: - QueryChainBuilder public struct QueryChainBuilder: QueryChainableBuilderType { // MARK: QueryChainableBuilderType public typealias ObjectType = D public typealias ResultType = R public var from: From public var select: Select public var queryClauses: [QueryClause] = [] } // MARK: - SectionMonitorChainBuilder @available(OSX 10.12, *) public struct SectionMonitorChainBuilder: SectionMonitorBuilderType { // MARK: SectionMonitorBuilderType public var from: From public var sectionBy: SectionBy public var fetchClauses: [FetchClause] = [] } // MARK: - From public extension From { public func select(_ clause: Select) -> QueryChainBuilder { return .init( from: self, select: clause, queryClauses: [] ) } public func select(_ resultType: R.Type, _ selectTerm: SelectTerm, _ selectTerms: SelectTerm...) -> QueryChainBuilder { return self.select(resultType, [selectTerm] + selectTerms) } public func select(_ resultType: R.Type, _ selectTerms: [SelectTerm]) -> QueryChainBuilder { return .init( from: self, select: .init(selectTerms), queryClauses: [] ) } @available(OSX 10.12, *) public func sectionBy(_ clause: SectionBy) -> SectionMonitorChainBuilder { return .init( from: self, sectionBy: clause, fetchClauses: [] ) } @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPathString) -> SectionMonitorChainBuilder { return self.sectionBy(sectionKeyPath, { $0 }) } @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPathString, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { return .init( from: self, sectionBy: .init(sectionKeyPath, sectionIndexTransformer), fetchClauses: [] ) } public func `where`(_ clause: Where) -> FetchChainBuilder { return self.fetchChain(appending: clause) } public func `where`(format: String, _ args: Any...) -> FetchChainBuilder { return self.fetchChain(appending: Where(format, argumentArray: args)) } public func `where`(format: String, argumentArray: [Any]?) -> FetchChainBuilder { return self.fetchChain(appending: Where(format, argumentArray: argumentArray)) } public func orderBy(_ sortKey: OrderBy.SortKey, _ sortKeys: OrderBy.SortKey...) -> FetchChainBuilder { return self.fetchChain(appending: OrderBy([sortKey] + sortKeys)) } public func tweak(_ fetchRequest: @escaping (NSFetchRequest) -> Void) -> FetchChainBuilder { return self.fetchChain(appending: Tweak(fetchRequest)) } public func appending(_ clause: FetchClause) -> FetchChainBuilder { return self.fetchChain(appending: clause) } public func appending(contentsOf clauses: S) -> FetchChainBuilder where S.Element == FetchClause { return self.fetchChain(appending: clauses) } // MARK: Private private func fetchChain(appending clause: FetchClause) -> FetchChainBuilder { return .init(from: self, fetchClauses: [clause]) } private func fetchChain(appending clauses: S) -> FetchChainBuilder where S.Element == FetchClause { return .init(from: self, fetchClauses: Array(clauses)) } } public extension From where D: NSManagedObject { public func select(_ keyPath: KeyPath) -> QueryChainBuilder { return self.select(R.self, [SelectTerm.attribute(keyPath)]) } @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath) -> SectionMonitorChainBuilder { return self.sectionBy(sectionKeyPath._kvcKeyPathString!, { $0 }) } @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { return self.sectionBy(sectionKeyPath._kvcKeyPathString!, sectionIndexTransformer) } } public extension From where D: CoreStoreObject { public func `where`(_ clause: (D) -> T) -> FetchChainBuilder { return self.fetchChain(appending: clause(D.meta)) } public func select(_ keyPath: KeyPath.Required>) -> QueryChainBuilder { return self.select(R.self, [SelectTerm.attribute(keyPath)]) } public func select(_ keyPath: KeyPath.Optional>) -> QueryChainBuilder { return self.select(R.self, [SelectTerm.attribute(keyPath)]) } public func select(_ keyPath: KeyPath.Required>) -> QueryChainBuilder { return self.select(R.self, [SelectTerm.attribute(keyPath)]) } public func select(_ keyPath: KeyPath.Optional>) -> QueryChainBuilder { return self.select(R.self, [SelectTerm.attribute(keyPath)]) } @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Required>) -> SectionMonitorChainBuilder { return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 }) } @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Optional>) -> SectionMonitorChainBuilder { return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 }) } @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Required>) -> SectionMonitorChainBuilder { return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 }) } @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Optional>) -> SectionMonitorChainBuilder { return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, { $0 }) } @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Required>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer) } @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Optional>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer) } @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Required>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer) } @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPath.Optional>, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { return self.sectionBy(D.meta[keyPath: sectionKeyPath].keyPath, sectionIndexTransformer) } } public extension FetchChainBuilder { public func `where`(_ clause: Where) -> FetchChainBuilder { return self.fetchChain(appending: clause) } public func `where`(format: String, _ args: Any...) -> FetchChainBuilder { return self.fetchChain(appending: Where(format, argumentArray: args)) } public func `where`(format: String, argumentArray: [Any]?) -> FetchChainBuilder { return self.fetchChain(appending: Where(format, argumentArray: argumentArray)) } public func orderBy(_ sortKey: OrderBy.SortKey, _ sortKeys: OrderBy.SortKey...) -> FetchChainBuilder { return self.fetchChain(appending: OrderBy([sortKey] + sortKeys)) } public func tweak(_ fetchRequest: @escaping (NSFetchRequest) -> Void) -> FetchChainBuilder { return self.fetchChain(appending: Tweak(fetchRequest)) } public func appending(_ clause: FetchClause) -> FetchChainBuilder { return self.fetchChain(appending: clause) } public func appending(contentsOf clauses: S) -> FetchChainBuilder where S.Element == FetchClause { return self.fetchChain(appending: clauses) } // MARK: Private private func fetchChain(appending clause: FetchClause) -> FetchChainBuilder { return .init( from: self.from, fetchClauses: self.fetchClauses + [clause] ) } private func fetchChain(appending clauses: S) -> FetchChainBuilder where S.Element == FetchClause { return .init( from: self.from, fetchClauses: self.fetchClauses + Array(clauses) ) } } public extension FetchChainBuilder where D: CoreStoreObject { public func `where`(_ clause: (D) -> T) -> FetchChainBuilder { return self.fetchChain(appending: clause(D.meta)) } } public extension QueryChainBuilder { public func groupBy(_ clause: GroupBy) -> QueryChainBuilder { return self.queryChain(appending: clause) } public func groupBy(_ keyPath: KeyPathString, _ keyPaths: KeyPathString...) -> QueryChainBuilder { return self.groupBy(GroupBy([keyPath] + keyPaths)) } public func groupBy(_ keyPaths: [KeyPathString]) -> QueryChainBuilder { return self.queryChain(appending: GroupBy(keyPaths)) } public func `where`(_ clause: Where) -> QueryChainBuilder { return self.queryChain(appending: clause) } public func `where`(format: String, _ args: Any...) -> QueryChainBuilder { return self.queryChain(appending: Where(format, argumentArray: args)) } public func `where`(format: String, argumentArray: [Any]?) -> QueryChainBuilder { return self.queryChain(appending: Where(format, argumentArray: argumentArray)) } public func orderBy(_ sortKey: OrderBy.SortKey, _ sortKeys: OrderBy.SortKey...) -> QueryChainBuilder { return self.queryChain(appending: OrderBy([sortKey] + sortKeys)) } public func tweak(_ fetchRequest: @escaping (NSFetchRequest) -> Void) -> QueryChainBuilder { return self.queryChain(appending: Tweak(fetchRequest)) } public func appending(_ clause: QueryClause) -> QueryChainBuilder { return self.queryChain(appending: clause) } public func appending(contentsOf clauses: S) -> QueryChainBuilder where S.Element == QueryClause { return self.queryChain(appending: clauses) } // MARK: Private private func queryChain(appending clause: QueryClause) -> QueryChainBuilder { return .init( from: self.from, select: self.select, queryClauses: self.queryClauses + [clause] ) } private func queryChain(appending clauses: S) -> QueryChainBuilder where S.Element == QueryClause { return .init( from: self.from, select: self.select, queryClauses: self.queryClauses + Array(clauses) ) } } public extension QueryChainBuilder where D: NSManagedObject { public func groupBy(_ keyPath: KeyPath) -> QueryChainBuilder { return self.groupBy(GroupBy(keyPath)) } } public extension QueryChainBuilder where D: CoreStoreObject { public func `where`(_ clause: (D) -> T) -> QueryChainBuilder { return self.queryChain(appending: clause(D.meta)) } public func groupBy(_ keyPath: KeyPath.Required>) -> QueryChainBuilder { return self.groupBy(GroupBy(keyPath)) } public func groupBy(_ keyPath: KeyPath.Optional>) -> QueryChainBuilder { return self.groupBy(GroupBy(keyPath)) } public func groupBy(_ keyPath: KeyPath.Required>) -> QueryChainBuilder { return self.groupBy(GroupBy(keyPath)) } public func groupBy(_ keyPath: KeyPath.Optional>) -> QueryChainBuilder { return self.groupBy(GroupBy(keyPath)) } } @available(OSX 10.12, *) public extension SectionMonitorChainBuilder { public func `where`(_ clause: Where) -> SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: clause) } public func `where`(format: String, _ args: Any...) -> SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: Where(format, argumentArray: args)) } public func `where`(format: String, argumentArray: [Any]?) -> SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: Where(format, argumentArray: argumentArray)) } public func orderBy(_ sortKey: OrderBy.SortKey, _ sortKeys: OrderBy.SortKey...) -> SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: OrderBy([sortKey] + sortKeys)) } public func tweak(_ fetchRequest: @escaping (NSFetchRequest) -> Void) -> SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: Tweak(fetchRequest)) } public func appending(_ clause: FetchClause) -> SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: clause) } public func appending(contentsOf clauses: S) -> SectionMonitorChainBuilder where S.Element == FetchClause { return self.sectionMonitorChain(appending: clauses) } // MARK: Private private func sectionMonitorChain(appending clause: FetchClause) -> SectionMonitorChainBuilder { return .init( from: self.from, sectionBy: self.sectionBy, fetchClauses: self.fetchClauses + [clause] ) } private func sectionMonitorChain(appending clauses: S) -> SectionMonitorChainBuilder where S.Element == FetchClause { return .init( from: self.from, sectionBy: self.sectionBy, fetchClauses: self.fetchClauses + Array(clauses) ) } } @available(OSX 10.12, *) public extension SectionMonitorChainBuilder where D: CoreStoreObject { public func `where`(_ clause: (D) -> T) -> SectionMonitorChainBuilder { return self.sectionMonitorChain(appending: clause(D.meta)) } }