EnigmaMachineCore 0.1.0
A modular Enigma Machine simulation in C++20
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1
2#include <CLI/CLI.hpp>
3#include <cctype>
4#include <filesystem>
5#include <iostream>
6#include <string>
7
8#include "EnigmaCore.hpp"
9#include "config.hpp"
10
11namespace fs = std::filesystem;
12
19 // Check for local 'assets' directory
20 if (fs::exists("assets") && fs::is_directory("assets")) {
21 return "assets/";
22 }
23
24#ifdef ENIGMA_INSTALL_ASSETS_PATH
25 // Fallback to the installed assets path if defined via CMake
26 if (fs::exists(ENIGMA_INSTALL_ASSETS_PATH) && fs::is_directory(ENIGMA_INSTALL_ASSETS_PATH)) {
27 return ENIGMA_INSTALL_ASSETS_PATH;
28 }
29#endif
30
31 // Default to the header-defined constant if everything else fails
32 return std::string(assetsDir);
33}
34
35struct AppConfig {
37 std::string configPath = ""; // Will be set after assetPath is finalized
38 std::string message = "HELLOWORLD";
39 bool debug = false;
40 bool encode = false;
41 bool decode = false;
42};
43
47class ConsoleObserver : public IEnigmaObserver, public ILogger {
48public:
49 // IEnigmaObserver implementation
50 void onRotorStepped(int rotorIndex, AlphabetIndex position) override {
51 std::cout << "[Event] Rotor " << rotorIndex << " stepped to position " << position << "\n";
52 }
53
54 void onCharEncrypted(char input, char output) override {
55 std::cout << "[Event] Encrypted: " << input << " -> " << output << "\n";
56 }
57
58 // ILogger implementation
59 void log(LogLevel level, std::string_view message) override {
60 std::string prefix = "[Log]";
61 switch (level) {
62 case LogLevel::Debug:
63 prefix = "[DEBUG]";
64 break;
65 case LogLevel::Info:
66 prefix = "[INFO]";
67 break;
68 case LogLevel::Warning:
69 prefix = "[WARN]";
70 break;
71 case LogLevel::Error:
72 prefix = "[ERROR]";
73 break;
74 }
75 std::cout << prefix << " " << message << "\n";
76 }
77};
78
83std::string processMessage(EnigmaMachine& machine, const std::string& input, bool /*debug*/) {
84 std::string output = "";
85 for (char c : input) {
86 if (!std::isalpha(c)) {
87 continue; // Skip non-alphabetic characters
88 }
89 char upperC = std::toupper(c);
90 char res = static_cast<char>(machine.keyTransform(upperC - 'A') + 'A');
91 output += res;
92 }
93 return output;
94}
95
100AppConfig parseArguments(int argc, char** argv) {
101 CLI::App app{"Enigma Machine CLI"};
102 AppConfig config;
103
104 app.add_option("-c,--config", config.configPath, "Path to the TOML configuration file");
105 app.add_option("-a,--assets", config.assetPath, "Base directory for assets (rotors/reflectors)");
106 app.add_option("-m,--message", config.message, "Message to process");
107 app.add_flag("-d,--debug", config.debug, "Enable verbose event logging (rotor steps, encryption details)");
108 app.add_flag("--encode", config.encode, "Encode the message");
109 app.add_flag("--decode", config.decode, "Decode the message");
110
111 try {
112 app.parse(argc, argv);
113 } catch (const CLI::ParseError& e) {
114 std::exit(app.exit(e));
115 }
116
117 // Default to a standard config file if none provided, relative to finalized assetPath
118 if (config.configPath.empty()) {
119 config.configPath = (fs::path(config.assetPath) / "EnigmaMachineConfig1.toml").string();
120 }
121
122 // Default to round-trip if neither is specified
123 if (!config.encode && !config.decode) {
124 config.encode = true;
125 config.decode = true;
126 }
127
128 return config;
129}
130
131void runApplication(const AppConfig& config) {
132 // Create observer first so it can be used during machine initialization
133 ConsoleObserver observer;
134
135 EnigmaMachine machine(config.configPath, config.assetPath, config.debug ? &observer : nullptr);
136
137 // Register observer for events if debug is enabled
138 if (config.debug) {
139 machine.registerObserver(&observer);
140 std::cout << "Debug mode enabled: Observer and Logger registered.\n";
141 // machine.keyTransform(0); // Removed dummy call
142 // We can't call machine.printTransformers() because it's not in the public API.
143 // It's in RotorBox. But Issue 59 is about decoupling.
144 }
145
146 std::string currentMessage = config.message;
147
148 if (config.encode) {
149 std::cout << "Encoding message: " << currentMessage << "\n";
150 currentMessage = processMessage(machine, currentMessage, config.debug);
151 std::cout << "Result (Ciphertext): " << currentMessage << "\n";
152 }
153
154 if (config.decode) {
155 // Re-initialize for decryption (symmetric cipher starting from same state)
156 EnigmaMachine decodeMachine(config.configPath, config.assetPath, config.debug ? &observer : nullptr);
157
158 // Register observer for the decode machine events as well
159 if (config.debug) {
160 decodeMachine.registerObserver(&observer);
161 }
162
163 std::cout << "Decoding message: " << currentMessage << "\n";
164 std::string decoded = processMessage(decodeMachine, currentMessage, config.debug);
165 std::cout << "Result (Plaintext): " << decoded << "\n";
166
167 if (config.encode) {
168 // Verify round-trip success against normalized input
169 std::string cleanOriginal = "";
170 for (char c : config.message)
171 if (std::isalpha(c)) cleanOriginal += std::toupper(c);
172
173 if (decoded == cleanOriginal) {
174 std::cout << "Success: Decoded message matches original!"
175 << "\n";
176 } else {
177 std::cout << "Failure: Decoded message mismatch."
178 << "\n";
179 }
180 }
181 }
182}
183
187int main(int argc, char** argv) {
188 try {
189 AppConfig config = parseArguments(argc, argv);
190 runApplication(config);
191 } catch (const std::exception& e) {
192 std::cerr << "Error: " << e.what() << "\n";
193 return 1;
194 }
195
196 std::cout << "End of program."
197 << "\n";
198 return 0;
199}
Observer that logs Enigma machine events and engine messages to the console.
Definition main.cpp:47
void log(LogLevel level, std::string_view message) override
Definition main.cpp:59
void onRotorStepped(int rotorIndex, AlphabetIndex position) override
Called when a rotor steps.
Definition main.cpp:50
void onCharEncrypted(char input, char output) override
Called when a character is encrypted/decrypted.
Definition main.cpp:54
Class representing the Enigma machine.
void registerObserver(IEnigmaObserver *observer)
Registers an observer to receive notifications.
AlphabetIndex keyTransform(AlphabetIndex input)
Transforms the input key through the rotor box.
Global configuration constants for the Enigma machine.
constexpr std::string_view assetsDir
Default assets base directory.
Definition config.hpp:16
std::string processMessage(EnigmaMachine &machine, const std::string &input, bool)
Processes a message through the Enigma Machine. Transforms each character and optionally prints debug...
Definition main.cpp:83
int main(int argc, char **argv)
Main entry point.
Definition main.cpp:187
void runApplication(const AppConfig &config)
Definition main.cpp:131
AppConfig parseArguments(int argc, char **argv)
Parses command line arguments and populates the AppConfig struct. Exits the program (via CLI11::Exit)...
Definition main.cpp:100
std::string resolveDefaultAssetPath()
Resolves the default asset path based on execution context. Checks for a local 'assets/' folder first...
Definition main.cpp:18
bool encode
Definition main.cpp:40
std::string message
Definition main.cpp:38
bool debug
Definition main.cpp:39
std::string configPath
Definition main.cpp:37
bool decode
Definition main.cpp:41
std::string assetPath
Definition main.cpp:36
Interface for observing Enigma Machine events. Implement this interface to receive notifications abou...