Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- Program Name: Locker Tracker
- Programmer Name: Osa George-Mannella
- Programming Language: C++
- Date Project started: Jan 22nd 20 2024
- Date Last edited: Jan 22nd 20 2024
- Program Description: The Locker Tracker tracks the location and owner of each locker within TAS.
- Data Dictionary:
- command_list: A list of objects that represent the commands which the user can run.
- command_line: Represents the text entered by the user and is then parsed and passed into the individual handlers.
- command: Represents the command when iterating through the command_list
- found: Represents whether or not the entered command line matches an existing command.
- selected_function: Represents the ActionCall function of the command that the user entered.
- */
- #include <iostream>
- #include <string>
- #include <unordered_map>
- #include <vector>
- #include <fstream>
- #include <regex>
- #include <sstring>
- #include <functional>
- #include "json.hpp"
- #define RESET "\033[0m" //Resets the colour to default
- #define RED "\033[31m" //Defines the colour of red
- #define GREEN "\033[32m" //Defines the colour of green
- #define YELLOW "\033[33m" //Defines the colour of yellow
- #define BLUE "\033[34m" //Defines the colour of blue
- #define SOFTTAB " " //Defines the softtab to three spaces
- #define OPTIONAL_PREFIX "--" // Defined optional prefix for optional args
- #ifdef __linux__ // Defines the clear command based on what operating system project was compiled with
- #define CLEAR "clear"
- #elif _WIN32
- #define CLEAR "cls"
- #elif __APPLE__
- #define CLEAR "clear"
- #else
- #define CLEAR "clear"
- #endif
- void PrintColor(std::string color, std::string text) // Takes in the colour and string that should be formatted
- {
- std::cout << color << text << RESET << std::endl; // Pipe colour than text than RESET macro into output strea
- }
- int stringToInt(std::string string) // Variable to convert input string to integer
- {
- try
- {
- int myInteger = std::stoi(string); // Casts string to an int
- return myInteger; // Returns the int
- }
- catch (const std::invalid_argument &e) // Catched an error
- {
- std::cerr << "Invalid argument: " << e.what() << std::endl; // Outputs the error
- exit(1); // Exists program
- }
- catch (const std::out_of_range &e) // Catches an error
- {
- std::cerr << "Out of range: " << e.what() << std::endl; // Outputs the error
- exit(1); // Exists the program
- }
- }
- void Error(std::string message, bool exitProgram) // Prints a red error line unless the second argument is true thee it exits the program
- {
- PrintColor(RED, message); // Print the message in red
- if (exitProgram) // If the exit program is true
- exit(1); // Exit the program
- }
- std::vector<std::string> stringSplit(std::string string, char split_char) // creates a vector of strings
- {
- std::istringstream iss(string); // Create a stringstream of the entered string
- std::string token; // Defines the token as a storage media (passed by reference)
- std::vector<std::string> tokens; // This will be returned and is all of the tokens held by string split by split_char
- while (std::getline(iss, token, split_char)) // While this function returns true that pipes in iss, stores in token seperated by split_char
- {
- tokens.push_back(token); // Pushes the token into the token vec
- }
- return tokens; // Returns tokens
- }
- using json = nlohmann::json; // Bringing blohamnn/joson to top scope for ease of reference
- using FunctionType = std::function<void(std::string)>; // Defining the type of function that each command must abide by
- class Command // Defining a command class that will store the attributes of each command when defined for ease of interaction and to ensure a basic template is followed
- {
- public:
- std::string name; // The name of the command
- std::string description; // The description of what the command does
- std::string usage; // How the command should be used i.e what arguments are required/optional
- FunctionType ActionCall; // The function that is called if the command is entered
- Command(std::string newName, std::string newDescription, std::string newUsage, FunctionType func)
- : name(newName), description(newDescription), usage(newUsage), ActionCall(func) {} // Describes the class constructor, nothing fancy, this is boilerplate
- };
- std::vector<Command> command_list; // Defines the command list that will store the vector of commands. This has been defined here to prevent type errors with the help command
- void help(std::string helpArgs) // The help command handler is defined, follows the schema outlined by the class and iterates through the commands in the command_list
- {
- for (const Command cmd : command_list) // Iterates through each command in the vector of commmands
- {
- std::cout << cmd.name << "\t" << GREEN << cmd.description << std::endl // Formats and outputs all of the attributes of the command to display the required data to the user by giving the command name then a tab then changes the colour to green then pipes the description into the output stream followed by a newline where the colour is changed to blue then a tab is inserted followed by "Usage: " then the command usage and lastly the colour is reset to the default grey
- << BLUE << "\tUsage: " << cmd.usage << RESET << std::endl;
- }
- }
- void printOwner(json owner_json) // Outputs the owner of the locker, this is a helper function, NOT A HANDLER!!!
- {
- PrintColor(BLUE, "Owner:"); // Outputs the words "Owner: " (without quotes) in blue
- if (owner_json.is_null())
- {
- PrintColor(RED, std::string(SOFTTAB) + "This Locker has no Owner"); // If the locker selected has no owner assigned to it, the code Displays "Owner: " in blue text followed by "This locker has no owner" which is 3 spaces away and in red text 3
- return;
- }
- std::string student_name(owner_json["name"].get<std::string>()); // Retrieves the students name from the json passed into the function
- int student_grade(owner_json["grade"].get<int>()); // Gets the students grade from the json
- std::cout << SOFTTAB << BLUE << "Name: " << RESET << student_name << std::endl // leaves 3 spaces before "Name:", reseting the text colour to blue and then displays the Name assigned
- << SOFTTAB << BLUE << "Grade: " << RESET << student_grade << std::endl; // leaves 3 spaces before "Grade" and restest the text back to blue, displays the assigned grade followed by a % sign
- }
- void info(std::string infoArgs) // Provides info on a locker number
- {
- std::vector<std::string> tokens = stringSplit(infoArgs, ' '); // Splits input by space in order to isolate the entered locker number
- if (tokens.size() != 2) // If the split returns more than two tokens error
- {
- Error("Incorrect number of arguments", false); // Return incorrect number of args error
- return; // Return and void function
- }
- int lockerNumber = stringToInt(tokens[1]); // Parse the second token into the locker number
- json locker_value; // Define an empty json of name locker_value
- json data = json::parse(std::ifstream("students.json")); // Parses the data from students.json into a json object
- for (auto &el : data) // For every entry in the students json
- {
- if (el["lockerNumber"].get<int>() == lockerNumber) // Checks if the locker number of the item being iterated over is equal to the locker number passed in
- {
- locker_value = el; // If it is set the locker value variable equal to the item being iterated on
- break; // Break from the for loop since the locker has been found
- }
- }
- std::cout << BLUE << "Number #: " << YELLOW << locker_value["lockerNumber"].get<int>() << std::endl // Outputs the locker number of the locker with the "Number #:" part in blue and the number in yellow
- << BLUE << "Hallway: " << RESET << locker_value["Hallway"].get<std::string>() << std::endl; // Outputs the Hallway text in blue then the hallway in which the locker resides in default grey
- printOwner(locker_value["Owner"]); // Calls the printOwner function which will print the owner or the respective error
- }
- void clear(std::string clearArgs)
- {
- std::system(CLEAR); // Executes the clear command which was defined based on which machine compiled the code NOT BASED ON THE MACHINE WHICH THE CODE IS BEING RUN!
- std::cout << GREEN << "Console Cleared" << RESET << std::endl; // Output success message of "Console Cleared" then reset colour back to default
- }
- void list(std::string listArgs) // Defines the list function that will list commands with the respective filter
- {
- std::regex range_regex(R"(--range(\s|=)[0-9]+-[0-9]+)"); // Defines regex that outlines how the range of lockers should be inputted
- std::regex floor_regex(R"(--floor(\s|=)[0-9])"); // Defines regex that outlines how the floor of lockers should be inputted
- std::regex hallway_regex(R"(--hallway(\s|=)[a-zA-z\-]+)"); // Defines regex that outlines how the hallway of lockers should be inputted
- json data = json::parse(std::ifstream("students.json")); // Parses the students.json into a data json
- json data_copy = json::parse(std::ifstream("students.json")); // Parses students.json into a copy of the data variable
- std::smatch match{}; // Defines the match variable that will store the regex match
- if (std::regex_search(listArgs, match, range_regex)) // Checks if the listArgs matches the range regex
- {
- std::string matched_string = match.str().substr(8, 10); // Substrings the match to solely extract the number range inputted
- std::vector<std::string> tokens = stringSplit(matched_string, '-'); // Splits on the - so they can be each assigned their own variable later
- if (tokens.size() != 2) // Checks if the token array is not equal to two which would result in an error
- {
- Error("Incorrect number of arguments", false); // Returns an error
- return; // Exists program
- }
- int offset = 0; // Defines the offset that will later be releavent
- int bottomRange = stringToInt(tokens[0]); // Parses the first token into the bottom range
- int topRange = stringToInt(tokens[1]); // Parses the second token into the top range
- if (bottomRange >= topRange) // If the bottom range is greater than or equal to the top range error
- {
- Error("Top range must be greater than bottom range", false); // Print error
- return; // Return from request
- }
- for (auto &el : data) // Iterate through the entries in data
- {
- int locker_number = el["lockerNumber"].get<int>(); // Get the locker number from the entry
- if (locker_number > topRange || locker_number < bottomRange) // If the locker number is more than the top range and less than the bottom range delete it from the data copy
- {
- data_copy.erase(offset); // Erase from the data copy with index offset
- }
- else
- offset++; // Otherwise increase the offset, this is because we want to ensure we're overshooting the data we want to keep and not deleting solely the head of the vector (its not a vector but it basically is so just use ur imagination) so each time we hit a data that hits our criteria we want to increase the offset to ensure on the following iterations we will be deleting the data AFTER it and not deleting the wanted data.
- }
- }
- if (std::regex_search(listArgs, match, floor_regex)) // Checks if the inputted listArgs meets the floor_regex criteria
- {
- json local_revision_copy = json::parse(data_copy.dump()); // creates a local copy of the data_copy to edit because we want to make sure we iterating through a clone of our editable data when we're editing it and `data` is NOT (or may not be) a clone of `data_clone` since it may have been edited above so for safety we need to make a copy here
- int floor = stringToInt(match.str().substr(8, 11)); // Grabs the substring from 8 to 11 and converts it to an int so we have an int representing the floor
- int offset = 0; // Define the offset like we did above
- for (auto &el : local_revision_copy) // Iterate through the clone we made
- {
- if (el["Floor"].get<int>() != floor) // Check if the floor of the object we're on is not equal to the floor the user entered
- {
- data_copy.erase(offset); // If it's not equal we just erase it on the data_copy
- }
- else
- offset++; // Increase our offset like we did above
- }
- }
- if (std::regex_search(listArgs, match, hallway_regex)) // Checks if the listArgs matches the hallway_regex
- {
- json local_revision_copy = json::parse(data_copy.dump()); // Creates the copy of the data_copy again
- std::string hallway_string(match.str().substr(10, 25)); // Defines substring but no convert to int, just takes it as a string
- int offset = 0; // Defines offset
- for (auto &el : local_revision_copy) // Iterates through our local copy
- {
- if (el["Hallway"].get<std::string>() != hallway_string) // If the hallway is not equal to the hallway entered
- {
- data_copy.erase(offset); // Delete it from the data_copy
- }
- else
- offset++; // Increase offset blah blah blah
- }
- }
- if (!data_copy.size()) // If the data size is equal to zero
- {
- Error("No lockers meet this criteria", false); // Throw this error
- return; // And return
- }
- for (auto &el : data_copy) // Iterate through the data copy we created
- {
- std::cout << YELLOW << el["lockerNumber"].get<int>() << GREEN << " - " << BLUE << el["Hallway"].get<std::string>() << RESET << std::endl; // Outputs locker number in yellow then green dash and the hallway that the locker is in followed by a endline statement
- }
- }
- void exitHandler(std::string exitArgs)
- {
- Error("Bye!", true); // Prints "Bye!" in red and closes program
- }
- int main()
- {
- std::cout << "Type \"help\" for a list of commands" << std::endl; // Outputs the help command for new users
- command_list = {{"exit", "Exists the program", "exit", exitHandler}, {"help", "Displays all of the commands", "help", help}, {"list", "Lists all the lockers which follow the inputted parameters as shown below", "list --range=(int-int) --floor=(int) --hallway=(string)", list}, {"info", "Lists all the info on the locker owner, requires user to input locker number", "info (int)", info}, {"clear", "Clears the console", "clear", clear}}; // Defines all of the commands that the user can call with their name, description, usage and ActionCall function that will handle the command.
- while (true) // Defines a true look to ask the user unlimited times
- {
- std::string command_line; // Defines the command_line that will store the text entered by the user.
- std::cout << "> "; // Outputs line prefix
- std::getline(std::cin, command_line); // Uses getline to get the input from the user to ensure the entire line is captured.
- bool found = false; // Set found to false as a flag so it can later be set to true if a command matching the input is found.
- FunctionType selected_function; // Defines the selected function that will store the handler function.
- for (const Command command : command_list) // Iterates through the command_list
- {
- if (command.name == command_line.substr(0, command.name.length())) // Checks if the command name is equal to the first x letters of the comnmand line x being the length of the command.
- {
- found = true; // Sets found to true to prevent later error
- selected_function = command.ActionCall; // Sets the selected_function to the actioncall of the command.
- break; // Breaks from the for-loop. This does not terminate the upper while loop.
- }
- }
- if (!found) // If no command was found ouput an error.
- {
- PrintColor(RED, "Command Not Found");
- }
- else // Otherwise run the function that was found.
- {
- selected_function(command_line);
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment