22 #include <type_traits>
109 template <
typename stream_type,
110 typename seq_legal_alph_type,
111 bool structured_seq_combined,
115 typename structure_type,
116 typename energy_type,
118 typename comment_type,
119 typename offset_type>
125 structure_type & structure,
126 energy_type & energy,
127 react_type & SEQAN3_DOXYGEN_ONLY(react),
128 react_type & SEQAN3_DOXYGEN_ONLY(react_err),
129 comment_type & SEQAN3_DOXYGEN_ONLY(comment),
130 offset_type & SEQAN3_DOXYGEN_ONLY(offset))
132 auto stream_view = detail::istreambuf(stream);
135 auto constexpr is_id = is_char<'>
'>;
136 if (is_id(*begin(stream_view)))
138 if constexpr (!detail::decays_to_ignore_v<id_type>)
140 if (options.truncate_ids)
142 std::ranges::copy(stream_view | std::views::drop_while(is_id || is_blank) // skip leading >
143 | detail::take_until_or_throw(is_cntrl || is_blank)
144 | views::char_to<std::ranges::range_value_t<id_type>>,
145 std::cpp20::back_inserter(id));
146 detail::consume(stream_view | detail::take_line_or_throw);
150 std::ranges::copy(stream_view | std::views::drop_while(is_id || is_blank) // skip leading >
151 | detail::take_line_or_throw
152 | views::char_to<std::ranges::range_value_t<id_type>>,
153 std::cpp20::back_inserter(id));
158 detail::consume(stream_view | detail::take_line_or_throw);
161 else if constexpr (!detail::decays_to_ignore_v<id_type>)
163 auto constexpr is_legal_seq = char_is_valid_for<seq_legal_alph_type>;
164 if (!is_legal_seq(*begin(stream_view))) // if neither id nor seq found: throw
166 throw parse_error{std::string{"Expected to be on beginning of ID or sequence, but "} +
167 is_id.msg + " and char_is_valid_for<" +
168 detail::type_name_as_string<seq_legal_alph_type> + ">" +
169 " evaluated to false on " + detail::make_printable(*begin(stream_view))};
174 if constexpr (!detail::decays_to_ignore_v<seq_type>)
176 auto constexpr is_legal_seq = char_is_valid_for<seq_legal_alph_type>;
177 std::ranges::copy(stream_view | detail::take_line_or_throw // until end of line
178 | std::views::filter(!(is_space || is_digit)) // ignore whitespace and numbers
179 | std::views::transform([is_legal_seq](char const c)
181 if (!is_legal_seq(c)) // enforce legal alphabet
183 throw parse_error{std::string{"Encountered an unexpected letter: "} +
184 "char_is_valid_for<" +
185 detail::type_name_as_string<seq_legal_alph_type> +
186 "> evaluated to false on " +
187 detail::make_printable(c)};
191 | views::char_to<std::ranges::range_value_t<seq_type>>, // convert to actual target alphabet
192 std::cpp20::back_inserter(seq));
196 detail::consume(stream_view | detail::take_line_or_throw);
199 // READ STRUCTURE (if present)
200 [[maybe_unused]] int64_t structure_length{};
201 if constexpr (!detail::decays_to_ignore_v<structure_type>)
203 if constexpr (structured_seq_combined)
205 assert(std::addressof(seq) == std::addressof(structure));
206 using alph_type = typename std::ranges::range_value_t<structure_type>::structure_alphabet_type;
207 // We need the structure_length parameter to count the length of the structure while reading
208 // because we cannot infer it from the (already resized) structure_seq object.
209 auto res = std::ranges::copy(read_structure<alph_type>(stream_view), std::ranges::begin(structure));
210 structure_length = std::ranges::distance(std::ranges::begin(structure), res.out);
212 if constexpr (!detail::decays_to_ignore_v<bpp_type>)
213 detail::bpp_from_rna_structure<alph_type>(bpp, structure);
217 using alph_type = std::ranges::range_value_t<structure_type>;
218 std::ranges::copy(read_structure<alph_type>(stream_view), std::cpp20::back_inserter(structure));
219 structure_length = std::ranges::distance(structure);
221 if constexpr (!detail::decays_to_ignore_v<bpp_type>)
222 detail::bpp_from_rna_structure<alph_type>(bpp, structure);
225 else if constexpr (!detail::decays_to_ignore_v<bpp_type>)
227 detail::bpp_from_rna_structure<wuss51>(bpp, read_structure<wuss51>(stream_view));
228 structure_length = std::ranges::distance(bpp);
232 detail::consume(stream_view | detail::take_until(is_space)); // until whitespace
235 if constexpr (!detail::decays_to_ignore_v<seq_type> &&
236 !(detail::decays_to_ignore_v<structure_type> && detail::decays_to_ignore_v<bpp_type>))
238 if (std::ranges::distance(seq) != structure_length)
239 throw parse_error{"Found sequence and associated structure of different length."};
242 // READ ENERGY (if present)
243 if constexpr (!detail::decays_to_ignore_v<energy_type>)
245 std::string e_str = stream_view | detail::take_line
246 | std::views::filter(!(is_space || is_char<'(
'> || is_char<')
'>))
247 | views::to<std::string>;
250 size_t num_processed;
251 energy = std::stod(e_str, &num_processed);
252 if (num_processed != e_str.size()) // [[unlikely]]
254 throw parse_error{std::string{"Failed to parse energy value '"} + e_str + "'."};
260 detail::consume(stream_view | detail::take_line);
262 detail::consume(stream_view | detail::take_until(!is_space));
266 template <typename stream_type, // constraints checked by file
267 typename seq_type, // other constraints checked inside function
270 typename structure_type,
271 typename energy_type,
273 typename comment_type,
274 typename offset_type>
275 void write_structure_record(stream_type & stream,
276 structure_file_output_options const & options,
279 bpp_type && SEQAN3_DOXYGEN_ONLY(bpp),
280 structure_type && structure,
281 energy_type && energy,
282 react_type && SEQAN3_DOXYGEN_ONLY(react),
283 react_type && SEQAN3_DOXYGEN_ONLY(react_err),
284 comment_type && SEQAN3_DOXYGEN_ONLY(comment),
285 offset_type && SEQAN3_DOXYGEN_ONLY(offset))
287 std::cpp20::ostreambuf_iterator stream_it{stream};
289 // WRITE ID (optional)
290 if constexpr (!detail::decays_to_ignore_v<id_type>)
292 if (!std::ranges::empty(id))
296 std::ranges::copy(id, stream_it);
297 detail::write_eol(stream_it, options.add_carriage_return);
302 if constexpr (!detail::decays_to_ignore_v<seq_type>)
304 if (std::ranges::empty(seq)) //[[unlikely]]
305 throw std::runtime_error{"The SEQ field may not be empty when writing Vienna files."};
307 std::ranges::copy(seq | views::to_char, stream_it);
308 detail::write_eol(stream_it, options.add_carriage_return);
312 throw std::logic_error{"The SEQ and STRUCTURED_SEQ fields may not both be set to ignore "
313 "when writing Vienna files."};
316 // WRITE STRUCTURE (optional)
317 if constexpr (!detail::decays_to_ignore_v<structure_type>)
319 if (!std::ranges::empty(structure))
320 std::ranges::copy(structure | views::to_char, stream_it);
322 // WRITE ENERGY (optional)
323 if constexpr (!detail::decays_to_ignore_v<energy_type>)
327 // TODO(joergi-w) enable the following when std::to_chars is implemented for float types
328 // auto [endptr, ec] = std::to_chars(str.data(),
329 // str.data() + str.size(),
331 // std::chars_format::fixed,
332 // options.precision);
333 // if (ec == std::errc())
334 // std::ranges::copy(str.data(), endptr, stream_it);
336 // throw std::runtime_error{"The energy could not be transformed into a string."};
341 std::array<char, 100> str;
342 int len = std::snprintf(str.data(), 100, "%.*f", options.precision, energy);
343 if (len < 0 || len >= 100)
344 throw std::runtime_error{"The energy could not be transformed into a string."};
345 std::ranges::copy(str.data(), str.data() + len, stream_it);
350 detail::write_eol(stream_it, options.add_carriage_return);
352 else if constexpr (!detail::decays_to_ignore_v<energy_type>)
354 throw std::logic_error{"The ENERGY field cannot be written to a Vienna file without providing STRUCTURE."};
365 template <typename alph_type, typename stream_view_type>
366 auto read_structure(stream_view_type & stream_view)
368 auto constexpr is_legal_structure = char_is_valid_for<alph_type>;
369 return stream_view | detail::take_until(is_space) // until whitespace
370 | std::views::transform([is_legal_structure](char const c)
372 if (!is_legal_structure(c))
375 std::string{"Encountered an unexpected letter: char_is_valid_for<"} +
376 detail::type_name_as_string<alph_type> +
377 "> evaluated to false on " + detail::make_printable(c)};
380 }) // enforce legal alphabet
381 | views::char_to<alph_type>; // convert to actual target alphabet
385 } // namespace seqan3::detail
Adaptations of algorithms from the Ranges TS.
Core alphabet concept and free function/type trait wrappers.
Provides seqan3::views::char_to.
Provides seqan3::views::to_char.
Provides alphabet adaptations for standard char types.
Provides various utility functions.
Provides various transformation traits used by the range module.
Provides various utility functions.
Provides seqan3::detail::istreambuf.
The main SeqAn3 namespace.
Definition: aligned_sequence_concept.hpp:29
SeqAn specific customisations in the standard namespace.
Adaptations of concepts from the Ranges TS.
Provides seqan3::structure_file_output_options.
Provides seqan3::detail::take_line and seqan3::detail::take_line_or_throw.
Provides seqan3::views::take_until and seqan3::views::take_until_or_throw.
[DEPRECATED] Provides seqan3::views::take.
Provides traits to inspect some information of a type, for example its name.
Provides character predicates for tokenisation.
Provides seqan3::views::to.
Provides the WUSS format for RNA structure.