From 7e97653ddcfbf7e379c4362eb13bc1a51f029438 Mon Sep 17 00:00:00 2001 From: rrahn Date: Tue, 21 May 2019 14:50:37 +0200 Subject: [PATCH] [FEATURE] Adapt input/output file validator to read extensions from a file. --- include/seqan3/argument_parser/validators.hpp | 106 +++++++++++++++--- include/seqan3/io/detail/magic_header.hpp | 1 - .../validators_input_file_ext_from_file.cpp | 20 ++++ .../validators_output_file_ext_from_file.cpp | 20 ++++ .../format_parse_validators_test.cpp | 54 ++++++++- test/unit/io/detail/misc_test.cpp | 1 - 6 files changed, 177 insertions(+), 25 deletions(-) create mode 100644 test/snippet/argument_parser/validators_input_file_ext_from_file.cpp create mode 100644 test/snippet/argument_parser/validators_output_file_ext_from_file.cpp diff --git a/include/seqan3/argument_parser/validators.hpp b/include/seqan3/argument_parser/validators.hpp index 4d03460be7..8ca6f1dcd2 100644 --- a/include/seqan3/argument_parser/validators.hpp +++ b/include/seqan3/argument_parser/validators.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -236,11 +237,15 @@ value_list_validator(std::initializer_list) -> value_list_validato /*!\brief An abstract base class for the file and directory validators. * \ingroup argument_parser + * \tparam file_t The type of the file to get the valid extensions for; `void` on default. * * \details * * This class provides a common interface for seqan3::input_file_validator and the seqan3::output_file_validator as * well as the seqan3::input_directory_validator and seqan3::output_directory_validator. + * + * The type can be further specialised for the seqan3::input_file_validator and the seqan3::output_file_validator + * using the template argument to determine the valid extensions from the given file type. */ class file_validator_base { @@ -258,12 +263,6 @@ class file_validator_base file_validator_base & operator=(file_validator_base const &) = default; //!< Defaulted. file_validator_base & operator=(file_validator_base &&) = default; //!< Defaulted. virtual ~file_validator_base() = default; //!< Virtual destructor. - - /*!\brief Constructs from a set of valid extensions. - * \param[in] extensions The valid extensions to validate for. - */ - explicit file_validator_base(std::vector extensions) : extensions{std::move(extensions)} - {} //!\} /*!\brief Tests if the given path is a valid input, respectively output, file or directory. @@ -384,6 +383,7 @@ class file_validator_base /*!\brief A validator that checks if a given path is a valid input file. * \ingroup argument_parser * \implements seqan3::Validator + * \tparam file_t The type of the file to get the valid extensions for; `void` on default. * * \details * @@ -394,24 +394,66 @@ class file_validator_base * * \include test/snippet/argument_parser/validators_input_file.cpp * + * The valid extensions can also be obtained from a seqan3 formatted file type, e.g. seqan3::sequence_input_file, if + * it is given as template argument to this class. The following snippet demonstrates the different ways to instantiate + * the seqan3::output_file_validator. + * + * \include test/snippet/argument_parser/validators_input_file_ext_from_file.cpp + * * \note The validator works on every type that can be implicitly converted to std::filesystem::path. */ +template class input_file_validator : public file_validator_base { public: + + static_assert(std::Same || detail::has_type_valid_formats, + "Expected either a template type with a static member called valid_formats (a file type) or void."); + // Import from base class. - using file_validator_base::value_type; + using typename file_validator_base::value_type; /*!\name Constructors, destructor and assignment * \{ */ - input_file_validator() = default; //!< Defaulted. + + /*!\brief Default constructor. + * + * \details + * + * If the class' template argument `file_t` names a valid seqan3 file type that contains a + * static member `valid_formats`, e.g. seqan3::sequence_input_file::valid_formats, then it generates the + * list of valid extensions from this file. Otherwise the extensions list is empty. + */ + input_file_validator() + { + if constexpr (!std::Same) + file_validator_base::extensions = detail::valid_file_extensions(); + } + input_file_validator(input_file_validator const &) = default; //!< Defaulted. input_file_validator(input_file_validator &&) = default; //!< Defaulted. input_file_validator & operator=(input_file_validator const &) = default; //!< Defaulted. input_file_validator & operator=(input_file_validator &&) = default; //!< Defaulted. virtual ~input_file_validator() = default; //!< Virtual destructor. + /*!\brief Constructs from a given collection of valid extensions. + * \param[in] extensions The valid extensions to validate for. + * + * \details + * + * This constructor is only available if `file_t` does not name a valid seqan3 file type that contains a + * static member `valid_formats`. + */ + explicit input_file_validator(std::vector extensions) + //!\cond + requires std::Same + //!\endcond + : file_validator_base{} + { + file_validator_base::extensions = std::move(extensions); + } + // Import base class constructor. using file_validator_base::file_validator_base; //!\} @@ -450,15 +492,16 @@ class input_file_validator : public file_validator_base //!\brief Returns a message that can be appended to the (positional) options help page info. std::string get_help_page_message() const { - return detail::to_string("Valid input file formats: ", - file_validator_base::extensions | std::view::join(std::string{", "}), - "."); + return detail::to_string("Valid input file formats: [", + extensions | std::view::join(std::string{", "}), + "]"); } }; /*!\brief A validator that checks if a given path is a valid output file. * \ingroup argument_parser * \implements seqan3::Validator + * \tparam file_t The type of the file to get the valid extensions for; `void` on default. * * \details * @@ -469,24 +512,52 @@ class input_file_validator : public file_validator_base * * \include test/snippet/argument_parser/validators_output_file.cpp * + * The valid extensions can also be obtained from a seqan3 formatted file type, e.g. seqan3::sequence_input_file, if + * it is given as template argument to this class. The following snippet demonstrates the different ways to instantiate + * the seqan3::output_file_validator. + * + * \include test/snippet/argument_parser/validators_output_file_ext_from_file.cpp + * * \note The validator works on every type that can be implicitly converted to std::filesystem::path. */ +template class output_file_validator : public file_validator_base { public: + + static_assert(std::Same || detail::has_type_valid_formats, + "Expected either a template type with a static member called valid_formats (a file type) or void."); + // Import from base class. - using file_validator_base::value_type; + using typename file_validator_base::value_type; /*!\name Constructors, destructor and assignment * \{ */ - output_file_validator() = default; //!< Defaulted. + + //!\copydoc seqan3::input_file_validator::input_file_validator() + output_file_validator() + { + if constexpr (!std::Same) + file_validator_base::extensions = detail::valid_file_extensions(); + } + output_file_validator(output_file_validator const &) = default; //!< Defaulted. output_file_validator(output_file_validator &&) = default; //!< Defaulted. output_file_validator & operator=(output_file_validator const &) = default; //!< Defaulted. output_file_validator & operator=(output_file_validator &&) = default; //!< Defaulted. virtual ~output_file_validator() = default; //!< Virtual Destructor. + //!\copydoc seqan3::input_file_validator::input_file_validator(std::vector) + explicit output_file_validator(std::vector extensions) + //!\cond + requires std::Same + //!\endcond + : file_validator_base{} + { + file_validator_base::extensions = std::move(extensions); + } + // Import base constructor. using file_validator_base::file_validator_base; //!\} @@ -524,8 +595,9 @@ class output_file_validator : public file_validator_base //!\brief Returns a message that can be appended to the (positional) options help page info. std::string get_help_page_message() const { - return detail::to_string("Valid output file formats: ", - file_validator_base::extensions | std::view::join(std::string{", "}), "."); + return detail::to_string("Valid output file formats: [", + extensions | std::view::join(std::string{", "}), + "]"); } }; @@ -547,7 +619,7 @@ class input_directory_validator : public file_validator_base { public: // Import from base class. - using file_validator_base::value_type; + using typename file_validator_base::value_type; /*!\name Constructors, destructor and assignment * \{ @@ -619,7 +691,7 @@ class output_directory_validator : public file_validator_base { public: // Imported from base class. - using file_validator_base::value_type; + using typename file_validator_base::value_type; /*!\name Constructors, destructor and assignment * \{ diff --git a/include/seqan3/io/detail/magic_header.hpp b/include/seqan3/io/detail/magic_header.hpp index 31b0bf8e72..420f0b540d 100644 --- a/include/seqan3/io/detail/magic_header.hpp +++ b/include/seqan3/io/detail/magic_header.hpp @@ -15,7 +15,6 @@ #include #include -#include #include #include #include diff --git a/test/snippet/argument_parser/validators_input_file_ext_from_file.cpp b/test/snippet/argument_parser/validators_input_file_ext_from_file.cpp new file mode 100644 index 0000000000..1aa28e83fa --- /dev/null +++ b/test/snippet/argument_parser/validators_input_file_ext_from_file.cpp @@ -0,0 +1,20 @@ +#include +#include +#include + +int main(int argc, const char ** argv) +{ + // Default constructed validator has an empty extension list. + seqan3::input_file_validator validator1{}; + seqan3::debug_stream << validator1.get_help_page_message() << "\n"; + + // Specify your own extensions for the input file. + seqan3::input_file_validator validator2{std::vector{std::string{"exe"}, std::string{"fasta"}}}; + seqan3::debug_stream << validator2.get_help_page_message() << "\n"; + + // Give the seqan3 file type as a template argument to get all valid extensions for this file. + seqan3::input_file_validator> validator3{}; + seqan3::debug_stream << validator3.get_help_page_message() << "\n"; + + return 0; +} diff --git a/test/snippet/argument_parser/validators_output_file_ext_from_file.cpp b/test/snippet/argument_parser/validators_output_file_ext_from_file.cpp new file mode 100644 index 0000000000..7209091b67 --- /dev/null +++ b/test/snippet/argument_parser/validators_output_file_ext_from_file.cpp @@ -0,0 +1,20 @@ +#include +#include +#include + +int main(int argc, const char ** argv) +{ + // Default constructed validator has an empty extension list. + seqan3::output_file_validator validator1{}; + seqan3::debug_stream << validator1.get_help_page_message() << "\n"; + + // Specify your own extensions for the output file. + seqan3::output_file_validator validator2{std::vector{std::string{"exe"}, std::string{"fasta"}}}; + seqan3::debug_stream << validator2.get_help_page_message() << "\n"; + + // Give the seqan3 file type as a template argument to get all valid extensions for this file. + seqan3::output_file_validator> validator3{}; + seqan3::debug_stream << validator3.get_help_page_message() << "\n"; + + return 0; +} diff --git a/test/unit/argument_parser/format_parse_validators_test.cpp b/test/unit/argument_parser/format_parse_validators_test.cpp index 4659e1d0d3..0db2314014 100644 --- a/test/unit/argument_parser/format_parse_validators_test.cpp +++ b/test/unit/argument_parser/format_parse_validators_test.cpp @@ -21,6 +21,22 @@ using namespace seqan3; +struct dummy_file +{ + + struct format1 + { + static inline std::vector file_extensions{ {"fa"}, {"fasta"}}; + }; + + struct format2 + { + static inline std::vector file_extensions{ {"sam"}, {"bam"}}; + }; + + using valid_formats = type_list; +}; + std::string const basic_options_str = "OPTIONS" "Basic options:" "-h, --help Prints the help page." @@ -47,8 +63,8 @@ TEST(validator_test, fullfill_concept) EXPECT_TRUE(Validator); EXPECT_TRUE(Validator>); EXPECT_TRUE(Validator>); - EXPECT_TRUE(Validator); - EXPECT_TRUE(Validator); + EXPECT_TRUE(Validator>); + EXPECT_TRUE(Validator>); EXPECT_TRUE(Validator); EXPECT_TRUE(Validator); EXPECT_TRUE(Validator); @@ -92,6 +108,11 @@ TEST(validator_test, input_file) EXPECT_THROW(my_validator(does_not_exist), parser_invalid_argument); } + { // read from file + input_file_validator my_validator{}; + EXPECT_NO_THROW(my_validator(tmp_name.get_path())); + } + std::filesystem::path file_in_path; // option @@ -135,13 +156,21 @@ TEST(validator_test, input_file) "===========" "POSITIONAL ARGUMENTS" " ARGUMENT-1 (std::filesystem::path)" - " desc Valid input file formats: fa, sam, fasta."} + + " desc Valid input file formats: [fa, sam, fasta]"} + basic_options_str + basic_version_str; EXPECT_TRUE(ranges::equal((my_stdout | std::view::filter(!is_space)), expected | std::view::filter(!is_space))); } } +TEST(validator_test, input_file_ext_from_file) +{ + // Give as a template argument the seqan3 file type to get all valid extensions for this file. + input_file_validator validator{}; + + EXPECT_EQ(validator.get_help_page_message(), "Valid input file formats: [fa, fasta, sam, bam]"); +} + TEST(validator_test, output_file) { test::tmp_filename tmp_name{"testbox.fasta"}; @@ -166,7 +195,7 @@ TEST(validator_test, output_file) { // file has wrong format. output_file_validator my_validator{std::vector{std::string{"sam"}}}; - EXPECT_THROW(my_validator(tmp_name.get_path()), parser_invalid_argument); + EXPECT_THROW(my_validator(tmp_name.get_path()), parser_invalid_argument); } { // file has no extension. @@ -176,6 +205,11 @@ TEST(validator_test, output_file) EXPECT_THROW(my_validator(no_extension), parser_invalid_argument); } + { // read from file + output_file_validator my_validator{}; + EXPECT_NO_THROW(my_validator(tmp_name.get_path())); + } + std::filesystem::path file_out_path; // option @@ -220,13 +254,21 @@ TEST(validator_test, output_file) "===========" "POSITIONAL ARGUMENTS" " ARGUMENT-1 (std::filesystem::path)" - " desc Valid output file formats: fa, sam, fasta."} + + " desc Valid output file formats: [fa, sam, fasta]"} + basic_options_str + basic_version_str; EXPECT_TRUE(ranges::equal((my_stdout | std::view::filter(!is_space)), expected | std::view::filter(!is_space))); } } +TEST(validator_test, output_file_ext_from_file) +{ + // Give as a template argument the seqan3 file type to get all valid extensions for this file. + output_file_validator validator{}; + + EXPECT_EQ(validator.get_help_page_message(), "Valid output file formats: [fa, fasta, sam, bam]"); +} + TEST(validator_test, input_directory) { test::tmp_filename tmp_name{"testbox.fasta"}; @@ -937,7 +979,7 @@ TEST(validator_test, chaining_validators) basic_options_str + " -s, --string-option (std::string)" " desc Default:. Value must match the pattern '(/[^/]+)+/.*\\.[^/\\.]+$'. " - " Valid output file formats: sa, so." + " Valid output file formats: [sa, so]" " Value must match the pattern '.*'."} + basic_version_str; EXPECT_TRUE(ranges::equal((my_stdout | ranges::view::remove_if(is_space)), diff --git a/test/unit/io/detail/misc_test.cpp b/test/unit/io/detail/misc_test.cpp index 654272a1ed..a4955d55b3 100644 --- a/test/unit/io/detail/misc_test.cpp +++ b/test/unit/io/detail/misc_test.cpp @@ -55,7 +55,6 @@ TEST(misc, valid_file_extensions) TEST(misc, valid_compression_extensions) { - std::vector valid_compression = detail::valid_file_extensions(); #if defined(SEQAN3_HAS_ZLIB)