2#include "fmt/color.hpp"
3#include "qip/String.hpp"
20 const std::vector<std::string> &list) {
22 std::cout <<
": Unknown option: " << test_string <<
"\n";
54 constexpr static bool v =
false;
59 constexpr static bool v =
true;
66 constexpr static bool v =
false;
68 static constexpr std::size_t size = 0;
70template <
typename T, std::
size_t N>
71struct IsArray<std::array<T, N>> {
72 constexpr static bool v =
true;
74 static constexpr std::size_t size = N;
79inline void print_line(
const char c =
'*',
const int num = 80) {
80 for (
int i = 0; i < num; i++)
89 std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
91 std::strftime(buffer, 30,
"%F %T", localtime(&now));
97 std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
99 std::strftime(buffer, 30,
"%F", localtime(&now));
105 std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
107 std::strftime(buffer, 30,
"%T", localtime(&now));
115 std::string value_str;
117 friend bool operator==(
Option option, std::string_view tkey) {
121 friend bool operator==(std::string_view tkey,
Option option) {
122 return option == tkey;
124 friend bool operator!=(
Option option, std::string_view tkey) {
125 return !(option == tkey);
127 friend bool operator!=(std::string_view tkey,
Option option) {
128 return !(option == tkey);
156 std::string m_name{};
157 std::vector<Option> m_options{};
158 std::vector<InputBlock> m_blocks{};
195 inline void add(
const std::vector<Option> &
options);
204 inline void add(
const std::string &
string,
bool merge =
false);
207 inline void merge(
const std::string &
string) {
add(
string,
true); }
210 std::string_view
name()
const {
return m_name; }
213 const std::vector<Option> &
options()
const {
return m_options; }
216 const std::vector<InputBlock> &
blocks()
const {
return m_blocks; }
221 friend inline bool operator!=(
InputBlock block, std::string_view
name);
222 friend inline bool operator!=(std::string_view
name,
InputBlock block);
232 template <
typename T>
233 T
get(std::string_view key, T default_value)
const;
243 template <
typename T = std::
string>
244 std::optional<T>
get(std::string_view key)
const;
248 const auto option = std::find(m_options.crbegin(), m_options.crend(), key);
249 return !(option == m_options.crend());
254 return !(
get(key) == std::nullopt);
258 template <
typename T>
259 T
get(std::initializer_list<std::string>
blocks, std::string_view key,
260 T default_value)
const;
263 template <
typename T>
264 std::optional<T>
get(std::initializer_list<std::string>
blocks,
265 std::string_view key)
const;
268 inline std::optional<InputBlock>
getBlock(std::string_view
name)
const;
271 inline std::optional<InputBlock>
273 std::string_view
name)
const;
286 return temp != std::nullopt;
291 std::string_view
name)
const {
293 return temp != std::nullopt;
297 inline std::optional<Option>
getOption(std::string_view key)
const;
300 inline void print(std::ostream &os = std::cout,
int indent_depth = 0)
const;
316 const std::vector<std::pair<std::string, std::string>> &list,
317 bool print =
false)
const;
321 check(
const std::vector<std::pair<std::string, std::string>> &list,
322 bool print =
false)
const {
323 return checkBlock(list,
print);
328 checkBlock(
const std::vector<std::pair<std::string, std::string>> &list,
329 bool print =
false)
const;
333 inline const InputBlock *getBlock_cptr(std::string_view
name)
const;
336 template <
typename T>
337 std::optional<std::vector<T>> get_vector(std::string_view key)
const;
340 template <
typename T, std::
size_t N>
341 std::optional<std::array<T, N>> get_array(std::string_view key)
const;
343 inline void add_option(std::string_view in_string);
344 inline void add_blocks_from_string(std::string_view
string,
bool merge);
345 inline void consolidate();
351 auto existing_block = getBlock_ptr(block.m_name);
352 if (
merge && existing_block) {
353 existing_block->m_options.insert(existing_block->m_options.end(),
354 block.m_options.cbegin(),
355 block.m_options.cend());
357 m_blocks.push_back(block);
364 for (
const auto &option :
options)
365 m_options.push_back(option);
370 add_blocks_from_string(
379bool operator==(std::string_view name,
InputBlock block) {
380 return block == name;
382bool operator!=(InputBlock block, std::string_view name) {
383 return !(block == name);
385bool operator!=(std::string_view name, InputBlock block) {
386 return !(block == name);
393 return get_vector<typename IsVector<T>::t>(key);
396 }
else if constexpr (std::is_same_v<T, bool>) {
397 const auto option = std::find(m_options.crbegin(), m_options.crend(), key);
398 if (option == m_options.crend())
401 option->value_str ==
"")
403 const auto &str = option->value_str;
411 const auto option = std::find(m_options.crbegin(), m_options.crend(), key);
412 if (option == m_options.crend())
415 option->value_str ==
"")
417 return parse_str_to_T<T>(option->value_str);
426std::optional<std::vector<T>>
427InputBlock::get_vector(std::string_view key)
const {
431 const auto option = std::find(m_options.crbegin(), m_options.crend(), key);
432 if (option == m_options.crend())
434 if (option->value_str ==
"")
436 std::stringstream ss(option->value_str);
440 std::getline(ss, substr,
',');
441 out.push_back(parse_str_to_T<T>(substr));
446template <
typename T, std::
size_t N>
447std::optional<std::array<T, N>>
448InputBlock::get_array(std::string_view key)
const {
451 std::array<T, N> out;
452 const auto option = std::find(m_options.crbegin(), m_options.crend(), key);
453 if (option == m_options.crend())
455 if (option->value_str ==
"")
457 std::stringstream ss(option->value_str);
458 std::size_t index = 0;
462 std::getline(ss, substr,
',');
464 out.at(index) = parse_str_to_T<T>(substr);
472 static_assert(!std::is_same_v<T, const char *>,
473 "Cannot use get with const char* - use std::string");
474 return get<T>(key).value_or(default_value);
479 std::string_view key, T default_value)
const {
480 return get<T>(
blocks, key).value_or(default_value);
485 std::string_view key)
const {
488 for (
const auto &block :
blocks) {
489 pB = pB->getBlock_cptr(block);
493 return pB->
get<T>(key);
499 const auto block = std::find(m_blocks.crbegin(), m_blocks.crend(),
name);
500 if (block == m_blocks.crend())
505std::optional<InputBlock>
507 std::string_view name)
const {
510 for (
const auto &block :
blocks) {
511 pB = pB->getBlock_cptr(block);
522 const auto option = std::find(m_options.crbegin(), m_options.crend(), key);
523 if (option != m_options.crend())
531 std::string indent =
"";
532 for (
int i = 1; i < depth; ++i)
537 os << indent << m_name <<
" { ";
539 const auto multi_entry = (!m_blocks.empty() || (m_options.size() > 1));
541 if (depth != 0 && multi_entry)
544 for (
const auto &[key, value] : m_options) {
545 os << (depth != 0 && multi_entry ? indent +
" " :
"");
549 os << key <<
" = " << value <<
';';
550 os << (multi_entry ?
'\n' :
' ');
553 for (
const auto &block : m_blocks)
554 block.print(os, depth + 1);
556 if (depth != 0 && multi_entry)
564bool InputBlock::checkBlock(
565 const std::vector<std::pair<std::string, std::string>> &list,
571 for (
const auto &option : m_options) {
572 const auto is_optionQ = [&](
const auto &l) {
576 const auto bad_option =
577 !std::any_of(list.cbegin(), list.cend(), is_optionQ);
581 if (bad_option && !help) {
583 fmt2::styled_print(fg(fmt::color::orange),
"\nWARNING\n");
584 std::cout <<
"Unclear input option in " << m_name <<
": " << option.key
585 <<
" = " << option.value_str <<
";\n"
586 <<
"Option may be ignored!\n"
587 <<
"Check spelling (or update list of options)\n";
589 auto compare_sc = [&option](
const auto &s1,
const auto &s2) {
593 auto guess = std::min_element(list.cbegin(), list.cend(), compare_sc);
594 if (guess != list.cend()) {
595 std::cout <<
"\nDid you mean: " << guess->first <<
" ?\n";
600 using namespace std::string_literals;
601 for (
const auto &block : m_blocks) {
602 const auto is_blockQ = [&](
const auto &b) {
605 const auto bad_block = !std::any_of(list.cbegin(), list.cend(), is_blockQ);
608 fmt2::styled_print(fg(fmt::color::orange),
"\nWARNING\n");
609 std::cout <<
"Unclear input block within " << m_name <<
": "
610 << block.name() <<
"{}\n"
611 <<
"Block and containing options may be ignored!\n"
612 <<
"Check spelling (or update list of options)\n";
614 auto compare_sc = [&block](
const auto &s1,
const auto &s2) {
618 auto guess = std::min_element(list.cbegin(), list.cend(), compare_sc);
619 if (guess != list.cend()) {
620 std::cout <<
"\nDid you mean: " << guess->first <<
" ?\n";
625 if (!all_ok ||
print) {
626 fmt2::styled_print(fg(fmt::color::light_blue),
627 "\n// Available {} options/blocks\n", m_name);
628 fmt2::styled_print(fmt::emphasis::bold, m_name);
630 std::for_each(list.cbegin(), list.cend(), [](
const auto &s) {
631 const auto option_is_block = s.first.back() ==
'}';
632 const auto leading_spaces = s.first.empty() ?
""s :
" ";
633 if (!s.first.empty()) {
634 std::cout <<
" " << s.first << (option_is_block ?
"\n" :
";\n");
636 if (!s.second.empty()) {
637 fmt2::styled_print(fg(fmt::color::light_blue),
"{}\n",
638 qip::wrap(s.second, 60, leading_spaces +
" // "));
643 std::cout <<
"}\n\n";
649bool InputBlock::check(
650 std::initializer_list<std::string> blocks,
651 const std::vector<std::pair<std::string, std::string>> &list,
655 for (
const auto &block : blocks) {
656 pB = pB->getBlock_cptr(block);
664 return pB->
check(list, print);
668void InputBlock::add_blocks_from_string(std::string_view
string,
bool merge) {
673 while (start <
string.length()) {
677 auto end = std::min(
string.find(
';', start),
string.find(
'{', start));
678 if (end >
string.length() || start >= end)
681 if (
string.at(end) ==
';') {
684 this->add_option(
string.substr(start, end - start));
690 const auto block_name =
string.substr(start, end - start);
695 auto next_start = start;
696 while (depth_count != 0) {
697 if (next_start >
string.length())
699 const auto next_end =
700 std::min(
string.find(
'{', next_start),
string.find(
'}', next_start));
701 if (next_end >
string.length())
705 if (
string.at(next_end) ==
'{')
710 if (depth_count == 0) {
714 if (depth_count > 100) {
715 std::cerr <<
"FAIL 271 in InputBlock::add_blocks_from_string: Depth "
716 "error. Check balanced {} in input\n";
720 next_start = next_end + 1;
725 auto &block = m_blocks.emplace_back(block_name);
728 block.add_blocks_from_string(
string.substr(start, end - start), merge);
741void InputBlock::add_option(std::string_view in_string) {
742 const auto pos = in_string.find(
'=');
743 const auto option = in_string.substr(0, pos);
744 const auto value = pos < in_string.length() ? in_string.substr(pos + 1) :
"";
745 m_options.push_back({std::string(option), std::string(value)});
749InputBlock *InputBlock::getBlock_ptr(std::string_view name) {
750 auto block = std::find(m_blocks.rbegin(), m_blocks.rend(), name);
751 if (block == m_blocks.rend())
756const InputBlock *InputBlock::getBlock_cptr(std::string_view name)
const {
757 auto block = std::find(m_blocks.crbegin(), m_blocks.crend(), name);
758 if (block == m_blocks.rend())
764void InputBlock::consolidate() {
765 for (
auto bl = m_blocks.end() - 1; bl != m_blocks.begin() - 1; --bl) {
767 auto bl2 = std::find(m_blocks.begin(), bl, bl->name());
769 bl2->m_options.insert(bl2->m_options.end(), bl->m_options.cbegin(),
770 bl->m_options.cend());
780 const std::string include_text =
"#include";
782 for (
auto ipos = str.find(include_text); ipos != std::string::npos;
783 ipos = str.find(include_text)) {
784 const auto start = std::min(str.find(
'"', ipos), str.find(
'<', ipos));
786 std::min(str.find(
'"', start + 1), str.find(
'>', start + 1));
787 const auto fname = str.substr(start + 1, end - start - 1);
788 str.erase(ipos, end - ipos + 1);
789 std::ifstream ifile(fname);
802 auto lambda = [&inside](
unsigned char x) {
803 if (x ==
'\"' || x ==
'\'')
805 return ((x ==
' ' || x ==
'\t' || x ==
'\n') && !inside);
808 str.erase(std::remove_if(str.begin(), str.end(), lambda), str.end());
816 str.erase(std::remove_if(str.begin(), str.end(),
817 [](
unsigned char x) { return x ==
'\''; }),
819 str.erase(std::remove_if(str.begin(), str.end(),
820 [](
unsigned char x) { return x ==
'\"'; }),
827inline void removeBlockComments(std::string &input) {
828 for (
auto posi = input.find(
"/*"); posi != std::string::npos;
829 posi = input.find(
"/*")) {
830 auto posf = input.find(
"*/");
831 if (posf != std::string::npos) {
832 input = input.substr(0, posi) + input.substr(posf + 2);
834 input = input.substr(0, posi);
841 std::string str =
"";
844 std::stringstream stream1(input);
845 while (std::getline(stream1, line,
'\n')) {
848 const auto comm3 = line.find(
"//");
850 str += line.substr(0, comm3);
854 removeBlockComments(str);
862 if constexpr (std::is_same_v<T, std::string>) {
868 std::stringstream ss(value_as_str);
880 std::ostringstream ss;
In-out (timers, profilers, and read/write data)
Definition ChronoTimer.hpp:9
std::string time()
Returns current local time as a string, e.g. "13:01:02".
Definition InputBlock.hpp:103
std::string date()
Returns current local date as a string, e.g. "2026-05-14".
Definition InputBlock.hpp:95
void unkown_option(std::string_view test_string, const std::vector< std::string > &list)
Prints unkown option warning, with suggested alternative.
Definition InputBlock.hpp:19
void removeBlockComments(std::string &input)
Removes all c++ style block comments from a string.
Definition InputBlock.hpp:827
std::string expandIncludes(std::string input)
Expands "#include" files.
Definition InputBlock.hpp:779
std::string time_date()
Returns current local date and time as a string, e.g. "2026-05-14 13:01:02".
Definition InputBlock.hpp:87
std::string removeSpaces(std::string str)
Removes all white space (space, tab, newline), except for those in quotes.
Definition InputBlock.hpp:799
void print_line(const char c=' *', const int num=80)
Prints a line of 'c' characters (dflt '*'), num chars long (dflt 80) to cout.
Definition InputBlock.hpp:79
std::string file_to_string(const std::istream &file)
Parses entire file into string. Note: v. inefficient.
Definition InputBlock.hpp:875
std::string removeComments(const std::string &input)
Removes all c++ style comments from a string (block and line)
Definition InputBlock.hpp:840
T parse_str_to_T(const std::string &value_as_str)
Parses a string to type T by stringstream.
Definition InputBlock.hpp:861
std::string removeQuoteMarks(std::string str)
Removes all quote marks.
Definition InputBlock.hpp:813
std::string ci_closest_match(const std::string_view test_string, const std::vector< std::string > &list)
Returns the closest match (case insensitive) to test_string in list, using ci_Levenstein distance.
Definition String.hpp:246
auto ci_Levenstein(std::string_view a, std::string_view b)
Case-insensitive version of Levenstein.
Definition String.hpp:208
bool ci_wc_compare(std::string_view s1, std::string_view s2)
Case-insensitive version of wildcard_compare.
Definition String.hpp:155
Compile-time trait: IsArray<T>::v is true if T is std::array. IsArray<T>::t is the element type; IsAr...
Definition InputBlock.hpp:65
Compile-time trait: IsVector<T>::v is true if T is std::vector. IsVector<T>::t is the element type.
Definition InputBlock.hpp:53
Simple struct; holds key-value pair, both strings. == compares key.
Definition InputBlock.hpp:113