diff --git a/Package.resolved b/Package.resolved index da70cf3f66..121cd4de92 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,6 +1,15 @@ { - "originHash" : "f688316170c1771e6db10ff04cd9ed454130cdedcd7f04ed4c16bf050623d1ee", + "originHash" : "fd63ba598fb7bdb17467c0e757a41f2afdb4c10f98a605d67f8f8168321d4443", "pins" : [ + { + "identity" : "normalization", + "kind" : "remoteSourceControl", + "location" : "https://github.com/VergeGroup/Normalization", + "state" : { + "revision" : "2cb57bc6ffce81e3ffa2b0781c82f13f00bf1e98", + "version" : "1.0.0" + } + }, { "identity" : "rxswift", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index 290d3cf4d4..ef594c05ea 100644 --- a/Package.swift +++ b/Package.swift @@ -13,7 +13,6 @@ let package = Package( products: [ .library(name: "Verge", targets: ["Verge"]), .library(name: "VergeTiny", targets: ["VergeTiny"]), - .library(name: "VergeNormalization", targets: ["VergeNormalization"]), .library(name: "VergeNormalizationDerived", targets: ["VergeNormalizationDerived"]), .library(name: "VergeRx", targets: ["VergeRx"]), .library(name: "VergeClassic", targets: ["VergeClassic"]), @@ -26,6 +25,7 @@ let package = Package( .package(url: "https://github.com/VergeGroup/swift-concurrency-task-manager", from: "1.1.0"), .package(url: "https://github.com/VergeGroup/TypedIdentifier", from: "2.0.2"), .package(url: "https://github.com/VergeGroup/TypedComparator", from: "1.0.0"), + .package(url: "https://github.com/VergeGroup/Normalization", from: "1.0.0"), /// for testing .package(url: "https://github.com/nalexn/ViewInspector.git", from: "0.10.0"), @@ -63,20 +63,11 @@ let package = Package( "VergeRx" ] ), - .target( - name: "VergeNormalization", - dependencies: [ - "VergeMacros", - .product(name: "TypedComparator", package: "TypedComparator"), - .product(name: "TypedIdentifier", package: "TypedIdentifier"), - .product(name: "HashTreeCollections", package: "swift-collections"), - ] - ), .target( name: "VergeNormalizationDerived", dependencies: [ "Verge", - "VergeNormalization", + .product(name: "Normalization", package: "Normalization"), .product(name: "HashTreeCollections", package: "swift-collections"), ] ), @@ -92,10 +83,6 @@ let package = Package( name: "VergeClassicTests", dependencies: ["VergeClassic"] ), - .testTarget( - name: "VergeNormalizationTests", - dependencies: ["VergeNormalization"] - ), .testTarget( name: "VergeNormalizationDerivedTests", dependencies: ["VergeNormalizationDerived"] diff --git a/Sources/VergeComparator/PackedCompare.swift b/Sources/VergeComparator/PackedCompare.swift deleted file mode 100644 index 799eb34c5c..0000000000 --- a/Sources/VergeComparator/PackedCompare.swift +++ /dev/null @@ -1,11 +0,0 @@ - -@_spi(Internal) -public func areEqual(_ lhs: (repeat each Element), _ rhs: (repeat each Element)) -> Bool { - - for (left, right) in repeat (each lhs, each rhs) { - guard left == right else { return false } - } - return true - -} - diff --git a/Sources/VergeComparator/TypedComparator.swift b/Sources/VergeComparator/TypedComparator.swift deleted file mode 100644 index 60de9d6c49..0000000000 --- a/Sources/VergeComparator/TypedComparator.swift +++ /dev/null @@ -1,141 +0,0 @@ -// -// Copyright (c) 2020 muukii -// -// 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 os.log - -public protocol TypedComparator: Sendable { - associatedtype Input - - @Sendable - func callAsFunction(_ lhs: Input, _ rhs: Input) -> Bool -} - -struct NotEqual: Error {} - -extension TypedComparator { - - public static func equality() -> Self where Self == EqualityComparator { - .init() - } - - /** - TODO: Use typed comparison instead of AnyEqualityComparison. - */ - public static func equality() -> Self where Self == AnyEqualityComparator<(repeat each T)> { - return .init { a, b in - - for (left, right) in repeat (each a, each b) { - guard left == right else { return false } - } - return true - - } - } - - public static func any(_ isEqual: @escaping @Sendable (T, T) -> Bool) -> Self where Self == AnyEqualityComparator { - .init(isEqual) - } - - public static func any(selector: @escaping @Sendable (T) -> U) -> Self where Self == AnyEqualityComparator { - .init { - selector($0) == selector($1) - } - } - - public static func alwaysFalse() -> Self where Self == FalseComparator { - .init() - } - -} - -extension TypedComparator { - - public func and(_ otherExpression: C) -> AndComparator { - .init(self, otherExpression) - } - - public func or(_ otherExpression: C) -> OrComparator { - .init(self, otherExpression) - } -} - -public struct FalseComparator: TypedComparator { - public init() {} - - public func callAsFunction(_ lhs: Input, _ rhs: Input) -> Bool { - return false - } -} - -public struct EqualityComparator: TypedComparator { - - public init() {} - - @Sendable - public func callAsFunction(_ lhs: Input, _ rhs: Input) -> Bool { - lhs == rhs - } - -} - -public struct AnyEqualityComparator: TypedComparator { - - private let closure: @Sendable (Input, Input) -> Bool - - public init(_ isEqual: @escaping @Sendable (Input, Input) -> Bool) { - self.closure = isEqual - } - - public func callAsFunction(_ lhs: Input, _ rhs: Input) -> Bool { - closure(lhs, rhs) - } - -} - -public struct AndComparator: TypedComparator where C1.Input == Input, C2.Input == Input { - - public let c1: C1 - public let c2: C2 - - public init(_ c1: C1, _ c2: C2) { - self.c1 = c1 - self.c2 = c2 - } - - public func callAsFunction(_ lhs: Input, _ rhs: Input) -> Bool { - c1(lhs, rhs) && c2(lhs, rhs) - } -} - -public struct OrComparator: TypedComparator where C1.Input == Input, C2.Input == Input { - public let c1: C1 - public let c2: C2 - - public init(_ c1: C1, _ c2: C2) { - self.c1 = c1 - self.c2 = c2 - } - - public func callAsFunction(_ lhs: Input, _ rhs: Input) -> Bool { - c1(lhs, rhs) || c2(lhs, rhs) - } -} diff --git a/Sources/VergeMacrosPlugin/DatabaseStateMacro.swift b/Sources/VergeMacrosPlugin/DatabaseStateMacro.swift deleted file mode 100644 index b70cac9304..0000000000 --- a/Sources/VergeMacrosPlugin/DatabaseStateMacro.swift +++ /dev/null @@ -1,54 +0,0 @@ -import SwiftSyntax -import SwiftSyntaxBuilder -import SwiftSyntaxMacros - -public struct DatabaseStateMacro { - -} - -extension DatabaseStateMacro: ExtensionMacro { - - public static func expansion(of node: AttributeSyntax, attachedTo declaration: some DeclGroupSyntax, providingExtensionsOf type: some TypeSyntaxProtocol, conformingTo protocols: [TypeSyntax], in context: some MacroExpansionContext) throws -> [ExtensionDeclSyntax] { - - // Decode the expansion arguments. - guard let structDecl = declaration.as(StructDeclSyntax.self) else { - // context.diagnose(OptionSetMacroDiagnostic.requiresStruct.diagnose(at: decl)) - return [] - } - - // If there is an explicit conformance to OptionSet already, don't add one. - if let inheritedTypes = structDecl.inheritanceClause?.inheritedTypes, - inheritedTypes.contains(where: { inherited in inherited.type.trimmedDescription == "DatabaseType" }) { - return [] - } - - let stateTypeExtension: DeclSyntax = - """ - extension \(type.trimmed): DatabaseType {} - """ - - guard let extensionDecl = stateTypeExtension.as(ExtensionDeclSyntax.self) else { - return [] - } - - return [extensionDecl] - - } - -} - -extension DatabaseStateMacro: MemberMacro { - - public static func expansion( - of node: SwiftSyntax.AttributeSyntax, - providingMembersOf declaration: Declaration, - in context: Context - ) throws -> [SwiftSyntax.DeclSyntax] - where Declaration: SwiftSyntax.DeclGroupSyntax, Context: SwiftSyntaxMacros.MacroExpansionContext { - - return [ - "var _backingStorage: DatabaseStorage = .init()" - ] - - } -} diff --git a/Sources/VergeMacrosPlugin/IndexMacro.swift b/Sources/VergeMacrosPlugin/IndexMacro.swift deleted file mode 100644 index 91e213d218..0000000000 --- a/Sources/VergeMacrosPlugin/IndexMacro.swift +++ /dev/null @@ -1,17 +0,0 @@ -import SwiftSyntax -import SwiftSyntaxBuilder -import SwiftSyntaxMacros - -public struct IndexMacro: Macro { - -} - -extension IndexMacro: PeerMacro { - public static func expansion( - of node: SwiftSyntax.AttributeSyntax, - providingPeersOf declaration: some SwiftSyntax.DeclSyntaxProtocol, - in context: some SwiftSyntaxMacros.MacroExpansionContext - ) throws -> [SwiftSyntax.DeclSyntax] { - [] - } -} diff --git a/Sources/VergeMacrosPlugin/NormalizedStorageMacro.swift b/Sources/VergeMacrosPlugin/NormalizedStorageMacro.swift deleted file mode 100644 index 4420583cc5..0000000000 --- a/Sources/VergeMacrosPlugin/NormalizedStorageMacro.swift +++ /dev/null @@ -1,260 +0,0 @@ -import SwiftSyntax -import SwiftSyntaxBuilder -import SwiftSyntaxMacros - -public struct NormalizedStorageMacro: Macro { - -} - -extension NormalizedStorageMacro: ExtensionMacro { - - struct Table { - let node: VariableDeclSyntax - let typeAnnotation: TypeAnnotationSyntax - } - - public static func expansion( - of node: SwiftSyntax.AttributeSyntax, - attachedTo declaration: some SwiftSyntax.DeclGroupSyntax, - providingExtensionsOf type: some SwiftSyntax.TypeSyntaxProtocol, - conformingTo protocols: [SwiftSyntax.TypeSyntax], - in context: some SwiftSyntaxMacros.MacroExpansionContext - ) throws -> [SwiftSyntax.ExtensionDeclSyntax] { - - guard let structDecl = declaration.as(StructDeclSyntax.self) else { - fatalError() - } - - let tables: [Table] = structDecl.memberBlock.members - .compactMap { - $0.decl.as(VariableDeclSyntax.self) - } - .filter { - $0.attributes.contains { - switch $0 { - case .attribute(let attribute): - return attribute.attributeName.description == "Table" - case .ifConfigDecl: - return false - } - } - } - .filter { - guard $0.bindings.count == 1 else { - context.addDiagnostics( - from: MacroError(message: "@Table macro does not support multiple binding, such as `let a, b = 0`"), - node: $0 - ) - return false - } - guard $0.bindings.first!.typeAnnotation != nil else { - context.addDiagnostics( - from: MacroError(message: "@Table macro requires a type annotation, such as `identifier: Type`"), - node: $0 - ) - return false - } - return true - } - .map { - return Table.init(node: $0, typeAnnotation: $0.bindings.first!.typeAnnotation!) - } - - let comparatorExtension = { - - let markerComparators = tables.map { member in - member.node.bindings.first!.pattern.trimmed - } - .map { name in - "guard lhs.\(name).updatedMarker == rhs.\(name).updatedMarker else { return lhs == rhs }" - } - - return (""" - extension \(structDecl.name.trimmed) { - public static func compare(lhs: Self, rhs: Self) -> Bool { - \(raw: markerComparators.joined(separator: "\n")) - return true - } - } - """ as DeclSyntax).cast(ExtensionDeclSyntax.self) - - }() - - let selectorsExtension = { - - let decls = tables.map { member in - """ - public struct TableSelector_\(member.node.bindings.first!.pattern.trimmed): TableSelector { - public typealias _Table = \(member.node.bindings.first!.typeAnnotation!.type.description) - public typealias Entity = _Table.Entity - public typealias Storage = \(structDecl.name.trimmed) - - public let identifier: String = "\(member.node.bindings.first!.pattern.trimmed)" - - public func select(storage: Storage) -> _Table { - storage.\(member.node.bindings.first!.pattern.trimmed) - } - - public init() {} - } - """ - } - - return (""" - extension \(structDecl.name.trimmed) { - \(raw: decls.joined(separator: "\n")) - } - """ as DeclSyntax).cast(ExtensionDeclSyntax.self) - }() - - return [ - comparatorExtension, - selectorsExtension, - (""" - extension \(structDecl.name.trimmed): NormalizedStorageType {} - """ as DeclSyntax).cast(ExtensionDeclSyntax.self), - (""" - extension \(structDecl.name.trimmed): Sendable {} - """ as DeclSyntax).cast(ExtensionDeclSyntax.self), - (""" - extension \(structDecl.name.trimmed): Equatable {} - """ as DeclSyntax).cast(ExtensionDeclSyntax.self), - (""" - extension \(structDecl.name.trimmed) { - } - """ as DeclSyntax).cast(ExtensionDeclSyntax.self) - ] - } - -} - -#if false -/// Add @Table -extension NormalizedStorageMacro: MemberAttributeMacro { - - public static func expansion( - of node: SwiftSyntax.AttributeSyntax, - attachedTo declaration: some SwiftSyntax.DeclGroupSyntax, - providingAttributesFor member: some SwiftSyntax.DeclSyntaxProtocol, - in context: some SwiftSyntaxMacros.MacroExpansionContext - ) throws -> [SwiftSyntax.AttributeSyntax] { - - /** - Add macro attribute to member only Table type. - */ - - if let variableDecl = member.as(VariableDeclSyntax.self) { - - let isGenerated = variableDecl - .bindings - .allSatisfy { - $0.cast(PatternBindingSyntax.self).pattern.cast(IdentifierPatternSyntax.self).identifier - .description.hasPrefix("_$") - } - - if isGenerated { - return [] - } - -// if isComputedProperty(from: variableDecl) { -// return [] -// } - - return [ - "@Table" - ] - - } - - return [] - - } - -} - -#endif - -/// Add member -extension NormalizedStorageMacro: MemberMacro { - - final class RenamingVisitor: SyntaxRewriter { - - init() {} - - override func visit(_ node: IdentifierPatternSyntax) -> PatternSyntax { - return "_$\(node.identifier)" - } - - override func visit(_ node: VariableDeclSyntax) -> DeclSyntax { - - // TODO: make variable private - return super.visit(node) - } - } - - final class StoredPropertyCollector: SyntaxVisitor { - - var storedProperties: [VariableDeclSyntax] = [] - - var onFoundMultipleBindings: () -> Void = {} - - override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind { - - if node.bindingSpecifier == "let" { - storedProperties.append(node) - return super.visit(node) - } - - if node.bindings.count > 1 { - // let a,b,c = 0 - // it's stored - onFoundMultipleBindings() - return super.visit(node) - } - - if node.bindings.first?.accessorBlock == nil { - storedProperties.append(node) - return super.visit(node) - } - - // computed property - - return .visitChildren - } - - } - - static func makeVariableFromConstant(_ node: VariableDeclSyntax) -> VariableDeclSyntax { - var modified = node - modified.bindingSpecifier = "var" - return modified - } - - public static func expansion( - of node: SwiftSyntax.AttributeSyntax, - providingMembersOf declaration: some SwiftSyntax.DeclGroupSyntax, - in context: some SwiftSyntaxMacros.MacroExpansionContext - ) throws -> [SwiftSyntax.DeclSyntax] { - - - let v = StoredPropertyCollector(viewMode: .fixedUp) - v.onFoundMultipleBindings = { - context.addDiagnostics(from: MacroError(message: "Cannot use multiple bindings"), node: node) - } - v.walk(declaration.memberBlock) - - let storageMembers = v.storedProperties - .map(makeVariableFromConstant) - .map { - - let rename = RenamingVisitor() - let renamed = rename.visit($0) - - return renamed - } - - return storageMembers - - } - -} diff --git a/Sources/VergeMacrosPlugin/Plugin.swift b/Sources/VergeMacrosPlugin/Plugin.swift index 5dfae0a262..34a7092c72 100644 --- a/Sources/VergeMacrosPlugin/Plugin.swift +++ b/Sources/VergeMacrosPlugin/Plugin.swift @@ -6,10 +6,6 @@ import SwiftSyntaxMacros @main struct Plugin: CompilerPlugin { let providingMacros: [Macro.Type] = [ - DatabaseStateMacro.self, - NormalizedStorageMacro.self, - TableMacro.self, - IndexMacro.self, KeyPathMap.self, ] } diff --git a/Sources/VergeMacrosPlugin/StateMacro.swift b/Sources/VergeMacrosPlugin/StateMacro.swift deleted file mode 100644 index 475de84fa3..0000000000 --- a/Sources/VergeMacrosPlugin/StateMacro.swift +++ /dev/null @@ -1,20 +0,0 @@ -import SwiftSyntax -import SwiftSyntaxBuilder -import SwiftSyntaxMacros - -public struct StateMacro { - -} -// -//extension StateMacro: MemberMacro { -// -// public static func expansion( -// of node: SwiftSyntax.AttributeSyntax, -// providingMembersOf declaration: Declaration, -// in context: Context -// ) throws -> [SwiftSyntax.DeclSyntax] -// where Declaration: SwiftSyntax.DeclGroupSyntax, Context: SwiftSyntaxMacros.MacroExpansionContext { -// -// } -// -//} diff --git a/Sources/VergeMacrosPlugin/TableMacro.swift b/Sources/VergeMacrosPlugin/TableMacro.swift deleted file mode 100644 index b66078655a..0000000000 --- a/Sources/VergeMacrosPlugin/TableMacro.swift +++ /dev/null @@ -1,43 +0,0 @@ -import SwiftSyntax -import SwiftSyntaxBuilder -import SwiftSyntaxMacros - -public struct TableMacro: Macro { - -} - -extension TableMacro: PeerMacro { - public static func expansion(of node: SwiftSyntax.AttributeSyntax, providingPeersOf declaration: some SwiftSyntax.DeclSyntaxProtocol, in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [SwiftSyntax.DeclSyntax] { - return [] - } - -} - -extension TableMacro: AccessorMacro { - public static func expansion( - of node: SwiftSyntax.AttributeSyntax, - providingAccessorsOf declaration: some SwiftSyntax.DeclSyntaxProtocol, - in context: some SwiftSyntaxMacros.MacroExpansionContext - ) throws -> [SwiftSyntax.AccessorDeclSyntax] { - - func identifier(from node: VariableDeclSyntax) -> TokenSyntax { - node.bindings.first!.cast(PatternBindingSyntax.self).pattern.cast(IdentifierPatternSyntax.self).identifier - } - - let id = identifier(from: declaration.cast(VariableDeclSyntax.self)) - - return [ - // """ - // get { - // _$\(id) - // } - // """, - // """ - // set { - // _$\(id) = newValue - // } - // """ , - ] - } - -} diff --git a/Sources/VergeNormalization/Documentation.docc/Documentation.md b/Sources/VergeNormalization/Documentation.docc/Documentation.md deleted file mode 100644 index 6888bf6223..0000000000 --- a/Sources/VergeNormalization/Documentation.docc/Documentation.md +++ /dev/null @@ -1,151 +0,0 @@ -# ``VergeNormalization`` - -## Overview - -State management plays a crucial role in building efficient and maintainable applications. One of the essential aspects of state management is organizing the data in a way that simplifies its manipulation and usage. This is where normalization becomes vital. - -Normalization is the process of structuring data in a way that eliminates redundancy and ensures data consistency. It is essential in state-management libraries because it significantly reduces the computational complexity of operations and makes it easier to manage the state. - -Docs: - - [VergeNormalization](https://swiftpackageindex.com/VergeGroup/swift-Verge/main/documentation/vergenormalization) - - [VergeNormalizationDerived](https://swiftpackageindex.com/VergeGroup/swift-Verge/main/documentation/vergenormalizationderived) - -Let's take a look at an example to illustrate the difference between normalized and denormalized data structures. - -**Denormalized data structure:** - - -```yaml -posts: - - id: 1 - title: "Post 1" - author: - id: 1 - name: "Alice" - - id: 2 - title: "Post 2" - author: - id: 1 - name: "Alice" - - id: 3 - title: "Post 3" - author: - id: 2 - name: "Bob" -``` - -In the denormalized structure, author data is duplicated in each post, which can lead to inconsistencies and make it harder to manage the state. - -**Normalized data structure:** - - -```yaml -entities: - authors: - 1: - id: 1 - name: "Alice" - 2: - id: 2 - name: "Bob" - posts: - 1: - id: 1 - title: "Post 1" - authorId: 1 - 2: - id: 2 - title: "Post 2" - authorId: 1 - 3: - id: 3 - title: "Post 3" - authorId: 2 -``` - -In the normalized structure, author data is stored separately from posts, eliminating data redundancy and ensuring data consistency. The relationship between posts and authors is represented by the `authorId` field in the posts. - -VergeORM is designed to handle normalization in state-management libraries effectively. By leveraging VergeORM, you can simplify your state management, reduce the computational complexity of operations, and improve the overall performance and maintainability of your application. - -**Defining Entities** - -Here's an example of how to define the `Book` and `Author` entities: - -```swift -struct Book: EntityType { - - typealias TypedIdentifierRawValue = String - - var typedID: TypedID { - .init(rawID) - } - - let rawID: String - var name: String = "initial" - let authorID: Author.EntityID -} - -struct Author: EntityType { - - typealias TypedIdentifierRawValue = String - - var typedID: TypedID { - .init(rawID) - } - - let rawID: String - var name: String = "" -} -``` - -**Defining Database Schema** - -To store the entities in the state, you need to define the database schema: - -```swift -@NormalizedStorage -struct Database { - - @Table - var books: Tables.Hash = .init() - - @Table - var authors: Tables.Hash = .init() -} -``` - -**Embedding the Database in State** - -Embed the `Database` in your application's state: - -```swift -struct RootState: StateType { - var database: Database = .init() -} -``` - -**Storing and Querying Entities** - -Here's an example of how to store and query entities using a `store` property - -```swift -// Storing entities -store.commit { - $0.database.performBatchUpdates { context in - let authors = (0..<10).map { i in - Author(rawID: "\(i)") - } - let result = context.modifying.author.insert(authors) - } -} - -// Querying entities -let book = store.state.database.db.book.find(by: .init("1")) -let author = store.state.database.db.author.find(by: .init("1")) -``` - -In this example, we use `store.commit` to perform batch updates on the database. We insert a new set of authors into the `author` entity table. Then, we use `store.state.database.db` to query the `book` and `author` entities by their identifiers. - -By using VergeNormalization, you can efficiently manage your application state with a normalized data structure, which simplifies your state management, reduces the computational complexity of operations, and improves the overall performance and maintainability of your application. - - diff --git a/Sources/VergeNormalization/EntityType.swift b/Sources/VergeNormalization/EntityType.swift deleted file mode 100644 index a3e02f10ce..0000000000 --- a/Sources/VergeNormalization/EntityType.swift +++ /dev/null @@ -1,10 +0,0 @@ -@_exported import TypedIdentifier - -public protocol EntityType: TypedIdentifiable, Equatable, Sendable { -} - -extension EntityType { - public var entityID: TypedID { - return typedID - } -} diff --git a/Sources/VergeNormalization/Indexes/Indexes.swift b/Sources/VergeNormalization/Indexes/Indexes.swift deleted file mode 100644 index 39484ca791..0000000000 --- a/Sources/VergeNormalization/Indexes/Indexes.swift +++ /dev/null @@ -1,41 +0,0 @@ - -import HashTreeCollections - -public enum Indexes { - - /// A Indexing store - /// - /// { - /// Grouping-ID : [ - /// - Grouped-ID - /// - Grouped-ID - /// - Grouped-ID - /// ], - /// Grouping-ID : [ - /// - Grouped-ID - /// - Grouped-ID - /// - Grouped-ID - /// ] - /// } - /// -// public typealias GroupByEntity = Never - -// public typealias GroupByKey = Never - - /** - Mapping another key to the entity id. - ``` - [ - Key: EntityID - Key: EntityID - Key: EntityID - ] - ``` - */ - public typealias Hash = HashTreeCollections.TreeDictionary - - public typealias Ordered = Array - - public typealias Set = Never - -} diff --git a/Sources/VergeNormalization/NonAtomicCounter.swift b/Sources/VergeNormalization/NonAtomicCounter.swift deleted file mode 100644 index 6191ebbb2c..0000000000 --- a/Sources/VergeNormalization/NonAtomicCounter.swift +++ /dev/null @@ -1,12 +0,0 @@ -/// A container that manages raw value to describe mark as updated. -public struct NonAtomicCounter: Hashable, Sendable { - - private(set) public var value: UInt64 = 0 - - public init() {} - - public mutating func increment() { - value &+= 1 - } - -} diff --git a/Sources/VergeNormalization/NormalizedStorageComparators.swift b/Sources/VergeNormalization/NormalizedStorageComparators.swift deleted file mode 100644 index ff7a379d8c..0000000000 --- a/Sources/VergeNormalization/NormalizedStorageComparators.swift +++ /dev/null @@ -1,62 +0,0 @@ -import TypedComparator - -public enum NormalizedStorageComparators { - - /// True indicates database is not changed - public struct StorageComparator: TypedComparator { - public typealias Input = Storage - - public init() {} - - public func callAsFunction(_ lhs: Input, _ rhs: Input) -> Bool { - Storage.compare(lhs: lhs, rhs: rhs) - } - } - - /// Returns true if the table of the entity in database has no changes. - public struct TableComparator: TypedComparator { - - public typealias Input = Table - - public init() {} - - public func callAsFunction(_ lhs: Input, _ rhs: Input) -> Bool { - guard lhs.updatedMarker == rhs.updatedMarker else { - return lhs == rhs - } - return true - } - - } - - /// Returns true if the updates result does not contain the entity. -// public struct UpdateComparison: Comparison { -// -// public typealias Input = Storage -// -// public let entityID: Entity.TypedID -// -// public init(entityID: Entity.TypedID) { -// self.entityID = entityID -// } -// -// public func callAsFunction(_ lhs: Storage, _ rhs: Storage) -> Bool { -// -// fatalError() -// -//// guard let result = rhs._backingStorage.lastUpdatesResult else { -//// return false -//// } -//// guard !result.wasUpdated(entityID) else { -//// return false -//// } -//// guard !result.wasDeleted(entityID) else { -//// return false -//// } -//// return true -// -// } -// } - -} - diff --git a/Sources/VergeNormalization/NormalizedStorageType.swift b/Sources/VergeNormalization/NormalizedStorageType.swift deleted file mode 100644 index 63535b307e..0000000000 --- a/Sources/VergeNormalization/NormalizedStorageType.swift +++ /dev/null @@ -1,88 +0,0 @@ - -public protocol NormalizedStorageType: Equatable, Sendable { - - /** - Performs any additional operations for updating. - */ - func finalizeTransaction(transaction: inout ModifyingTransaction) - - static func compare(lhs: Self, rhs: Self) -> Bool -} - -extension NormalizedStorageType { - public func finalizeTransaction(transaction: inout ModifyingTransaction) { - - } - - public func table( - _ selector: consuming Selector - ) -> Selector.Table where Selector.Storage == Self { - selector.select(storage: self) - } -} - -extension NormalizedStorageType { - - public func beginBatchUpdates() -> ModifyingTransaction { - let context = ModifyingTransaction(target: self) - return context - } - - public mutating func commitBatchUpdates(transaction: consuming ModifyingTransaction) { - - // middlewareAfter - do { - finalizeTransaction(transaction: &transaction) - } - - // apply - do { - self = transaction.modifying - } - - } - - /// Performs operations to update entities and indexes - /// If can be run on background thread with locking. - /// - /// - Parameter update: - @discardableResult - public mutating func performBatchUpdates(_ update: (inout ModifyingTransaction) throws -> Result) rethrows -> Result { - do { - var transaction = beginBatchUpdates() - let result = try update(&transaction) - commitBatchUpdates(transaction: transaction) - return result - } catch { - throw error - } - } -} - -public enum ModifyingTransactionError: Error { - case aborted - case storedEntityNotFound -} - -/** - The context of the current modifying storage update. - */ -public struct ModifyingTransaction: ~Copyable { - - /// a storage before updated - public let current: NormalizedStorage - - /// a modifiable storage that will be applied. - public var modifying: NormalizedStorage - - init(target: NormalizedStorage) { - self.current = target - self.modifying = target - } - - /// raises an error - public func abort() throws -> Never { - throw ModifyingTransactionError.aborted - } -} - diff --git a/Sources/VergeNormalization/Selector.swift b/Sources/VergeNormalization/Selector.swift deleted file mode 100644 index e1414546e7..0000000000 --- a/Sources/VergeNormalization/Selector.swift +++ /dev/null @@ -1,110 +0,0 @@ - -public protocol TableSelector: Hashable, Sendable { - associatedtype Storage: NormalizedStorageType - associatedtype Table: TableType - func select(storage: consuming Storage) -> Table -} - -public struct KeyPathTableSelector< - Storage: NormalizedStorageType, - Table: TableType ->: TableSelector, Equatable { - - public let keyPath: KeyPath & Sendable - - public init(keyPath: KeyPath & Sendable) { - self.keyPath = keyPath - } - - public func select(storage: consuming Storage) -> Table { - storage[keyPath: keyPath] - } - -} - -extension TableSelector { - - public static func keyPath< - Storage: NormalizedStorageType, - Table: TableType - >( - _ keyPath: KeyPath & Sendable - ) -> Self where Self == KeyPathTableSelector { - return .init(keyPath: keyPath) - } -} - -public protocol StorageSelector: Hashable, Sendable { - associatedtype Source: Equatable - associatedtype Storage: NormalizedStorageType - - func select(source: consuming Source) -> Storage -} - -public struct KeyPathStorageSelector< - Source: Equatable, - Storage: NormalizedStorageType ->: StorageSelector, Equatable { - - public let keyPath: KeyPath & Sendable - - public init(keyPath: KeyPath & Sendable) { - self.keyPath = keyPath - } - - public func select(source: consuming Source) -> Storage { - source[keyPath: keyPath] - } - -} - -extension StorageSelector { - - public static func keyPath< - Source: Equatable, - Storage: NormalizedStorageType - > - ( - _ keyPath: KeyPath & Sendable - ) -> Self where Self == KeyPathStorageSelector { - return .init(keyPath: keyPath) - } - - public consuming func appending<_TableSelector: TableSelector>( - _ tableSelector: consuming _TableSelector - ) - -> AbsoluteTableSelector - { - AbsoluteTableSelector(storage: self, table: tableSelector) - } - -} - -public struct AbsoluteTableSelector< - _StorageSelector: StorageSelector, - _TableSelector: TableSelector ->: Sendable, Hashable where _StorageSelector.Storage == _TableSelector.Storage { - - public typealias Storage = _StorageSelector.Storage - public typealias Entity = _TableSelector.Table.Entity - - public let storageSelector: _StorageSelector - public let tableSelector: _TableSelector - - public init( - storage: consuming _StorageSelector, - table: consuming _TableSelector - ) { - self.storageSelector = storage - self.tableSelector = table - } - - public func storage(source: consuming _StorageSelector.Source) -> _StorageSelector.Storage { - storageSelector.select(source: source) - } - - public func table(source: consuming _StorageSelector.Source) -> _TableSelector.Table { - tableSelector.select(storage: storageSelector.select(source: source)) - } - -} diff --git a/Sources/VergeNormalization/Tables/TableType.swift b/Sources/VergeNormalization/Tables/TableType.swift deleted file mode 100644 index 013e7a43ef..0000000000 --- a/Sources/VergeNormalization/Tables/TableType.swift +++ /dev/null @@ -1,53 +0,0 @@ -import TypedIdentifier - -/// a storage of the entity -public protocol TableType: Equatable, Sendable { - - associatedtype Entity: EntityType - - typealias InsertionResult = VergeNormalization.InsertionResult - - var updatedMarker: NonAtomicCounter { get } - - var count: Int { get } - var isEmpty: Bool { get } - - borrowing func find(by id: consuming Entity.TypedID) -> Entity? - - borrowing func find(in ids: consuming some Sequence) -> [Entity] - - mutating func updateExists( - id: consuming Entity.TypedID, - update: (inout Entity) throws -> Void - ) throws -> Entity - - mutating func updateIfExists( - id: consuming Entity.TypedID, - update: (inout Entity) throws -> Void - ) rethrows -> Entity? - - @discardableResult - mutating func insert(_ entity: consuming Entity) -> InsertionResult - - @discardableResult - mutating func insert(_ addingEntities: consuming some Sequence) -> [InsertionResult] - - mutating func remove(_ id: Entity.TypedID) - - mutating func removeAll() -} - -/// An object indicates result of insertion -/// It can be used to create a getter object. -public struct InsertionResult { - - public var entityID: Entity.TypedID { - entity.typedID - } - - public let entity: Entity - - init(entity: consuming Entity) { - self.entity = entity - } -} diff --git a/Sources/VergeNormalization/Tables/Tables.Hash.swift b/Sources/VergeNormalization/Tables/Tables.Hash.swift deleted file mode 100644 index b3af533fa9..0000000000 --- a/Sources/VergeNormalization/Tables/Tables.Hash.swift +++ /dev/null @@ -1,148 +0,0 @@ -import struct HashTreeCollections.TreeDictionary - -extension Tables { - - /** - A table that stores entities with hash table. - */ - public struct Hash: TableType, Sendable { - - public typealias Entity = Entity - - private var storage: TreeDictionary - public private(set) var updatedMarker = NonAtomicCounter() - - /// The number of entities in table - public var count: Int { - _read { yield storage.count } - } - - /// A Boolean value that indicates whether the dictionary is empty. - public var isEmpty: Bool { - _read { yield storage.isEmpty } - } - - public init(entities: TreeDictionary = .init()) { - self.storage = entities - } - - /// Returns all entity ids that stored. - public borrowing func allIDs() -> TreeDictionary.Keys { - return storage.keys - } - - /// Returns all entity that stored. - public borrowing func allEntities() -> some Collection { - return storage.values - } - - /** - Finds an entity by the identifier of the entity. - - Returns: An entity that found by identifier. Nil if the table does not have that entity. - */ - public borrowing func find(by id: consuming Entity.TypedID) -> Entity? { - return storage[id] - } - - /// Finds entities by set of ids. - /// The order of array would not be sorted, it depends on dictionary's buffer. - /// - /// if ids contains same id, result also contains same element. - /// - Parameter ids: sequence of Entity.ID - public borrowing func find(in ids: consuming some Sequence) -> [Entity] { - - return ids.reduce(into: [Entity]()) { (buf, id) in - guard let entity = storage[id] else { return } - buf.append(entity) - } - } - - /** - Updates the entity that already exsisting in the table. - - - Attention: Please don't change `EntityType.TypedID` value. if we changed, the crash happens (precondition) - */ - @discardableResult - @inline(__always) - public mutating func updateExists( - id: consuming Entity.TypedID, - update: (inout Entity) throws -> Void - ) throws -> Entity { - - guard var current = storage[id] else { - throw ModifyingTransactionError.storedEntityNotFound - } - - try update(¤t) - precondition(current.typedID == id) - storage[id] = current - - updatedMarker.increment() - - return current - } - - /** - Updates the entity that already exsisting in the table. - - - Attention: Please don't change `EntityType.TypedID` value. if we changed, the crash happens (precondition) - */ - @discardableResult - public mutating func updateIfExists( - id: consuming Entity.TypedID, - update: (inout Entity) throws -> Void - ) rethrows -> Entity? { - try? updateExists(id: id, update: update) - } - - /** - Inserts an entity - */ - @discardableResult - public mutating func insert(_ entity: Entity) -> Self.InsertionResult { - - storage[entity.typedID] = entity - - updatedMarker.increment() - - return .init(entity: entity) - } - - /** - Inserts a collection of the entity. - */ - @discardableResult - public mutating func insert(_ addingEntities: consuming some Sequence) -> [Self.InsertionResult] { - - let results = addingEntities.map { entity -> Self.InsertionResult in - storage[entity.typedID] = entity - return .init(entity: entity) - } - - updatedMarker.increment() - - return results - } - - /** - Removes the entity by the identifier. - */ - public mutating func remove(_ id: Entity.TypedID) { - storage.removeValue(forKey: id) - updatedMarker.increment() - } - - /** - Removes the all of the entities in the table. - */ - public mutating func removeAll() { - storage.removeAll(where: { _ in true }) - updatedMarker.increment() - } - - public var values: TreeDictionary.Values { - storage.values - } - - } -} diff --git a/Sources/VergeNormalization/Tables/Tables.swift b/Sources/VergeNormalization/Tables/Tables.swift deleted file mode 100644 index 661f82bef5..0000000000 --- a/Sources/VergeNormalization/Tables/Tables.swift +++ /dev/null @@ -1,3 +0,0 @@ - -public enum Tables { -} diff --git a/Sources/VergeNormalization/VergeNormalization+Macros.swift b/Sources/VergeNormalization/VergeNormalization+Macros.swift deleted file mode 100644 index 3ae9512d83..0000000000 --- a/Sources/VergeNormalization/VergeNormalization+Macros.swift +++ /dev/null @@ -1,49 +0,0 @@ - -//@attached(member, names: arbitrary) -//@attached(memberAttribute) -@attached(extension, conformances: NormalizedStorageType, Equatable, Sendable, names: named(Context), named(BBB), arbitrary) -public macro NormalizedStorage() = #externalMacro(module: "VergeMacrosPlugin", type: "NormalizedStorageMacro") - -@attached(peer) -public macro Table() = #externalMacro(module: "VergeMacrosPlugin", type: "TableMacro") - -@attached(peer) -public macro Index() = #externalMacro(module: "VergeMacrosPlugin", type: "IndexMacro") - -#if DEBUG - -struct A: EntityType { - typealias TypedIdentifierRawValue = String - var typedID: TypedID { - .init("") - } -} - -@NormalizedStorage -struct MyDatabase { - @Table - var user: Tables.Hash - - @Table - var user2: Tables.Hash = .init() - - @Table - var user3: Tables.Hash = .init() - - @Table - var user4: Tables.Hash = .init() -} - -private func play() { - - var db = MyDatabase.init(user: .init()) - - db.performBatchUpdates { t in - t.modifying.user.insert(.init()) - } - -// db.user = .init(identifier: "") -} -//#Database(tables: Table(), Table()) - -#endif diff --git a/Sources/VergeNormalization/VergeNormalization.swift b/Sources/VergeNormalization/VergeNormalization.swift deleted file mode 100644 index 2b09ec9c10..0000000000 --- a/Sources/VergeNormalization/VergeNormalization.swift +++ /dev/null @@ -1 +0,0 @@ -@_exported import TypedIdentifier diff --git a/Sources/VergeNormalizationDerived/EntityWrapper.swift b/Sources/VergeNormalizationDerived/EntityWrapper.swift index 2288677327..bbe1f376fa 100644 --- a/Sources/VergeNormalizationDerived/EntityWrapper.swift +++ b/Sources/VergeNormalizationDerived/EntityWrapper.swift @@ -1,4 +1,4 @@ -import VergeNormalization +import Normalization /// A value that wraps an entity and results of fetching. public struct EntityWrapper: Sendable { diff --git a/Sources/VergeNormalizationDerived/NonNullEntityWrapper.swift b/Sources/VergeNormalizationDerived/NonNullEntityWrapper.swift index be6f92c5c1..6dd1186064 100644 --- a/Sources/VergeNormalizationDerived/NonNullEntityWrapper.swift +++ b/Sources/VergeNormalizationDerived/NonNullEntityWrapper.swift @@ -1,4 +1,4 @@ -import VergeNormalization +import Normalization /// A value that wraps an entity and results of fetching. @dynamicMemberLookup diff --git a/Sources/VergeNormalizationDerived/VergeNormalizationDerived.swift b/Sources/VergeNormalizationDerived/VergeNormalizationDerived.swift index 50c8aee2d7..5d30de6112 100644 --- a/Sources/VergeNormalizationDerived/VergeNormalizationDerived.swift +++ b/Sources/VergeNormalizationDerived/VergeNormalizationDerived.swift @@ -1,2 +1,2 @@ @_exported import Verge -@_exported import VergeNormalization +@_exported import Normalization diff --git a/Tests/VergeMacrosTests/DatabaseMacroTests.swift b/Tests/VergeMacrosTests/DatabaseMacroTests.swift deleted file mode 100644 index 98c7bd150a..0000000000 --- a/Tests/VergeMacrosTests/DatabaseMacroTests.swift +++ /dev/null @@ -1,119 +0,0 @@ -import SwiftSyntaxMacros -import SwiftSyntaxMacrosTestSupport -import XCTest -import MacroTesting - -#if canImport(VergeMacrosPlugin) -import VergeMacrosPlugin - -final class DatabaseMacroTests: XCTestCase { - - override func invokeTest() { - withMacroTesting( - isRecording: false, - macros: [ "NormalizedStorage": NormalizedStorageMacro.self] - ) { - super.invokeTest() - } - } - - func test_table() { - - assertMacro { - #""" - @NormalizedStorage - struct MyDatabase { - @TableAccessor - let user: String - @TableAccessor(hoge) - let user: String - } - """# - } expansion: { - """ - struct MyDatabase { - @TableAccessor - let user: String - @TableAccessor(hoge) - let user: String - - @TableAccessor var _$user: String - - @TableAccessor(hoge) var _$user: String - } - - extension MyDatabase { - public static func compare(lhs: Self, rhs: Self) -> Bool { - - return true - } - } - - extension MyDatabase { - - } - - extension MyDatabase: NormalizedStorageType { - } - - extension MyDatabase: Sendable { - } - - extension MyDatabase: Equatable { - } - - extension MyDatabase { - } - """ - } - - } - - func test_member() { - - assertMacro { - #""" - @NormalizedStorage - struct MyDatabase { - let user: String - } - """# - } expansion: { - """ - struct MyDatabase { - let user: String - - var _$user: String - } - - extension MyDatabase { - public static func compare(lhs: Self, rhs: Self) -> Bool { - - return true - } - } - - extension MyDatabase { - - } - - extension MyDatabase: NormalizedStorageType { - } - - extension MyDatabase: Sendable { - } - - extension MyDatabase: Equatable { - } - - extension MyDatabase { - } - """ - } - - - } - -} - -#endif diff --git a/Tests/VergeMacrosTests/VergeMacrosTests.swift b/Tests/VergeMacrosTests/VergeMacrosTests.swift deleted file mode 100644 index cff6e50cb1..0000000000 --- a/Tests/VergeMacrosTests/VergeMacrosTests.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// File.swift -// -// -// Created by Muukii on 2023/06/17. -// - -import Foundation diff --git a/Tests/VergeNormalizationTests/Entities.swift b/Tests/VergeNormalizationTests/Entities.swift deleted file mode 100644 index 0ff516c72c..0000000000 --- a/Tests/VergeNormalizationTests/Entities.swift +++ /dev/null @@ -1,41 +0,0 @@ -import VergeNormalization - -struct Book: EntityType, Hashable { - - typealias TypedIdentifierRawValue = String - - var typedID: TypedID { - .init(rawID) - } - - let rawID: String - let authorID: Author.TypedID - var name: String = "initial" -} - -struct Author: EntityType { - - typealias TypedIdentifierRawValue = String - - var typedID: TypedID { - .init(rawID) - } - - let rawID: String - var name: String = "" - - static let anonymous: Author = .init(rawID: "anonymous") -} - -@NormalizedStorage -struct MyStorage { - - @Table - var book: Tables.Hash = .init() - @Table - var author: Tables.Hash = .init() - - @Index - var bookIndex: Indexes.Ordered = .init() - -} diff --git a/Tests/VergeNormalizationTests/Source.swift b/Tests/VergeNormalizationTests/Source.swift deleted file mode 100644 index 3d0f7563e4..0000000000 --- a/Tests/VergeNormalizationTests/Source.swift +++ /dev/null @@ -1,277 +0,0 @@ -import VergeNormalization -import XCTest - -final class Tests: XCTestCase { - - func test_insert() { - - var storage = MyStorage() - - storage.performBatchUpdates { t in - t.modifying.author.insert(.init(rawID: "M")) - } - - XCTAssertEqual(storage.author.count, 1) - - } - - func test_init_count() { - - let db = MyStorage() - XCTAssertEqual(db.book.allEntities().count, 0) - } - - func testCommit() { - - var state = MyStorage() - - var transaction = state.beginBatchUpdates() - - let book = Book(rawID: "some", authorID: Author.anonymous.typedID) - transaction.modifying.book.insert(book) - - state.commitBatchUpdates(transaction: transaction) - - let a = state.book - let b = state.book - - XCTAssertEqual(a, b) - - } - - func testEqualityEntityTable() { - - var state = MyStorage() - - state.performBatchUpdates { (context) in - - let book = Book(rawID: "some", authorID: Author.anonymous.typedID) - context.modifying.book.insert(book) - } - - let a = state.book - let b = state.book - - XCTAssertEqual(a, b) - - } - - func testSimpleInsert() { - - var state = MyStorage() - - state.performBatchUpdates { (context) in - - let book = Book(rawID: "some", authorID: Author.anonymous.typedID) - context.modifying.book.insert(book) - } - - XCTAssertEqual(state.book.count, 1) - - } - - func testManagingOrderTable() { - - var state = MyStorage() - - state.performBatchUpdates { (context) in - - let book = Book(rawID: "some", authorID: Author.anonymous.typedID) - context.modifying.book.insert(book) - context.modifying.bookIndex.append(book.typedID) - } - - XCTAssertEqual(state.book.count, 1) - XCTAssertEqual(state.bookIndex.count, 1) - - print(state.bookIndex) - - state.performBatchUpdates { (context) -> Void in - context.modifying.book.remove(Book.TypedID.init("some")) - } - - XCTAssertEqual(state.book.count, 0) - - /// should not be deleted automatically - XCTAssertEqual(state.bookIndex.count, 1) - - } - - func testUpdate() { - - var state = MyStorage() - - let id = Book.TypedID.init("some") - - state.performBatchUpdates { (context) in - - let book = Book(rawID: id.raw, authorID: Author.anonymous.typedID) - context.modifying.book.insert(book) - } - - XCTAssertNotNil(state.book.find(by: id)) - - state.performBatchUpdates { (context) in - - guard var book = context.modifying.book.find(by: id) else { - XCTFail() - return - } - book.name = "hello" - - context.modifying.book.insert(book) - } - - XCTAssertNotNil(state.book.find(by: id)) - XCTAssertNotNil(state.book.find(by: id)!.name == "hello") - - } - - func testUpdateIfExists() { - - var state = MyStorage() - - state.performBatchUpdates { (context) -> Void in - - context.modifying.author.insert(Author(rawID: "muukii", name: "muukii")) - - } - - state.performBatchUpdates { context in - - context.modifying.author.updateIfExists(id: .init("muukii")) { (author) in - XCTAssertEqual(author.name, "muukii") - author.name = "Hiroshi" - } - - context.modifying.author.updateIfExists(id: .init("muukii")) { (author) in - XCTAssertEqual(author.name, "Hiroshi") - author.name = "Kimura" - } - - context.modifying.author.updateIfExists(id: .init("muukii")) { (author) in - XCTAssertEqual(author.name, "Kimura") - } - - } - - } - - func testGetAll() { - - var state = MyStorage() - - state.performBatchUpdates { (context) -> Void in - - context.modifying.author.insert(Author(rawID: "muukii", name: "muukii")) - - } - - state.performBatchUpdates { context in - - XCTAssertEqual(context.modifying.author.allEntities().first?.name, "muukii") - - context.modifying.author.updateIfExists(id: .init("muukii")) { (author) in - XCTAssertEqual(author.name, "muukii") - author.name = "Hiroshi" - } - - XCTAssertEqual(context.modifying.author.allEntities().first?.name, "Hiroshi") - - context.modifying.author.updateIfExists(id: .init("muukii")) { (author) in - XCTAssertEqual(author.name, "Hiroshi") - author.name = "Kimura" - } - - XCTAssertEqual(context.modifying.author.allEntities().first?.name, "Kimura") - - context.modifying.author.updateIfExists(id: .init("muukii")) { (author) in - XCTAssertEqual(author.name, "Kimura") - } - - } - - } - - func testDescription() { - - let authorID = Author.TypedID("author.id") - XCTAssertEqual(authorID.description, "(author.id)") - } - - func testFind() { - - var state = MyStorage() - - state.performBatchUpdates { (context) -> Void in - - for i in 0..<100 { - - let a = Author(rawID: "\(i)", name: "\(i)") - - context.modifying.author.insert(a) - context.modifying.book.insert(Book(rawID: "\(i)", authorID: a.typedID)) - - } - - } - - XCTAssertNotNil( - state.book.find(by: .init("\(1)")) - ) - - XCTAssertEqual( - state.book.find(in: [.init("\(1)"), .init("\(2)")]).count, - 2 - ) - - XCTAssertNotNil( - state.author.find(by: .init("\(1)")) - ) - - XCTAssertEqual( - state.author.find(in: [.init("\(1)"), .init("\(2)")]).count, - 2 - ) - - } - - func testDeletionAndInsertionInTransaction() { - - var state = MyStorage() - - let record = Author(rawID: "1", name: "1") - - state.performBatchUpdates { (context) -> Void in - context.modifying.author.insert(record) - } - - XCTAssertEqual( - state.author.allEntities().count, - 1 - ) - - state.performBatchUpdates { (context) -> Void in - context.modifying.author.removeAll() - context.modifying.author.insert(record) - } - - XCTAssertEqual( - state.author.allEntities().count, - 1 - ) - - state.performBatchUpdates { (context) -> Void in - context.modifying.author.removeAll() - context.modifying.author.insert([record]) - } - - XCTAssertEqual( - state.author.allEntities().count, - 1 - ) - - } - - -} diff --git a/Tests/VergeNormalizationTests/VergeNormalizationTests.swift b/Tests/VergeNormalizationTests/VergeNormalizationTests.swift deleted file mode 100644 index b66746f472..0000000000 --- a/Tests/VergeNormalizationTests/VergeNormalizationTests.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// File.swift -// -// -// Created by Muukii on 2023/08/30. -// - -import Foundation