Hermod
A cross-platform, modular and fully GDPR-compliant email archival solution!
Loading...
Searching...
No Matches
Hermod.Hermod Class Reference

The main application class. More...

Public Member Functions

 Hermod (ConfigManager configManager, ILogger logger)
 Main constructor; initialises the object.
 

Package Functions

void StartUp ()
 
async Task< int > Execute ()
 Executes the main business logic of the application.
 
void ShutDown ()
 Shuts Hermod down.
 
void SetTerminalTitle ()
 Sets the terminal's title.
 

Package Attributes

volatile bool m_keepAlive
 The application's logger instance.
 

Properties

List< ICommandCommands [get]
 
bool InteractiveMode [get, set]
 

Private Member Functions

bool TryGetCommand (string cmdName, out ICommand? outCommand)
 
ICommandResult HandleDisplayHelp (params string[] args)
 
ICommandResult HandleDisplayCommandHelp (string arg)
 
ICommandResult HandleLoadPlugin (params string[] args)
 
ICommandResult HandleUnloadPlugin (params string[] args)
 
ICommandResult HandleGetPlugins (params string[] args)
 
string? ShowPrompt ()
 Displays the input prompt.
 
string? GetAutocompletion (string input, int maxDistance=2)
 Attempts to get an auto completed string for the user's input.
 
int LevenshteinDistance (string haystack, string needle)
 
void Console_CancelKeyPress (object? sender, ConsoleCancelEventArgs e)
 Handles SIGINT (CTRL+C)
 
void ConsoleWrite (string message)
 
void ConsoleErrorWrite (string message)
 

Private Attributes

Stack< string > m_previousCommands = new Stack<string>()
 
ConfigManager m_configManager
 
ILogger m_appLogger
 The ConfigManager instance for the application and first-party plugins.
 
CancellationTokenSource m_inputCancellationToken
 A volatile bool indicating whether or not to keep the application alive.
 
List< ICommand >? m_commands = null
 CancellationTokenSource for reading input from the console in interactive mode.
 
readonly object m_consoleLock
 A list of built-in commands.
 

Detailed Description

The main application class.

This class handles all the main logic within the application, such as timing operations, executing commands, handling user input, etc.

Definition at line 11 of file Hermod.Commands.cs.

Constructor & Destructor Documentation

◆ Hermod()

Hermod.Hermod.Hermod ( ConfigManager  configManager,
ILogger  logger 
)
inline

Main constructor; initialises the object.

Parameters
configManagerThe application-wide config instance.
loggerThe application logger.

Definition at line 30 of file Hermod.cs.

30 {
31 m_consoleLock = new object();
32 m_configManager = configManager;
33 m_appLogger = logger;
34 m_keepAlive = true;
35 InteractiveMode = configManager.GetConfig<bool>("Terminal.EnableInteractive");
36 m_inputCancellationToken = new CancellationTokenSource();
37 }
ConfigManager m_configManager
ILogger m_appLogger
The ConfigManager instance for the application and first-party plugins.
bool InteractiveMode
Definition: Hermod.cs:21
readonly object m_consoleLock
A list of built-in commands.
volatile bool m_keepAlive
The application's logger instance.
CancellationTokenSource m_inputCancellationToken
A volatile bool indicating whether or not to keep the application alive.

Member Function Documentation

◆ Console_CancelKeyPress()

void Hermod.Hermod.Console_CancelKeyPress ( object?  sender,
ConsoleCancelEventArgs  e 
)
inlineprivate

Handles SIGINT (CTRL+C)

Parameters
sender
e

Definition at line 239 of file Hermod.cs.

239 {
240 Console.WriteLine(); // make sure the output isn't on the same line as the prompt or output
241 m_appLogger.Warning("Received signal SIGNIT (CTRL+C)!");
242 e.Cancel = true;
243
244 if (InteractiveMode) {
245 m_appLogger.Warning("Hermod is running in interactive mode! Please use \"quit\" command!");
246 ShowPrompt();
247 return;
248 }
249
251 m_keepAlive = false;
252 }
string? ShowPrompt()
Displays the input prompt.
Definition: Hermod.cs:111

◆ ConsoleErrorWrite()

void Hermod.Hermod.ConsoleErrorWrite ( string  message)
inlineprivate

Definition at line 260 of file Hermod.cs.

260 {
261 lock (m_consoleLock) {
262 var prevBackground = Console.BackgroundColor;
263 var prevForegound = Console.ForegroundColor;
264 Console.ForegroundColor = ConsoleColor.Red;
265 Console.Error.WriteLine(message);
266 Console.BackgroundColor = prevBackground;
267 Console.ForegroundColor = prevForegound;
268 }
269 }

◆ ConsoleWrite()

void Hermod.Hermod.ConsoleWrite ( string  message)
inlineprivate

Definition at line 254 of file Hermod.cs.

254 {
255 lock (m_consoleLock) {
256 Console.WriteLine(message);
257 }
258 }

◆ Execute()

async Task< int > Hermod.Hermod.Execute ( )
inlinepackage

Executes the main business logic of the application.

Returns

Definition at line 67 of file Hermod.cs.

67 {
68
69 while (m_keepAlive) {
70
71 if (InteractiveMode) {
72 var promptInput = ShowPrompt();
73 if (string.IsNullOrEmpty(promptInput)) { continue; }
74
75 var splitString = promptInput.Split(' ', '\t');
76 if (TryGetCommand(splitString.First(), out var command)) {
77 if (command is null) { continue; }
78
79 var argArray = new string[splitString.Length - 1];
80 if (argArray.Length > 0) {
81 Array.Copy(splitString[1..], argArray, argArray.Length);
82 }
83
84 var result = await command.ExecuteAsync(argArray);
85
86 if (result is CommandErrorResult errResult && !string.IsNullOrEmpty(result?.Message)) {
87 ConsoleErrorWrite(result.Message);
88
89 m_appLogger.Error(errResult.Result as Exception, $"Command execution failed! Command: { command.Name } { string.Join(' ', argArray) }");
90 } else if (!string.IsNullOrEmpty(result?.Message)) {
91 ConsoleWrite(result.Message);
92 }
93 } else {
94 m_appLogger.Error($"Command \"{splitString[0] }\" not found!");
95 }
96 } else {
97 Thread.Sleep(50);
98 }
99
100 }
101
102 ShutDown();
103
104 return 0; // for the moment; this will also be the exit code for the application.
105 }
void ShutDown()
Shuts Hermod down.
Definition: Hermod.cs:215
void ConsoleErrorWrite(string message)
Definition: Hermod.cs:260
void ConsoleWrite(string message)
Definition: Hermod.cs:254
bool TryGetCommand(string cmdName, out ICommand? outCommand)

◆ GetAutocompletion()

string? Hermod.Hermod.GetAutocompletion ( string  input,
int  maxDistance = 2 
)
inlineprivate

Attempts to get an auto completed string for the user's input.

Parameters
inputThe current input in the interactive prompt.
maxDistanceThe max levenshtein distance for the string to match.
Returns
The matched string or
default
if not matches were found.

Definition at line 176 of file Hermod.cs.

176 {
177 var matches =
178 from command in PluginRegistry.Instance.GetAllCommands()
179 let distance = LevenshteinDistance(command.Name, input)
180 where distance <= maxDistance
181 select command.Name;
182
183 return matches.FirstOrDefault();
184 }
int LevenshteinDistance(string haystack, string needle)
Definition: Hermod.cs:186

References Hermod.PluginFramework.PluginRegistry.GetAllCommands(), and Hermod.PluginFramework.PluginRegistry.Instance.

◆ HandleDisplayCommandHelp()

ICommandResult Hermod.Hermod.HandleDisplayCommandHelp ( string  arg)
inlineprivate

Definition at line 100 of file Hermod.Commands.cs.

100 {
101 var command =
102 PluginRegistry.Instance.GetAllCommands()
103 .FirstOrDefault(c => c.Name.Equals(arg, StringComparison.InvariantCulture));
104 if (command is null) {
105 return new CommandErrorResult($"Command \"{ arg }\" doesn't exist!");
106 }
107
108 return new CommandResult(command.LongDescription, null);
109 }

References Hermod.PluginFramework.PluginRegistry.GetAllCommands(), and Hermod.PluginFramework.PluginRegistry.Instance.

◆ HandleDisplayHelp()

ICommandResult Hermod.Hermod.HandleDisplayHelp ( params string[]  args)
inlineprivate

Definition at line 72 of file Hermod.Commands.cs.

72 {
73 if (args.Length > 0) { return HandleDisplayCommandHelp(args.First()); }
74
75 var sBuilder = new StringBuilder();
76
77 void DumpCommandShortHelp(ICommand command) {
78 sBuilder.AppendLine($"{ command.Name,-30 }{ command.ShortDescription,-80 }");
79 }
80
81 sBuilder.AppendLine("Built-ins:");
82
83 foreach (var command in Commands) {
84 DumpCommandShortHelp(command);
85 }
86 sBuilder.AppendLine();
87
88 sBuilder.AppendLine("Plugin provided:");
89 foreach (var plugin in PluginRegistry.Instance.Plugins) {
90 sBuilder.AppendLine($"{ plugin.PluginName }:");
91 foreach (var command in plugin.PluginCommands) {
92 DumpCommandShortHelp(command);
93 }
94 sBuilder.AppendLine();
95 }
96
97 return new CommandResult(sBuilder.ToString(), null);
98 }
List< ICommand > Commands
ICommandResult HandleDisplayCommandHelp(string arg)

References Hermod.PluginFramework.PluginRegistry.Instance, and Hermod.PluginFramework.PluginRegistry.Plugins.

◆ HandleGetPlugins()

ICommandResult Hermod.Hermod.HandleGetPlugins ( params string[]  args)
inlineprivate

Definition at line 138 of file Hermod.Commands.cs.

138 {
139 var sBuilder = new StringBuilder();
140 PluginRegistry.Instance.Plugins
141 .Select(x => $"{ x.PluginName } v{ x.PluginVersion }")
142 .ToList()
143 .ForEach(x => sBuilder.AppendLine(x));
144
145 return new CommandResult(sBuilder.ToString(), null);
146 }

◆ HandleLoadPlugin()

ICommandResult Hermod.Hermod.HandleLoadPlugin ( params string[]  args)
inlineprivate

Definition at line 111 of file Hermod.Commands.cs.

111 {
112 if (args.Length == 0) {
113 return new CommandErrorResult("Missing input parameters!", new ArgumentNullException(nameof(args), "Input args must not be null or empty!"));
114 }
115
116 foreach (var arg in args) {
117 if (string.IsNullOrEmpty(arg)) {
118 m_appLogger.Warning($"Encountered empty argument in { nameof(HandleLoadPlugin) }. Ignoring...");
119 continue;
120 }
121
122 try {
123 var fInfo = new FileInfo(arg);
124 PluginRegistry.Instance.LoadPlugin(fInfo);
125 PluginRegistry.Instance.LastRegisteredPlugin?.OnStart();
126 } catch (Exception ex) {
127 return new CommandErrorResult("Failed to load one or more plugins!", ex);
128 }
129 }
130
131 return new CommandResult($"Successfully loaded plugin(s).", null);
132 }

References Hermod.PluginFramework.PluginRegistry.Instance, Hermod.PluginFramework.PluginRegistry.LastRegisteredPlugin, Hermod.PluginFramework.PluginRegistry.LoadPlugin(), and Hermod.PluginFramework.IPlugin.OnStart().

◆ HandleUnloadPlugin()

ICommandResult Hermod.Hermod.HandleUnloadPlugin ( params string[]  args)
inlineprivate

Definition at line 134 of file Hermod.Commands.cs.

134 {
135 return new CommandErrorResult("This command has not yet been implemented!");
136 }

◆ LevenshteinDistance()

int Hermod.Hermod.LevenshteinDistance ( string  haystack,
string  needle 
)
inlineprivate

Definition at line 186 of file Hermod.cs.

186 {
187 // Special cases
188 if (haystack == needle) { return 0; }
189 if (haystack.Length == 0) { return needle.Length; }
190 if (needle.Length == 0) { return haystack.Length; }
191
192 // Initialize the distance matrix
193 int[,] distance = new int[haystack.Length + 1, needle.Length + 1];
194 for (int i = 0; i <= haystack.Length; i++) {
195 distance[i, 0] = i;
196 }
197 for (int j = 0; j <= needle.Length; j++) {
198 distance[0, j] = j;
199 }
200
201 // Calculate the distance
202 for (int i = 1; i <= haystack.Length; i++) {
203 for (int j = 1; j <= needle.Length; j++) {
204 int cost = (haystack[i - 1] == needle[j - 1]) ? 0 : 1;
205 distance[i, j] = Math.Min(Math.Min(distance[i - 1, j] + 1, distance[i, j - 1] + 1), distance[i - 1, j - 1] + cost);
206 }
207 }
208 // Return the distance
209 return distance[haystack.Length, needle.Length];
210 }

◆ SetTerminalTitle()

void Hermod.Hermod.SetTerminalTitle ( )
inlinepackage

Sets the terminal's title.

Definition at line 225 of file Hermod.cs.

225 {
226 var version = GetType().Assembly.GetName().Version;
227 var appTitle = new StringBuilder().Append("Hermod ");
228
229 if (InteractiveMode) { appTitle.Append("[interactive] "); }
230
231 Console.Title = $"{ appTitle.ToString() } - v{ version?.Major }.{ version?.MajorRevision }.{ version?.Minor }.{ version?.MinorRevision }";
232 }

◆ ShowPrompt()

string? Hermod.Hermod.ShowPrompt ( )
inlineprivate

Displays the input prompt.

Returns
An awaitable string?.

Definition at line 111 of file Hermod.cs.

111 {
112 const string PROMPT_STR = "hermod > ";
113
114 void WritePrompt(bool newLine = true) {
115 if (newLine) { Console.WriteLine(); }
116 Console.Write(PROMPT_STR);
117 }
118
119 WritePrompt();
120 StringBuilder lineCache = new StringBuilder();
121
122 ConsoleKeyInfo keyCode;
123 var historyStartIndex = m_previousCommands.Count;
124
125 while ((keyCode = Console.ReadKey()).Key != ConsoleKey.Enter) {
126 switch (keyCode.Key) {
127 case ConsoleKey.Tab: {
128 var autocompletedString = GetAutocompletion(lineCache.ToString());
129 if (autocompletedString is null) {
130 Console.Beep();
131 continue;
132 }
133
134 Console.Write(autocompletedString);
135 lineCache.Append(autocompletedString);
136 break;
137 }
138 case ConsoleKey.Backspace:
139 if (lineCache.Length == 0) { Console.Beep(); continue; }
140 lineCache.Remove(lineCache.Length - 1, 1);
141
142 // This surely isn't the best way to handle this, but apparently the terminal doesn't response correctly to \b
143 Console.Write('\b');
144 Console.Write(' ');
145 Console.Write('\b');
146 break;
147 case ConsoleKey.UpArrow:
148 if (m_previousCommands.Count == 0 || historyStartIndex == m_previousCommands.Count) {
149 Console.Beep();
150 continue;
151 }
152
153 lineCache.Clear();
154 lineCache.Append(m_previousCommands.ElementAt(m_previousCommands.Count - historyStartIndex++));
155 Console.CursorLeft = 0;
156 WritePrompt();
157 Console.Write(lineCache.ToString());
158 break;
159 }
160
161 lineCache.Append(keyCode.KeyChar);
162 }
163
164 Console.WriteLine();
165 var cmdString = lineCache.ToString().Trim();
166 m_previousCommands.Push(cmdString);
167 return cmdString;
168 }
string? GetAutocompletion(string input, int maxDistance=2)
Attempts to get an auto completed string for the user's input.
Definition: Hermod.cs:176
Stack< string > m_previousCommands
Definition: Hermod.cs:23

◆ ShutDown()

void Hermod.Hermod.ShutDown ( )
inlinepackage

Shuts Hermod down.

Definition at line 215 of file Hermod.cs.

215 {
216 m_appLogger.Warning("Shutting down plugins...");
217
218 m_appLogger.Warning("Preparing for graceful exit.");
219 m_keepAlive = false;
220 }

◆ StartUp()

void Hermod.Hermod.StartUp ( )
inlinepackage

Definition at line 39 of file Hermod.cs.

39 {
41 m_appLogger.Information("Setting up OS event handlers...");
42 Console.CancelKeyPress += Console_CancelKeyPress;
43
44 m_appLogger.Debug("Setting up PluginRegistry...");
45 PluginRegistry.Instance.AppLogger = m_appLogger;
46 PluginRegistry.Instance.BuiltInCommands = Commands;
47
48 m_appLogger.Information("Loading plugins...");
49 m_appLogger.Information($"Plugin dir: { m_configManager.GetPluginInstallDir() }");
50 foreach (var plugin in m_configManager.GetPluginInstallDir().EnumerateFiles("*.dll")) {
51 m_appLogger.Information($"Attempting to load { plugin.FullName }...");
52 try {
53 PluginRegistry.Instance.LoadPlugin(plugin);
54 } catch (Exception ex) {
55 m_appLogger.Error($"Failed to load assembly { plugin.FullName }!");
56 m_appLogger.Error($"Error: { ex.Message }");
57
58 m_appLogger.Debug(ex.StackTrace);
59 }
60 }
61 }
void SetTerminalTitle()
Sets the terminal's title.
Definition: Hermod.cs:225
void Console_CancelKeyPress(object? sender, ConsoleCancelEventArgs e)
Handles SIGINT (CTRL+C)
Definition: Hermod.cs:239

References Hermod.PluginFramework.PluginRegistry.Instance, and Hermod.PluginFramework.PluginRegistry.LoadPlugin().

◆ TryGetCommand()

bool Hermod.Hermod.TryGetCommand ( string  cmdName,
out ICommand outCommand 
)
inlineprivate

Definition at line 13 of file Hermod.Commands.cs.

13 {
14 bool Predicate(ICommand x) => x.Name.Equals(cmdName, StringComparison.CurrentCulture);
15
16 // built-ins have priority
17 outCommand = Commands.FirstOrDefault(Predicate);
18
19 if (outCommand != null) { return true; }
20
21 foreach (var plugin in PluginRegistry.Instance.Plugins) {
22 outCommand = plugin.PluginCommands.FirstOrDefault(Predicate);
23
24 if (outCommand != null) {
25 return true;
26 }
27 }
28
29 return false;
30 }

References Hermod.PluginFramework.PluginRegistry.Instance, Hermod.Core.Commands.ICommand< T >.Name, and Hermod.PluginFramework.PluginRegistry.Plugins.

Member Data Documentation

◆ m_appLogger

ILogger Hermod.Hermod.m_appLogger
private

The ConfigManager instance for the application and first-party plugins.

Definition at line 15 of file Hermod.Members.cs.

◆ m_commands

List<ICommand>? Hermod.Hermod.m_commands = null
private

CancellationTokenSource for reading input from the console in interactive mode.

Definition at line 21 of file Hermod.Members.cs.

◆ m_configManager

ConfigManager Hermod.Hermod.m_configManager
private

Definition at line 14 of file Hermod.Members.cs.

◆ m_consoleLock

readonly object Hermod.Hermod.m_consoleLock
private

A list of built-in commands.

Definition at line 23 of file Hermod.Members.cs.

◆ m_inputCancellationToken

CancellationTokenSource Hermod.Hermod.m_inputCancellationToken
private

A volatile bool indicating whether or not to keep the application alive.

Definition at line 19 of file Hermod.Members.cs.

◆ m_keepAlive

volatile bool Hermod.Hermod.m_keepAlive
package

The application's logger instance.

Definition at line 17 of file Hermod.Members.cs.

◆ m_previousCommands

Stack<string> Hermod.Hermod.m_previousCommands = new Stack<string>()
private

Definition at line 23 of file Hermod.cs.

Property Documentation

◆ Commands

List<ICommand> Hermod.Hermod.Commands
getprivate

Definition at line 32 of file Hermod.Commands.cs.

◆ InteractiveMode

bool Hermod.Hermod.InteractiveMode
getset

Definition at line 21 of file Hermod.cs.

21{ get; internal set; }

The documentation for this class was generated from the following files: