| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "na64util/exception.hh" | ||
| 4 | |||
| 5 | #include <string> | ||
| 6 | #include <stdexcept> | ||
| 7 | #include <map> | ||
| 8 | #include <vector> | ||
| 9 | #include <algorithm> | ||
| 10 | #include <functional> | ||
| 11 | #include <unordered_set> | ||
| 12 | #include <sstream> | ||
| 13 | |||
| 14 | namespace na64dp { | ||
| 15 | namespace util { | ||
| 16 | |||
| 17 | /// A C++ bastard of printf() function | ||
| 18 | std::string format(const char *fmt, ...) throw(); | ||
| 19 | |||
| 20 | /// String substitution dictionary type | ||
| 21 | typedef std::map<std::string, std::string> StrSubstDict; | ||
| 22 | |||
| 23 | extern const struct StringSubstFormat { | ||
| 24 | char bgnMarker[4], endMarker[4]; | ||
| 25 | } gDefaultStrFormat; | ||
| 26 | |||
| 27 | /**\brief Renders string template wrt given context dictionary | ||
| 28 | * | ||
| 29 | * This function performs substitution in string expression `pattern` using the | ||
| 30 | * set of key+value pairs provided by `context`. | ||
| 31 | * | ||
| 32 | * Produces output relying on format string and a map of key-value pairs. | ||
| 33 | * E.g. for string template (1st argument) `"Hi, {user}!"` and pair | ||
| 34 | * `"user" -> "Joe"` the output string will be `"Hi, Joe!"`. This function is | ||
| 35 | * used across the library to "render" various "string templates" producing | ||
| 36 | * unique string for various detector types, messages, etc. | ||
| 37 | * | ||
| 38 | * If `requireCompleteness` is invokes `assert_str_has_no_fillers()` on result. | ||
| 39 | * | ||
| 40 | * \todo Recusrsive subst (e.g. "{kin}" -> "{kin}{statNum}") leads to | ||
| 41 | * infinite loop. Add exception for this. | ||
| 42 | */ | ||
| 43 | std::string str_subst( const std::string & pattern | ||
| 44 | , const StrSubstDict & context | ||
| 45 | , bool requireCompleteness=true | ||
| 46 | , const StringSubstFormat * fmt=&gDefaultStrFormat ); | ||
| 47 | |||
| 48 | /// Returns if the string has no template fillers | ||
| 49 | bool str_has_no_fillers( const std::string &, const StringSubstFormat * fmt=&gDefaultStrFormat ); | ||
| 50 | |||
| 51 | /// Raises an exception if string has template fillers | ||
| 52 | void assert_str_has_no_fillers( const std::string &, const StringSubstFormat * fmt=&gDefaultStrFormat ); | ||
| 53 | |||
| 54 | /// Retrieves list of fillers from string | ||
| 55 | /// | ||
| 56 | /// For instance, input "clusters/{kin@det}/{number@det}/clusters" will result | ||
| 57 | /// in two elements: "kin@det" and "numer@det". | ||
| 58 | std::unordered_set<std::string> get_fillers(const std::string & expr); | ||
| 59 | |||
| 60 | /// A generic utility function splitting space-separated tokens with | ||
| 61 | /// single-leveled commas expression support. | ||
| 62 | std::vector<std::string> tokenize_quoted_expression(const std::string &); | ||
| 63 | |||
| 64 | // trim from start (in place) | ||
| 65 | ✗ | static inline void ltrim(std::string &s) { | |
| 66 | ✗ | s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { | |
| 67 | ✗ | return !std::isspace(ch); | |
| 68 | })); | ||
| 69 | } | ||
| 70 | |||
| 71 | // trim from end (in place) | ||
| 72 | ✗ | static inline void rtrim(std::string &s) { | |
| 73 | ✗ | s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { | |
| 74 | ✗ | return !std::isspace(ch); | |
| 75 | ✗ | }).base(), s.end()); | |
| 76 | } | ||
| 77 | |||
| 78 | // trim from both ends (in place) | ||
| 79 | ✗ | static inline void trim(std::string &s) { | |
| 80 | ✗ | ltrim(s); | |
| 81 | ✗ | rtrim(s); | |
| 82 | } | ||
| 83 | |||
| 84 | // trim from start (copying) | ||
| 85 | static inline std::string ltrim_copy(std::string s) { | ||
| 86 | ltrim(s); | ||
| 87 | return s; | ||
| 88 | } | ||
| 89 | |||
| 90 | // trim from end (copying) | ||
| 91 | static inline std::string rtrim_copy(std::string s) { | ||
| 92 | rtrim(s); | ||
| 93 | return s; | ||
| 94 | } | ||
| 95 | |||
| 96 | // trim from both ends (copying) | ||
| 97 | static inline std::string trim_copy(std::string s) { | ||
| 98 | trim(s); | ||
| 99 | return s; | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Traits for context substitution; shall provide `append_dict(dct, val)` | ||
| 103 | template<typename T> struct StringSubstContextTraits; | ||
| 104 | |||
| 105 | /// Performs substitution of the string with placeholders with respect to | ||
| 106 | /// context dictionary taken from traits | ||
| 107 | template<typename T> std::string | ||
| 108 | str_subst_val( const T & val | ||
| 109 | , const std::string & pattern | ||
| 110 | , bool requireCompleteness=true | ||
| 111 | , const StringSubstFormat * fmt=&gDefaultStrFormat | ||
| 112 | ) { | ||
| 113 | StrSubstDict dct; | ||
| 114 | StringSubstContextTraits<T>::append_dict(dct, val); | ||
| 115 | return str_subst(pattern, dct, requireCompleteness, fmt); | ||
| 116 | } | ||
| 117 | |||
| 118 | /// Replaces phrase in a string | ||
| 119 | std::string | ||
| 120 | str_replace( std::string src | ||
| 121 | , const std::string & search | ||
| 122 | , const std::string & replace | ||
| 123 | ); | ||
| 124 | |||
| 125 | /**\brief Converts string expression to boolean option | ||
| 126 | * | ||
| 127 | * Kind of similar to `lexical_cast<bool>()` proposed by many libs around: | ||
| 128 | * converts string option that is supposed to be boolean option. | ||
| 129 | * Case-insensitive. | ||
| 130 | * Variants yielding `true`: "1", "yes", "true", "enable", "on". | ||
| 131 | * Variants yielding `false`: null ptr, "0", "no", "false", "disable", "off", empty string. | ||
| 132 | * All other strings lead to `errors::InvalidOptionExpression` error. | ||
| 133 | * */ | ||
| 134 | bool str_to_bool(std::string); | ||
| 135 | |||
| 136 | /**\brief Concatenates string-convertible items into delimited string | ||
| 137 | * | ||
| 138 | * This templated function comes in hand at many places to produce | ||
| 139 | * human-friendly form of comma-separated strings, mostly from STL-compatible | ||
| 140 | * containers. */ | ||
| 141 | template<typename IteratorT> std::string | ||
| 142 | ✗ | str_join( IteratorT itemsBgn | |
| 143 | , IteratorT itemsEnd | ||
| 144 | , const std::string & delimiter=", " | ||
| 145 | , std::function<std::string(IteratorT)> f=[](IteratorT tokIt){return *tokIt;} | ||
| 146 | ) { | ||
| 147 | ✗ | std::ostringstream oss; | |
| 148 | ✗ | bool isFirst = true; | |
| 149 | ✗ | for(IteratorT it = itemsBgn; it != itemsEnd; ++it) { | |
| 150 | ✗ | if(isFirst) isFirst = false; else oss << delimiter; | |
| 151 | ✗ | oss << f(it); | |
| 152 | } | ||
| 153 | ✗ | return oss.str(); | |
| 154 | } | ||
| 155 | |||
| 156 | /**\brief Time string formatting function | ||
| 157 | * | ||
| 158 | * Converts real value which is assumed to be rough estimation of seconds | ||
| 159 | * into human-readable string: | ||
| 160 | * | ||
| 161 | * [<hours>h][<minutes>m]<seconds>.<mseconds> | ||
| 162 | * | ||
| 163 | * Time formatted this way is widely used for logging as it seems to be a good | ||
| 164 | * tradeoff between precision, simplicity and a kind of compact form for | ||
| 165 | * typical application lifetime (from few seconds up to few hours). */ | ||
| 166 | size_t seconds_to_hmsm(float seconds, char * dest, size_t destLen); | ||
| 167 | |||
| 168 | /**\brief Parses time+date string expression of somewhat standard form | ||
| 169 | * | ||
| 170 | * Generally, this function is to fix certain time+date standard format across | ||
| 171 | * NA64sw framework. | ||
| 172 | * | ||
| 173 | * Examples: "2018-05-14-20:14:57", "2016-07-09-18:50:42", i.e. corresponding | ||
| 174 | * `strptime()`-format is `"%s-%m-%d-%H:%M:%S"`. | ||
| 175 | * | ||
| 176 | * Symmetric to `str_format_timedate()`. | ||
| 177 | * | ||
| 178 | * \note technically, returned value is a real timestamp only at current | ||
| 179 | * timezone | ||
| 180 | * */ | ||
| 181 | time_t | ||
| 182 | parse_timedate_strexpr( const char * c ); | ||
| 183 | |||
| 184 | /**\brief Returns time+date string expression of given `time_t` struct | ||
| 185 | * | ||
| 186 | * Symmetric to `parse_timedate_strexpr()`. | ||
| 187 | * | ||
| 188 | * \note technically, returned value is a real timestamp only at current | ||
| 189 | * timezone | ||
| 190 | * */ | ||
| 191 | std::string | ||
| 192 | str_format_timedate(const time_t &); | ||
| 193 | |||
| 194 | } // namespace ::na64dp::util | ||
| 195 | |||
| 196 | namespace errors { | ||
| 197 | |||
| 198 | class NoDataInEventError : public GenericRuntimeError { | ||
| 199 | public: | ||
| 200 | NoDataInEventError( const char * s ) throw() : GenericRuntimeError( s ) {} | ||
| 201 | }; | ||
| 202 | |||
| 203 | class StringIncomplete : public std::runtime_error { | ||
| 204 | private: | ||
| 205 | std::string _tok; | ||
| 206 | std::string _s; | ||
| 207 | public: | ||
| 208 | 1 | StringIncomplete( const std::string & s, const std::string & tok ) throw() | |
| 209 | 2 | : std::runtime_error( util::format( "String \"%s\" has template markup" | |
| 210 | " after substitution.", s.c_str() ) ) | ||
| 211 | 1 | , _tok(tok) | |
| 212 | 3 | , _s(s) | |
| 213 | 1 | {} | |
| 214 | 1 | const std::string & incomplete_string() const throw() { | |
| 215 | 1 | return _s; | |
| 216 | } | ||
| 217 | |||
| 218 | 2 | const std::string & token() const throw() { | |
| 219 | 2 | return _tok; | |
| 220 | } | ||
| 221 | }; // class StringIncomplete | ||
| 222 | |||
| 223 | class InvalidOptionExpression : public GenericRuntimeError { | ||
| 224 | public: | ||
| 225 | ✗ | InvalidOptionExpression( const std::string & expr ) throw() | |
| 226 | ✗ | : GenericRuntimeError( util::format( "Invalid option expression: \"%s\"", expr.c_str() ).c_str() ) | |
| 227 | ✗ | {} | |
| 228 | }; | ||
| 229 | |||
| 230 | } // namespace na64dp::errors | ||
| 231 | } // namespace na64dp | ||
| 232 | |||
| 233 | #ifndef NA64DP_RUNTIME_ERROR | ||
| 234 | #define NA64DP_RUNTIME_ERROR( fmt, ... ) { \ | ||
| 235 | throw ::na64dp::errors::GenericRuntimeError(::na64dp::util::format( \ | ||
| 236 | "at %s:%d: " fmt, __FILE__, __LINE__, ##__VA_ARGS__ \ | ||
| 237 | ).c_str() ); \ | ||
| 238 | } | ||
| 239 | #endif | ||
| 240 | |||
| 241 | #ifndef NA64DP_ASSERT_EVENT_DATA | ||
| 242 | #define NA64DP_ASSERT_EVENT_DATA( data, fmt, ... ) if(!data){ \ | ||
| 243 | throw ::na64dp::errors::NoDataInEventError(::na64dp::util::format( \ | ||
| 244 | "at %s:%d: requested data is not set/does not exist in the" \ | ||
| 245 | " event (" fmt ").", __FILE__, __LINE__, ##__VA_ARGS__ \ | ||
| 246 | ).c_str()); \ | ||
| 247 | } | ||
| 248 | #endif | ||
| 249 |