SeqAn3  3.0.0
The Modern C++ library for sequence analysis.
validators.hpp
Go to the documentation of this file.
1 // -----------------------------------------------------------------------------------------------------
2 // Copyright (c) 2006-2019, Knut Reinert & Freie Universität Berlin
3 // Copyright (c) 2016-2019, Knut Reinert & MPI für molekulare Genetik
4 // This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License
5 // shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md
6 // -----------------------------------------------------------------------------------------------------
7 
13 #pragma once
14 
15 #include <regex>
16 #include <fstream>
17 #include <sstream>
18 
28 #include <seqan3/std/algorithm>
29 #include <seqan3/std/concepts>
30 #include <seqan3/std/filesystem>
31 #include <seqan3/std/ranges>
32 
33 namespace seqan3
34 {
35 
79 template <typename validator_type>
82 SEQAN3_CONCEPT Validator = std::Copyable<remove_cvref_t<validator_type>> &&
83  requires(validator_type validator,
85 {
87 
88  { validator(value) } -> void;
89  { validator.get_help_page_message() } -> std::string;
90 };
92 
108 {
109 public:
111  using value_type = double;
112 
118  min{min_}, max{max_}
119  {}
120 
125  void operator()(value_type const & cmp) const
126  {
127  if (!((cmp <= max) && (cmp >= min)))
128  throw parser_invalid_argument(detail::to_string("Value ", cmp, " is not in range [", min, ",", max, "]."));
129  }
130 
137  template <std::ranges::ForwardRange range_type>
141  void operator()(range_type const & range) const
142  {
143  std::for_each(range.begin(), range.end(), [&] (auto cmp) { (*this)(cmp); });
144  }
145 
148  {
149  return detail::to_string("Value must be in range [", min, ",", max, "].");
150  }
151 
152 private:
154  value_type min{};
155 
157  value_type max{};
158 };
159 
172 template <typename option_value_type>
174 {
175 public:
177  using value_type = option_value_type;
178 
183  {}
184 
189  void operator()(value_type const & cmp) const
190  {
191  if (!(std::find(values.begin(), values.end(), cmp) != values.end()))
192  throw parser_invalid_argument(detail::to_string("Value ", cmp, " is not one of ", std::view::all(values), "."));
193  }
194 
200  template <std::ranges::ForwardRange range_type>
204  void operator()(range_type const & range) const
205  {
206  std::for_each(range.begin(), range.end(), [&] (auto cmp) { (*this)(cmp); });
207  }
208 
211  {
212  return detail::to_string("Value must be one of ", std::view::all(values), ".");
213  }
214 
215 private:
216 
219 };
220 
226 template <Arithmetic option_value_type>
227 value_list_validator(std::vector<option_value_type>) -> value_list_validator<double>;
228 
229 template <Arithmetic option_value_type>
230 value_list_validator(std::initializer_list<option_value_type>) -> value_list_validator<double>;
231 
232 value_list_validator(std::vector<const char *>) -> value_list_validator<std::string>;
233 
234 value_list_validator(std::initializer_list<const char *>) -> value_list_validator<std::string>;
236 
246 {
247 public:
248 
251 
255  file_validator_base() = default;
256  file_validator_base(file_validator_base const &) = default;
258  file_validator_base & operator=(file_validator_base const &) = default;
260  virtual ~file_validator_base() = default;
261 
266  {}
268 
276  virtual void operator()(std::filesystem::path const & path) const = 0;
277 
285  template <std::ranges::ForwardRange range_type>
289  void operator()(range_type const & v) const
290  {
291  std::for_each(v.begin(), v.end(), [&] (auto cmp) { this->operator()(cmp); });
292  }
293 
294 protected:
295 
301  void validate_filename(std::filesystem::path const & path) const
302  {
303  // If no valid extensions are given we can safely return here.
304  if (extensions.empty())
305  return;
306 
307  // Check if extension is available.
308  if (!path.has_extension())
309  throw parser_invalid_argument{detail::to_string("The given filename ", path.string(),
310  " has no extension. Expected one of the following valid"
311  " extensions:", extensions, "!")};
312 
313  // Drop the dot.
314  std::string tmp_str = path.extension().string();
315  auto drop_less_ext = tmp_str | view::drop(1);
316 
317  // Compares the extensions in lower case.
318  auto cmp_lambda = [&] (std::string const & cmp)
319  {
320  return std::ranges::equal(cmp | view::to_lower, drop_less_ext | view::to_lower);
321  };
322 
323  // Check if requested extension is present.
324  if (std::ranges::find_if(extensions, cmp_lambda) == extensions.end())
325  {
326  throw parser_invalid_argument{detail::to_string("Expected one of the following valid extensions: ",
327  extensions, "! Got ", drop_less_ext, " instead!")};
328  }
329  }
330 
338  {
339  // Check if input directory is readable.
341  {
342  std::error_code ec{};
343  std::filesystem::directory_iterator{path, ec}; // if directory iterator cannot be created, ec will be set.
344  if (static_cast<bool>(ec))
345  throw parser_invalid_argument{detail::to_string("Cannot read the directory ", path ,"!")};
346  }
347  else
348  {
349  // Must be a regular file.
351  throw parser_invalid_argument{detail::to_string("Expected a regular file ", path, "!")};
352 
353  std::ifstream file{path};
354  if (!file.is_open() || !file.good())
355  throw parser_invalid_argument{detail::to_string("Cannot read the file ", path, "!")};
356  }
357  }
358 
366  {
367  std::ofstream file{path};
368  detail::safe_filesystem_entry file_guard{path};
369 
370  bool is_open = file.is_open();
371  bool is_good = file.good();
372  file.close();
373 
374  if (!is_good || !is_open)
375  throw parser_invalid_argument(detail::to_string("Cannot write ", path, "!"));
376 
377  file_guard.remove();
378  }
379 
382 };
383 
400 {
401 public:
402  // Import from base class.
404 
408  input_file_validator() = default;
409  input_file_validator(input_file_validator const &) = default;
413  virtual ~input_file_validator() = default;
414 
415  // Import base class constructor.
418 
419  // Import the base::operator()
420  using file_validator_base::operator();
421 
427  virtual void operator()(std::filesystem::path const & file) const override
428  {
429  try
430  {
431  if (!std::filesystem::exists(file))
432  throw parser_invalid_argument(detail::to_string("The file ", file, " does not exist!"));
433 
434  // Check if file is regular and can be opened for reading.
435  validate_readability(file);
436 
437  // Check extension.
438  validate_filename(file);
439  }
441  {
442  std::throw_with_nested(parser_invalid_argument("Unhandled filesystem error!"));
443  }
444  catch (...)
445  {
447  }
448  }
449 
452  {
453  return detail::to_string("Valid input file formats: ",
455  ".");
456  }
457 };
458 
475 {
476 public:
477  // Import from base class.
479 
483  output_file_validator() = default;
484  output_file_validator(output_file_validator const &) = default;
488  virtual ~output_file_validator() = default;
489 
490  // Import base constructor.
493 
494  // Import the base::operator()
495  using file_validator_base::operator();
496 
502  virtual void operator()(std::filesystem::path const & file) const override
503  {
504  try
505  {
506  if (std::filesystem::exists(file))
507  throw parser_invalid_argument(detail::to_string("The file ", file, " already exists!"));
508 
509  // Check if file has any write permissions.
510  validate_writeability(file);
511 
512  validate_filename(file);
513  }
515  {
516  std::throw_with_nested(parser_invalid_argument("Unhandled filesystem error!"));
517  }
518  catch (...)
519  {
521  }
522  }
523 
526  {
527  return detail::to_string("Valid output file formats: ",
529  }
530 };
531 
547 {
548 public:
549  // Import from base class.
551 
555  input_directory_validator() = default;
560  virtual ~input_directory_validator() = default;
561 
562  // Import base constructor.
565 
566  // Import the base::operator()
567  using file_validator_base::operator();
568 
574  virtual void operator()(std::filesystem::path const & dir) const override
575  {
576  try
577  {
578  if (!std::filesystem::exists(dir))
579  throw parser_invalid_argument(detail::to_string("The directory ", dir, " does not exists!"));
580 
582  throw parser_invalid_argument(detail::to_string("The path ", dir, " is not a directory!"));
583 
584  // Check if directory has any read permissions.
586  }
588  {
589  std::throw_with_nested(parser_invalid_argument("Unhandled filesystem error!"));
590  }
591  catch (...)
592  {
594  }
595  }
596 
599  {
600  return detail::to_string("An existing, readable path for the input directory.");
601  }
602 };
603 
619 {
620 public:
621  // Imported from base class.
623 
627  output_directory_validator() = default;
632  virtual ~output_directory_validator() = default;
633 
634  // Import base constructor.
637 
638  // Import the base::operator().
639  using file_validator_base::operator();
640 
646  virtual void operator()(std::filesystem::path const & dir) const override
647  {
648  bool dir_exists = std::filesystem::exists(dir);
649  // Make sure the created dir is deleted after we are done.
650  std::error_code ec;
651  std::filesystem::create_directory(dir, ec); // does nothing and is not treated as error if path already exists.
652  // if error code was set or if dummy.txt could not be created within the output dir, throw an error.
653  if (static_cast<bool>(ec))
654  throw parser_invalid_argument(detail::to_string("Cannot create directory: ", dir, "!"));
655 
656  try
657  {
658  if (!dir_exists)
659  {
660  detail::safe_filesystem_entry dir_guard{dir};
661  validate_writeability(dir / "dummy.txt");
662  dir_guard.remove_all();
663  }
664  else
665  {
666  validate_writeability(dir / "dummy.txt");
667  }
668  }
670  {
671  std::throw_with_nested(parser_invalid_argument("Unhandled filesystem error!"));
672  }
673  catch (...)
674  {
676  }
677  }
678 
681  {
682  return detail::to_string("A valid path for the output directory.");
683  }
684 };
685 
704 {
705 public:
708 
712  regex_validator(std::string const & pattern_) :
713  pattern{pattern_}
714  {}
715 
720  void operator()(value_type const & cmp) const
721  {
722  std::regex rgx(pattern);
723  if (!std::regex_match(cmp, rgx))
724  throw parser_invalid_argument(detail::to_string("Value ", cmp, " did not match the pattern ", pattern, "."));
725  }
726 
733  template <std::ranges::ForwardRange range_type>
737  void operator()(range_type const & v) const
738  {
739  std::for_each(v.begin(), v.end(), [&] (auto cmp) { (*this)(cmp); });
740  }
741 
744  {
745  return detail::to_string("Value must match the pattern '", pattern, "'.");
746  }
747 
748 private:
750  std::string pattern;
751 };
752 
753 namespace detail
754 {
755 
765 template <typename option_value_type>
766 struct default_validator
767 {
769  using value_type = option_value_type;
770 
772  void operator()(option_value_type const & /*cmp*/) const noexcept
773  {}
774 
777  {
778  return "";
779  }
780 };
781 
793 template <Validator validator1_type, Validator validator2_type>
797 class validator_chain_adaptor
798 {
799 public:
801  using value_type = typename validator1_type::value_type;
802 
806  validator_chain_adaptor() = delete;
807  validator_chain_adaptor(validator_chain_adaptor const & pf) = default;
808  validator_chain_adaptor & operator=(validator_chain_adaptor const & pf) = default;
809  validator_chain_adaptor(validator_chain_adaptor &&) = default;
810  validator_chain_adaptor & operator=(validator_chain_adaptor &&) = default;
811 
816  validator_chain_adaptor(validator1_type vali1_, validator2_type vali2_) :
817  vali1{std::move(vali1_)}, vali2{std::move(vali2_)}
818  {}
819 
821  ~validator_chain_adaptor() = default;
823 
832  template <typename cmp_type>
836  void operator()(cmp_type const & cmp) const
837  {
838  vali1(cmp);
839  vali2(cmp);
840  }
841 
844  {
845  return detail::to_string(vali1.get_help_page_message(), " ", vali2.get_help_page_message());
846  }
847 
848 private:
850  validator1_type vali1;
852  validator2_type vali2;
853 };
854 
855 } // namespace detail
856 
884 template <Validator validator1_type, Validator validator2_type>
889 auto operator|(validator1_type && vali1, validator2_type && vali2)
890 {
891  return detail::validator_chain_adaptor{std::forward<validator1_type>(vali1),
892  std::forward<validator2_type>(vali2)};
893 }
894 
895 } // namespace seqan3
virtual void operator()(std::filesystem::path const &path) const =0
Tests if the given path is a valid input, respectively output, file or directory. ...
virtual ~output_file_validator()=default
Virtual Destructor.
::ranges::equal equal
Alias for ranges::equal. Determines if two sets of elements are the same.
Definition: algorithm:54
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition: validators.hpp:147
Provides concepts for core language types and relations that don&#39;t have concepts in C++20 (yet)...
T exists(T... args)
A validator that checks if a given path is a valid output directory.
Definition: validators.hpp:618
T empty(T... args)
virtual ~output_directory_validator()=default
Virtual Destructor.
T extension(T... args)
T regex_match(T... args)
A validator that checks if a given path is a valid output file.
Definition: validators.hpp:474
std::string value_type
Type of values that are tested by validator.
Definition: validators.hpp:250
void operator()(range_type const &v) const
Tests whether every path in list v passes validation. See operator()(value_type const & value) for fu...
Definition: validators.hpp:289
Provides various transformation trait base templates and shortcuts.
input_file_validator & operator=(input_file_validator const &)=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:574
file_validator_base()=default
Defaulted.
void operator()(value_type const &cmp) const
Validates the value &#39;cmp&#39; and throws a seqan3::validation_failed on failure.
virtual ~file_validator_base()=default
Virtual destructor.
virtual ~input_directory_validator()=default
Virtual Destructor.
void operator()(value_type const &cmp) const
Tests whether cmp lies inside values.
Definition: validators.hpp:720
Provides parser related exceptions.
T is_regular_file(T... args)
A validator that checks if a matches a regular expression pattern.
Definition: validators.hpp:703
constexpr auto drop
A view adaptor that returns all elements after n from the underlying range (or an empty range if the ...
Definition: drop.hpp:171
constexpr auto all
A view adaptor that behaves like std::view:all, but type erases contiguous ranges.
Definition: view_all.hpp:160
T end(T... args)
The main SeqAn3 namespace.
Auxiliary for pretty printing of exception messages.
std::vector< std::string > extensions
Stores the extensions.
Definition: validators.hpp:381
constexpr auto join
Flattens a View of ranges into a View.
Definition: ranges:683
input_file_validator()=default
Defaulted.
value_list_validator(std::vector< value_type > v)
Constructing from a vector.
Definition: validators.hpp:182
void operator()(range_type const &v) const
Tests whether every filename in list v matches the pattern.
Definition: validators.hpp:737
T current_exception(T... args)
void operator()(value_type const &cmp) const
Tests whether cmp lies inside values.
Definition: validators.hpp:189
input_directory_validator & operator=(input_directory_validator const &)=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:680
The concept std::ConvertibleTo<From, To> specifies that an expression of the type and value category ...
input_directory_validator()=default
Defaulted.
void operator()(value_type const &cmp) const
Tests whether cmp lies inside [min, max].
Definition: validators.hpp:125
file_validator_base(std::vector< std::string > extensions)
Constructs from a set of valid extensions.
Definition: validators.hpp:265
The Concepts library.
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
T has_extension(T... args)
Adaptations of concepts from the Ranges TS.
auto const to_lower
A view that calls seqan3::to_lower() on each element in the input range.
Definition: to_lower.hpp:67
T throw_with_nested(T... args)
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition: validators.hpp:210
option_value_type value_type
Type of values that are tested by validator.
Definition: validators.hpp:177
virtual ~input_file_validator()=default
Virtual destructor.
virtual void operator()(std::filesystem::path const &dir) const override
Tests whether path is writable.
Definition: validators.hpp:646
arithmetic_range_validator(value_type const min_, value_type const max_)
The constructor.
Definition: validators.hpp:117
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition: validators.hpp:598
Argument parser exception that is thrown whenever there is an error while parsing the command line ar...
Definition: exceptions.hpp:37
An abstract base class for the file and directory validators.
Definition: validators.hpp:245
A validator that checks whether a number is inside a given range.
Definition: validators.hpp:107
regex_validator(std::string const &pattern_)
Constructing from a vector.
Definition: validators.hpp:712
virtual void operator()(std::filesystem::path const &file) const override
Tests whether path is does not already exists and is writable.
Definition: validators.hpp:502
T create_directory(T... args)
T find(T... args)
double value_type
The type of value that this validator invoked upon.
Definition: validators.hpp:111
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition: validators.hpp:525
output_directory_validator()=default
Defaulted.
output_file_validator()=default
Defaulted.
Subsumes std::Movable, std::CopyConstructible, and requires that the type be std::Assignable bool fro...
T begin(T... args)
void validate_filename(std::filesystem::path const &path) const
Validates the given filename path based on the specified extensions.
Definition: validators.hpp:301
Provides various type traits on generic types.
auto operator|(validator1_type &&vali1, validator2_type &&vali2)
Enables the chaining of validators.
Definition: validators.hpp:889
void operator()(range_type const &range) const
Tests whether every element in range lies inside [min, max].
Definition: validators.hpp:141
Adaptations of algorithms from the Ranges TS.
Provides seqan3::view::to_lower.
output_file_validator & operator=(output_file_validator const &)=default
Defaulted.
A validator that checks whether a value is inside a list of valid values.
Definition: validators.hpp:173
Specifies whether the given callable is invocable with the given arguments.
void validate_writeability(std::filesystem::path const &path) const
Checks if the given path is writable.
Definition: validators.hpp:365
::ranges::find_if find_if
Alias for ranges::find_if. Returns the first element in the range for which the predicate returns tru...
Definition: algorithm:69
A validator that checks if a given path is a valid input file.
Definition: validators.hpp:399
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition: validators.hpp:743
Provides seqan3::detail::safe_filesystem_entry.
T rethrow_exception(T... args)
Provides seqan3::view::drop.
The concept std::Same<T, U> is satisfied if and only if T and U denote the same type.
Adaptations of concepts from the standard library.
Exposes the value_type of another type.
Definition: pre.hpp:41
virtual void operator()(std::filesystem::path const &file) const override
Tests whether path is an existing regular file and is readable.
Definition: validators.hpp:427
T for_each(T... args)
void validate_readability(std::filesystem::path const &path) const
Checks if the given path is readable.
Definition: validators.hpp:337
A type that satisfies std::is_arithmetic_v<t>.
output_directory_validator & operator=(output_directory_validator const &)=default
Defaulted.
void operator()(range_type const &range) const
Tests whether every element in range lies inside values.
Definition: validators.hpp:204
using value_type
The type of value on which the validator is called on.
file_validator_base & operator=(file_validator_base const &)=default
Defaulted.
A validator that checks if a given path is a valid input directory.
Definition: validators.hpp:546
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
Definition: validators.hpp:451
This header includes C++17 filesystem support and imports it into namespace seqan3::filesystem (indep...
T is_directory(T... args)