33#define GEMPYREUTILSDEBUG(x) GempyreUtils::log(Utils::LogLevel::Debug, x, __FILE__, __LINE__)
36#define gempyre_utils_assert(b) (b || GempyreUtils::do_fatal("Panic!", nullptr, __FILE__, __LINE__))
38#define gempyre_utils_assert_x(b, x) (b || GempyreUtils::do_fatal(x, nullptr, __FILE__, __LINE__))
40#define gempyre_utils_assert_x_f(b, x, f) (b || GempyreUtils::do_fatal(x, f, __FILE__, __LINE__))
42#define gempyre_utils_fatal(x) GempyreUtils::do_fatal(x, nullptr, __FILE__, __LINE__)
44#define gempyre_utils_fatal_f(x, f) GempyreUtils::do_fatal(x, f, __FILE__, __LINE__)
46#define gempyre_utils_auto_clean(p, f) std::unique_ptr<std::remove_pointer<decltype(p)>::type, decltype(&f)> _ ## p (p, &f)
48#define gempyre_utils_auto_close(p, f) GempyreUtils::_Close<std::decay_t<decltype(p)>, decltype(&f)> _ ## p (p, &f)
53 #define UTILS_EX __declspec(dllexport)
59using SSIZE_T =
long long;
61using SSIZE_T = ssize_t;
67namespace GempyreUtils {
99 virtual bool do_write(
const char* buffer,
size_t count) = 0;
107class UTILS_EX FileLogWriter :
public LogWriter {
109 FileLogWriter(std::string_view path);
111 bool do_write(
const char* buffer,
size_t count)
override;
113 std::ofstream m_file;
116class UTILS_EX StreamLogWriter :
public LogWriter {
118 StreamLogWriter(std::ostream& os);
120 bool do_write(
const char* buffer,
size_t count)
override;
140UTILS_EX std::ostream log_stream(
LogLevel logLevel);
142template <
typename T,
typename ...Args>
143inline void log_line(
LogLevel level, std::ostream& os,
const T& e, Args... args) {
145 log_line(level, os, args...);
149UTILS_EX
void process_exit(
int);
152inline void log_line(LogLevel level, std::ostream& os,
const T& e) {
153 os << e << std::endl;
154 if(level == LogLevel::Fatal) {
164template <
typename T,
typename ...Args>
167 auto os = log_stream(level);
168 log_line(level, os, e, args...);
173template <LogLevel level,
typename T,
typename ...Args>
174inline void write_log(
const T& e, Args... args) {
175 log(level, e, args...);
180template <
typename T,
typename ...Args>
182 write_log<LogLevel::Debug, T, Args...>(e, args...);
187inline bool do_fatal(std::string_view txt, std::function<
void()> f,
const char* file,
int line) {
189 log(LogLevel::Fatal, txt,
"at", file,
"line:", line);
196#define __PRETTY_FUNCTION__ __FUNCSIG__
200#define GEM_DEBUG(...) GempyreUtils::log(GempyreUtils::LogLevel::Debug, __PRETTY_FUNCTION__, __VA_ARGS__)
210namespace GempyreUtils {
214template <
typename T,
typename A>
217 _Close(T ref, A&& f) : t(ref), d(f) {}
218 ~_Close() {(void) this->d(this->t);}
219 _Close(_Close&& other) =
default;
220 _Close& operator=(_Close&& other) =
default;
221 _Close(
const _Close& other) =
delete;
222 _Close& operator=(
const _Close& other) =
delete;
232UTILS_EX std::string current_time_string();
233UTILS_EX std::string last_error();
239 ErrorValue(E&& e) : err{e} {}
240 ErrorValue(
const E& e) : err{e} {}
249template <
typename E>
struct Error {
251 using value_type = E;
256template <
typename R,
typename E = std::
string>
257struct Result :
private std::variant<R, Error<E>> {
259 using Err = Error<E>;
262 using value_type = R;
264 using error_type = E;
267 constexpr Result(R&& r) : std::variant<R, Err>{r} {}
270 constexpr Result(Err&& e) : std::variant<R, Err>{e} {}
273 constexpr Result(
const R& r) : std::variant<R, Err>{r} {}
276 constexpr Result(
const Err& e) : std::variant<R,Err>{e} {}
280 constexpr Result& operator=(R&& r) {*
this = std::move(r);
return *
this;}
284 constexpr Result& operator=(Err&& e) {*
this = std::move(e);
return *
this;}
288 constexpr Result& operator=(
const R& r) {*
this = r;
return *
this;}
292 constexpr Result& operator=(
const Err &e) {*
this = e;
return *
this;}
294 constexpr operator bool()
const {
return std::get_if<R>(
this);}
296 constexpr operator std::optional<R>()
const {
return has_value() ? std::make_optional<R>(value()) : std::nullopt;}
298 constexpr bool has_value()
const {
return operator bool();}
300 constexpr const R& value()
const {
return std::get<R>(*
this);}
302 constexpr const E& error()
const {
return std::get<Err>(*this).error;}
304 constexpr R& value() {
return std::get<R>(*
this);}
306 constexpr R& operator *() {
return std::get<R>(*
this);}
308 constexpr const R& operator *()
const {
return std::get<R>(*
this);}
310 constexpr R* operator ->() {
return &std::get<R>(*
this);}
312 constexpr const R* operator ->()
const {
return &std::get<R>(*
this);}
316 constexpr static auto make_error(
const E& e) {
return Result<R, E>{Error<E>{e}};}
320 constexpr static auto make_error(E&& e) {
return Result<R, E>{Error<E>{e}};}
323 constexpr static auto make_error() {
return Result<R, E>{Error<E>{}};}
326 constexpr static auto ok() {
return Result<R, E>{R{}};}
330template <
typename R,
typename E>
331constexpr inline bool operator==(
const Result<R, E>& a,
const Result<R, E>& b) {
return Result<R, E>::ParentType::operator==(a, b);}
333template <
typename R,
typename E>
334constexpr inline bool operator!=(
const Result<R, E>& a,
const Result<R, E>& b) {
return !operator==(a, b);}
337using ResultTrue = Result<std::true_type, std::string>;
344template<
typename T,
typename... A>
345Result<T, std::string> make_error(A&&... a) {
346 std::stringstream str;
348 return Result<T, std::string>::make_error(str.str());
358UTILS_EX std::string qq(std::string_view s);
363UTILS_EX std::string chop(std::string_view s);
369UTILS_EX std::string chop(std::string_view s, std::string_view chopped);
376UTILS_EX std::string substitute(std::string_view str, std::string_view substring, std::string_view substitution);
381UTILS_EX std::string remove_spaces(std::string_view str);
385T convert(std::string_view source) {
386 std::istringstream ss(std::string{source});
387 typename std::remove_const<T>::type v{};
397std::string_view right(std::string_view str,
size_t p) {
398 return str.substr(str.length() - p);
402inline std::string convert<std::string>(std::string_view source)
404 return std::string{source};
414std::optional<T> parse(std::string_view source,
const std::optional<int>& forced_base = std::nullopt) {
417 std::stringstream ss;
419 ss << std::setbase(*forced_base) << source;
421 auto base = std::dec;
422 if (source.size() > 1 && source[0] ==
'0') {
423 if (source.size() > 2 && (source[1] ==
'x' || source[1] ==
'X')) {
429 ss << base << source;
434 return !ss.fail() && (!forced_base || ss.eof()) ? std::make_optional(v) : std::nullopt;
441std::string to_low(
const T& str) {
443 std::transform(std::begin(str), std::end(str), std::back_inserter(n),
444 [](
auto c){
return std::tolower(c);});
453std::string to_upper(
const T& str) {
455 std::transform(std::begin(str), std::end(str), std::back_inserter(n),
456 [](
auto c){
return std::toupper(c);});
464inline std::string_view ltrim(std::string_view str) {
465 const auto begin = std::find_if(str.begin(), str.end(), [](
auto ch) {
468 return str.substr(
static_cast<std::string_view::size_type
>(
469 std::distance(str.begin(), begin)));
475inline std::string_view rtrim(std::string_view str) {
476 const auto end = std::find_if(str.rbegin(), str.rend(), [](
auto ch) {
479 return str.substr(0,
static_cast<std::string_view::size_type
>(
480 std::distance(end, str.rend())));
486inline std::string_view trim(std::string_view str) {
487 return ltrim(rtrim(str));
495 template<
typename T >
496 std::string to_hex(T ival) {
497 std::stringstream stream;
498 stream << std::setfill(
'0') << std::setw(
sizeof(T) * 2)
508UTILS_EX
int levenshtein_distance(std::string_view s1, std::string_view s2);
513UTILS_EX
bool is_valid_utf8(std::string_view str);
519inline bool iequals(std::string_view a, std::string_view b) {
521 std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b), [](
auto a,
auto b) {
522 return std::tolower(a) == std::tolower(b);
531template<
typename C,
typename T>
532std::optional<T> at(
const C& container, std::string_view s,
unsigned index = 0) {
533 const auto range = container.equal_range(s);
534 const auto it = std::next(range.first, index);
535 return (std::distance(range.first, it) < std::distance(range.first, range.second)) ?
536 std::make_optional(it->second) : std::nullopt;
539template<
typename C,
typename T>
540T at_or(
const C& container, std::string_view s,
const T& defaultValue,
unsigned index = 0) {
541 const auto v = at<C, T>(container, s, index);
542 return v.has_value() ? *v : defaultValue;
551template <
class Container = std::vector<std::
string>>
552Container split(std::string_view str,
const char splitChar =
' ') {
554 std::istringstream iss(std::string{str});
555 for(std::string token; iss.good() && std::getline(iss, token, splitChar);) {
556 con.insert(con.end(), convert<typename Container::value_type>(token));
562template <
typename IT>
563inline constexpr std::string_view make_string_view(IT begin, IT end)
565 return (begin == end) ? std::string_view{
nullptr} : std::string_view{&*begin, std::distance(begin, end)};
574template <
class T,
typename K =
typename T::key_type>
575std::vector<K> keys(
const T& map) {
576 std::vector<K> ks; ks.resize(map.size());
577 std::transform(map.begin(), map.end(), ks.begin(), [](
const auto& p) {return p.first;});
583 struct DefaultJoiner {
584 auto operator()(
const V& v)
const {
return v;}
589template <
class IT,
typename In,
typename Out>
590[[deprecated(
"See join with Callable")]] std::string join(
const IT& begin,
592 std::string_view joinChar =
"",
593 const std::function<Out (
const In&)>& f = [](
const In& k)->Out{
return k;}) {
595 std::ostringstream iss(s);
597 for(
auto it = begin;;) {
599 if(!(++it != end))
break;
600 if(!joinChar.empty())
607template <
class T,
typename In,
typename Out>
608[[deprecated(
"See join with Callable")]] std::string join(
const T& t,
609 std::string_view joinChar =
"",
610 const std::function<Out (
const In&)>& f = [](
const In& v)->Out{
return v;}) {
611 return join(t.begin(), t.end(), joinChar, f);
614template <class IT, typename In, typename Out, typename = std::enable_if_t<std::is_pointer<IT>::value>>
615[[deprecated(
"See join with Callable")]] std::string join(
const IT begin,
617 std::string_view joinChar =
"",
618 const std::function<Out (
const In&)>& f = [](
const In& k)->Out{
return k;}) {
620 std::ostringstream iss(s);
622 for(
auto it = begin;;) {
624 if(!(++it != end))
break;
625 if(!joinChar.empty())
642template <
typename IT,
typename Callable = DefaultJoiner<
typename IT::value_type>,
643 typename = std::enable_if_t<!std::is_po
inter<IT>::value>>
644std::string join(
const IT& begin,
646 std::string_view joinChar =
"",
647 const Callable& f = Callable{}) {
649 std::ostringstream iss(s);
651 for(
auto it = begin;;) {
653 if(!(++it != end))
break;
654 if(!joinChar.empty())
669template <typename IT, typename Callable = DefaultJoiner<typename std::remove_pointer<IT>::type>,
670 typename = std::enable_if_t<std::is_pointer<IT>::value>>
671std::string join(
const IT begin,
673 std::string_view joinChar =
"",
674 const Callable& f = Callable{}) {
676 std::ostringstream iss(s);
678 for(
auto it = begin;;) {
680 if(!(++it != end))
break;
681 if(!joinChar.empty())
695template <
typename T,
typename Callable = DefaultJoiner<
typename T::value_type>>
696std::string join(
const T& t,
697 std::string_view joinChar =
"",
698 const Callable& f = Callable{}) {
699 return join(std::begin(t), std::end(t), joinChar, f);
704T merge(
const T& b1,
const T& b2) {
705 T bytes(b1.size() + b2.size());
706 auto begin = bytes.begin();
707 std::copy(b1.begin(), b1.end(), begin);
708 std::advance(begin, std::distance(b1.begin(), b1.end()));
709 std::copy(b2.begin(), b2.end(), begin);
714template <
typename T,
typename ...Arg>
715T merge(
const T& b1,
const T& b2, Arg ...args) {
716 T bytes(b1.size() + b2.size());
717 auto begin = bytes.begin();
718 std::copy(b1.begin(), b1.end(), begin);
719 std::advance(begin, std::distance(b1.begin(), b1.end()));
720 std::copy(b2.begin(), b2.end(), begin);
721 return merge(bytes, args...);
726template <
typename IT> IT advanced(IT it,
int distance) {
727 std::advance(it, distance);
731template <
typename K,
typename V >
733std::optional<V> get_value(
const std::multimap<K, V>& map,
const K& key,
int index = 0)
735 const auto range = map.equal_range(key);
736 return std::distance(range.first, range.second) > index ?
737 std::make_optional((advanced(range.first, index))->second) : std::nullopt;
749UTILS_EX std::shared_ptr<expiror> wait_expire(std::chrono::seconds s,
const std::function<
void ()>& onExpire);
752 ~expiror() {m_f.wait();}
755 expiror(std::future<void>&& f) : m_f(std::move(f)) {}
756 std::future<void> m_f{};
757 friend std::shared_ptr<GempyreUtils::expiror> GempyreUtils::wait_expire(std::chrono::seconds s,
const std::function<
void ()>& onExpire);
766UTILS_EX std::string hexify(std::string_view src, std::string_view pat);
771UTILS_EX std::string unhexify(std::string_view src);
774enum class OS {OtherOs, MacOs, WinOs, LinuxOs, AndroidOs, RaspberryOs};
777UTILS_EX OS current_os();
780UTILS_EX std::string html_file_launch_cmd();
785enum AddressType :
unsigned {Ipv4 = 0x1, Ipv6 = 0x2};
789UTILS_EX std::vector<std::string> ip_addresses(
unsigned addressType);
796enum class PathStyle {Native, Unix, Win};
799UTILS_EX std::string get_link(std::string_view fname);
801UTILS_EX
bool is_dir(std::string_view fname);
803UTILS_EX std::string working_dir();
805UTILS_EX std::string home_dir();
807UTILS_EX std::string root_dir();
809UTILS_EX std::string abs_path(std::string_view rpath);
815UTILS_EX std::string path_pop(std::string_view filename,
int steps = 1, PathStyle path_style = PathStyle::Native);
817UTILS_EX std::vector<std::string> entries(std::string_view dirname);
820[[deprecated(
"use entries")]]
821inline std::vector<std::string> directory(std::string_view dirname) {
return entries(dirname);}
828UTILS_EX std::optional<std::string> read_process(std::string_view processName,
const std::vector<std::string>& params);
830UTILS_EX std::string base_name(std::string_view filename, PathStyle path_style = PathStyle::Native);
832UTILS_EX std::tuple<std::string, std::string> split_name(std::string_view filename, PathStyle path_style = PathStyle::Native);
834UTILS_EX std::string temp_name();
836UTILS_EX std::string host_name();
838UTILS_EX std::optional<std::string> system_env(std::string_view env);
840UTILS_EX
bool is_hidden_entry(std::string_view filename);
842UTILS_EX
bool is_executable(std::string_view filename);
844UTILS_EX SSIZE_T file_size(std::string_view filename);
846UTILS_EX
bool rename(std::string_view of, std::string_view nf);
848UTILS_EX
void remove_file(std::string_view filename);
850UTILS_EX
bool file_exists(std::string_view filename);
852UTILS_EX std::optional<std::string> which(std::string_view filename);
854UTILS_EX std::string push_path(std::string_view path, std::string_view name);
856template<
class ...NAME>
857std::string push_path(std::string_view path, std::string_view name, NAME...names) {
858 return push_path(push_path(path, name), names...);
862UTILS_EX
int execute(std::string_view prog,
const std::vector<std::string_view>& parameters);
865template<
class ...PARAM>
866UTILS_EX
int execute(std::string_view prog, PARAM...parameters) {
867 std::vector<std::string_view> parameter_list{parameters...};
868 return execute(prog, parameter_list);
879std::string write_to_temp(
const T& data) {
880 const auto name = GempyreUtils::temp_name();
881 std::ofstream out(name, std::ios::out | std::ios::binary);
882 std::ostreambuf_iterator<typename T::value_type> iter(out);
883 std::copy(data.begin(), data.end(), iter);
893std::vector<T> slurp(std::string_view file,
const size_t max = std::numeric_limits<size_t>::max()) {
896 std::ifstream stream(std::string{file}, std::ios::in | std::ios::binary | std::ios::ate);
897 if(!stream.is_open()) {
898 log(LogLevel::Error,
"Cannot open file", qq(file));
901 const auto size = std::min(max,
static_cast<size_t>(stream.tellg()));
905 vec.resize(size /
sizeof (T), 0);
906 stream.seekg(std::ios_base::beg);
907 auto ptr =
reinterpret_cast<char*
>(vec.data());
908 stream.read(ptr,
static_cast<std::streamsize
>(size));
914UTILS_EX std::string slurp(std::string_view file,
const size_t max = std::numeric_limits<size_t>::max());
917enum class JsonMode {Compact, Pretty};
924UTILS_EX Result<std::string> to_json_string(
const std::any& any, JsonMode mode = JsonMode::Compact);
927enum class MapType {Map, UnorderedMap};
932UTILS_EX Result<std::any> json_to_any(std::string_view str, MapType map_type = MapType::UnorderedMap);
935using JsonType = std::variant<
941std::vector<std::any>,
942std::map<std::string, std::any>,
943std::unordered_map<std::string, std::any>
952UTILS_EX ResultTrue set_json_value(std::any& any, std::string_view path, JsonType&& value);
958UTILS_EX ResultTrue remove_json_value(std::any& any, std::string_view path);
964UTILS_EX Result<JsonType> get_json_value(
const std::any& any, std::string_view path);
971UTILS_EX ResultTrue make_json_path(std::any& any, std::string_view path,
972 const std::function<JsonType (std::string_view, std::string_view)>& f = [](
auto,
auto name) {
973 return (GempyreUtils::parse<int>(name)) ?
974 GempyreUtils::JsonType{std::vector<std::any>{}} :
975 GempyreUtils::JsonType{std::unordered_map<std::string, std::any>{}};});
978UTILS_EX
bool is_available(
int port);
981UTILS_EX std::string base64_encode(
const unsigned char* bytes,
size_t sz);
983UTILS_EX std::string base64_encode(
const std::vector<uint8_t> & vec);
985UTILS_EX std::string base64_encode(std::string_view str);
987UTILS_EX std::vector<uint8_t> base64_decode(std::string_view data);
998using ParamList = std::vector<std::string>;
1001using Options = std::multimap<std::string, std::string>;
1004using Params = std::tuple<ParamList, Options>;
1011UTILS_EX Params parse_args(
int argc,
char* argv[],
const std::initializer_list<std::tuple<std::string, char, ArgType>>& args);
1019template <
typename T>
1020T option_or(
const Options& opts, std::string_view key,
const T& default_value) {
1021 const auto it = opts.find(std::string{key});
1022 if(it == opts.end())
1023 return default_value;
1024 const auto parsed = parse<T>(it->second);
1025 return parsed ? parsed.value() : default_value;
Parent class for LogWriters.
Definition gempyre_utils.h:82
virtual bool do_write(const char *buffer, size_t count)=0
Implement write to the medium.
virtual std::string header(LogLevel logLevel)
header of class, called before every line, default just returns a timestamp and loglevel string.
virtual ~LogWriter()
Destructor.
virtual bool has_ansi() const
override to return true if this write supports ANSI colors, default just return false
UTILS_EX std::string to_str(LogLevel log_level)
Log Level to string.
UTILS_EX LogLevel log_level()
Get current log level.
void log_debug(const T &e, Args... args)
Write a debug log.
Definition gempyre_utils.h:181
void log(LogLevel level, const T &e, Args... args)
Write a log line.
Definition gempyre_utils.h:165
UTILS_EX void set_log_level(LogLevel level)
Set current log level.
LogLevel
The LogLevel enum.
Definition gempyre_utils.h:71
@ Warning
Something is wrong, Default.
@ Info
At least developer should be worried.
@ Debug_Trace
What is going on.
@ Fatal
All logs disabled.
@ Error
Execution ends here.
@ Debug
Something developer should know.