Skip to content

Instantly share code, notes, and snippets.

@emadflash
Created August 31, 2021 07:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emadflash/a19f40c6a17aeb9858ea995dbedc018d to your computer and use it in GitHub Desktop.
Save emadflash/a19f40c6a17aeb9858ea995dbedc018d to your computer and use it in GitHub Desktop.
#ifndef FTODO_HPP
#define FTODO_HPP
#include <string_view>
#include <vector>
#include <filesystem>
#include <cstring>
#include <cassert>
#include <memory>
#include <optional>
#define basic_println(X, ...) fprintf(X, __VA_ARGS__); printf("\n")
#define println(...) basic_println(stdout, __VA_ARGS__)
#define eprintln(...) basic_println(stderr, __VA_ARGS__)
#define STRCMP(X, Y) (strcmp((X), (Y)) == 0)
using String_Vec = std::vector<std::string>;
namespace fs = std::filesystem;
#ifndef _ITERABLE
#define _ITERABLE
template <typename It>
concept iterable = std::random_access_iterator<It>;
#endif
template <typename T, typename It>
bool _in_container(It begin , It end, T element);
template <typename T>
bool in_container(T Container, auto x);
void prefix_file_extension_with_dot(String_Vec& sv);
//
// Return status code from command
bool check_status_code(std::string_view command);
//
// Returns if directory is a git repo, by returning a status code of `git status` command
bool _is_git_repository(const fs::path& current_path, const fs::path& target_path);
//
// Check if its a file
bool is_file(const fs::path& _path);
//
// Path stuff
bool file_exists(const fs::path& _path);
//
// Prefixs paths
template<iterable It>
void prefix_paths(It begin , It end, const fs::path& path);
// ------------------------------------
// CONFIG
// ------------------------------------
struct Config {
String_Vec exclude_directories, file_extensions;
Config(String_Vec exclude_directories, String_Vec file_extensions);
};
// ------------------------------------
// DIRECTORY ITERATOR CONFIG
// ------------------------------------
struct DirectoryIteratorConfig {
bool follow_symlinks,
follow_exclude_directories;
};
using iterator_callback = void(*)(fs::path& path);
// ------------------------------------
// TARGET
// ------------------------------------
struct Target {
private:
std::vector<std::string> exclude_directories;
std::vector<std::string> file_extensions;
//
// Prefix string with target path
std::string prefix_file_w_target_path(std::string& _path);
//
// Get gitmodules path
std::string get_gitmodules_path();
public:
Config config;
bool is_git_repository {};
const ::fs::path path;
const ::fs::path current_path;
Target(const ::fs::path& current_path, const ::fs::path& target_path, Config config);
Target() = default;
inline bool in_file_extensions(const fs::path& path);
inline bool in_exclude_directories(const fs::path& path);
//
// Updates exclude_directories
void add_to_exclude_directories(std::string_view sv);
// Getters
auto get_exclude_directories() -> std::vector<std::string>;
};
// ---------------------------------
// DIRECTORY ITERATOR
// ---------------------------------
struct Director_Iterator {
Target target;
DirectoryIteratorConfig Directory_Iterator_Config;
Director_Iterator(Target& target, const DirectoryIteratorConfig& Directory_Iterator_Config);
void iterate(iterator_callback callback);
//
// Iterates over a directory and lists files with given extensions
void _iterate(const fs::path& path, iterator_callback callback);
//
// Is follow symlinks
bool is_follow_symlinks(const fs::path& path);
//
// Is follow excluded directories
bool is_follow_exclude_directories(const fs::path& path);
};
// --------------------------------------------------------------------------------------------
// Argv_Iterator: does bound checks, throws Argv_Iterator_Out_Of_Bounds if bound exceeed
// --------------------------------------------------------------------------------------------
struct Argv_Iterator_Out_Of_Bounds : public std::exception {
const char* what () const throw () {
return "Argv_Iterator: Out of bounds memory access";
}
};
struct Argv_Iterator {
char** begin;
char** end;
char** curr;
Argv_Iterator(int argc, char** argv);
char* get();
// compare current value with dest
bool compare(const char* dest) {
return STRCMP(this->get(), dest);
}
friend void operator++(Argv_Iterator& iterator);
friend void operator--(Argv_Iterator& iterator);
};
// --------------------------------------------------------------------------------------------
// Flag: a flag
// --------------------------------------------------------------------------------------------
// flag types
enum class flag_type {
flag_string,
flag_boolean,
flag_int,
};
const char* flag_type_to_string(const flag_type& type) {
#define TO_STRING(X, Y)\
case X:\
return Y
switch (type) {
TO_STRING(flag_type::flag_string, "STRING");
TO_STRING(flag_type::flag_boolean, "BOOL");
TO_STRING(flag_type::flag_int, "INT");
}
return "???";
}
struct Flag_Arg_Value_Null_Error: public std::exception {
const char* what () const throw () {
return "Flag: Value is null, which might mean its not parsed yet!";
}
};
// Flag_Values: Contains all values a Flag might have
//struct Flag_Values {
//bool value_bool {};
//std::string value_string {};
//std::uint8_t value_int {};
//Flag_Values() = default;
//template <typename T>
//constexpr T get(flag_type& type) {
//#define FLAG_VALUE_GET(X, Y)\
//case X:\
//return Y
//switch (type) {
//FLAG_VALUE_GET(flag_type::flag_boolean, this->value_bool);
//FLAG_VALUE_GET(flag_type::flag_string, this->value_string);
//FLAG_VALUE_GET(flag_type::flag_int, this->value_int);
//}
//assert(1 == 1 && "Invaild flag_type");
//}
//template <typename T>
//constexpr void set(flag_type& type, T& value) {
//#define FLAG_VALUE_SET(X, Y)\
//case X:\
//Y = value;\
//return
//switch (type) {
//FLAG_VALUE_SET(flag_type::flag_boolean, this->value_bool);
//FLAG_VALUE_SET(flag_type::flag_string, this->value_string);
//FLAG_VALUE_SET(flag_type::flag_int, this->value_int);
//}
//assert(1 == 1 && "Invaild flag_type");
//}
//};
struct Flag {
flag_type type;
const char* short_switch;
const char* long_switch;
const char* description;
bool is_on {}, is_positional {};
std::string value_str {};
std::uint8_t value_int {};
bool value_bool {};
Flag(const flag_type& type, const char* short_switch, const char* long_switch, const char* description):
type { type },
short_switch { short_switch },
long_switch { long_switch },
description { description }
{}
Flag(const flag_type& type, const bool&& is_positional, const char* short_switch, const char* long_switch, const char* description):
type { type },
is_positional { is_positional },
short_switch { short_switch },
long_switch { long_switch },
description { description }
{}
operator bool() const {
return this->is_on;
}
void set_int(const std::uint8_t& val) {
assert(this->type == flag_type::flag_int && "Flag type must be flag_int");
this->value_int = val;
}
void set_str(const std::string& val) {
assert(this->type == flag_type::flag_string && "Flag type must be flag_string");
this->value_str = val;
}
void set_bool(const bool& val) {
assert(this->type == flag_type::flag_boolean && "Flag type must be flag_boolean");
this->value_bool = val;
}
std::uint8_t get_int() {
assert(this->type == flag_type::flag_int && "Flag type must be flag_int");
return this->value_int;
}
std::string get_string() {
assert(this->type == flag_type::flag_string && "Flag type must be flag_string");
if (this->value_str.empty()) {
throw Flag_Arg_Value_Null_Error();
}
return this->value_str;
}
bool get_bool() {
assert(this->type == flag_type::flag_boolean && "Flag type must be flag_boolean");
return this->value_bool;
}
const std::string usage() const {
std::stringstream ss {};
ss << "\t"
<< this->short_switch << ", " << this->long_switch << " [" << flag_type_to_string(this->type) << "]"
<< "\n\t\t"
<< this->description
<< "\n\n";
return ss.str();
}
};
// --------------------------------------------------------------------------------------------
// What_Usage: It contains usage things
// --------------------------------------------------------------------------------------------
struct What_Usage {
const char* name;
const char* description;
const std::vector<Flag*> flags;
std::string optionals_usage {}, positionals_usage {};
What_Usage(const char* name, const char* description, const std::vector<Flag*>& flags);
friend std::ostream& operator<<(std::ostream& os, const What_Usage& usage);
};
// --------------------------------------------------------------------------------------------
// Flag_Parser: a flag parser
// --------------------------------------------------------------------------------------------
struct Flag_Parser {
const std::uint8_t max_buffer_size { 255 }; // Max buffer size
using argv_iterator_callback = void(*)(const What_Usage& what_usage, Argv_Iterator& argv_iterator);
Argv_Iterator& iterator;
const What_Usage& what_usage;
const std::vector<Flag*> flags;
Flag_Parser(const What_Usage& what_usage, Argv_Iterator& iterator, const std::vector<Flag*>& flags):
what_usage { what_usage },
iterator { iterator },
flags { flags }
{}
std::optional<Flag*> check();
void parse(argv_iterator_callback callback); // if used, have to do the invaild flag error checking yourself
void parse(); // Without the callback will use default callback which checks for invaild flag
};
#endif // FTODO_HPP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment