-
Notifications
You must be signed in to change notification settings - Fork 97
Expand file tree
/
Copy pathActionDispatcher.cs
More file actions
164 lines (149 loc) · 5.92 KB
/
ActionDispatcher.cs
File metadata and controls
164 lines (149 loc) · 5.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using autoShell.Handlers;
using autoShell.Handlers.Settings;
using autoShell.Logging;
using autoShell.Services;
namespace autoShell;
/// <summary>
/// Routes incoming JSON actions to the appropriate handler via a direct dictionary lookup.
/// </summary>
internal class ActionDispatcher
{
private readonly Dictionary<string, IActionHandler> _handlers = new(StringComparer.OrdinalIgnoreCase);
private readonly ILogger _logger;
public ActionDispatcher(ILogger logger)
{
_logger = logger;
}
/// <summary>
/// Gets the names of all registered actions.
/// </summary>
public IEnumerable<string> RegisteredActions => _handlers.Keys;
/// <summary>
/// Creates a <see cref="ActionDispatcher"/> with all production services and handlers registered.
/// </summary>
public static ActionDispatcher Create(ILogger logger)
{
return Create(
logger,
new WindowsRegistryService(),
new WindowsSystemParametersService(),
new WindowsProcessService(),
new WindowsAudioService(logger),
new WindowsAppRegistry(logger),
new WindowsDebuggerService(),
new WindowsBrightnessService(logger),
new WindowsDisplayService(logger),
new WindowsWindowService(logger),
new WindowsNetworkService(logger),
new WindowsVirtualDesktopService(logger)
);
}
/// <summary>
/// Creates a <see cref="ActionDispatcher"/> with the specified services, enabling integration testing
/// with mock services while exercising real handler wiring.
/// </summary>
internal static ActionDispatcher Create(
ILogger logger,
IRegistryService registry,
ISystemParametersService systemParams,
IProcessService process,
IAudioService audio,
IAppRegistry appRegistry,
IDebuggerService debugger,
IBrightnessService brightness,
IDisplayService display,
IWindowService window,
INetworkService network,
IVirtualDesktopService virtualDesktop)
{
var dispatcher = new ActionDispatcher(logger);
dispatcher.Register(
new AudioActionHandler(audio),
new AppActionHandler(appRegistry, process, window, logger),
new WindowActionHandler(appRegistry, window),
new ThemeActionHandler(registry, process, systemParams),
new VirtualDesktopActionHandler(appRegistry, window, virtualDesktop, logger),
new NetworkActionHandler(network, process, logger),
new DisplayActionHandler(display, logger),
new TaskbarSettingsHandler(registry, process),
new DisplaySettingsHandler(registry, process, brightness, logger),
new PersonalizationSettingsHandler(registry, process),
new MouseSettingsHandler(registry, process, systemParams, logger),
new AccessibilitySettingsHandler(registry, process, systemParams),
new PowerSettingsHandler(registry, process),
new FileExplorerSettingsHandler(registry),
new PrivacySettingsHandler(registry),
new SystemSettingsHandler(registry, process),
new SystemActionHandler(process, debugger)
);
var validator = new SchemaValidator(logger);
var schemaDir = Path.Combine(AppContext.BaseDirectory, SchemaValidator.DefaultSchemaRelativePath);
var schemaActions = validator.LoadActionNames(schemaDir);
if (schemaActions.Count > 0)
{
validator.ValidateWiring(schemaActions, dispatcher.RegisteredActions);
}
return dispatcher;
}
/// <summary>
/// Registers one or more handlers with the dispatcher.
/// Throws if an action name is already registered.
/// </summary>
public void Register(params IActionHandler[] handlers)
{
foreach (var handler in handlers)
{
foreach (string action in handler.SupportedActions)
{
if (!_handlers.TryAdd(action, handler))
{
throw new InvalidOperationException(
$"Action '{action}' is already registered by {_handlers[action].GetType().Name}. " +
$"Cannot register again from {handler.GetType().Name}.");
}
}
}
}
/// <summary>
/// Dispatches an action in the format <c>{"actionName":"Volume","parameters":{"targetVolume":50}}</c>
/// to the appropriate handler.
/// </summary>
/// <returns>
/// A <see cref="ActionResult"/> for the executed action. Check <see cref="ActionResult.IsQuit"/>
/// to determine if the caller should exit the interactive loop.
/// </returns>
public ActionResult Dispatch(JsonElement root)
{
string actionName = root.TryGetProperty("actionName", out JsonElement actionNameElement)
? actionNameElement.GetString()
: null;
if (string.IsNullOrEmpty(actionName))
{
return ActionResult.Fail("Missing actionName in action JSON");
}
if (string.Equals(actionName, "quit", StringComparison.OrdinalIgnoreCase))
{
return ActionResult.Quit();
}
JsonElement parameters = root.TryGetProperty("parameters", out JsonElement p)
? p
: JsonDocument.Parse("{}").RootElement.Clone();
try
{
return _handlers.TryGetValue(actionName, out IActionHandler handler)
? handler.Handle(actionName, parameters)
: ActionResult.Fail($"Unknown action: {actionName}");
}
catch (Exception ex)
{
_logger.Error(ex);
return ActionResult.Fail($"Error executing {actionName}: {ex.Message}");
}
}
}