Leonetienne/Hazelnupp
Simple, easy to use, command line parameter interface
Hazelnupp.cpp
Go to the documentation of this file.
1 #include "Hazelnupp.h"
2 #include "VoidValue.h"
3 #include "IntValue.h"
4 #include "FloatValue.h"
5 #include "StringValue.h"
6 #include "ListValue.h"
7 #include "HazelnuppException.h"
8 #include "StringTools.h"
9 #include <iostream>
10 #include <cstdlib>
11 
12 using namespace Hazelnp;
13 
15 {
16  return;
17 }
18 
19 Hazelnupp::Hazelnupp(const int argc, const char* const* argv)
20 {
21  Parse(argc, argv);
22  return;
23 }
24 
26 {
27  for (auto& it : parameters)
28  delete it.second;
29 
30  parameters.clear();
31 
32  return;
33 }
34 
35 void Hazelnupp::Parse(const int argc, const char* const* argv)
36 {
37  try
38  {
39  // Populate raw arguments
40  PopulateRawArgs(argc, argv);
41 
42  // Expand abbreviations
43  ExpandAbbreviations();
44 
45  executableName = std::string(rawArgs[0]);
46 
47  std::size_t i = 1;
48  while (i < rawArgs.size())
49  {
50  if ((rawArgs[i].length() > 2) && (rawArgs[i].substr(0, 2) == "--"))
51  {
52  Parameter* param = nullptr;
53  i = ParseNextParameter(i, param);
54 
55  parameters.insert(std::pair<std::string, Parameter*>(param->Key(), param));
56  }
57  else
58  i++;
59  }
60 
61  // Apply constraints such as default values, and required parameters.
62  // Types have already been enforced.
63  ApplyConstraints();
64  }
65  catch (const HazelnuppConstraintTypeMissmatch& hctm)
66  {
67  if (crashOnFail)
68  {
69  std::cerr << "Fatal error: Command-line parameter value-type mismatch at \"" << hctm.What() << "\"!";
70  quick_exit(-1009);
71  }
72  else
73  throw hctm; // yeet
74  }
75  catch (const HazelnuppConstraintMissingValue& hctm)
76  {
77  if (crashOnFail)
78  {
79  std::cerr << "Fatal error: Missing required command-line parameter \"" << hctm.What() << "\"!";
80  quick_exit(-1010);
81  }
82  else
83  throw hctm; // yeet
84  }
85 
86  return;
87 }
88 
89 std::size_t Hazelnupp::ParseNextParameter(const std::size_t parIndex, Parameter*& out_Par)
90 {
91  std::size_t i = parIndex;
92  const std::string key = rawArgs[parIndex];
93  std::vector<std::string> values;
94 
95  // Get values
96  for (i++; i < rawArgs.size(); i++)
97  // If not another parameter
98  if ((rawArgs[i].length() < 2) || (rawArgs[i].substr(0, 2) != "--"))
99  values.emplace_back(rawArgs[i]);
100  else
101  {
102  break;
103  }
104 
105  // Fetch constraint info
106  const ParamConstraint* pcn = GetConstraintForKey(key);
107 
108  Value* parsedVal = ParseValue(values, pcn);
109  if (parsedVal != nullptr)
110  {
111  out_Par = new Parameter(key, parsedVal);
112 
113  delete parsedVal;
114  parsedVal = nullptr;
115  }
116  else
117  throw std::runtime_error("Unable to parse parameter!");
118 
119  return i;
120 }
121 
122 void Hazelnupp::PopulateRawArgs(const int argc, const char* const* argv)
123 {
124  rawArgs.clear();
125  rawArgs.reserve(argc);
126 
127  for (int i = 0; i < argc; i++)
128  rawArgs.emplace_back(std::string(argv[i]));
129 
130  return;
131 }
132 
133 void Hazelnupp::ExpandAbbreviations()
134 {
135  // Abort if no abbreviations
136  if (abbreviations.size() == 0)
137  return;
138 
139  for (std::string& arg : rawArgs)
140  {
141  // Is arg registered as an abbreviation?
142  auto abbr = abbreviations.find(arg);
143  if (abbr != abbreviations.end())
144  {
145  // Yes: replace arg with the long form
146  arg = abbr->second;
147  }
148  }
149 
150  return;
151 }
152 
153 bool Hazelnupp::HasParam(const std::string& key) const
154 {
155  return parameters.find(key) != parameters.end();
156 }
157 
158 Value* Hazelnupp::ParseValue(const std::vector<std::string>& values, const ParamConstraint* constraint)
159 {
160  // Constraint values
161  const bool constrainType = (constraint != nullptr) && (constraint->constrainType);
162 
163  // Void-type
164  if (values.size() == 0)
165  {
166  // Is a list forced via a constraint? If yes, return an empty list
167  if ((constrainType) &&
168  (constraint->wantedType == DATA_TYPE::LIST))
169  return new ListValue();
170 
171  return new VoidValue;
172  }
173 
174  // Force void type by constraint
175  if ((constrainType) &&
176  (constraint->wantedType == DATA_TYPE::VOID))
177  {
178  return new VoidValue;
179  }
180 
181  // List-type
182  else if (values.size() > 1)
183  {
184  // Should the type be something other than list?
185  if ((constrainType) &&
186  (constraint->wantedType != DATA_TYPE::LIST))
187  {
188  throw HazelnuppConstraintTypeMissmatch(values[0] + " " + values[1]);
189  }
190 
191  ListValue* newList = new ListValue();
192  for (const std::string& val : values)
193  {
194  Value* tmp = ParseValue({ val });
195  newList->AddValue(tmp);
196  delete tmp;
197  }
198  return newList;
199  }
200 
201  // Now we're only dealing with a single value
202  const std::string& val = values[0];
203 
204  // String
205  if (!StringTools::IsNumeric(val, true))
206  {
207  // Is the type not supposed to be a string?
208  // void and list are already sorted out
209  if ((constrainType) &&
210  (constraint->wantedType != DATA_TYPE::STRING))
211  {
212  // We can only force a list-value from here
213  if (constraint->wantedType == DATA_TYPE::LIST)
214  {
215  ListValue* list = new ListValue();
216  Value* tmp = ParseValue({ val });
217  list->AddValue(tmp);
218  delete tmp;
219  tmp = nullptr;
220  return list;
221  }
222  // Else it not possible to convert to a numeric
223  else
225  }
226 
227  return new StringValue(val);
228  }
229 
230  // In this case we have a numeric value.
231  // We should still produce a string if requested
232  if ((constrainType) &&
233  (constraint->wantedType == DATA_TYPE::STRING))
234  return new StringValue(val);
235 
236  // Numeric
237  bool isInt;
238  long double num;
239 
240  if (StringTools::ParseNumber(val, isInt, num))
241  {
242  // Is the type constrained?
243  // (only int and float left)
244  if (constrainType)
245  {
246  // Must it be an integer?
247  if (constraint->wantedType == DATA_TYPE::INT)
248  return new IntValue((long long int)num);
249  // Must it be a floating point?
250  else if (constraint->wantedType == DATA_TYPE::FLOAT)
251  return new FloatValue(num);
252  // Else it must be a List
253  else
254  {
255  ListValue* list = new ListValue();
256  Value* tmp = ParseValue({ val });
257  list->AddValue(tmp);
258  delete tmp;
259  tmp = nullptr;
260  return list;
261  }
262  }
263  // Type is not constrained
264  else
265  {
266  // Integer
267  if (isInt)
268  return new IntValue((long long int)num);
269 
270  // Double
271  return new FloatValue(num);
272  }
273  }
274 
275  // Failed
276  return nullptr;
277 }
278 
280 {
281  return crashOnFail;
282 }
283 
284 void Hazelnupp::ApplyConstraints()
285 {
286  // Enforce required parameters / default values
287  for (const auto& pc : constraints)
288  // Parameter in question is not supplied
289  if (!HasParam(pc.second.key))
290  {
291  // Do we have a default value?
292  if (pc.second.defaultValue.size() > 0)
293  {
294  // Then create it now, by its default value
295 
296  Value* tmp = ParseValue(pc.second.defaultValue, &pc.second);
297  parameters.insert(std::pair<std::string, Parameter*>(
298  pc.second.key,
299  new Parameter(pc.second.key, tmp)
300  ));
301 
302  delete tmp;
303  tmp = nullptr;
304  }
305  // So we do not have a default value...
306  else
307  {
308  // Is it important to have the missing parameter?
309  if (pc.second.required)
310  // Throw an error message then
311  throw HazelnuppConstraintMissingValue(pc.second.key);
312  }
313  }
314 
315  return;
316 }
317 
318 const std::string& Hazelnupp::GetExecutableName() const
319 {
320  return executableName;
321 }
322 
323 const Value& Hazelnupp::operator[](const std::string& key) const
324 {
325  // Throw exception if param is unknown
326  if (!HasParam(key))
328 
329  return *parameters.find(key)->second->GetValue();
330 }
331 
332 void Hazelnupp::RegisterAbbreviation(const std::string& abbrev, const std::string& target)
333 {
334  abbreviations.insert(std::pair<std::string, std::string>(abbrev, target));
335  return;
336 }
337 
338 const std::string& Hazelnupp::GetAbbreviation(const std::string& abbrev) const
339 {
340  return abbreviations.find(abbrev)->second;
341 }
342 
343 bool Hazelnupp::HasAbbreviation(const std::string& abbrev) const
344 {
345  return abbreviations.find(abbrev) != abbreviations.end();
346 }
347 
349 {
350  abbreviations.clear();
351  return;
352 }
353 
354 void Hazelnupp::RegisterConstraints(const std::vector<ParamConstraint>& constraints)
355 {
356  for (const ParamConstraint& pc : constraints)
357  {
358  // Does this constraint already exist?
359  const auto constraint = this->constraints.find(pc.key);
360  // If yes, replace it.
361  if (constraint != this->constraints.end())
362  constraint->second = pc;
363 
364  // Else, create a new pair
365  else
366  this->constraints.insert(std::pair<std::string, ParamConstraint>(
367  pc.key,
368  pc
369  ));
370  }
371 
372  return;
373 }
374 
376 {
377  constraints.clear();
378  return;
379 }
380 
381 void Hazelnupp::SetCrashOnFail(bool crashOnFail)
382 {
383  this->crashOnFail = crashOnFail;
384  return;
385 }
386 
387 const ParamConstraint* Hazelnupp::GetConstraintForKey(const std::string& key) const
388 {
389  const auto constraint = constraints.find(key);
390 
391  if (constraint == constraints.end())
392  return nullptr;
393 
394  return &constraint->second;
395 }
HazelnuppException.h
Hazelnp
Definition: DataType.h:3
Hazelnp::IntValue
Specializations for integer values (uses long long int)
Definition: IntValue.h:8
Hazelnp::StringTools::IsNumeric
static bool IsNumeric(const std::string &str, const bool allowDecimalPoint=false)
Will return true if the given string consists only of digits (including signage)
Definition: StringTools.cpp:56
Hazelnp::StringTools::ParseNumber
static bool ParseNumber(const std::string &str, bool &out_isInt, long double &out_number)
Will convert the number in str to a number.
Definition: StringTools.cpp:82
Hazelnp::Hazelnupp::ClearConstraints
void ClearConstraints()
Will delete all constraints.
Definition: Hazelnupp.cpp:375
Hazelnp::HazelnuppInvalidKeyException
Gets thrown when an non-existent key gets dereferenced.
Definition: HazelnuppException.h:26
Hazelnp::Hazelnupp::GetExecutableName
const std::string & GetExecutableName() const
Will return argv[0], the name of the executable.
Definition: Hazelnupp.cpp:318
Hazelnp::Hazelnupp::~Hazelnupp
~Hazelnupp()
Definition: Hazelnupp.cpp:25
Hazelnp::Hazelnupp::operator[]
const Value & operator[](const std::string &key) const
Will return the value given a key.
Definition: Hazelnupp.cpp:323
StringValue.h
Hazelnp::HazelnuppException::What
const std::string & What() const
Will return an error message.
Definition: HazelnuppException.h:15
IntValue.h
Hazelnp::Hazelnupp::Hazelnupp
Hazelnupp()
Definition: Hazelnupp.cpp:14
Hazelnp::Hazelnupp::ClearAbbreviations
void ClearAbbreviations()
Will delete all abbreviations.
Definition: Hazelnupp.cpp:348
Hazelnp::DATA_TYPE::VOID
@ VOID
Hazelnp::FloatValue
Specializations for floating point values (uses long double)
Definition: FloatValue.h:9
Hazelnp::Value
Abstract class for values.
Definition: Value.h:10
Hazelnp::Hazelnupp::GetCrashOnFail
bool GetCrashOnFail() const
Gets whether the application crashes on an exception whilst parsing, and prints to stderr.
Definition: Hazelnupp.cpp:279
Hazelnp::DATA_TYPE::LIST
@ LIST
Hazelnp::Parameter
Definition: Parameter.h:8
Hazelnp::ParamConstraint::wantedType
DATA_TYPE wantedType
Constrain the parameter to this value. Requires constrainType to be set to true.
Definition: ParamConstraint.h:57
Hazelnp::ParamConstraint
Definition: ParamConstraint.h:8
Hazelnp::Hazelnupp::HasParam
bool HasParam(const std::string &key) const
Will check wether a parameter exists given a key, or not.
Definition: Hazelnupp.cpp:153
Hazelnp::Hazelnupp::Parse
void Parse(const int argc, const char *const *argv)
Will parse command line arguments.
Definition: Hazelnupp.cpp:35
ListValue.h
Hazelnp::ParamConstraint::constrainType
bool constrainType
Should this parameter be forced to be of a certain type? Remember to set constrainTo to the wanted ...
Definition: ParamConstraint.h:54
Hazelnp::Parameter::Key
const std::string & Key() const
Will return the key of this parameter.
Definition: Parameter.cpp:21
Hazelnp::HazelnuppConstraintMissingValue
Gets thrown when a parameter constrained to be required is not provided, and has no default value set...
Definition: HazelnuppException.h:62
Hazelnp::Hazelnupp::RegisterAbbreviation
void RegisterAbbreviation(const std::string &abbrev, const std::string &target)
Will register an abbreviation (like -f for –force)
Definition: Hazelnupp.cpp:332
Hazelnp::HazelnuppConstraintTypeMissmatch
Gets thrown when a parameter is of a type that does not match the required type, and is not convertib...
Definition: HazelnuppException.h:53
VoidValue.h
Hazelnp::Hazelnupp::RegisterConstraints
void RegisterConstraints(const std::vector< ParamConstraint > &constraints)
Will register parameter constraints.
Definition: Hazelnupp.cpp:354
Hazelnp::Hazelnupp::GetAbbreviation
const std::string & GetAbbreviation(const std::string &abbrev) const
Will return the long form of an abbreviation (like –force for -f)
Definition: Hazelnupp.cpp:338
Hazelnupp.h
Hazelnp::ListValue::AddValue
void AddValue(const Value *value)
Will add this value to the list.
Definition: ListValue.cpp:33
Hazelnp::VoidValue
Specializations for void values.
Definition: VoidValue.h:8
Hazelnp::DATA_TYPE::FLOAT
@ FLOAT
Hazelnp::DATA_TYPE::INT
@ INT
FloatValue.h
Hazelnp::Hazelnupp::HasAbbreviation
bool HasAbbreviation(const std::string &abbrev) const
Will check wether or not an abbreviation is registered.
Definition: Hazelnupp.cpp:343
Hazelnp::DATA_TYPE::STRING
@ STRING
Hazelnp::Hazelnupp::SetCrashOnFail
void SetCrashOnFail(bool crashOnFail)
Sets whether to crash the application, and print to stderr, when an exception is raised whilst parsin...
Definition: Hazelnupp.cpp:381
Hazelnp::ListValue
Specializations for list values (uses std::vector<Value*>)
Definition: ListValue.h:9
Hazelnp::StringValue
Specializations for string values (uses std::string)
Definition: StringValue.h:9
StringTools.h