diff --git a/.gitignore b/.gitignore
index 4b1edb6..4d0bd8f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,6 @@
/.build
/Packages
/*.xcodeproj
-.swiftpm
\ No newline at end of file
+.swiftpm
+build/
+docs/
\ No newline at end of file
diff --git a/Sources/SwiftHooks/Commands/Command+Permissions.swift b/Sources/SwiftHooks/Commands/Command+Permissions.swift
index d4f69a0..76e6845 100644
--- a/Sources/SwiftHooks/Commands/Command+Permissions.swift
+++ b/Sources/SwiftHooks/Commands/Command+Permissions.swift
@@ -1,3 +1,8 @@
+/// Check if the given user has the permission to execute the command.
+///
+/// let checker = MyChecker()
+/// guard checker.check(user, canUse: command, on: event) else { throw CommandError.InvalidPermissions }
+///
public protocol CommandPermissionChecker {
/// Check if the given user has the permission to execute the command.
@@ -11,7 +16,7 @@ public protocol CommandPermissionChecker {
/// - event: Event holding the command & related info
///
/// - Returns: Wether or not the user is allowed to execute the command
- func check(_ user: Userable, canUse command: Command, on event: CommandEvent) -> Bool
+ func check(_ user: Userable, canUse command: _ExecutableCommand, on event: CommandEvent) -> Bool
}
/// Checks if a user is allowed to execute based on their ID
@@ -27,7 +32,7 @@ public struct IDChecker: CommandPermissionChecker {
/// List of whitelisted IDs
let ids: [String]
- public func check(_ user: Userable, canUse command: Command, on event: CommandEvent) -> Bool {
+ public func check(_ user: Userable, canUse command: _ExecutableCommand, on event: CommandEvent) -> Bool {
guard let id = user.identifier else { return false }
return ids.contains(id)
}
diff --git a/Sources/SwiftHooks/Commands/Command.swift b/Sources/SwiftHooks/Commands/Command.swift
index bf07d67..6360b16 100644
--- a/Sources/SwiftHooks/Commands/Command.swift
+++ b/Sources/SwiftHooks/Commands/Command.swift
@@ -1,3 +1,4 @@
+/// Base command
public struct Command: ExecutableCommand {
public let name: String
public let group: String?
@@ -23,6 +24,10 @@ public struct Command: ExecutableCommand {
self.closure = closure
}
+ /// Create a new command.
+ ///
+ /// - parameters:
+ /// - name: Name and trigger of the command.
public init(_ name: String) {
self.name = name
self.group = nil
@@ -34,10 +39,18 @@ public struct Command: ExecutableCommand {
public func validate() throws { }
- public func arg(_ t: A.Type, named n: String) -> OneArgCommand {
- let x = GenericCommandArgument(componentType: A.typedName, componentName: n)
+ /// Add an argument to this command
+ ///
+ /// Command("echo")
+ /// .arg(String.Consuming.self, named: "content")
+ ///
+ /// - parameters:
+ /// - t: Type of the argument.
+ /// - name: Name of the argument.
+ public func arg(_ t: A.Type, named name: String) -> OneArgCommand {
+ let x = GenericCommandArgument(componentType: A.typedName, componentName: name)
return OneArgCommand(
- name: name,
+ name: self.name,
group: group,
alias: alias,
hookWhitelist: hookWhitelist,
@@ -72,6 +85,7 @@ fileprivate extension ExecutableCommand {
}
}
+/// Base command with one argument
public struct OneArgCommand: ExecutableCommand where A: CommandArgumentConvertible {
public let name: String
public let group: String?
@@ -107,20 +121,29 @@ public struct OneArgCommand: ExecutableCommand where A: CommandArgumentConver
try closure(hooks, event, a)
}
- public func arg(_ t: B.Type, named: String) -> TwoArgCommand {
+ /// Add an argument to this command
+ ///
+ /// Command("echo")
+ /// .arg(String.Consuming.self, named: "content")
+ ///
+ /// - parameters:
+ /// - t: Type of the argument.
+ /// - name: Name of the argument.
+ public func arg(_ t: B.Type, named name: String) -> TwoArgCommand {
return TwoArgCommand(
- name: name,
+ name: self.name,
group: group,
alias: alias,
hookWhitelist: hookWhitelist,
permissionChecks: permissionChecks,
closure: { _, _, _, _ in },
argOne: arg,
- argTwo: GenericCommandArgument(componentType: B.typedName, componentName: named)
+ argTwo: GenericCommandArgument(componentType: B.typedName, componentName: name)
)
}
}
+/// Base command with two arguments
public struct TwoArgCommand: ExecutableCommand where A: CommandArgumentConvertible, B: CommandArgumentConvertible {
public let name: String
public let group: String?
@@ -163,9 +186,17 @@ public struct TwoArgCommand: ExecutableCommand where A: CommandArgumentCon
try self.closure(hooks, event, a, b)
}
- public func arg(_ t: C.Type, named: String) -> ThreeArgCommand {
+ /// Add an argument to this command
+ ///
+ /// Command("echo")
+ /// .arg(String.Consuming.self, named: "content")
+ ///
+ /// - parameters:
+ /// - t: Type of the argument.
+ /// - name: Name of the argument.
+ public func arg(_ t: C.Type, named name: String) -> ThreeArgCommand {
return ThreeArgCommand(
- name: name,
+ name: self.name,
group: group,
alias: alias,
hookWhitelist: hookWhitelist,
@@ -173,11 +204,12 @@ public struct TwoArgCommand: ExecutableCommand where A: CommandArgumentCon
closure: { _, _, _, _, _ in },
argOne: argOne,
argTwo: argTwo,
- argThree: GenericCommandArgument(componentType: C.typedName, componentName: named)
+ argThree: GenericCommandArgument(componentType: C.typedName, componentName: name)
)
}
}
+/// Base command with three arguments
public struct ThreeArgCommand: ExecutableCommand where A: CommandArgumentConvertible, B: CommandArgumentConvertible, C: CommandArgumentConvertible {
public let name: String
public let group: String?
@@ -226,19 +258,28 @@ public struct ThreeArgCommand: ExecutableCommand where A: CommandArgume
try self.closure(hooks, event, a, b, c)
}
- public func arg(_ t: T.Type, named: String) -> ArrayArgCommand where T: CommandArgumentConvertible {
+ /// Add an argument to this command
+ ///
+ /// Command("echo")
+ /// .arg(String.Consuming.self, named: "content")
+ ///
+ /// - 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(
- name: name,
+ name: self.name,
group: group,
alias: alias,
hookWhitelist: hookWhitelist,
permissionChecks: permissionChecks,
closure: { _, _, _ in },
- arguments: [argOne, argTwo, argThree, GenericCommandArgument(componentType: T.typedName, componentName: named)]
+ arguments: [argOne, argTwo, argThree, GenericCommandArgument(componentType: T.typedName, componentName: name)]
)
}
}
+/// Base command with four or more arguments
public struct ArrayArgCommand: ExecutableCommand {
public let name: String
public let group: String?
@@ -246,6 +287,7 @@ public struct ArrayArgCommand: ExecutableCommand {
public let hookWhitelist: [HookID]
public let permissionChecks: [CommandPermissionChecker]
public let closure: (SwiftHooks, CommandEvent, Arguments) throws -> Void
+ /// Arguments for this command.
public let arguments: [CommandArgument]
public var readableArguments: String? {
@@ -278,19 +320,29 @@ public struct ArrayArgCommand: ExecutableCommand {
try closure(hooks, event, Arguments(arguments))
}
- public func arg(_ t: T.Type, named: String) -> ArrayArgCommand where T: CommandArgumentConvertible {
+ /// Add an argument to this command
+ ///
+ /// Command("echo")
+ /// .arg(String.Consuming.self, named: "content")
+ ///
+ /// - 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(
- name: name,
+ name: self.name,
group: group,
alias: alias,
hookWhitelist: hookWhitelist,
permissionChecks: permissionChecks,
closure: { _, _, _ in },
- arguments: arguments + GenericCommandArgument(componentType: T.typedName, componentName: named)
+ arguments: arguments + GenericCommandArgument(componentType: T.typedName, componentName: name)
)
}
}
+
+/// Arguments container used in `ArrayArgCommand`.
public class Arguments {
let arguments: [CommandArgument]
private(set) var nilArgs: [String]
@@ -300,10 +352,20 @@ public class Arguments {
self.nilArgs = []
}
- public func getArg(named name: String, on event: CommandEvent) throws -> A where A: CommandArgumentConvertible, A.ResolvedArgument == A {
- return try self.get(A.self, named: name, on: event)
- }
-
+ /// Resolve an argument from the command arguments
+ ///
+ /// let reason = try args.get(String.self, named: "reason", on: event)
+ ///
+ /// - parameters:
+ /// - arg: Type to resolve.
+ /// - name: Name of the argument to resolve.
+ /// - event: `CommandEvent` to resolve on.
+ ///
+ /// - throws:
+ /// `CommandError.UnableToConvertArgument` when resolving fails.
+ /// `CommandError.ArgumentNotFound` when no argument is found.
+ ///
+ /// - returns: The resolved argument.
public func get(_ arg: A.Type, named name: String, on event: CommandEvent) throws -> A.ResolvedArgument where A: CommandArgumentConvertible {
guard let foundArg = self.arguments.first(where: {
$0.componentType == arg.typedName &&
diff --git a/Sources/SwiftHooks/Commands/CommandArgumentConvertible.swift b/Sources/SwiftHooks/Commands/CommandArgumentConvertible.swift
index fde9861..e4d72cb 100644
--- a/Sources/SwiftHooks/Commands/CommandArgumentConvertible.swift
+++ b/Sources/SwiftHooks/Commands/CommandArgumentConvertible.swift
@@ -78,7 +78,9 @@ extension CommandArgument {
}
}
-
+/// Any type capable of consuming.
+///
+/// See also `ConsumingCommandArgumentConvertible`
public protocol AnyConsuming {}
protocol AnyOptionalType {
var isNil: Bool { get }
@@ -122,6 +124,10 @@ extension String: CommandArgumentConvertible {
}
public extension String {
+ /// Helper to create consuming `String` arguments.
+ ///
+ /// Command("test")
+ /// .arg(String.Consuming.self, "consumingString")
struct Consuming: ConsumingCommandArgumentConvertible {
public static var typedName: String {
String.typedName
@@ -134,6 +140,18 @@ public extension String {
}
extension FixedWidthInteger {
+
+ /// Attempts to resolve an argument from the provided string.
+ ///
+ /// - parameters:
+ /// - argument: String taken from the message body.
+ /// - event: The `CommandEvent` this argument should be resolved for.
+ ///
+ /// - throws:
+ /// `CommandError.UnableToConvertArgument` when `ResolvedArgument` can not be created from `argument`
+ /// `CommandError.ArgumentNotFound` when no argument is found
+ ///
+ /// - returns: The resolved argument
public static func resolveArgument(_ argument: String, on event: CommandEvent) throws -> Self {
guard let number = Self(argument) else {
throw CommandError.UnableToConvertArgument(argument, "\(self.self)")
@@ -154,6 +172,18 @@ extension UInt32: CommandArgumentConvertible { }
extension UInt64: CommandArgumentConvertible { }
extension BinaryFloatingPoint {
+
+ /// Attempts to resolve an argument from the provided string.
+ ///
+ /// - parameters:
+ /// - argument: String taken from the message body.
+ /// - event: The `CommandEvent` this argument should be resolved for.
+ ///
+ /// - throws:
+ /// `CommandError.UnableToConvertArgument` when `ResolvedArgument` can not be created from `argument`
+ /// `CommandError.ArgumentNotFound` when no argument is found
+ ///
+ /// - returns: The resolved argument
public static func resolveArgument(_ argument: String, on event: CommandEvent) throws -> Self {
guard let number = Double(argument) else {
throw CommandError.UnableToConvertArgument(argument, "\(self.self)")
diff --git a/Sources/SwiftHooks/Commands/ExecutableCommand.swift b/Sources/SwiftHooks/Commands/ExecutableCommand.swift
index a8dd88f..b0fd0c7 100644
--- a/Sources/SwiftHooks/Commands/ExecutableCommand.swift
+++ b/Sources/SwiftHooks/Commands/ExecutableCommand.swift
@@ -1,3 +1,4 @@
+/// Base `ExecutableCommand`
public protocol _ExecutableCommand: Commands {
/// Help description of the command. Used to explain usage to users.
var help: String { get }
@@ -24,7 +25,9 @@ public protocol _ExecutableCommand: Commands {
func invoke(on event: CommandEvent, using hooks: SwiftHooks) throws
}
+/// 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 }
@@ -34,9 +37,17 @@ public protocol ExecutableCommand: _ExecutableCommand {
}
public extension ExecutableCommand {
+ /// Human readable string of arguments required for this command.
+ ///
+ /// command.readableArguments // " [c:String]"
var readableArguments: String? { return nil }
+
+ /// Trigger of the command.
+ ///
+ /// Synonym for `ExecutableCommand.name`
var trigger: String { name }
+ /// Human readable help string explaining command usage.
var help: String {
[group, name, readableArguments].compactMap { $0 }.joined(separator: " ").trimmingCharacters(in: .whitespacesAndNewlines)
}
diff --git a/Sources/SwiftHooks/Commands/SwiftHooks+Commands.swift b/Sources/SwiftHooks/Commands/SwiftHooks+Commands.swift
index bc372ba..566be7f 100644
--- a/Sources/SwiftHooks/Commands/SwiftHooks+Commands.swift
+++ b/Sources/SwiftHooks/Commands/SwiftHooks+Commands.swift
@@ -50,6 +50,7 @@ public enum CommandError: Error {
/// Thrown from argument decoding
case ArgumentNotFound(String)
+ /// Retrieve the localized description for this error.
public var localizedDescription: String {
switch self {
case .ArgumentNotFound(let arg):
@@ -64,15 +65,30 @@ public enum CommandError: Error {
}
}
+/// Event passed in to a command closure containing required data.
public struct CommandEvent {
+ /// Refference to `SwiftHooks` instance dispatching this command.
public let hooks: SwiftHooks
+ /// User that executed the command. Can be downcast to backend specific type.
public let user: Userable
+ /// String arguments passed in to the command. All space separated strings after the commands trigger.
public let args: [String]
+ /// Message that executed the command.
public let message: Messageable
+ /// Full trigger of the command. Either name or name and group.
public let name: String
+ /// Hook that originally dispatched this command. Can be downcast to backend specific type.
public let hook: _Hook
+ /// Command specific logger. Has command trigger set as command metadata by default.
public private(set) var logger: Logger
+ /// Create a new `CommandEvent`
+ ///
+ /// - parameters:
+ /// - hooks: `SwiftHooks` instance dispatching this command.
+ /// - cmd: Command this event is wrapping.
+ /// - msg: Message that executed the command.
+ /// - h: `_Hook` that originally dispatched this command.
public init(hooks: SwiftHooks, cmd: _ExecutableCommand, msg: Messageable, for h: _Hook) {
self.logger = Logger(label: "SwiftHooks.Command")
self.hooks = hooks
diff --git a/Sources/SwiftHooks/Listeners/EventListeners.swift b/Sources/SwiftHooks/Listeners/EventListeners.swift
index 98ac4ba..fbf667d 100644
--- a/Sources/SwiftHooks/Listeners/EventListeners.swift
+++ b/Sources/SwiftHooks/Listeners/EventListeners.swift
@@ -10,6 +10,10 @@ public protocol EventListeners {
public struct Listeners: EventListeners {
let listeners: EventListeners
+ /// Create new `Listeners`
+ ///
+ /// - parameters:
+ /// - listeners: `EventListeners` in this group.
public init(@ListenerBuilder listeners: () -> EventListeners) {
self.listeners = listeners()
}
diff --git a/Sources/SwiftHooks/SwiftHooks.swift b/Sources/SwiftHooks/SwiftHooks.swift
index 8c83757..bac3f3a 100644
--- a/Sources/SwiftHooks/SwiftHooks.swift
+++ b/Sources/SwiftHooks/SwiftHooks.swift
@@ -8,18 +8,27 @@ import Logging
///
/// Hooks and Plugins are both connected to the main SwiftHooks class.
public final class SwiftHooks {
+ /// EventLoopGroup this application runs on.
public let eventLoopGroup: EventLoopGroup
+ /// Wether or not `SwiftHooks` did shutdown.
public private(set) var didShutdown: Bool
private var isBooted: Bool
private var running: EventLoopPromise?
+ /// Config used for this `SwiftHooks` instance.
public let config: SwiftHooksConfig
+ /// Registered `_Hook`s.
public internal(set) var hooks: [_Hook]
+ /// Registered `GlobalListener`s.
public internal(set) var globalListeners: [GlobalEvent: [GlobalEventClosure]]
+ /// Registered `ExecutableCommand`s
public internal(set) var commands: [_ExecutableCommand]
+ /// Registered `Plugin`s.
public internal(set) var plugins: [_Plugin]
+ /// Global `JSONDecoder`
public static let decoder = JSONDecoder()
+ /// Global `JSONEncoder`
public static let encoder = JSONEncoder()
/// Create a new `SwiftHooks` instance
@@ -97,6 +106,7 @@ extension SwiftHooks {
return l
}
+ /// Global SwiftHooks logger
public var logger: Logger {
return type(of: self).logger
}
diff --git a/Sources/SwiftHooks/Types/Channelable.swift b/Sources/SwiftHooks/Types/Channelable.swift
index bc38089..ef4014a 100644
--- a/Sources/SwiftHooks/Types/Channelable.swift
+++ b/Sources/SwiftHooks/Types/Channelable.swift
@@ -1,5 +1,6 @@
import NIO
+/// A generic channel used in `GlobalEvent`s.
public protocol Channelable: PayloadType {
/// Mentions the channel. For example `#channelId`
var mention: String { get }