From 07526a7696606b2bd19827d4be0f8714a0ed9844 Mon Sep 17 00:00:00 2001 From: MrLotU Date: Mon, 28 Sep 2020 13:39:24 +0200 Subject: [PATCH] Make CommandEvent generic --- .github/workflows/test.yml | 6 +- Sources/SwiftHooks/Commands/Command.swift | 68 ++++++++++++------- .../Commands/ExecutableCommand.swift | 18 ++++- 3 files changed, 64 insertions(+), 28 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 63f3b3d..b2b145c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,20 +7,20 @@ jobs: swifthooks_macos: runs-on: macos-latest env: - DEVELOPER_DIR: /Applications/Xcode_11.4_beta.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_12.app/Contents/Developer steps: - uses: actions/checkout@v2 - run: xcrun swift test --enable-test-discovery --sanitize=thread swifthooks_xenial: container: - image: vapor/swift:5.2-xenial + image: swift:5.3-xenial runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - run: swift test --enable-test-discovery --sanitize=thread swifthooks_bionic: container: - image: vapor/swift:5.2-bionic + image: swift:5.3-bionic runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/Sources/SwiftHooks/Commands/Command.swift b/Sources/SwiftHooks/Commands/Command.swift index 076cd5c..36f45f1 100644 --- a/Sources/SwiftHooks/Commands/Command.swift +++ b/Sources/SwiftHooks/Commands/Command.swift @@ -12,22 +12,26 @@ extension EventLoopFuture: AnyFuture { } /// Base command -public struct Command: ExecutableCommand { +public struct Command: ExecutableCommand where EventType: _EventType { public let name: String public let group: String? public let alias: [String] public let hookWhitelist: [HookID] public let permissionChecks: [CommandPermissionChecker] - public let closure: (CommandEvent) -> AnyFuture + public let closure: (EventType) -> AnyFuture public func invoke(on event: CommandEvent) -> EventLoopFuture { - return closure(event).toVoidFuture() + return closure(EventType.from(event)).toVoidFuture() } public func copyWith(name: String, group: String?, alias: [String], hookWhitelist: [HookID], permissionChecks: [CommandPermissionChecker], closure: @escaping Execute) -> Self { return .init(name: name, group: group, alias: alias, hookWhitelist: hookWhitelist, permissionChecks: permissionChecks, closure: closure) } + public func changeEventType(_ to: N.Type) -> Command { + return .init(name: name, group: group, alias: alias, hookWhitelist: hookWhitelist, permissionChecks: permissionChecks, closure: { e in e.eventLoop.makeSucceededFuture(()) }) + } + internal init(name: String, group: String?, alias: [String], hookWhitelist: [HookID], permissionChecks: [CommandPermissionChecker], closure: @escaping Execute) { self.name = name self.group = group @@ -41,7 +45,7 @@ public struct Command: ExecutableCommand { /// /// - parameters: /// - name: Name and trigger of the command. - public init(_ name: String) { + public init(_ name: String) where EventType == CommandEvent { self.name = name self.group = nil self.alias = [] @@ -60,9 +64,9 @@ public struct Command: ExecutableCommand { /// - parameters: /// - t: Type of the argument. /// - name: Name of the argument. - public func arg(_ t: A.Type, named name: String) -> OneArgCommand { + public func arg(_ t: A.Type, named name: String) -> OneArgCommand { let x = GenericCommandArgument(componentType: A.typedName, componentName: name) - return OneArgCommand( + return OneArgCommand( name: self.name, group: group, alias: alias, @@ -93,13 +97,13 @@ fileprivate extension ExecutableCommand { } /// Base command with one argument -public struct OneArgCommand: ExecutableCommand where A: CommandArgumentConvertible { +public struct OneArgCommand: ExecutableCommand where A: CommandArgumentConvertible, EventType: _EventType { public let name: String public let group: String? public let alias: [String] public let hookWhitelist: [HookID] public let permissionChecks: [CommandPermissionChecker] - public let closure: (CommandEvent, A.ResolvedArgument) -> AnyFuture + public let closure: (EventType, A.ResolvedArgument) -> AnyFuture public var readableArguments: String? { arg.description } @@ -110,6 +114,10 @@ public struct OneArgCommand: ExecutableCommand where A: CommandArgumentConver return .init(name: name, group: group, alias: alias, hookWhitelist: hookWhitelist, permissionChecks: permissionChecks, closure: closure, arg: arg) } + public func changeEventType(_ to: N.Type) -> OneArgCommand { + return .init(name: name, group: group, alias: alias, hookWhitelist: hookWhitelist, permissionChecks: permissionChecks, closure: { e, _ in e.eventLoop.makeSucceededFuture(()) }, arg: arg) + } + internal init(name: String, group: String?, alias: [String], hookWhitelist: [HookID], permissionChecks: [CommandPermissionChecker], closure: @escaping Execute, arg: CommandArgument) { self.name = name self.group = group @@ -127,7 +135,7 @@ public struct OneArgCommand: ExecutableCommand where A: CommandArgumentConver do { var idx = 0 let a = try getArg(A.self, &idx, for: arg, on: event) - closure(event, a).toVoidFuture().cascade(to: p) + closure(EventType.from(event), a).toVoidFuture().cascade(to: p) } catch { p.fail(error) } @@ -142,8 +150,8 @@ public struct OneArgCommand: ExecutableCommand where A: CommandArgumentConver /// - parameters: /// - t: Type of the argument. /// - name: Name of the argument. - public func arg(_ t: B.Type, named name: String) -> TwoArgCommand { - return TwoArgCommand( + public func arg(_ t: B.Type, named name: String) -> TwoArgCommand { + return TwoArgCommand( name: self.name, group: group, alias: alias, @@ -157,13 +165,13 @@ public struct OneArgCommand: ExecutableCommand where A: CommandArgumentConver } /// Base command with two arguments -public struct TwoArgCommand: ExecutableCommand where A: CommandArgumentConvertible, B: CommandArgumentConvertible { +public struct TwoArgCommand: ExecutableCommand where A: CommandArgumentConvertible, B: CommandArgumentConvertible, EventType: _EventType { public let name: String public let group: String? public let alias: [String] public let hookWhitelist: [HookID] public let permissionChecks: [CommandPermissionChecker] - public let closure: (CommandEvent, A.ResolvedArgument, B.ResolvedArgument) -> AnyFuture + public let closure: (EventType, A.ResolvedArgument, B.ResolvedArgument) -> AnyFuture public var readableArguments: String? { [argOne, argTwo].map(\.description).joined(separator: " ") } @@ -172,6 +180,10 @@ public struct TwoArgCommand: ExecutableCommand where A: CommandArgumentCon return .init(name: name, group: group, alias: alias, hookWhitelist: hookWhitelist, permissionChecks: permissionChecks, closure: closure, argOne: argOne, argTwo: argTwo) } + public func changeEventType(_ to: N.Type) -> TwoArgCommand { + return .init(name: name, group: group, alias: alias, hookWhitelist: hookWhitelist, permissionChecks: permissionChecks, closure: { e, _, _ in e.eventLoop.makeSucceededFuture(()) }, argOne: argOne, argTwo: argTwo) + } + internal init(name: String, group: String?, alias: [String], hookWhitelist: [HookID], permissionChecks: [CommandPermissionChecker], closure: @escaping Execute, argOne: CommandArgument, argTwo: CommandArgument) { self.name = name self.group = group @@ -198,7 +210,7 @@ public struct TwoArgCommand: ExecutableCommand where A: CommandArgumentCon var idx = 0 let a = try getArg(A.self, &idx, for: argOne, on: event) let b = try getArg(B.self, &idx, for: argTwo, on: event) - self.closure(event, a, b).toVoidFuture().cascade(to: p) + self.closure(EventType.from(event), a, b).toVoidFuture().cascade(to: p) } catch { p.fail(error) } @@ -213,8 +225,8 @@ public struct TwoArgCommand: ExecutableCommand where A: CommandArgumentCon /// - parameters: /// - t: Type of the argument. /// - name: Name of the argument. - public func arg(_ t: C.Type, named name: String) -> ThreeArgCommand { - return ThreeArgCommand( + public func arg(_ t: C.Type, named name: String) -> ThreeArgCommand { + return ThreeArgCommand( name: self.name, group: group, alias: alias, @@ -229,13 +241,13 @@ public struct TwoArgCommand: ExecutableCommand where A: CommandArgumentCon } /// Base command with three arguments -public struct ThreeArgCommand: ExecutableCommand where A: CommandArgumentConvertible, B: CommandArgumentConvertible, C: CommandArgumentConvertible { +public struct ThreeArgCommand: ExecutableCommand where A: CommandArgumentConvertible, B: CommandArgumentConvertible, C: CommandArgumentConvertible, EventType: _EventType { public let name: String public let group: String? public let alias: [String] public let hookWhitelist: [HookID] public let permissionChecks: [CommandPermissionChecker] - public let closure: (CommandEvent, A.ResolvedArgument, B.ResolvedArgument, C.ResolvedArgument) -> AnyFuture + public let closure: (EventType, A.ResolvedArgument, B.ResolvedArgument, C.ResolvedArgument) -> AnyFuture public var readableArguments: String? { [argOne, argTwo, argThree].map(\.description).joined(separator: " ") } @@ -244,6 +256,10 @@ public struct ThreeArgCommand: ExecutableCommand where A: CommandArgume return .init(name: name, group: group, alias: alias, hookWhitelist: hookWhitelist, permissionChecks: permissionChecks, closure: closure, argOne: argOne, argTwo: argTwo, argThree: argThree) } + public func changeEventType(_ to: N.Type) -> ThreeArgCommand { + return .init(name: name, group: group, alias: alias, hookWhitelist: hookWhitelist, permissionChecks: permissionChecks, closure: { e, _, _, _ in e.eventLoop.makeSucceededFuture(()) }, argOne: argOne, argTwo: argTwo, argThree: argThree) + } + internal init(name: String, group: String?, alias: [String], hookWhitelist: [HookID], permissionChecks: [CommandPermissionChecker], closure: @escaping Execute, argOne: CommandArgument, argTwo: CommandArgument, argThree: CommandArgument) { self.name = name self.group = group @@ -276,7 +292,7 @@ public struct ThreeArgCommand: ExecutableCommand where A: CommandArgume let a = try getArg(A.self, &idx, for: argOne, on: event) let b = try getArg(B.self, &idx, for: argTwo, on: event) let c = try getArg(C.self, &idx, for: argThree, on: event) - self.closure(event, a, b, c).toVoidFuture().cascade(to: p) + self.closure(EventType.from(event), a, b, c).toVoidFuture().cascade(to: p) } catch { p.fail(error) } @@ -291,8 +307,8 @@ public struct ThreeArgCommand: ExecutableCommand where A: CommandArgume /// - parameters: /// - t: Type of the argument. /// - name: Name of the argument. - public func arg(_ t: T.Type, named name: String) -> ArrayArgCommand where T: CommandArgumentConvertible { - return ArrayArgCommand( + public func arg(_ t: T.Type, named name: String) -> ArrayArgCommand where T: CommandArgumentConvertible { + return ArrayArgCommand( name: self.name, group: group, alias: alias, @@ -305,13 +321,13 @@ public struct ThreeArgCommand: ExecutableCommand where A: CommandArgume } /// Base command with four or more arguments -public struct ArrayArgCommand: ExecutableCommand { +public struct ArrayArgCommand: ExecutableCommand where EventType: _EventType { public let name: String public let group: String? public let alias: [String] public let hookWhitelist: [HookID] public let permissionChecks: [CommandPermissionChecker] - public let closure: (CommandEvent, Arguments) throws -> AnyFuture + public let closure: (EventType, Arguments) throws -> AnyFuture /// Arguments for this command. public let arguments: [CommandArgument] @@ -323,6 +339,10 @@ public struct ArrayArgCommand: ExecutableCommand { return .init(name: name, group: group, alias: alias, hookWhitelist: hookWhitelist, permissionChecks: permissionChecks, closure: closure, arguments: arguments) } + public func changeEventType(_ to: N.Type) -> ArrayArgCommand { + return .init(name: name, group: group, alias: alias, hookWhitelist: hookWhitelist, permissionChecks: permissionChecks, closure: { e, _ in e.eventLoop.makeSucceededFuture(())}, arguments: arguments) + } + internal init(name: String, group: String?, alias: [String], hookWhitelist: [HookID], permissionChecks: [CommandPermissionChecker], closure: @escaping Execute, arguments: [CommandArgument]) { self.name = name self.group = group @@ -344,7 +364,7 @@ public struct ArrayArgCommand: ExecutableCommand { public func invoke(on event: CommandEvent) -> EventLoopFuture { let p = event.eventLoop.makePromise(of: Void.self) do { - try closure(event, Arguments(arguments)).toVoidFuture().cascade(to: p) + try closure(EventType.from(event), Arguments(arguments)).toVoidFuture().cascade(to: p) } catch { p.fail(error) } diff --git a/Sources/SwiftHooks/Commands/ExecutableCommand.swift b/Sources/SwiftHooks/Commands/ExecutableCommand.swift index fefc893..c027d24 100644 --- a/Sources/SwiftHooks/Commands/ExecutableCommand.swift +++ b/Sources/SwiftHooks/Commands/ExecutableCommand.swift @@ -1,4 +1,5 @@ import class NIO.EventLoopFuture +import protocol NIO.EventLoop /// Base `ExecutableCommand` public protocol _ExecutableCommand: Commands { @@ -27,15 +28,30 @@ public protocol _ExecutableCommand: Commands { func invoke(on event: CommandEvent) -> EventLoopFuture } +public protocol _EventType { + static func from(_ event: CommandEvent) -> Self + var eventLoop: EventLoop { get } +} + +extension CommandEvent: _EventType { + public static func from(_ event: CommandEvent) -> CommandEvent { + return event + } +} + /// Base `ExecutableCommand` public protocol ExecutableCommand: _ExecutableCommand { /// Closure type this command will execute. associatedtype Execute /// Closure to execute when command is invoked. var closure: Execute { get } - + + associatedtype EventType: _EventType + /// Used for FunctionBuilder Copy On Write. func copyWith(name: String, group: String?, alias: [String], hookWhitelist: [HookID], permissionChecks: [CommandPermissionChecker], closure: Execute) -> Self + + func changeEventType(_ to: N.Type) -> Self where Self.EventType == N } public extension ExecutableCommand {