SeqAn3 3.4.0-rc.4
The Modern C++ library for sequence analysis.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
validators.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2006-2025 Knut Reinert & Freie Universität Berlin
2// SPDX-FileCopyrightText: 2016-2025 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
32SEQAN3_DEPRECATED_HEADER("This header and its functionality is deprecated and will be removed in a future version of SeqAn. Please use the sharg-parser (url: https://github.com/seqan/sharg-parser) instead.");
33
34namespace seqan3
35{
36
96template <typename validator_type>
97concept validator =
98 std::copyable<std::remove_cvref_t<validator_type>>
99 && requires (validator_type validator, typename std::remove_reference_t<validator_type>::option_value_type value) {
101
102 { validator(value) } -> std::same_as<void>;
103 { validator.get_help_page_message() } -> std::same_as<std::string>;
104 };
106
122template <arithmetic option_value_t>
124{
125public:
127 using option_value_type = option_value_t;
128
135
140 void operator()(option_value_type const & cmp) const
141 {
142 if (!((cmp <= max) && (cmp >= min)))
143 throw validation_error{detail::to_string("Value ", cmp, " is not in range [", min, ",", max, "].")};
144 }
145
152 template <std::ranges::forward_range range_type>
154 void operator()(range_type const & range) const
155 {
156 std::for_each(range.begin(),
157 range.end(),
158 [&](auto cmp)
159 {
160 (*this)(cmp);
161 });
162 }
163
166 {
167 return detail::to_string("Value must be in range [", min, ",", max, "].");
168 }
169
170private:
172 option_value_type min{};
173
175 option_value_type max{};
176};
177
197template <typename option_value_t>
199{
200public:
202 using option_value_type = option_value_t;
203
213
219 template <std::ranges::forward_range range_type>
220 requires std::constructible_from<option_value_type, std::ranges::range_rvalue_reference_t<range_type>>
222 {
223 values.clear();
224 std::ranges::move(std::move(rng), std::back_inserter(values));
225 }
226
232 template <typename... option_types>
233 requires ((std::constructible_from<option_value_type, option_types> && ...))
235 {
236 (values.emplace_back(std::forward<option_types>(opts)), ...);
237 }
239
244 void operator()(option_value_type const & cmp) const
245 {
246 if (!(std::find(values.begin(), values.end(), cmp) != values.end()))
247 throw validation_error{detail::to_string("Value ", cmp, " is not one of ", std::views::all(values), ".")};
248 }
249
255 template <std::ranges::forward_range range_type>
256 requires std::convertible_to<std::ranges::range_value_t<range_type>, option_value_type>
257 void operator()(range_type const & range) const
258 {
260 std::ranges::end(range),
261 [&](auto cmp)
262 {
263 (*this)(cmp);
264 });
265 }
266
269 {
270 return detail::to_string("Value must be one of ", std::views::all(values), ".");
271 }
272
273private:
276};
277
283template <typename option_type, typename... option_types>
284 requires (std::constructible_from<std::string, std::decay_t<option_types>> && ...
285 && std::constructible_from<std::string, std::decay_t<option_type>>)
287
289template <typename range_type>
290 requires (std::ranges::forward_range<std::decay_t<range_type>>
291 && std::constructible_from<std::string, std::ranges::range_value_t<range_type>>)
293
295template <typename option_type, typename... option_types>
297
299template <typename range_type>
300 requires (std::ranges::forward_range<std::decay_t<range_type>>)
303
319{
320public:
323
332 virtual ~file_validator_base() = default;
334
342 virtual void operator()(std::filesystem::path const & path) const = 0;
343
351 template <std::ranges::forward_range range_type>
352 requires (std::convertible_to<std::ranges::range_value_t<range_type>, std::filesystem::path const &>
353 && !std::convertible_to<range_type, std::filesystem::path const &>)
354 void operator()(range_type const & v) const
355 {
356 std::for_each(v.begin(),
357 v.end(),
358 [&](auto cmp)
359 {
360 this->operator()(cmp);
361 });
362 }
363
364protected:
371 {
372 // If no valid extensions are given we can safely return here.
373 if (extensions.empty())
374 return;
375
376 // Check if extension is available.
377 if (!path.has_extension())
378 throw validation_error{detail::to_string("The given filename ",
379 path.string(),
380 " has no extension. Expected"
381 " one of the following valid extensions:",
383 "!")};
384
385 std::string file_path{path.filename().string()};
386
387 // Leading dot indicates a hidden file is not part of the extension.
388 if (file_path.front() == '.')
389 file_path.erase(0, 1);
390
391 // Store a string_view containing all extensions for a better error message.
392 std::string const all_extensions{file_path.substr(file_path.find(".") + 1)};
393
394 // Compares the extensions in lower case.
395 auto case_insensitive_ends_with = [&](std::string const & ext)
396 {
398 };
399
400 // Check if requested extension is present.
402 {
403 throw validation_error{detail::to_string("Expected one of the following valid extensions: ",
405 "! Got ",
407 " instead!")};
408 }
409 }
410
417 {
418 // Check if input directory is readable.
420 {
422 std::filesystem::directory_iterator{path, ec}; // if directory iterator cannot be created, ec will be set.
423 if (static_cast<bool>(ec))
424 throw validation_error{detail::to_string("Cannot read the directory ", path, "!")};
425 }
426 else
427 {
428 // Must be a regular file.
430 throw validation_error{detail::to_string("Expected a regular file ", path, "!")};
431
432 std::ifstream file{path};
433 if (!file.is_open() || !file.good())
434 throw validation_error{detail::to_string("Cannot read the file ", path, "!")};
435 }
436 }
437
444 {
445 std::ofstream file{path};
446 detail::safe_filesystem_entry file_guard{path};
447
448 bool is_open = file.is_open();
449 bool is_good = file.good();
450 file.close();
451
452 if (!is_good || !is_open)
453 throw validation_error{detail::to_string("Cannot write ", path, "!")};
454
455 file_guard.remove();
456 }
457
460 {
461 if (extensions.empty())
462 return "";
463 else
464 return detail::to_string(" Valid file extensions are: [",
466 "].");
467 }
468
475 {
476 size_t const suffix_length{suffix.size()};
477 size_t const str_length{str.size()};
480 suffix,
481 [](char const chr1, char const chr2)
482 {
483 return std::tolower(chr1) == std::tolower(chr2);
484 });
485 }
486
489};
490
515template <typename file_t = void>
517{
518public:
519 static_assert(std::same_as<file_t, void> || detail::has_type_valid_formats<file_t>,
520 "Expected either a template type with a static member called valid_formats (a file type) or void.");
521
522 // Import from base class.
524
538 {
539 if constexpr (!std::same_as<file_t, void>)
540 file_validator_base::extensions = detail::valid_file_extensions<typename file_t::valid_formats>();
541 }
542
547 virtual ~input_file_validator() = default;
548
558 requires std::same_as<file_t, void>
560 {
562 }
563
564 // Import base class constructor.
567
568 // Import the base::operator()
569 using file_validator_base::operator();
570
576 virtual void operator()(std::filesystem::path const & file) const override
577 {
578 try
579 {
581 throw validation_error{detail::to_string("The file ", file, " does not exist!")};
582
583 // Check if file is regular and can be opened for reading.
585
586 // Check extension.
588 }
589 // LCOV_EXCL_START
591 {
592 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
593 }
594 // LCOV_EXCL_STOP
595 catch (...)
596 {
598 }
599 }
600
603 {
604 return "The input file must exist and read permissions must be granted." + valid_extensions_help_page_message();
605 }
606};
607
616
645template <typename file_t = void>
647{
648public:
649 static_assert(std::same_as<file_t, void> || detail::has_type_valid_formats<file_t>,
650 "Expected either a template type with a static member called valid_formats (a file type) or void.");
651
652 // Import from base class.
654
662
667 virtual ~output_file_validator() = default;
668
682
683 // Import base constructor.
686
696 {
697 if constexpr (!std::same_as<file_t, void>)
698 return detail::valid_file_extensions<typename file_t::valid_formats>();
699 return {};
700 }
701
702 // Import the base::operator()
703 using file_validator_base::operator();
704
710 virtual void operator()(std::filesystem::path const & file) const override
711 {
712 try
713 {
715 throw validation_error{detail::to_string("The file ", file, " already exists!")};
716
717 // Check if file has any write permissions.
719
721 }
722 // LCOV_EXCL_START
724 {
725 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
726 }
727 // LCOV_EXCL_STOP
728 catch (...)
729 {
731 }
732 }
733
736 {
738 return "Write permissions must be granted." + valid_extensions_help_page_message();
739 else // mode == create_new
740 return "The output file must not exist already and write permissions must be granted."
742 }
743
744private:
747};
748
766{
767public:
768 // Import from base class.
770
779 virtual ~input_directory_validator() = default;
780
781 // Import base constructor.
784
785 // Import the base::operator()
786 using file_validator_base::operator();
787
793 virtual void operator()(std::filesystem::path const & dir) const override
794 {
795 try
796 {
798 throw validation_error{detail::to_string("The directory ", dir, " does not exists!")};
799
801 throw validation_error{detail::to_string("The path ", dir, " is not a directory!")};
802
803 // Check if directory has any read permissions.
805 }
806 // LCOV_EXCL_START
808 {
809 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
810 }
811 // LCOV_EXCL_STOP
812 catch (...)
813 {
815 }
816 }
817
820 {
821 return detail::to_string("An existing, readable path for the input directory.");
822 }
823};
824
842{
843public:
844 // Imported from base class.
846
855 virtual ~output_directory_validator() = default;
856
857 // Import base constructor.
860
861 // Import the base::operator().
862 using file_validator_base::operator();
863
869 virtual void operator()(std::filesystem::path const & dir) const override
870 {
872 // Make sure the created dir is deleted after we are done.
874 std::filesystem::create_directory(dir, ec); // does nothing and is not treated as error if path already exists.
875 // if error code was set or if dummy.txt could not be created within the output dir, throw an error.
876 if (static_cast<bool>(ec))
877 throw validation_error{detail::to_string("Cannot create directory: ", dir, "!")};
878
879 try
880 {
881 if (!dir_exists)
882 {
883 detail::safe_filesystem_entry dir_guard{dir};
884 validate_writeability(dir / "dummy.txt");
885 dir_guard.remove_all();
886 }
887 else
888 {
889 validate_writeability(dir / "dummy.txt");
890 }
891 }
892 // LCOV_EXCL_START
894 {
895 std::throw_with_nested(validation_error{"Unhandled filesystem error!"});
896 }
897 // LCOV_EXCL_STOP
898 catch (...)
899 {
901 }
902 }
903
906 {
907 return detail::to_string("A valid path for the output directory.");
908 }
909};
910
931{
932public:
935
940 {}
941
946 void operator()(option_value_type const & cmp) const
947 {
948 std::regex rgx(pattern);
949 if (!std::regex_match(cmp, rgx))
950 throw validation_error{detail::to_string("Value ", cmp, " did not match the pattern ", pattern, ".")};
951 }
952
959 template <std::ranges::forward_range range_type>
960 requires std::convertible_to<std::ranges::range_reference_t<range_type>, option_value_type const &>
961 void operator()(range_type const & v) const
962 {
963 for (auto && file_name : v)
964 {
965 // note: we explicitly copy/construct any reference type other than `std::string &`
966 (*this)(static_cast<option_value_type const &>(file_name));
967 }
968 }
969
972 {
973 return detail::to_string("Value must match the pattern '", pattern, "'.");
974 }
975
976private:
978 std::string pattern;
979};
980
981namespace detail
982{
983
996template <typename option_value_t>
997struct default_validator
998{
1000 using option_value_type = option_value_t;
1001
1003 void operator()(option_value_t const & /*cmp*/) const noexcept
1004 {}
1005
1008 {
1009 return "";
1010 }
1011};
1012
1026template <validator validator1_type, validator validator2_type>
1027 requires std::common_with<typename validator1_type::option_value_type, typename validator2_type::option_value_type>
1028class validator_chain_adaptor
1029{
1030public:
1032 using option_value_type =
1034
1038 validator_chain_adaptor() = delete;
1039 validator_chain_adaptor(validator_chain_adaptor const & pf) = default;
1040 validator_chain_adaptor & operator=(validator_chain_adaptor const & pf) = default;
1041 validator_chain_adaptor(validator_chain_adaptor &&) = default;
1042 validator_chain_adaptor & operator=(validator_chain_adaptor &&) = default;
1043
1048 validator_chain_adaptor(validator1_type vali1_, validator2_type vali2_) :
1049 vali1{std::move(vali1_)},
1050 vali2{std::move(vali2_)}
1051 {}
1052
1054 ~validator_chain_adaptor() = default;
1056
1065 template <typename cmp_type>
1066 requires std::invocable<validator1_type, cmp_type const> && std::invocable<validator2_type, cmp_type const>
1067 void operator()(cmp_type const & cmp) const
1068 {
1069 vali1(cmp);
1070 vali2(cmp);
1071 }
1072
1075 {
1076 return detail::to_string(vali1.get_help_page_message(), " ", vali2.get_help_page_message());
1077 }
1078
1079private:
1081 validator1_type vali1;
1083 validator2_type vali2;
1084};
1085
1086} // namespace detail
1087
1117template <validator validator1_type, validator validator2_type>
1118 requires std::common_with<typename std::remove_reference_t<validator1_type>::option_value_type,
1121{
1122 return detail::validator_chain_adaptor{std::forward<validator1_type>(vali1), std::forward<validator2_type>(vali2)};
1123}
1124
1125} // 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:124
void operator()(option_value_type const &cmp) const
Tests whether cmp lies inside [min, max].
Definition validators.hpp:140
option_value_t option_value_type
The type of value that this validator invoked upon.
Definition validators.hpp:127
void operator()(range_type const &range) const
Tests whether every element in range lies inside [min, max].
Definition validators.hpp:154
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:165
arithmetic_range_validator(option_value_type const min_, option_value_type const max_)
The constructor.
Definition validators.hpp:133
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:319
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:474
void validate_filename(std::filesystem::path const &path) const
Validates the given filename path based on the specified extensions.
Definition validators.hpp:370
std::string valid_extensions_help_page_message() const
Returns the information of valid file extensions.
Definition validators.hpp:459
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:322
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:416
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:488
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:443
A validator that checks if a given path is a valid input directory.
Definition validators.hpp:766
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:819
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:793
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:517
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:602
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:557
virtual void operator()(std::filesystem::path const &file) const override
Tests whether path is an existing regular file and is readable.
Definition validators.hpp:576
input_file_validator()
Default constructor.
Definition validators.hpp:537
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:842
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:869
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:905
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:647
static std::vector< std::string > default_extensions()
The default extensions of file_t.
Definition validators.hpp:695
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:710
output_file_validator()
Default constructor.
Definition validators.hpp:660
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:735
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:675
A validator that checks if a matches a regular expression pattern.
Definition validators.hpp:931
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:971
void operator()(option_value_type const &cmp) const
Tests whether cmp lies inside values.
Definition validators.hpp:946
std::string option_value_type
Type of values that are tested by validator.
Definition validators.hpp:934
void operator()(range_type const &v) const
Tests whether every filename in list v matches the pattern.
Definition validators.hpp:961
regex_validator(std::string const &pattern_)
Constructing from a vector.
Definition validators.hpp:939
Argument parser exception thrown when an argument could not be casted to the according type.
Definition exceptions.hpp:130
A validator that checks whether a value is inside a list of valid values.
Definition validators.hpp:199
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:244
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:202
void operator()(range_type const &range) const
Tests whether every element in range lies inside values.
Definition validators.hpp:257
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition validators.hpp:268
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:221
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:234
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:1120
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:610
@ 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