-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
120 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import Foundation | ||
import Shellac | ||
|
||
enum ActionError: Error { | ||
/// Queue is currently busy | ||
case busy | ||
} | ||
|
||
/// ActionGroupQueue represents a queue of actions. | ||
/// Helps ensure that groups of commands are executed correctly | ||
/// and allows for fine tuning of debounce. | ||
class ActionGroupQueue { | ||
let name: String | ||
let interval: TimeInterval | ||
|
||
private let semaphore = DispatchSemaphore(value: 1) | ||
|
||
init(name: String, interval: TimeInterval? = nil) { | ||
self.name = name | ||
guard let interval = interval else { | ||
self.interval = 0 | ||
return | ||
} | ||
self.interval = interval | ||
} | ||
|
||
/// Run action and return output | ||
func run(_ action: Action) throws -> String { | ||
if semaphore.wait(timeout: .now()) == .timedOut { | ||
throw ActionError.busy | ||
} | ||
let output = try shell(with: action.commands, timeout: action.timeout) | ||
if interval == 0 { | ||
semaphore.signal() | ||
return output | ||
} | ||
DispatchQueue.main.asyncAfter(deadline: .now() + interval) { | ||
self.semaphore.signal() | ||
} | ||
return output | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import Shellac | ||
|
||
struct ActionRunnerLogger { | ||
private let logger: Logger | ||
|
||
init(with logger: Logger) { | ||
self.logger = logger | ||
} | ||
|
||
func actionNotFound() { | ||
self.logger.info("action not found, skipping") | ||
} | ||
|
||
func queueNotFound(_ name: String) { | ||
self.logger.error("queue not found: \(name)") | ||
} | ||
|
||
func actionSuccess(_ output: String) { | ||
self.logger.info("command successfully finished".green) | ||
if !output.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { | ||
self.logger.debug("output: \(output)") | ||
} | ||
} | ||
|
||
func actionStarted(_ action: Action) { | ||
var message = "starting action \(action.source.cyan):\(action.kind.blue)" | ||
if let target = action.target { | ||
message += " with \(target.yellow)" | ||
} | ||
message += " on \(action.group.magenta)" | ||
self.logger.info(message) | ||
} | ||
|
||
func actionFailed(_ error: Error) { | ||
if let error = error as? ShellError { | ||
self.logger.error("the process exited with a non-zero status code: \(error.code).") | ||
if !error.error.isEmpty { | ||
self.logger.error("output: \(error.error)") | ||
} | ||
} else if let error = error as? ActionError { | ||
switch error { | ||
case .busy: | ||
self.logger.info("queue busy, skipping") | ||
} | ||
} else { | ||
self.logger.error(String(describing: error)) | ||
} | ||
} | ||
|
||
func eventReceived(_ event: Event) { | ||
self.logger.info("received event \(event.source.cyan):\(event.kind.blue) with \(event.target.yellow)") | ||
} | ||
} | ||
|
||
let kActionLogger = ActionRunnerLogger(with: logger.child(prefix: "Runner")) |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,42 @@ | ||
import Foundation | ||
import Shellac | ||
|
||
typealias ActionGroupQueueMap = [String: ActionGroupQueue] | ||
|
||
class ActionRunner: EventListener { | ||
let handler: ConfigHandler | ||
var queues: [String: ActionQueue] = [:] | ||
var queues: ActionGroupQueueMap = [:] | ||
|
||
init(with handler: ConfigHandler) { | ||
self.handler = handler | ||
for (name, group) in handler.groupMap { | ||
queues[name] = ActionQueue( | ||
queues[name] = ActionGroupQueue( | ||
name: name, | ||
interval: group.debounce | ||
) | ||
} | ||
} | ||
|
||
func handle(_ event: Event) { | ||
kActionLogger.eventReceived(event) | ||
guard let action = handler.findAction( | ||
source: event.source, | ||
kind: event.kind, | ||
target: event.target | ||
) else { | ||
logger.debug("handler not found") | ||
kActionLogger.actionNotFound() | ||
return | ||
} | ||
queues[action.group]?.run(action) | ||
guard let queue = queues[action.group] else { | ||
kActionLogger.queueNotFound(action.group) | ||
return | ||
} | ||
do { | ||
kActionLogger.actionStarted(action) | ||
let output = try queue.run(action) | ||
kActionLogger.actionSuccess(output) | ||
} catch { | ||
kActionLogger.actionFailed(error) | ||
} | ||
} | ||
} |