SeqAn3 3.4.0-rc.1
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 <filesystem>
15#include <fstream>
16#include <ranges>
17#include <regex>
18#include <sstream>
19
30
31namespace seqan3
32{
33
93template <typename validator_type>
94concept validator =
95 std::copyable<std::remove_cvref_t<validator_type>>
96 && requires (validator_type validator, typename std::remove_reference_t<validator_type>::option_value_type value) {
98
99 { validator(value) } -> std::same_as<void>;
100 { validator.get_help_page_message() } -> std::same_as<std::string>;
101 };
103
119template <arithmetic option_value_t>
121{
122public:
124 using option_value_type = option_value_t;
125
132
137 void operator()(option_value_type const & cmp) const
138 {
139 if (!((cmp <= max) && (cmp >= min)))
140 throw validation_error{detail::to_string("Value ", cmp, " is not in range [", min, ",", max, "].")};
141 }
142
149 template <std::ranges::forward_range range_type>
151 void operator()(range_type const & range) const
152 {
153 std::for_each(range.begin(),
154 range.end(),
155 [&](auto cmp)
156 {
157 (*this)(cmp);
158 });
159 }
160
163 {
164 return detail::to_string("Value must be in range [", min, ",", max, "].");
165 }
166
167private:
169 option_value_type min{};
170
172 option_value_type max{};
173};
174
194template <typename option_value_t>
196{
197public:
199 using option_value_type = option_value_t;
200
210
216 template <std::ranges::forward_range range_type>
217 requires std::constructible_from<option_value_type, std::ranges::range_rvalue_reference_t<range_type>>
219 {
220 values.clear();
221 std::ranges::move(std::move(rng), std::back_inserter(values));
222 }
223
229 template <typename... option_types>
230 requires ((std::constructible_from<option_value_type, option_types> && ...))
232 {
233 (values.emplace_back(std::forward<option_types>(opts)), ...);
234 }
236
241 void operator()(option_value_type const & cmp) const
242 {
243 if (!(std::find(values.begin(), values.end(), cmp) != values.end()))
244 throw validation_error{detail::to_string("Value ", cmp, " is not one of ", std::views::all(values), ".")};
245 }
246
252 template <std::ranges::forward_range range_type>
253 requires std::convertible_to<std::ranges::range_value_t<range_type>, option_value_type>
254 void operator()(range_type const & range) const
255 {
257 std::ranges::end(range),
258 [&](auto cmp)
259 {
260 (*this)(cmp);
261 });
262 }
263
266 {
267 return detail::to_string("Value must be one of ", std::views::all(values), ".");
268 }
269
270private:
273};
274
280template <typename option_type, typename... option_types>
281 requires (std::constructible_from<std::string, std::decay_t<option_types>> && ...
282 && std::constructible_from<std::string, std::decay_t<option_type>>)
284
286template <typename range_type>
287 requires (std::ranges::forward_range<std::decay_t<range_type>>
288 && std::constructible_from<std::string, std::ranges::range_value_t<range_type>>)
290
292template <typename option_type, typename... option_types>
294
296template <typename range_type>
297 requires (std::ranges::forward_range<std::decay_t<range_type>>)
300
316{
317public:
320
329 virtual ~file_validator_base() = default;
331
339 virtual void operator()(std::filesystem::path const & path) const = 0;
340
348 template <std::ranges::forward_range range_type>
349 requires (std::convertible_to<std::ranges::range_value_t<range_type>, std::filesystem::path const &>
350 && !std::convertible_to<range_type, std::filesystem::path const &>)
351 void operator()(range_type const & v) const
352 {
353 std::for_each(v.begin(),
354 v.end(),
355 [&](auto cmp)
356 {
357 this->operator()(cmp);
358 });
359 }
360
361protected:
368 {
369 // If no valid extensions are given we can safely return here.
370 if (extensions.empty())
371 return;
372
373 // Check if extension is available.
374 if (!path.has_extension())
375 throw validation_error{detail::to_string("The given filename ",
376 path.string(),
377 " has no extension. Expected"
378 " one of the following valid extensions:",
380 "!")};
381
382 std::string file_path{path.filename().string()};
383
384 // Leading dot indicates a hidden file is not part of the extension.
385 if (file_path.front() == '.')
386 file_path.erase(0, 1);
387
388 // Store a string_view containing all extensions for a better error message.
389 std::string const all_extensions{file_path.substr(file_path.find(".") + 1)};
390
391 // Compares the extensions in lower case.
392 auto case_insensitive_ends_with = [&](std::string const & ext)
393 {
395 };
396
397 // Check if requested extension is present.
399 {
400 throw validation_error{detail::to_string("Expected one of the following valid extensions: ",
402 "! Got ",
404 " instead!")};
405 }
406 }
407
414 {
415 // Check if input directory is readable.
417 {
419 std::filesystem::directory_iterator{path, ec}; // if directory iterator cannot be created, ec will be set.
420 if (static_cast<bool>(ec))
421 throw validation_error{detail::to_string("Cannot read the directory ", path, "!")};
422 }
423 else
424 {
425 // Must be a regular file.
427 throw validation_error{detail::to_string("Expected a regular file ", path, "!")};
428
429 std::ifstream file{path};
430 if (!file.is_open() || !file.good())
431 throw validation_error{detail::to_string("Cannot read the file ", path, "!")};
432 }
433 }
434
441 {
442 std::ofstream file{path};
443 detail::safe_filesystem_entry file_guard{path};
444
445 bool is_open = file.is_open();
446 bool is_good = file.good();
447 file.close();
448
449 if (!is_good || !is_open)
450 throw validation_error{detail::to_string("Cannot write ", path, "!")};
451
452 file_guard.remove();
453 }
454
457 {
458 if (extensions.empty())
459 return "";
460 else
461 return detail::to_string(" Valid file extensions are: [",
463 "].");
464 }
465
472 {
473 size_t const suffix_length{suffix.size()};
474 size_t const str_length{str.size()};
477 suffix,
478 [](char const chr1, char const chr2)
479 {
480 return std::tolower(chr1) == std::tolower(chr2);
481 });
482 }
483
486};
487
512template <typename file_t = void>
514{
515public:
516 static_assert(std::same_as<file_t, void> || detail::has_type_valid_formats<file_t>,
517 "Expected either a template type with a static member called valid_formats (a file type) or void.");
518
519 // Import from base class.
521
535 {
536 if constexpr (!std::same_as<file_t, void>)
537 file_validator_base::extensions = detail::valid_file_extensions<typename file_t::valid_formats>();
538 }
539
544 virtual ~input_file_validator() = default;
545
555 requires std::same_as<file_t, void>
557 {
559 }
560
561 // Import base class constructor.
564
565 // Import the base::operator()
566 using file_validator_base::operator();
567
573 virtual void operator()(std::filesystem::path const & file) const override
574 {
575 try
576 {
578 throw validation_error{detail::to_string("The file ", file, " does not exist!")};
579
580 // Check if file is regular and can be opened for reading.
582
583 // Check extension.
585 }
586 // LCOV_EXCL_START
588 {
589 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
590 }
591 // LCOV_EXCL_STOP
592 catch (...)
593 {
595 }
596 }
597
600 {
601 return "The input file must exist and read permissions must be granted." + valid_extensions_help_page_message();
602 }
603};
604
613
642template <typename file_t = void>
644{
645public:
646 static_assert(std::same_as<file_t, void> || detail::has_type_valid_formats<file_t>,
647 "Expected either a template type with a static member called valid_formats (a file type) or void.");
648
649 // Import from base class.
651
659
664 virtual ~output_file_validator() = default;
665
679
680 // Import base constructor.
683
693 {
694 if constexpr (!std::same_as<file_t, void>)
695 return detail::valid_file_extensions<typename file_t::valid_formats>();
696 return {};
697 }
698
699 // Import the base::operator()
700 using file_validator_base::operator();
701
707 virtual void operator()(std::filesystem::path const & file) const override
708 {
709 try
710 {
712 throw validation_error{detail::to_string("The file ", file, " already exists!")};
713
714 // Check if file has any write permissions.
716
718 }
719 // LCOV_EXCL_START
721 {
722 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
723 }
724 // LCOV_EXCL_STOP
725 catch (...)
726 {
728 }
729 }
730
733 {
735 return "Write permissions must be granted." + valid_extensions_help_page_message();
736 else // mode == create_new
737 return "The output file must not exist already and write permissions must be granted."
739 }
740
741private:
744};
745
763{
764public:
765 // Import from base class.
767
776 virtual ~input_directory_validator() = default;
777
778 // Import base constructor.
781
782 // Import the base::operator()
783 using file_validator_base::operator();
784
790 virtual void operator()(std::filesystem::path const & dir) const override
791 {
792 try
793 {
795 throw validation_error{detail::to_string("The directory ", dir, " does not exists!")};
796
798 throw validation_error{detail::to_string("The path ", dir, " is not a directory!")};
799
800 // Check if directory has any read permissions.
802 }
803 // LCOV_EXCL_START
805 {
806 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
807 }
808 // LCOV_EXCL_STOP
809 catch (...)
810 {
812 }
813 }
814
817 {
818 return detail::to_string("An existing, readable path for the input directory.");
819 }
820};
821
839{
840public:
841 // Imported from base class.
843
852 virtual ~output_directory_validator() = default;
853
854 // Import base constructor.
857
858 // Import the base::operator().
859 using file_validator_base::operator();
860
866 virtual void operator()(std::filesystem::path const & dir) const override
867 {
869 // Make sure the created dir is deleted after we are done.
871 std::filesystem::create_directory(dir, ec); // does nothing and is not treated as error if path already exists.
872 // if error code was set or if dummy.txt could not be created within the output dir, throw an error.
873 if (static_cast<bool>(ec))
874 throw validation_error{detail::to_string("Cannot create directory: ", dir, "!")};
875
876 try
877 {
878 if (!dir_exists)
879 {
880 detail::safe_filesystem_entry dir_guard{dir};
881 validate_writeability(dir / "dummy.txt");
882 dir_guard.remove_all();
883 }
884 else
885 {
886 validate_writeability(dir / "dummy.txt");
887 }
888 }
889 // LCOV_EXCL_START
891 {
892 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
893 }
894 // LCOV_EXCL_STOP
895 catch (...)
896 {
898 }
899 }
900
903 {
904 return detail::to_string("A valid path for the output directory.");
905 }
906};
907
928{
929public:
932
937 {}
938
943 void operator()(option_value_type const & cmp) const
944 {
945 std::regex rgx(pattern);
946 if (!std::regex_match(cmp, rgx))
947 throw validation_error{detail::to_string("Value ", cmp, " did not match the pattern ", pattern, ".")};
948 }
949
956 template <std::ranges::forward_range range_type>
957 requires std::convertible_to<std::ranges::range_reference_t<range_type>, option_value_type const &>
958 void operator()(range_type const & v) const
959 {
960 for (auto && file_name : v)
961 {
962 // note: we explicitly copy/construct any reference type other than `std::string &`
963 (*this)(static_cast<option_value_type const &>(file_name));
964 }
965 }
966
969 {
970 return detail::to_string("Value must match the pattern '", pattern, "'.");
971 }
972
973private:
975 std::string pattern;
976};
977
978namespace detail
979{
980
993template <typename option_value_t>
994struct default_validator
995{
997 using option_value_type = option_value_t;
998
1000 void operator()(option_value_t const & /*cmp*/) const noexcept
1001 {}
1002
1005 {
1006 return "";
1007 }
1008};
1009
1023template <validator validator1_type, validator validator2_type>
1024 requires std::common_with<typename validator1_type::option_value_type, typename validator2_type::option_value_type>
1025class validator_chain_adaptor
1026{
1027public:
1029 using option_value_type =
1031
1035 validator_chain_adaptor() = delete;
1036 validator_chain_adaptor(validator_chain_adaptor const & pf) = default;
1037 validator_chain_adaptor & operator=(validator_chain_adaptor const & pf) = default;
1038 validator_chain_adaptor(validator_chain_adaptor &&) = default;
1039 validator_chain_adaptor & operator=(validator_chain_adaptor &&) = default;
1040
1045 validator_chain_adaptor(validator1_type vali1_, validator2_type vali2_) :
1046 vali1{std::move(vali1_)},
1047 vali2{std::move(vali2_)}
1048 {}
1049
1051 ~validator_chain_adaptor() = default;
1053
1062 template <typename cmp_type>
1063 requires std::invocable<validator1_type, cmp_type const> && std::invocable<validator2_type, cmp_type const>
1064 void operator()(cmp_type const & cmp) const
1065 {
1066 vali1(cmp);
1067 vali2(cmp);
1068 }
1069
1072 {
1073 return detail::to_string(vali1.get_help_page_message(), " ", vali2.get_help_page_message());
1074 }
1075
1076private:
1078 validator1_type vali1;
1080 validator2_type vali2;
1081};
1082
1083} // namespace detail
1084
1114template <validator validator1_type, validator validator2_type>
1115 requires std::common_with<typename std::remove_reference_t<validator1_type>::option_value_type,
1118{
1119 return detail::validator_chain_adaptor{std::forward<validator1_type>(vali1), std::forward<validator2_type>(vali2)};
1120}
1121
1122} // 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:121
void operator()(option_value_type const &cmp) const
Tests whether cmp lies inside [min, max].
Definition validators.hpp:137
option_value_t option_value_type
The type of value that this validator invoked upon.
Definition validators.hpp:124
void operator()(range_type const &range) const
Tests whether every element in range lies inside [min, max].
Definition validators.hpp:151
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:162
arithmetic_range_validator(option_value_type const min_, option_value_type const max_)
The constructor.
Definition validators.hpp:130
A "pretty printer" for most SeqAn data structures and related types.
Definition debug_stream_type.hpp:79
An abstract base class for the file and directory validators.
Definition validators.hpp:316
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:471
void validate_filename(std::filesystem::path const &path) const
Validates the given filename path based on the specified extensions.
Definition validators.hpp:367
std::string valid_extensions_help_page_message() const
Returns the information of valid file extensions.
Definition validators.hpp:456
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:319
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:413
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:485
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:440
A validator that checks if a given path is a valid input directory.
Definition validators.hpp:763
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:816
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:790
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:514
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:599
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:554
virtual void operator()(std::filesystem::path const &file) const override
Tests whether path is an existing regular file and is readable.
Definition validators.hpp:573
input_file_validator()
Default constructor.
Definition validators.hpp:534
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:839
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:866
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:902
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:644
static std::vector< std::string > default_extensions()
The default extensions of file_t.
Definition validators.hpp:692
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:707
output_file_validator()
Default constructor.
Definition validators.hpp:657
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:732
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:672
A validator that checks if a matches a regular expression pattern.
Definition validators.hpp:928
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:968
void operator()(option_value_type const &cmp) const
Tests whether cmp lies inside values.
Definition validators.hpp:943
std::string option_value_type
Type of values that are tested by validator.
Definition validators.hpp:931
void operator()(range_type const &v) const
Tests whether every filename in list v matches the pattern.
Definition validators.hpp:958
regex_validator(std::string const &pattern_)
Constructing from a vector.
Definition validators.hpp:936
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:196
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:241
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:199
void operator()(range_type const &range) const
Tests whether every element in range lies inside values.
Definition validators.hpp:254
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:265
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:218
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:231
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:1117
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)
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:607
@ 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