SeqAn3 3.4.0-rc.3
The Modern C++ library for sequence analysis.
Loading...
Searching...
No Matches
validators.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2006-2024 Knut Reinert & Freie Universität Berlin
2// SPDX-FileCopyrightText: 2016-2024 Knut Reinert & MPI für molekulare Genetik
3// SPDX-License-Identifier: BSD-3-Clause
4
10#pragma once
11
12#include <algorithm>
13#include <concepts>
14#include <exception>
15#include <filesystem>
16#include <fstream>
17#include <ranges>
18#include <regex>
19#include <sstream>
20
31
32namespace seqan3
33{
34
94template <typename validator_type>
95concept validator =
96 std::copyable<std::remove_cvref_t<validator_type>>
97 && requires (validator_type validator, typename std::remove_reference_t<validator_type>::option_value_type value) {
99
100 { validator(value) } -> std::same_as<void>;
101 { validator.get_help_page_message() } -> std::same_as<std::string>;
102 };
104
120template <arithmetic option_value_t>
122{
123public:
125 using option_value_type = option_value_t;
126
131 arithmetic_range_validator(option_value_type const min_, option_value_type const max_) : min{min_}, max{max_}
132 {}
133
138 void operator()(option_value_type const & cmp) const
139 {
140 if (!((cmp <= max) && (cmp >= min)))
141 throw validation_error{detail::to_string("Value ", cmp, " is not in range [", min, ",", max, "].")};
142 }
143
150 template <std::ranges::forward_range range_type>
152 void operator()(range_type const & range) const
153 {
154 std::for_each(range.begin(),
155 range.end(),
156 [&](auto cmp)
157 {
158 (*this)(cmp);
159 });
160 }
161
164 {
165 return detail::to_string("Value must be in range [", min, ",", max, "].");
166 }
167
168private:
170 option_value_type min{};
171
173 option_value_type max{};
174};
175
195template <typename option_value_t>
197{
198public:
200 using option_value_type = option_value_t;
201
211
217 template <std::ranges::forward_range range_type>
218 requires std::constructible_from<option_value_type, std::ranges::range_rvalue_reference_t<range_type>>
219 value_list_validator(range_type rng)
220 {
221 values.clear();
222 std::ranges::move(std::move(rng), std::back_inserter(values));
223 }
224
230 template <typename... option_types>
231 requires ((std::constructible_from<option_value_type, option_types> && ...))
232 value_list_validator(option_types &&... opts)
233 {
234 (values.emplace_back(std::forward<option_types>(opts)), ...);
235 }
237
242 void operator()(option_value_type const & cmp) const
243 {
244 if (!(std::find(values.begin(), values.end(), cmp) != values.end()))
245 throw validation_error{detail::to_string("Value ", cmp, " is not one of ", std::views::all(values), ".")};
246 }
247
253 template <std::ranges::forward_range range_type>
254 requires std::convertible_to<std::ranges::range_value_t<range_type>, option_value_type>
255 void operator()(range_type const & range) const
256 {
258 std::ranges::end(range),
259 [&](auto cmp)
260 {
261 (*this)(cmp);
262 });
263 }
264
267 {
268 return detail::to_string("Value must be one of ", std::views::all(values), ".");
269 }
270
271private:
274};
275
281template <typename option_type, typename... option_types>
282 requires (std::constructible_from<std::string, std::decay_t<option_types>> && ...
283 && std::constructible_from<std::string, std::decay_t<option_type>>)
285
287template <typename range_type>
288 requires (std::ranges::forward_range<std::decay_t<range_type>>
289 && std::constructible_from<std::string, std::ranges::range_value_t<range_type>>)
291
293template <typename option_type, typename... option_types>
295
297template <typename range_type>
298 requires (std::ranges::forward_range<std::decay_t<range_type>>)
301
317{
318public:
321
330 virtual ~file_validator_base() = default;
332
340 virtual void operator()(std::filesystem::path const & path) const = 0;
341
349 template <std::ranges::forward_range range_type>
350 requires (std::convertible_to<std::ranges::range_value_t<range_type>, std::filesystem::path const &>
351 && !std::convertible_to<range_type, std::filesystem::path const &>)
352 void operator()(range_type const & v) const
353 {
354 std::for_each(v.begin(),
355 v.end(),
356 [&](auto cmp)
357 {
358 this->operator()(cmp);
359 });
360 }
361
362protected:
369 {
370 // If no valid extensions are given we can safely return here.
371 if (extensions.empty())
372 return;
373
374 // Check if extension is available.
375 if (!path.has_extension())
376 throw validation_error{detail::to_string("The given filename ",
377 path.string(),
378 " has no extension. Expected"
379 " one of the following valid extensions:",
381 "!")};
382
383 std::string file_path{path.filename().string()};
384
385 // Leading dot indicates a hidden file is not part of the extension.
386 if (file_path.front() == '.')
387 file_path.erase(0, 1);
388
389 // Store a string_view containing all extensions for a better error message.
390 std::string const all_extensions{file_path.substr(file_path.find(".") + 1)};
391
392 // Compares the extensions in lower case.
393 auto case_insensitive_ends_with = [&](std::string const & ext)
394 {
395 return case_insensitive_string_ends_with(file_path, ext);
396 };
397
398 // Check if requested extension is present.
399 if (std::ranges::find_if(extensions, case_insensitive_ends_with) == extensions.end())
400 {
401 throw validation_error{detail::to_string("Expected one of the following valid extensions: ",
403 "! Got ",
404 all_extensions,
405 " instead!")};
406 }
407 }
408
415 {
416 // Check if input directory is readable.
418 {
419 std::error_code ec{};
420 std::filesystem::directory_iterator{path, ec}; // if directory iterator cannot be created, ec will be set.
421 if (static_cast<bool>(ec))
422 throw validation_error{detail::to_string("Cannot read the directory ", path, "!")};
423 }
424 else
425 {
426 // Must be a regular file.
428 throw validation_error{detail::to_string("Expected a regular file ", path, "!")};
429
430 std::ifstream file{path};
431 if (!file.is_open() || !file.good())
432 throw validation_error{detail::to_string("Cannot read the file ", path, "!")};
433 }
434 }
435
442 {
443 std::ofstream file{path};
444 detail::safe_filesystem_entry file_guard{path};
445
446 bool is_open = file.is_open();
447 bool is_good = file.good();
448 file.close();
449
450 if (!is_good || !is_open)
451 throw validation_error{detail::to_string("Cannot write ", path, "!")};
452
453 file_guard.remove();
454 }
455
458 {
459 if (extensions.empty())
460 return "";
461 else
462 return detail::to_string(" Valid file extensions are: [",
464 "].");
465 }
466
473 {
474 size_t const suffix_length{suffix.size()};
475 size_t const str_length{str.size()};
476 return suffix_length > str_length ? false
477 : std::ranges::equal(str.substr(str_length - suffix_length),
478 suffix,
479 [](char const chr1, char const chr2)
480 {
481 return std::tolower(chr1) == std::tolower(chr2);
482 });
483 }
484
487};
488
513template <typename file_t = void>
515{
516public:
517 static_assert(std::same_as<file_t, void> || detail::has_type_valid_formats<file_t>,
518 "Expected either a template type with a static member called valid_formats (a file type) or void.");
519
520 // Import from base class.
522
536 {
537 if constexpr (!std::same_as<file_t, void>)
538 file_validator_base::extensions = detail::valid_file_extensions<typename file_t::valid_formats>();
539 }
540
545 virtual ~input_file_validator() = default;
546
556 requires std::same_as<file_t, void>
558 {
560 }
561
562 // Import base class constructor.
565
566 // Import the base::operator()
567 using file_validator_base::operator();
568
574 virtual void operator()(std::filesystem::path const & file) const override
575 {
576 try
577 {
578 if (!std::filesystem::exists(file))
579 throw validation_error{detail::to_string("The file ", file, " does not exist!")};
580
581 // Check if file is regular and can be opened for reading.
583
584 // Check extension.
585 validate_filename(file);
586 }
587 // LCOV_EXCL_START
589 {
590 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
591 }
592 // LCOV_EXCL_STOP
593 catch (...)
594 {
596 }
597 }
598
601 {
602 return "The input file must exist and read permissions must be granted." + valid_extensions_help_page_message();
603 }
604};
605
614
643template <typename file_t = void>
645{
646public:
647 static_assert(std::same_as<file_t, void> || detail::has_type_valid_formats<file_t>,
648 "Expected either a template type with a static member called valid_formats (a file type) or void.");
649
650 // Import from base class.
652
660
665 virtual ~output_file_validator() = default;
666
680
681 // Import base constructor.
684
694 {
695 if constexpr (!std::same_as<file_t, void>)
696 return detail::valid_file_extensions<typename file_t::valid_formats>();
697 return {};
698 }
699
700 // Import the base::operator()
701 using file_validator_base::operator();
702
708 virtual void operator()(std::filesystem::path const & file) const override
709 {
710 try
711 {
713 throw validation_error{detail::to_string("The file ", file, " already exists!")};
714
715 // Check if file has any write permissions.
717
718 validate_filename(file);
719 }
720 // LCOV_EXCL_START
722 {
723 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
724 }
725 // LCOV_EXCL_STOP
726 catch (...)
727 {
729 }
730 }
731
734 {
736 return "Write permissions must be granted." + valid_extensions_help_page_message();
737 else // mode == create_new
738 return "The output file must not exist already and write permissions must be granted."
740 }
741
742private:
745};
746
764{
765public:
766 // Import from base class.
768
777 virtual ~input_directory_validator() = default;
778
779 // Import base constructor.
782
783 // Import the base::operator()
784 using file_validator_base::operator();
785
791 virtual void operator()(std::filesystem::path const & dir) const override
792 {
793 try
794 {
795 if (!std::filesystem::exists(dir))
796 throw validation_error{detail::to_string("The directory ", dir, " does not exists!")};
797
799 throw validation_error{detail::to_string("The path ", dir, " is not a directory!")};
800
801 // Check if directory has any read permissions.
803 }
804 // LCOV_EXCL_START
806 {
807 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
808 }
809 // LCOV_EXCL_STOP
810 catch (...)
811 {
813 }
814 }
815
818 {
819 return detail::to_string("An existing, readable path for the input directory.");
820 }
821};
822
840{
841public:
842 // Imported from base class.
844
853 virtual ~output_directory_validator() = default;
854
855 // Import base constructor.
858
859 // Import the base::operator().
860 using file_validator_base::operator();
861
867 virtual void operator()(std::filesystem::path const & dir) const override
868 {
869 bool dir_exists = std::filesystem::exists(dir);
870 // Make sure the created dir is deleted after we are done.
872 std::filesystem::create_directory(dir, ec); // does nothing and is not treated as error if path already exists.
873 // if error code was set or if dummy.txt could not be created within the output dir, throw an error.
874 if (static_cast<bool>(ec))
875 throw validation_error{detail::to_string("Cannot create directory: ", dir, "!")};
876
877 try
878 {
879 if (!dir_exists)
880 {
881 detail::safe_filesystem_entry dir_guard{dir};
882 validate_writeability(dir / "dummy.txt");
883 dir_guard.remove_all();
884 }
885 else
886 {
887 validate_writeability(dir / "dummy.txt");
888 }
889 }
890 // LCOV_EXCL_START
892 {
893 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
894 }
895 // LCOV_EXCL_STOP
896 catch (...)
897 {
899 }
900 }
901
904 {
905 return detail::to_string("A valid path for the output directory.");
906 }
907};
908
929{
930public:
933
937 regex_validator(std::string const & pattern_) : pattern{pattern_}
938 {}
939
944 void operator()(option_value_type const & cmp) const
945 {
946 std::regex rgx(pattern);
947 if (!std::regex_match(cmp, rgx))
948 throw validation_error{detail::to_string("Value ", cmp, " did not match the pattern ", pattern, ".")};
949 }
950
957 template <std::ranges::forward_range range_type>
958 requires std::convertible_to<std::ranges::range_reference_t<range_type>, option_value_type const &>
959 void operator()(range_type const & v) const
960 {
961 for (auto && file_name : v)
962 {
963 // note: we explicitly copy/construct any reference type other than `std::string &`
964 (*this)(static_cast<option_value_type const &>(file_name));
965 }
966 }
967
970 {
971 return detail::to_string("Value must match the pattern '", pattern, "'.");
972 }
973
974private:
976 std::string pattern;
977};
978
979namespace detail
980{
981
994template <typename option_value_t>
995struct default_validator
996{
998 using option_value_type = option_value_t;
999
1001 void operator()(option_value_t const & /*cmp*/) const noexcept
1002 {}
1003
1006 {
1007 return "";
1008 }
1009};
1010
1024template <validator validator1_type, validator validator2_type>
1025 requires std::common_with<typename validator1_type::option_value_type, typename validator2_type::option_value_type>
1026class validator_chain_adaptor
1027{
1028public:
1030 using option_value_type =
1032
1036 validator_chain_adaptor() = delete;
1037 validator_chain_adaptor(validator_chain_adaptor const & pf) = default;
1038 validator_chain_adaptor & operator=(validator_chain_adaptor const & pf) = default;
1039 validator_chain_adaptor(validator_chain_adaptor &&) = default;
1040 validator_chain_adaptor & operator=(validator_chain_adaptor &&) = default;
1041
1046 validator_chain_adaptor(validator1_type vali1_, validator2_type vali2_) :
1047 vali1{std::move(vali1_)},
1048 vali2{std::move(vali2_)}
1049 {}
1050
1052 ~validator_chain_adaptor() = default;
1054
1063 template <typename cmp_type>
1064 requires std::invocable<validator1_type, cmp_type const> && std::invocable<validator2_type, cmp_type const>
1065 void operator()(cmp_type const & cmp) const
1066 {
1067 vali1(cmp);
1068 vali2(cmp);
1069 }
1070
1073 {
1074 return detail::to_string(vali1.get_help_page_message(), " ", vali2.get_help_page_message());
1075 }
1076
1077private:
1079 validator1_type vali1;
1081 validator2_type vali2;
1082};
1083
1084} // namespace detail
1085
1115template <validator validator1_type, validator validator2_type>
1116 requires std::common_with<typename std::remove_reference_t<validator1_type>::option_value_type,
1118auto operator|(validator1_type && vali1, validator2_type && vali2)
1119{
1120 return detail::validator_chain_adaptor{std::forward<validator1_type>(vali1), std::forward<validator2_type>(vali2)};
1121}
1122
1123} // namespace seqan3
T back_inserter(T... args)
Provides various type traits on generic types.
T begin(T... args)
A validator that checks whether a number is inside a given range.
Definition validators.hpp:122
void operator()(option_value_type const &cmp) const
Tests whether cmp lies inside [min, max].
Definition validators.hpp:138
option_value_t option_value_type
The type of value that this validator invoked upon.
Definition validators.hpp:125
void operator()(range_type const &range) const
Tests whether every element in range lies inside [min, max].
Definition validators.hpp:152
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:163
arithmetic_range_validator(option_value_type const min_, option_value_type const max_)
The constructor.
Definition validators.hpp:131
An abstract base class for the file and directory validators.
Definition validators.hpp:317
bool case_insensitive_string_ends_with(std::string_view str, std::string_view suffix) const
Helper function that checks if a string is a suffix of another string. Case insensitive.
Definition validators.hpp:472
void validate_filename(std::filesystem::path const &path) const
Validates the given filename path based on the specified extensions.
Definition validators.hpp:368
std::string valid_extensions_help_page_message() const
Returns the information of valid file extensions.
Definition validators.hpp:457
virtual void operator()(std::filesystem::path const &path) const =0
Tests if the given path is a valid input, respectively output, file or directory.
std::string option_value_type
Type of values that are tested by validator.
Definition validators.hpp:320
file_validator_base(file_validator_base &&)=default
Defaulted.
file_validator_base & operator=(file_validator_base &&)=default
Defaulted.
void validate_readability(std::filesystem::path const &path) const
Checks if the given path is readable.
Definition validators.hpp:414
file_validator_base()=default
Defaulted.
file_validator_base(file_validator_base const &)=default
Defaulted.
std::vector< std::string > extensions
Stores the extensions.
Definition validators.hpp:486
virtual ~file_validator_base()=default
file_validator_base & operator=(file_validator_base const &)=default
Defaulted.
void validate_writeability(std::filesystem::path const &path) const
Checks if the given path is writable.
Definition validators.hpp:441
A validator that checks if a given path is a valid input directory.
Definition validators.hpp:764
input_directory_validator(input_directory_validator &&)=default
Defaulted.
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:817
input_directory_validator()=default
Defaulted.
input_directory_validator(input_directory_validator const &)=default
Defaulted.
input_directory_validator & operator=(input_directory_validator &&)=default
Defaulted.
virtual void operator()(std::filesystem::path const &dir) const override
Tests whether path is an existing directory and is readable.
Definition validators.hpp:791
input_directory_validator & operator=(input_directory_validator const &)=default
Defaulted.
virtual ~input_directory_validator()=default
Virtual Destructor.
A validator that checks if a given path is a valid input file.
Definition validators.hpp:515
input_file_validator(input_file_validator const &)=default
Defaulted.
virtual ~input_file_validator()=default
Virtual destructor.
input_file_validator & operator=(input_file_validator &&)=default
Defaulted.
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:600
input_file_validator(input_file_validator &&)=default
Defaulted.
input_file_validator(std::vector< std::string > extensions)
Constructs from a given collection of valid extensions.
Definition validators.hpp:555
virtual void operator()(std::filesystem::path const &file) const override
Tests whether path is an existing regular file and is readable.
Definition validators.hpp:574
input_file_validator()
Default constructor.
Definition validators.hpp:535
input_file_validator & operator=(input_file_validator const &)=default
Defaulted.
A validator that checks if a given path is a valid output directory.
Definition validators.hpp:840
output_directory_validator()=default
Defaulted.
output_directory_validator & operator=(output_directory_validator const &)=default
Defaulted.
virtual ~output_directory_validator()=default
Virtual Destructor.
output_directory_validator(output_directory_validator &&)=default
Defaulted.
output_directory_validator(output_directory_validator const &)=default
Defaulted.
virtual void operator()(std::filesystem::path const &dir) const override
Tests whether path is writable.
Definition validators.hpp:867
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:903
output_directory_validator & operator=(output_directory_validator &&)=default
Defaulted.
A validator that checks if a given path is a valid output file.
Definition validators.hpp:645
static std::vector< std::string > default_extensions()
The default extensions of file_t.
Definition validators.hpp:693
output_file_validator(output_file_validator &&)=default
Defaulted.
output_file_validator(output_file_validator const &)=default
Defaulted.
virtual void operator()(std::filesystem::path const &file) const override
Tests whether path is does not already exists and is writable.
Definition validators.hpp:708
output_file_validator()
Default constructor.
Definition validators.hpp:658
output_file_validator & operator=(output_file_validator const &)=default
Defaulted.
output_file_validator & operator=(output_file_validator &&)=default
Defaulted.
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:733
virtual ~output_file_validator()=default
Virtual Destructor.
output_file_validator(output_file_open_options const mode, std::vector< std::string > extensions=default_extensions())
Constructs from a given overwrite mode and a list of valid extensions.
Definition validators.hpp:673
A validator that checks if a matches a regular expression pattern.
Definition validators.hpp:929
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:969
void operator()(option_value_type const &cmp) const
Tests whether cmp lies inside values.
Definition validators.hpp:944
std::string option_value_type
Type of values that are tested by validator.
Definition validators.hpp:932
void operator()(range_type const &v) const
Tests whether every filename in list v matches the pattern.
Definition validators.hpp:959
regex_validator(std::string const &pattern_)
Constructing from a vector.
Definition validators.hpp:937
Argument parser exception thrown when an argument could not be casted to the according type.
Definition exceptions.hpp:128
A validator that checks whether a value is inside a list of valid values.
Definition validators.hpp:197
value_list_validator(option_type, option_types...) -> value_list_validator< option_type >
Deduction guide for a parameter pack.
value_list_validator()=default
Defaulted.
void operator()(option_value_type const &cmp) const
Tests whether cmp lies inside values.
Definition validators.hpp:242
value_list_validator(value_list_validator const &)=default
Defaulted.
value_list_validator & operator=(value_list_validator const &)=default
Defaulted.
option_value_t option_value_type
Type of values that are tested by validator.
Definition validators.hpp:200
void operator()(range_type const &range) const
Tests whether every element in range lies inside values.
Definition validators.hpp:255
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:266
value_list_validator(value_list_validator &&)=default
Defaulted.
~value_list_validator()=default
Defaulted.
value_list_validator(range_type &&rng) -> value_list_validator< std::string >
Deduction guide for ranges over a value type convertible to std::string.
value_list_validator(option_type, option_types...) -> value_list_validator< std::string >
Type deduction guides.
value_list_validator & operator=(value_list_validator &&)=default
Defaulted.
value_list_validator(range_type rng)
Constructing from a range.
Definition validators.hpp:219
value_list_validator(range_type &&rng) -> value_list_validator< std::ranges::range_value_t< range_type > >
Deduction guide for ranges.
value_list_validator(option_types &&... opts)
Constructing from a parameter pack.
Definition validators.hpp:232
T clear(T... args)
T create_directory(T... args)
T current_exception(T... args)
T emplace_back(T... args)
T empty(T... args)
T end(T... args)
T equal(T... args)
Provides parser related exceptions.
T exists(T... args)
T filename(T... args)
T find(T... args)
T for_each(T... args)
auto operator|(validator1_type &&vali1, validator2_type &&vali2)
Enables the chaining of validators.
Definition validators.hpp:1118
seqan::stl::views::join_with join_with
A view adaptor that represents view consisting of the sequence obtained from flattening a view of ran...
Definition join_with.hpp:25
T has_extension(T... args)
A type that satisfies std::is_arithmetic_v<t>.
The concept for option validators passed to add_option/positional_option.
void operator()(option_value_type const &cmp) const
Validates the value 'cmp' and throws a seqan3::validation_error on failure.
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
using option_value_type
The type of value on which the validator is called on.
Provides various utility functions.
T is_directory(T... args)
T is_regular_file(T... args)
Provides seqan3::views::join_with.
T move(T... args)
The main SeqAn3 namespace.
Definition aligned_sequence_concept.hpp:26
output_file_open_options
Mode of an output file: Determines whether an existing file can be (silently) overwritten.
Definition validators.hpp:608
@ create_new
Forbid overwriting the output file.
@ open_or_create
Allow to overwrite the output file.
SeqAn specific customisations in the standard namespace.
Provides seqan3::debug_stream and related types.
T regex_match(T... args)
T rethrow_exception(T... args)
Provides seqan3::detail::safe_filesystem_entry.
T size(T... args)
T substr(T... args)
T throw_with_nested(T... args)
Auxiliary for pretty printing of exception messages.
Provides traits for seqan3::type_list.
Provides various traits for template packs.
Provides concepts that do not have equivalents in C++20.
Hide me