Skip to content

Commit

Permalink
Add participants and configuration.
Browse files Browse the repository at this point in the history
  • Loading branch information
acodcha committed Oct 17, 2023
1 parent 5593925 commit 5e8fd2a
Show file tree
Hide file tree
Showing 12 changed files with 687 additions and 20 deletions.
16 changes: 12 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)

# Define the global settings.
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math -O3 -Wall -Wextra -Wno-return-type -Wpedantic")
Expand Down Expand Up @@ -90,15 +90,23 @@ if(TEST_SECRET_SANTA)
# Define the Secret Santa test executables.

add_executable(messenger_settings ${PROJECT_SOURCE_DIR}/test/Messenger/Settings.cpp)
target_link_libraries(messenger_settings GTest::gtest_main)
target_link_libraries(messenger_settings yaml-cpp GTest::gtest_main)
gtest_discover_tests(messenger_settings)

add_executable(participant ${PROJECT_SOURCE_DIR}/test/Participant.cpp)
target_link_libraries(participant yaml-cpp GTest::gtest_main)
gtest_discover_tests(participant)

add_executable(randomizer_configuration ${PROJECT_SOURCE_DIR}/test/Randomizer/Configuration.cpp)
target_link_libraries(randomizer_configuration yaml-cpp GTest::gtest_main)
gtest_discover_tests(randomizer_configuration)

add_executable(randomizer_settings ${PROJECT_SOURCE_DIR}/test/Randomizer/Settings.cpp)
target_link_libraries(randomizer_settings GTest::gtest_main)
target_link_libraries(randomizer_settings yaml-cpp GTest::gtest_main)
gtest_discover_tests(randomizer_settings)

add_executable(string ${PROJECT_SOURCE_DIR}/test/String.cpp)
target_link_libraries(string GTest::gtest_main)
target_link_libraries(string yaml-cpp GTest::gtest_main)
gtest_discover_tests(string)

message(STATUS "The Secret Santa tests were configured. Build the tests with \"make --jobs=16\" and run them with \"make test\"")
Expand Down
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,32 @@ This builds the main executables:

### Usage: Configuration File

TODO.
A sample YAML configuration file is shown here: [test/Randomizer/configuration.yaml](test/Randomizer/configuration.yaml)

The YAML configuration file must follow the following schema:

```yaml
---
message:
subject: <text>
body: <text>
participants:
- <name>:
email: <text>
address: <text>
instructions: <text>
- <name>:
email: <text>
address: <text>
instructions: <text>
[...]
```

The YAML configuration file contains the following fields:

- `message->subject`: Subject of the email message that will be sent to each participant. A default value is used if no message subject is defined in the YAML configuration file.
- `message->body`: Body of the email message that will be sent to each participant. A default value is used if no message body is defined in the YAML configuration file. Information regarding the participant's giftee is automatically appended to this body.
- `participants`: List of participants. Each participant contains a name along with an email address, civic address, and instructions. Participant names must be unique. Participants' email addresses, civic addresses, and instructions are optional.

[(Back to Usage)](#usage)

Expand Down
15 changes: 15 additions & 0 deletions source/Messenger/Settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,21 @@ class Settings {
PrintSettings();
}

// Destructor. Destroys this settings object.
~Settings() noexcept = default;

// Deleted copy constructor.
Settings(const Settings& other) = delete;

// Deleted move constructor.
Settings(Settings&& other) noexcept = delete;

// Deleted copy assignment operator.
Settings& operator=(const Settings& other) = delete;

// Deleted move assignment operator.
Settings& operator=(Settings&& other) noexcept = delete;

// Path to the YAML configuration file to be read.
const std::filesystem::path& ConfigurationFile() const noexcept {
return configuration_file_;
Expand Down
209 changes: 209 additions & 0 deletions source/Participant.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// Copyright © 2023 Alexandre Coderre-Chabot
//
// This file is licensed under the MIT license. For more information, visit:
// https://mit-license.org
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// - The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
// - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// This file was originally obtained from:
// https://github.com/acodcha/secret-santa

#include <yaml-cpp/yaml.h>

#include <iostream>
#include <string>
#include <string_view>

namespace SecretSanta {

// A participant in the Secret Santa gift exchange. Contains the participant's
// information.
class Participant {
public:
// Default constructor. Creates a participant with an empty name, email,
// address, and instructions.
Participant() = default;

// Constructor. Creates a participant from a YAML node of the form:
// Alice Smith:
// email: [email protected]
// address: 123 First Ave, Apt 1, Townsville, CA, 91234 USA
// instructions: Leave the package with the doorman in the lobby.
Participant(const YAML::Node& node) {
if (!node.IsMap()) {
return;
}

if (node.size() != 1) {
return;
}

for (const YAML::detail::iterator_value& element : node) {
name_ = element.first.as<std::string>();

if (name_.empty()) {
return;
}

if (element.second["email"]) {
email_ = element.second["email"].as<std::string>();
}

if (element.second["address"]) {
address_ = element.second["address"].as<std::string>();
}

if (element.second["instructions"]) {
instructions_ = element.second["instructions"].as<std::string>();
}
}
}

// Destructor. Destroys this participant.
~Participant() noexcept = default;

// Copy constructor. Constructs a participant by copying another one.
Participant(const Participant& other) = default;

// Move constructor. Constructs a participant by moving another one.
Participant(Participant&& other) noexcept = default;

// Copy assignment operator. Assigns this participant by copying another one.
Participant& operator=(const Participant& other) = default;

// Move assignment operator. Assigns this participant by moving another one.
Participant& operator=(Participant&& other) noexcept = default;

// Name of this participant. Each participant must have a unique name.
std::string_view Name() const noexcept {
return name_;
}

// Email address of this participant.
std::string_view Email() const noexcept {
return email_;
}

// Civic address of this participant.
std::string_view Address() const noexcept {
return address_;
}

// Additional instructions for mailing packages to this participant.
std::string_view Instructions() const noexcept {
return instructions_;
}

// Prints this participant as a string.
std::string Print() const noexcept {
std::string details;

if (!email_.empty()) {
details.append("email: " + email_);
}

if (!address_.empty()) {
if (!details.empty()) {
details.append("; ");
}
details.append("address: " + address_);
}

if (!instructions_.empty()) {
if (!details.empty()) {
details.append("; ");
}
details.append("instructions: " + instructions_);
}

if (details.empty()) {
return name_;
} else {
return name_ + " (" + details + ")";
}
}

// Creates a YAML node containing this participant's information. The YAML
// node is of the form:
// Alice Smith:
// email: [email protected]
// address: 123 First Ave, Apt 1, Townsville, CA, 91234 USA
// instructions: Leave the package with the doorman in the lobby.
YAML::Node YAML() const {
YAML::Node node;
node[name_]["email"] = email_;
node[name_]["address"] = address_;
node[name_]["instructions"] = instructions_;
return node;
}

inline bool operator==(const Participant& other) const noexcept {
return name_ == other.name_;
}

inline bool operator!=(const Participant& other) const noexcept {
return name_ != other.name_;
}

inline bool operator<(const Participant& other) const noexcept {
return name_ < other.name_;
}

inline bool operator>(const Participant& other) const noexcept {
return name_ > other.name_;
}

inline bool operator<=(const Participant& other) const noexcept {
return name_ <= other.name_;
}

inline bool operator>=(const Participant& other) const noexcept {
return name_ >= other.name_;
}

private:
// Name of this participant. Each participant must have a unique name.
std::string name_;

// Email address of this participant.
std::string email_;

// Civic address of this participant.
std::string address_;

// Additional instructions for mailing packages to this participant.
std::string instructions_;
};

inline std::ostream& operator<<(
std::ostream& stream, const Participant& participant) {
stream << participant.Print();
return stream;
}

} // namespace SecretSanta

namespace std {

template <>
struct hash<SecretSanta::Participant> {
inline size_t operator()(const SecretSanta::Participant& participant) const {
return hash<std::string>()(std::string(participant.Name()));
}
};

} // namespace std
Loading

0 comments on commit 5e8fd2a

Please sign in to comment.