Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
b1ca8a5
misc: all error msg lowercase
csboo Apr 14, 2025
9f16615
feat(utils): get clean program name
csboo Apr 15, 2025
0805eb4
misc(meson) don't build the static lib, simpler for now
csboo Apr 15, 2025
6513976
BROKEN misc(Arg): better private member names
csboo Apr 15, 2025
d076ad8
BROKEN feat(Arg): getters and setters a A LOT better + more usable
csboo Apr 15, 2025
f688918
BROKEN misc(Arg): better nameing in source file
csboo Apr 15, 2025
ef7c2f8
BROKEN fix(Arg): adjust '<<' overload for new privates
csboo Apr 15, 2025
57eaf45
BROKEN fix(Arg): removed old getter/setter defs
csboo Apr 15, 2025
3403cb5
BROKEN fix(Arg): remove unneeded redeclaration
csboo Apr 15, 2025
84ddf89
BROKEN misc(Arg): use more explicit names again and even more
csboo Apr 15, 2025
a87a18f
BROKEN fix(Arg): reworked the constructor
csboo Apr 15, 2025
4d4b668
BROKEN fix(Arg): clean up includes
csboo Apr 15, 2025
ac7ca5b
BROKEN feat(utils): move src/utils.cpp -> include/utils.hpp
csboo Apr 15, 2025
4f46a22
BROKEN fix: cleaned all includes in all files
csboo Apr 15, 2025
078905f
BROKEN misc(Parser): added a const ref, removed unneeded
csboo Apr 15, 2025
ee66d93
BROKEN fix(Parser): using new arg getters/setters, we gettin' there!
csboo Apr 15, 2025
11d7ca2
fix(Parser): switch to PROGRAM_NAME() util, all fixes done maybe?
csboo Apr 15, 2025
ea9cf88
misc(Arg): rename try_env -> auto_env
csboo Apr 15, 2025
3006b4c
feat(example): WIP added example
csboo Apr 16, 2025
df464eb
misc(Arg): rename takes_value() -> is_flag()
csboo Apr 16, 2025
8561fea
fix(Arg): fixed a major logical mistake
csboo Apr 16, 2025
59c17ac
feat(Parser): less code, using new functions
csboo Apr 16, 2025
fcfdf3f
fix(Arg): corrected names
csboo Apr 17, 2025
4845e4f
feat(utils): added a utility to simply get a \' + varname + \' like a…
csboo Apr 19, 2025
d6a08c6
misc(parser): added 'this->' where needed
csboo Apr 19, 2025
28c6c22
fix(parser): big update, way better code, way less code, many optimiz…
csboo Apr 19, 2025
39fa72d
feat(example): updated example
csboo Apr 19, 2025
caca103
fix(parser): use new logic instead of old (wrong) functions
csboo Apr 30, 2025
81b7400
feat(Arg): proper debug print indentation for Arg
csboo May 7, 2025
1f4c6d3
feat(Parser): proper debug print indentation for Parser
csboo May 7, 2025
8d30500
fix(Arg): is_flag doesn't need an arg
csboo May 7, 2025
f1cbe85
fix(example): adjusted example code
csboo May 7, 2025
48823a0
feat(Arg): is_flag sets deafult value to '0' (false)
csboo May 7, 2025
17f04a8
fix(Parser): arg env value gets parsed (better) in the ClapParser::pa…
csboo May 7, 2025
cb34fdf
feat(tests): added many tests and a lot more to come, (meson test -C …
csboo May 7, 2025
b12723c
feat(Parser): cross platfrom program name maybe
May 10, 2025
b2f5f74
fix(Parser): fix file path handling on Windows
csboo May 12, 2025
953cc0e
fix(Parser): remove .exe from env name on windows
csboo May 12, 2025
df68f3e
fix(Parser): remove windows .exe from names (for real this time?)
csboo May 12, 2025
4a0b152
feat(github): added push tester workflow
csboo May 12, 2025
7544a4a
feat(github): always print test logs for verbosity
csboo May 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/meson-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Meson Tests

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
name: Meson tests (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]

steps:
- name: Checkout repo
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install Meson and Ninja
run: |
python -m pip install --upgrade pip
pip install meson ninja

- name: Setup build directory
run: meson setup build

- name: Build
run: meson compile -C build

- name: Run tests
run: meson test -C build --print-errorlogs

- name: Print test logs
run: cat build/meson-logs/testlog.txt || echo "No meson logs found"
151 changes: 121 additions & 30 deletions include/Arg.hpp
Original file line number Diff line number Diff line change
@@ -1,55 +1,146 @@
#pragma once

#include "utils.hpp"

#include <cerrno>
#include <ostream>
#include <iostream>
#include <complex>
#include <functional>
#include <optional>
#include <string>

class ClapParser;

class Arg {
public:
explicit Arg(std::string name);
Arg(const std::string& name);

Arg& short_name(const std::string& s);
Arg& long_name(const std::string& l);
Arg& short_name(const std::string& short_name);
Arg& long_name(const std::string& long_name);
Arg& help(const std::string& help);
Arg& required(bool is_required);
Arg& takes_value(bool takes);
Arg& is_flag();
Arg& default_value(const std::string& default_val);
Arg& from_env(const char* env_var_name);
Arg& try_env();
void set_try_env_name(const std::string& s);

// Getters
[[nodiscard]] const std::string& short_name() const;
[[nodiscard]] const std::string& long_name() const;
[[nodiscard]] const std::string& help() const;
[[nodiscard]] const std::string& default_value() const;
[[nodiscard]] bool is_required() const;
[[nodiscard]] bool requires_value() const;
Arg& auto_env();

static void print_arg(std::ostream& os, const Arg& arg, int indent);
friend std::ostream& operator<<(std::ostream& os, const Arg& arg);

private:
friend class ClapParser;

std::string name_;
std::string short_;
std::string long_;
std::string short_name_;
std::string long_name_;
std::string help_;
bool required_;
bool takes_value_;
bool has_env_;
bool is_required_;
bool is_flag_;
std::string env_name_;
bool try_env_;
std::string try_env_name_;
std::string default_;
bool auto_env_;
// std::string auto_env_name_;
std::string default_value_;
std::optional<std::string> value_;

[[nodiscard]] bool has_default() const;
[[nodiscard]] bool has_env() const;
[[nodiscard]] bool takes_value() const;
[[nodiscard]] const std::string& name() const;
// ----| Getters & Setters |----
// name_
[[nodiscard]] inline const std::string& get__name() const {
return this->name_;
}
inline void set__name(const std::string& name) {
this->name_ = name;
}

// short_
[[nodiscard]] inline const std::string& get__short_name() const {
return this->short_name_;
}
inline void set__short_name(const std::string& short_name) {
this->short_name_ = short_name;
}

// long_
[[nodiscard]] inline const std::string& get__long_name() const {
return this->long_name_;
}
inline void set__long_name(const std::string& long_name) {
this->long_name_ = long_name;
}

// help_
[[nodiscard]] inline const std::string& get__help() const {
return this->help_;
}
inline void set__help(const std::string& help) {
this->help_ = help;
}

// required_
[[nodiscard]] inline bool get__is_required() const {
return this->is_required_;
}
inline void set__is_required(const bool& is_required) {
this->is_required_ = is_required;
}

// takes_value_
[[nodiscard]] inline bool get__is_flag() const {
return this->is_flag_;
}
inline void set__is_flag(const bool& takes_value) {
this->is_flag_ = takes_value;
}

// env_name_
[[nodiscard]] inline const std::string& get__env_name() const {
return this->env_name_;
}
inline void set__env_name(const std::string& env_name) {
this->env_name_ = env_name;
}

// auto_env_
[[nodiscard]] inline bool get__auto_env() const {
return this->auto_env_;
}
inline void set__auto_env(const bool& auto_env) {
this->auto_env_ = auto_env;
}

// auto_env_name_
// [[nodiscard]] inline const std::string get__auto_env_name() const {
// std::string env_name = PROGRAM_NAME() + '_' + this->get__name();
// std::transform(env_name.begin(), env_name.end(), env_name.begin(), [](const unsigned char& c) { return std::toupper(c); });
// return env_name;
// }

// default_
[[nodiscard]] inline const std::string& get__default_value() const {
return this->default_value_;
}
inline void set__default_value(const std::string& default_value) {
this->default_value_ = default_value;
}

// value_
[[nodiscard]] inline const std::optional<std::string> get__value() const {
return this->value_;
}
inline void set__value(const std::string& value) {
this->value_ = value;
}

// ----| Checkers |----
// has_env_
[[nodiscard]] inline bool has_env() const {
return !this->env_name_.empty();
}

// has_default_
[[nodiscard]] inline bool has_default() const {
return !this->default_value_.empty();
}

// has_value_
[[nodiscard]] inline bool has_value() const {
return this->value_.has_value();
}

};
43 changes: 17 additions & 26 deletions include/Parser.hpp
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
#pragma once

#include "Arg.hpp"
#include "utils.hpp"

#include <iostream>
#include <optional>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <vector>

class ClapParser {
public:
void add_arg(const Arg& arg);
void parse(int argc, char* argv[]);
void parse(const int& argc, char* argv[]);
void print_help() const;

template <typename T> std::optional<T> get_one_as(const std::string& name) const;
bool has(const std::string& name) const;
template <typename T> inline std::optional<T> get_one_as(const std::string& name) {
Arg* arg = ok_or(ClapParser::find_arg(*this, "--" + name), []{ return std::nullopt; });

if (auto arg_value = arg->get__value(); arg_value) {
T value;
std::istringstream(*arg_value) >> value;
return value;
}
return std::nullopt;
}

static void print_parser(std::ostream& os, const ClapParser& parser, int indent);
friend std::ostream& operator<<(std::ostream& os, const ClapParser& parser);
private:
std::vector<Arg> args_;
Expand All @@ -27,31 +37,12 @@ class ClapParser {
inline bool is_option(const std::string& token) const ;
inline bool is_long_option(const std::string& token) const ;
inline bool is_short_option(const std::string& token) const ;
const Arg* find_option(const std::string& name) const;
static std::optional<Arg*> find_arg(ClapParser& parser, const std::string& name);
std::vector<Arg> get_positional_args() const;
void apply_defaults();

void parse_options(const std::vector<std::string>& args);
void parse_positional_args(const std::vector<std::string>& args);
void check_required_args();
void check_env();
void parse_positional_args(const std::vector<std::string>& args);
void handle_missing_positional(const Arg& arg);

size_t handle_long_option(const std::string& token, const std::vector<std::string>& args, size_t i);
size_t handle_short_option(const std::string& token, const std::vector<std::string>& args, size_t i);
size_t handle_option_with_value(const Arg* arg, const std::vector<std::string>& args, size_t i,
const std::string& token);

};

template <typename T> std::optional<T> ClapParser::get_one_as(const std::string& name) const {
auto it = values_.find(name);
if (it == values_.end()) {
return std::nullopt;
}

T value;
std::istringstream(it->second) >> value;
return value;
// return std::any_cast<T>(it->second);
}
21 changes: 16 additions & 5 deletions src/utils.cpp → include/utils.hpp
Original file line number Diff line number Diff line change
@@ -1,35 +1,46 @@
#pragma once

#include <optional>
#include <stdexcept>
#include <string>
#include <sstream>

template <typename T, typename E>
T ok_or(std::optional<T> opt, E&& err) {
inline T ok_or(std::optional<T> opt, E&& err) {
if (!opt) std::forward<E>(err)();
return *opt;
}

template <typename T>
T ok_or_throw_str(std::optional<T> opt, const std::string& err) {
inline T ok_or_throw_str(std::optional<T> opt, const std::string& err) {
if (!opt) throw std::runtime_error(err);
return *opt;
}

template <typename T>
T ptr_ok_or_throw_str(T pointer, const std::string& err) {
inline T ptr_ok_or_throw_str(T pointer, const std::string& err) {
if (!pointer) throw std::runtime_error(err);
return pointer;
}

template <typename P, typename E>
E ptr_unwrap_or(P pointer, const E other) {
inline E ptr_unwrap_or(P pointer, const E other) {
if (!pointer) return other;
}

// variadic template function to concatenate any number of arguments
template <typename... Args> std::string concat(Args&&... args) {
template <typename... Args> inline std::string concat(Args&&... args) {
std::ostringstream oss;
(void)std::initializer_list<int>{
(oss << std::forward<Args>(args), 0)...}; // using initializer_list for fold-like behavior
return oss.str();
}

inline const std::string quote(const std::string& name) {
return '\'' + name + '\'';
}

inline void print_indent(std::ostream& os, int indent_level) {
for (int i = 0; i < indent_level; ++i)
os << '\t';
}
Loading