diff --git a/include/seqan3/alphabet/views/char_strictly_to.hpp b/include/seqan3/alphabet/views/char_strictly_to.hpp new file mode 100644 index 00000000000..29d1441309d --- /dev/null +++ b/include/seqan3/alphabet/views/char_strictly_to.hpp @@ -0,0 +1,80 @@ +// ----------------------------------------------------------------------------------------------------- +// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin +// Copyright (c) 2016-2021, Knut Reinert & MPI für molekulare Genetik +// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License +// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md +// ----------------------------------------------------------------------------------------------------- + +/*!\file + * \brief Provides seqan3::views::char_strictly_to. + * \author Hannes Hauswedell + */ + +#pragma once + +#include + +#include +#include +#include + +namespace seqan3::views +{ +/*!\brief A view over an alphabet, given a range of characters. + * \tparam urng_t The type of the range being processed. See below for requirements. [template parameter is + * omitted in pipe notation] + * \tparam alphabet_t The alphabet to convert to; must satisfy seqan3::alphabet. + * \param[in] urange The range being processed. [parameter is omitted in pipe notation] + * \returns A range of converted elements. See below for the properties of the returned range. + * \throws seqan3::invalid_char_assignment if an invalid character is encountered. + * \ingroup alphabet_views + * + * \details + * + * \header_file{seqan3/alphabet/views/char_strictly_to.hpp} + * + * This view differs from seqan3::views::chars_to in that it throws an exception if an invalid character conversion + * happens. See seqan3::char_strictly_to for more details. + * + * ### View properties + * + * This view is a **deep view**. Given a range-of-range as input (as opposed to just a range), it will apply + * the transformation on the innermost range (instead of the outermost range). + * + * | Concepts and traits | `urng_t` (underlying range type) | `rrng_t` (returned range type) | + * |----------------------------------|:-------------------------------------:|:------------------------------:| + * | std::ranges::input_range | *required* | *preserved* | + * | std::ranges::forward_range | | *preserved* | + * | std::ranges::bidirectional_range | | *preserved* | + * | std::ranges::random_access_range | | *preserved* | + * | std::ranges::contiguous_range | | *lost* | + * | | | | + * | std::ranges::viewable_range | *required* | *guaranteed* | + * | std::ranges::view | | *guaranteed* | + * | std::ranges::sized_range | | *preserved* | + * | std::ranges::common_range | | *preserved* | + * | std::ranges::output_range | | *lost* | + * | std::semiregular | | *preserved* | + * | seqan3::const_iterable_range | | *preserved* | + * | | | | + * | std::ranges::range_reference_t | seqan3::alphabet_char_t | `alphabet_t` | + * + * See the \link views views submodule documentation \endlink for detailed descriptions of the view properties. + * + * ### Example + * + * \include test/snippet/alphabet/views/char_strictly_to.cpp + * \hideinitializer + * + * \experimentalapi{Experimental since version 3.2.} + */ +template +inline auto const char_strictly_to = deep{std::views::transform([] (auto const in) +{ + static_assert(std::common_reference_with>, + "The innermost value type must have a common reference to underlying char type of alphabet_type."); + // call element-wise assign_char from the alphabet + return assign_char_strictly_to(in, alphabet_type{}); +})}; + +} // namespace seqan3::views diff --git a/test/snippet/alphabet/views/char_strictly_to.cpp b/test/snippet/alphabet/views/char_strictly_to.cpp new file mode 100644 index 00000000000..8d7ef460bec --- /dev/null +++ b/test/snippet/alphabet/views/char_strictly_to.cpp @@ -0,0 +1,16 @@ +#include +#include +#include + +int main() +{ + std::string_view str{"ACTTTGATAN"}; + try + { + seqan3::debug_stream << (str | seqan3::views::char_strictly_to); // ACTTTGATA + } + catch (seqan3::invalid_char_assignment const &) + { + seqan3::debug_stream << "\n[ERROR] Invalid char!\n"; // Will throw on parsing 'N' + } +} diff --git a/test/snippet/alphabet/views/char_strictly_to.err b/test/snippet/alphabet/views/char_strictly_to.err new file mode 100644 index 00000000000..bab43e9fc19 --- /dev/null +++ b/test/snippet/alphabet/views/char_strictly_to.err @@ -0,0 +1,2 @@ +ACTTTGATA +[ERROR] Invalid char! diff --git a/test/unit/alphabet/views/CMakeLists.txt b/test/unit/alphabet/views/CMakeLists.txt index 72a12d75fe8..4919817fda6 100644 --- a/test/unit/alphabet/views/CMakeLists.txt +++ b/test/unit/alphabet/views/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectories() seqan3_test(char_to_test.cpp) +seqan3_test(char_strictly_to_test.cpp) seqan3_test(complement_test.cpp) seqan3_test(rank_to_test.cpp) seqan3_test(to_char_test.cpp) diff --git a/test/unit/alphabet/views/char_strictly_to_test.cpp b/test/unit/alphabet/views/char_strictly_to_test.cpp new file mode 100644 index 00000000000..66852109eb9 --- /dev/null +++ b/test/unit/alphabet/views/char_strictly_to_test.cpp @@ -0,0 +1,81 @@ +// ----------------------------------------------------------------------------------------------------- +// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin +// Copyright (c) 2016-2021, Knut Reinert & MPI für molekulare Genetik +// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License +// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md +// ----------------------------------------------------------------------------------------------------- + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +using seqan3::operator""_dna5; + +TEST(view_char_strictly_to, basic) +{ + std::string vec{"ACTTTGATA"}; + seqan3::dna5_vector cmp{"ACTTTGATA"_dna5}; + + // pipe notation + EXPECT_RANGE_EQ(cmp, vec | seqan3::views::char_strictly_to); + + // function notation + EXPECT_RANGE_EQ(cmp, seqan3::views::char_strictly_to(vec)); + + // combinability + seqan3::dna5_vector cmp2{"ATAGTTTCA"_dna5}; + EXPECT_RANGE_EQ(cmp2, vec | seqan3::views::char_strictly_to | std::views::reverse); +} + +TEST(view_char_strictly_to, deep_view) +{ + std::vector foo{"ACGTA", "TGCAT"}; + + auto v = foo | seqan3::views::char_strictly_to; + + ASSERT_EQ(std::ranges::size(v), 2u); + EXPECT_RANGE_EQ(v[0], "ACGTA"_dna5); + EXPECT_RANGE_EQ(v[1], "TGCAT"_dna5); +} + +TEST(view_char_strictly_to, concepts) +{ + std::string vec{"ACTTTGATA"}; + EXPECT_TRUE(std::ranges::input_range); + EXPECT_TRUE(std::ranges::forward_range); + EXPECT_TRUE(std::ranges::bidirectional_range); + EXPECT_TRUE(std::ranges::random_access_range); + EXPECT_FALSE(std::ranges::view); + EXPECT_TRUE(std::ranges::sized_range); + EXPECT_TRUE(std::ranges::common_range); + EXPECT_TRUE(seqan3::const_iterable_range); + EXPECT_TRUE((std::ranges::output_range)); + + auto v1 = vec | seqan3::views::char_strictly_to; + EXPECT_TRUE(std::ranges::input_range); + EXPECT_TRUE(std::ranges::forward_range); + EXPECT_TRUE(std::ranges::bidirectional_range); + EXPECT_TRUE(std::ranges::random_access_range); + EXPECT_TRUE(std::ranges::view); + EXPECT_TRUE(std::ranges::sized_range); + EXPECT_TRUE(std::ranges::common_range); + EXPECT_TRUE(seqan3::const_iterable_range); + EXPECT_FALSE((std::ranges::output_range)); + EXPECT_FALSE((std::ranges::output_range)); +} + +TEST(view_char_strictly_to, exception) +{ + std::string foo = "ACGPTA"; + + auto v = foo | seqan3::views::char_strictly_to; + EXPECT_THROW((std::ranges::equal(v, "ACGNTA"_dna5)), seqan3::invalid_char_assignment); +}