Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Checking validity of names #55

Merged
merged 13 commits into from
Jan 21, 2021
48 changes: 32 additions & 16 deletions include/opzioni.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,18 +330,28 @@ consteval auto operator*(std::array<Arg, N> const args, Arg const other) noexcep
}

consteval void validate_arg(Arg const &arg) noexcept {
if (arg.name.empty())
throw "An argument cannot have an empty name";

if (arg.type == ArgType::POS && arg.has_abbrev())
throw "Positionals cannot have abbreviations";

if (arg.has_abbrev() && arg.abbrev.length() != 1)
throw "Abbreviations must be a single character";

if (!is_valid_name(arg.name))
throw "Argument names can only contain alphanumeric characters and - or _,"
"and must begin with a letter and end with a letter or a number";

if (!is_valid_abbrev(arg.abbrev))
throw "Argument abbreviations can only contain alphanumeric characters";

if (arg.type == ArgType::POS && arg.has_set())
throw "Positionals cannot use set value because they always take a value from the command-line";

if (arg.type == ArgType::FLG && arg.gather_amount != 1) // 1 is the default
throw "Flags cannot use gather because they do not take values from the command-line";

if (arg.has_abbrev() && arg.abbrev.length() != 1)
throw "Abbreviations must be a single letter";

if (arg.is_required && arg.has_default())
throw "A required argument cannot have a default value";

Expand All @@ -367,28 +377,30 @@ consteval void validate_args(std::array<Arg, N> const &args, Arg const &other) n
// +----------------------+

consteval Arg Flg(std::string_view name, std::string_view abbrev) noexcept {
if (!abbrev.empty() && abbrev.length() != 1)
throw "Abbreviations must be a single letter";
return Arg{.type = ArgType::FLG,
.name = name,
.abbrev = abbrev,
.default_value = false,
.set_value = true,
.action_fn = actions::assign<bool>};
auto const arg = Arg{.type = ArgType::FLG,
.name = name,
.abbrev = abbrev,
.default_value = false,
.set_value = true,
.action_fn = actions::assign<bool>};
validate_arg(arg);
return arg;
}

consteval Arg Flg(std::string_view name) noexcept { return Flg(name, {}); }

consteval Arg Opt(std::string_view name, std::string_view abbrev) noexcept {
if (!abbrev.empty() && abbrev.length() != 1)
throw "Abbreviations must be a single letter";
return Arg{.type = ArgType::OPT, .name = name, .abbrev = abbrev, .default_value = ""};
auto const arg = Arg{.type = ArgType::OPT, .name = name, .abbrev = abbrev, .default_value = ""};
validate_arg(arg);
return arg;
}

consteval Arg Opt(std::string_view name) noexcept { return Opt(name, {}); }

consteval Arg Pos(std::string_view name) noexcept {
return Arg{.type = ArgType::POS, .name = name, .is_required = true};
auto const arg = Arg{.type = ArgType::POS, .name = name, .is_required = true};
validate_arg(arg);
return arg;
}

consteval Arg Help(std::string_view description) noexcept {
Expand Down Expand Up @@ -511,7 +523,11 @@ class Program {
std::array<ProgramView, CmdsSize> cmds;

consteval Program(std::string_view name) : Program(name, {}) {}
consteval Program(std::string_view name, std::string_view title) : metadata{.name = name, .title = title} {}
consteval Program(std::string_view name, std::string_view title) : metadata{.name = name, .title = title} {
if (!is_valid_name(name))
throw "Program names cannot be empty, can only contain alphanumeric characters and - or _,"
"and must begin with a letter and end with a letter or a number";
}

template <std::size_t OtherArgsSize, std::size_t OtherCmdsSize>
consteval Program(Program<OtherArgsSize, OtherCmdsSize> const &other) : metadata(other.metadata) {
Expand Down
16 changes: 16 additions & 0 deletions include/string.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef OPZIONI_STRING_H
#define OPZIONI_STRING_H

#include <algorithm>
#include <concepts>
#include <string>
#include <string_view>
Expand Down Expand Up @@ -35,6 +36,21 @@ auto limit_within(std::string_view const, std::size_t const) noexcept -> std::ve

std::string limit_string_within(std::string_view const, std::size_t const) noexcept;

constexpr bool is_alphabetic(char const ch) noexcept { return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); }

constexpr bool is_numeric(char const ch) noexcept { return ch >= '0' && ch <= '9'; }

constexpr bool is_valid_name(std::string_view name) noexcept {
return name.length() > 0 && is_alphabetic(name.front()) &&
(is_alphabetic(name.back()) || (name.length() > 1 && is_numeric(name.back()))) &&
std::ranges::all_of(
name, [](char const ch) { return is_alphabetic(ch) || is_numeric(ch) || ch == '-' || ch == '_'; });
}

constexpr bool is_valid_abbrev(std::string_view abbrev) noexcept {
return abbrev.length() == 0 || (abbrev.length() == 1 && (is_alphabetic(abbrev[0]) || is_numeric(abbrev[0])));
}

} // namespace opzioni

#endif // OPZIONI_STRING_H
2 changes: 1 addition & 1 deletion meson.build
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
project(
'opzioni', 'cpp',
version: '0.45.1',
version: '0.45.2',
license: 'BSL-1.0',
default_options: ['cpp_std=c++2a', 'buildtype=debug']
)
Expand Down
2 changes: 1 addition & 1 deletion test_package/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ project(
default_options: ['cpp_std=c++2a', 'buildtype=debug']
)

opzioni_dep = dependency('opzioni', version: '0.45.1')
opzioni_dep = dependency('opzioni', version: '0.45.2')

main = executable(
'main', 'main.cpp',
Expand Down
6 changes: 6 additions & 0 deletions tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ program_test = executable(
['catch2_main.cpp', 'opzioni.program.cpp'],
dependencies: test_deps
)
string_test = executable(
'string',
['catch2_main.cpp', 'string.cpp'],
dependencies: test_deps
)

test('arg', arg_test)
test('converters', converters_test)
test('program', program_test)
test('string', string_test)