/// <summary> /// Triggers the listeners of an event. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="eventArgs">The event object to pass to the event listeners.</param> protected virtual void DoDispatch(object sender, BucketEventArgs eventArgs) { PreparationEnvironment(bucket.GetConfig()); PushEventStack(eventArgs); try { foreach (var listener in GetListeners(eventArgs)) { if (eventArgs is WrappedEventArgs wrappedEventArgs) { listener(sender, wrappedEventArgs.GetBaseEventArgs()); } else { listener(sender, eventArgs); } if (eventArgs is IStoppableEvent stoppableEvent && stoppableEvent.IsPropagationStopped) { break; } } } finally { PopEventStack(eventArgs); } }
/// <summary> /// Pops the active event from the stack. /// </summary> protected virtual void PopEventStack(BucketEventArgs eventArgs) { var eventName = eventStack.Pop(); Guard.Requires <UnexpectedException>( eventName == eventArgs.Name, $"The name of the event that pops up is inconsistent with the name of the event that pops up ({eventName} != {eventArgs.Name}), and the event stack is confused."); }
/// <summary> /// Push into the event stack to avoid relying on loop calls. /// </summary> protected virtual void PushEventStack(BucketEventArgs eventArgs) { if (eventStack.Contains(eventArgs.Name)) { throw new RuntimeException($"Circular call to script handler \"{eventArgs.Name}\" detected. {GetEventStackDebugMessage()}"); } eventStack.Push(eventArgs.Name); }
/// <summary> /// Retrieves all listeners for a given event. /// </summary> protected virtual IList <EventHandler> GetListeners(BucketEventArgs eventArgs) { var collection = new List <EventHandler>(); if (listeners.TryGetValue(eventArgs.Name, out IList <EventHandler> handlers)) { collection.AddRange(handlers); } collection.AddRange(GetScriptListeners(eventArgs)); return(collection); }
/// <inheritdoc /> public virtual void Dispatch(string eventName, object sender, EventArgs eventArgs = null) { if (string.IsNullOrEmpty(eventName)) { return; } if (eventArgs is null) { eventArgs = new BucketEventArgs(eventName); } else if (!(eventArgs is BucketEventArgs)) { eventArgs = new WrappedEventArgs(eventName, eventArgs); } DoDispatch(sender, (BucketEventArgs)eventArgs); }
/// <summary> /// Finds all listeners defined as scripts in the package. /// </summary> protected virtual IList <EventHandler> GetScriptListeners(BucketEventArgs eventArgs) { var package = bucket.GetPackage(); var scripts = package?.GetScripts(); if (scripts == null || scripts.Count <= 0 || !scripts.TryGetValue(eventArgs.Name, out string callable) || string.IsNullOrEmpty(callable)) { return(Array.Empty <EventHandler>()); } void ScriptEventHandler(object sender, EventArgs args) { ExecuteScript(callable, sender, eventArgs); } return(new EventHandler[] { ScriptEventHandler }); }
/// <summary> /// Execution script command. /// </summary> protected internal virtual void ExecuteScript(string callable, object sender, BucketEventArgs eventArgs) { var args = string.Join(Str.Space, Arr.Map(eventArgs.GetArguments(), ProcessExecutor.Escape)); var exec = callable + (string.IsNullOrEmpty(args) ? string.Empty : (Str.Space + args)); if (io.IsVerbose) { io.WriteError($"> {eventArgs.Name}: {exec}"); } else { io.WriteError($"> {exec}"); } var possibleLocalBinaries = bucket.GetPackage().GetBinaries() ?? Array.Empty <string>(); foreach (var localBinary in possibleLocalBinaries) { if (!Regex.IsMatch(localBinary, $"\\b{Regex.Escape(callable)}$")) { continue; } var caller = InstallerBinary.DetermineBinaryCaller(localBinary, fileSystem); exec = Regex.Replace(exec, $"^{Regex.Escape(callable)}", $"{caller} {localBinary}"); } var exitCode = process.Execute(exec, out string stdout, out string stderr); if (exitCode != ExitCodes.Normal) { io.WriteError($"<error>Script \"{callable}\" handling the \"{eventArgs.Name}\" event returned with error code: {exitCode}</error>", verbosity: Verbosities.Quiet); throw new ScriptExecutionException($"Error Output: {stderr}", exitCode); } if (io.IsDebug) { io.WriteError(stdout); } }
/// <summary> /// Dispatches an event to all registered listeners. /// </summary> /// <param name="dispatcher">The name of the event.</param> /// <param name="sender">The source of the event.</param> /// <param name="eventArgs">The event object to pass to the event listeners.</param> public static void Dispatch(this IEventDispatcher dispatcher, object sender, BucketEventArgs eventArgs) { dispatcher.Dispatch(eventArgs.Name, sender, eventArgs); }