-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmessage.cpp
More file actions
242 lines (220 loc) · 6.54 KB
/
Copy pathmessage.cpp
File metadata and controls
242 lines (220 loc) · 6.54 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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/*
* Implementation of the Message class.
* Represents a single message (request or response) in the network protocol.
*
* CSF Assignment 5
*
* Ifrah Attar - iattar1@jh.edu
* Morgan Huberty - mhubert3@jh.edu
*
*/
#include <set>
#include <map>
#include <regex>
#include <cctype>
#include <cassert>
#include "message.h"
namespace {
/**
* @brief Checks if a string is a valid identifier.
* An identifier must begin with a letter (a-z or A-Z), and the rest of
* the identifier must consist of letters, underscores (_), and/or digits (0-9).
* @param s The string to check.
* @return True if the string is a valid identifier, false otherwise.
*/
bool is_identifier(const std::string &s) {
if (s.empty() || !isalpha(s[0])) {
return false;
}
for (size_t i = 1; i < s.length(); ++i) {
if (!isalnum(s[i]) && s[i] != '_') {
return false;
}
}
return true;
}
/**
* @brief Checks if a string is a valid value.
* A value must be a sequence of 1 or more non-whitespace characters.
* @param s The string to check.
* @return True if string is valid value, false otherwise.
*/
bool is_value(const std::string &s) {
if (s.empty()) {
return false;
}
for (char c : s) {
if (isspace(c)) {
return false;
}
}
return true;
}
}
/**
* @brief Default constructor. Creates a message of type NONE.
*/
Message::Message()
: m_message_type(MessageType::NONE)
{
}
/**
* @brief Constructs a message with a specific type and arguments.
* @param message_type The type of the message.
* @param args An initializer list of string arguments for the message.
*/
Message::Message( MessageType message_type, std::initializer_list<std::string> args )
: m_message_type( message_type )
, m_args( args )
{
}
/**
* @brief Copy constructor.
* @param other The Message object to copy.
*/
Message::Message( const Message &other )
: m_message_type( other.m_message_type )
, m_args( other.m_args )
{
}
/**
* @brief Destructor.
*/
Message::~Message()
{
}
/**
* @brief Assignment operator.
* @param rhs The Message object to assign from.
* @return A reference to this Message object.
*/
Message &Message::operator=( const Message &rhs )
{
if (this != &rhs) {
this->m_message_type = rhs.m_message_type;
this->m_args = rhs.m_args;
}
return *this;
}
/**
* @brief Gets the type of the message.
* @return The MessageType of this message.
*/
MessageType Message::get_message_type() const
{
return m_message_type;
}
/**
* @brief Sets the type of the message.
* @param message_type The new MessageType for this message.
*/
void Message::set_message_type(MessageType message_type)
{
m_message_type = message_type;
}
/**
* @brief Gets the username from a LOGIN message.
* Asserts that the message is a valid LOGIN message.
* @return The username string.
*/
std::string Message::get_username() const
{
assert(m_message_type == MessageType::LOGIN && m_args.size() >= 1);
return m_args.at(0);
}
/**
* @brief Gets the table name from a CREATE, SET, or GET message.
* Asserts that the message is a valid message of one of these types.
* @return The table name string.
*/
std::string Message::get_table() const
{
assert((m_message_type == MessageType::CREATE || m_message_type == MessageType::SET || m_message_type == MessageType::GET) && m_args.size() >= 1);
return m_args.at(0);
}
/**
* @brief Gets the key from a SET or GET message.
* Asserts that the message is a valid message of one of these types.
* @return The key string.
*/
std::string Message::get_key() const
{
assert((m_message_type == MessageType::SET || m_message_type == MessageType::GET) && m_args.size() >= 2);
return m_args.at(1);
}
/**
* @brief Gets the value from a PUSH or DATA message.
* Asserts that the message is a valid message of one of these types.
* @return The value string.
*/
std::string Message::get_value() const
{
assert((m_message_type == MessageType::PUSH || m_message_type == MessageType::DATA) && m_args.size() >= 1);
return m_args.at(0);
}
/**
* @brief Gets the quoted text from a FAILED or ERROR message.
* Asserts that the message is a valid message of one of these types.
* @return The text content of the response.
*/
std::string Message::get_quoted_text() const
{
assert((m_message_type == MessageType::FAILED || m_message_type == MessageType::ERROR) && m_args.size() >= 1);
return m_args.at(0);
}
/**
* @brief Adds an argument to the message's argument list.
* @param arg The string argument to add.
*/
void Message::push_arg( const std::string &arg )
{
m_args.push_back( arg );
}
/**
* @brief Validates the message against the protocol specification.
* This function checks if the message has the correct number of arguments
* for its type, and if the arguments have the correct format (e.g.,
* identifier, value).
* @return True if the message is valid, false otherwise.
*/
bool Message::is_valid() const
{
switch (m_message_type) {
// Requests with 0 arguments
case MessageType::POP:
case MessageType::TOP:
case MessageType::ADD:
case MessageType::SUB:
case MessageType::MUL:
case MessageType::DIV:
case MessageType::BEGIN:
case MessageType::COMMIT:
case MessageType::BYE:
return m_args.empty();
// Requests with 1 argument
case MessageType::LOGIN:
return m_args.size() == 1 && is_identifier(m_args[0]);
case MessageType::CREATE:
return m_args.size() == 1 && is_identifier(m_args[0]);
case MessageType::PUSH:
return m_args.size() == 1 && is_value(m_args[0]);
// Requests with 2 arguments
case MessageType::SET:
case MessageType::GET:
return m_args.size() == 2 && is_identifier(m_args[0]) && is_identifier(m_args[1]);
// Responses with 0 arguments
case MessageType::OK:
return m_args.empty();
// Responses with 1 argument
case MessageType::FAILED:
case MessageType::ERROR:
// quoted_text can be any string, so we just check the count.
return m_args.size() == 1;
case MessageType::DATA:
return m_args.size() == 1 && is_value(m_args[0]);
// The NONE type is never valid in a fully-formed message.
case MessageType::NONE:
default:
return false;
}
}