20 #include <type_traits>
107 template <
typename stream_type,
108 typename seq_legal_alph_type,
109 bool structured_seq_combined,
113 typename structure_type,
114 typename energy_type,
116 typename comment_type,
117 typename offset_type>
123 structure_type & structure,
124 energy_type & energy,
125 react_type & SEQAN3_DOXYGEN_ONLY(react),
126 react_type & SEQAN3_DOXYGEN_ONLY(react_err),
127 comment_type & SEQAN3_DOXYGEN_ONLY(comment),
128 offset_type & SEQAN3_DOXYGEN_ONLY(offset))
133 auto constexpr is_id = is_char<'>
'>;
134 if (is_id(*begin(stream_view)))
136 if constexpr (!detail::decays_to_ignore_v<id_type>)
138 if (options.truncate_ids)
140 std::ranges::copy(stream_view | std::views::drop_while(is_id || is_blank) // skip leading >
141 | views::take_until_or_throw(is_cntrl || is_blank)
142 | views::char_to<std::ranges::range_value_t<id_type>>,
143 std::cpp20::back_inserter(id));
144 detail::consume(stream_view | views::take_line_or_throw);
148 std::ranges::copy(stream_view | std::views::drop_while(is_id || is_blank) // skip leading >
149 | views::take_line_or_throw
150 | views::char_to<std::ranges::range_value_t<id_type>>,
151 std::cpp20::back_inserter(id));
156 detail::consume(stream_view | views::take_line_or_throw);
159 else if constexpr (!detail::decays_to_ignore_v<id_type>)
161 auto constexpr is_legal_seq = is_in_alphabet<seq_legal_alph_type>;
162 if (!is_legal_seq(*begin(stream_view))) // if neither id nor seq found: throw
164 throw parse_error{std::string{"Expected to be on beginning of ID or sequence, but "} +
165 is_id.msg + " and " + is_legal_seq.msg +
166 " evaluated to false on " + detail::make_printable(*begin(stream_view))};
171 if constexpr (!detail::decays_to_ignore_v<seq_type>)
173 auto constexpr is_legal_seq = is_in_alphabet<seq_legal_alph_type>;
174 std::ranges::copy(stream_view | views::take_line_or_throw // until end of line
175 | std::views::filter(!(is_space || is_digit)) // ignore whitespace and numbers
176 | std::views::transform([is_legal_seq](char const c)
178 if (!is_legal_seq(c)) // enforce legal alphabet
180 throw parse_error{std::string{"Encountered an unexpected letter: "} +
182 " evaluated to false on " +
183 detail::make_printable(c)};
187 | views::char_to<std::ranges::range_value_t<seq_type>>, // convert to actual target alphabet
188 std::cpp20::back_inserter(seq));
192 detail::consume(stream_view | views::take_line_or_throw);
195 // READ STRUCTURE (if present)
196 [[maybe_unused]] int64_t structure_length{};
197 if constexpr (!detail::decays_to_ignore_v<structure_type>)
199 if constexpr (structured_seq_combined)
201 assert(std::addressof(seq) == std::addressof(structure));
202 using alph_type = typename std::ranges::range_value_t<structure_type>::structure_alphabet_type;
203 // We need the structure_length parameter to count the length of the structure while reading
204 // because we cannot infer it from the (already resized) structure_seq object.
205 auto res = std::ranges::copy(read_structure<alph_type>(stream_view), std::ranges::begin(structure));
206 structure_length = std::ranges::distance(std::ranges::begin(structure), res.out);
208 if constexpr (!detail::decays_to_ignore_v<bpp_type>)
209 detail::bpp_from_rna_structure<alph_type>(bpp, structure);
213 using alph_type = std::ranges::range_value_t<structure_type>;
214 std::ranges::copy(read_structure<alph_type>(stream_view), std::cpp20::back_inserter(structure));
215 structure_length = std::ranges::distance(structure);
217 if constexpr (!detail::decays_to_ignore_v<bpp_type>)
218 detail::bpp_from_rna_structure<alph_type>(bpp, structure);
221 else if constexpr (!detail::decays_to_ignore_v<bpp_type>)
223 detail::bpp_from_rna_structure<wuss51>(bpp, read_structure<wuss51>(stream_view));
224 structure_length = std::ranges::distance(bpp);
228 detail::consume(stream_view | views::take_until(is_space)); // until whitespace
231 if constexpr (!detail::decays_to_ignore_v<seq_type> &&
232 !(detail::decays_to_ignore_v<structure_type> && detail::decays_to_ignore_v<bpp_type>))
234 if (std::ranges::distance(seq) != structure_length)
235 throw parse_error{"Found sequence and associated structure of different length."};
238 // READ ENERGY (if present)
239 if constexpr (!detail::decays_to_ignore_v<energy_type>)
241 std::string e_str = stream_view | views::take_line
242 | std::views::filter(!(is_space || is_char<'(
'> || is_char<')
'>))
243 | views::to<std::string>;
246 size_t num_processed;
247 energy = std::stod(e_str, &num_processed);
248 if (num_processed != e_str.size()) // [[unlikely]]
250 throw parse_error{std::string{"Failed to parse energy value '"} + e_str + "'."};
256 detail::consume(stream_view | views::take_line);
258 detail::consume(stream_view | views::take_until(!is_space));
262 template <typename stream_type, // constraints checked by file
263 typename seq_type, // other constraints checked inside function
266 typename structure_type,
267 typename energy_type,
269 typename comment_type,
270 typename offset_type>
271 void write_structure_record(stream_type & stream,
272 structure_file_output_options const & options,
275 bpp_type && SEQAN3_DOXYGEN_ONLY(bpp),
276 structure_type && structure,
277 energy_type && energy,
278 react_type && SEQAN3_DOXYGEN_ONLY(react),
279 react_type && SEQAN3_DOXYGEN_ONLY(react_err),
280 comment_type && SEQAN3_DOXYGEN_ONLY(comment),
281 offset_type && SEQAN3_DOXYGEN_ONLY(offset))
283 std::cpp20::ostreambuf_iterator stream_it{stream};
285 // WRITE ID (optional)
286 if constexpr (!detail::decays_to_ignore_v<id_type>)
288 if (!std::ranges::empty(id))
292 std::ranges::copy(id, stream_it);
293 detail::write_eol(stream_it, options.add_carriage_return);
298 if constexpr (!detail::decays_to_ignore_v<seq_type>)
300 if (std::ranges::empty(seq)) //[[unlikely]]
301 throw std::runtime_error{"The SEQ field may not be empty when writing Vienna files."};
303 std::ranges::copy(seq | views::to_char, stream_it);
304 detail::write_eol(stream_it, options.add_carriage_return);
308 throw std::logic_error{"The SEQ and STRUCTURED_SEQ fields may not both be set to ignore "
309 "when writing Vienna files."};
312 // WRITE STRUCTURE (optional)
313 if constexpr (!detail::decays_to_ignore_v<structure_type>)
315 if (!std::ranges::empty(structure))
316 std::ranges::copy(structure | views::to_char, stream_it);
318 // WRITE ENERGY (optional)
319 if constexpr (!detail::decays_to_ignore_v<energy_type>)
323 // TODO(joergi-w) enable the following when std::to_chars is implemented for float types
324 // auto [endptr, ec] = std::to_chars(str.data(),
325 // str.data() + str.size(),
327 // std::chars_format::fixed,
328 // options.precision);
329 // if (ec == std::errc())
330 // std::ranges::copy(str.data(), endptr, stream_it);
332 // throw std::runtime_error{"The energy could not be transformed into a string."};
337 std::array<char, 100> str;
338 int len = std::snprintf(str.data(), 100, "%.*f", options.precision, energy);
339 if (len < 0 || len >= 100)
340 throw std::runtime_error{"The energy could not be transformed into a string."};
341 std::ranges::copy(str.data(), str.data() + len, stream_it);
346 detail::write_eol(stream_it, options.add_carriage_return);
348 else if constexpr (!detail::decays_to_ignore_v<energy_type>)
350 throw std::logic_error{"The ENERGY field cannot be written to a Vienna file without providing STRUCTURE."};
361 template <typename alph_type, typename stream_view_type>
362 auto read_structure(stream_view_type & stream_view)
364 auto constexpr is_legal_structure = is_in_alphabet<alph_type>;
365 return stream_view | views::take_until(is_space) // until whitespace
366 | std::views::transform([is_legal_structure](char const c)
368 if (!is_legal_structure(c))
371 std::string{"Encountered an unexpected letter: "} +
372 is_legal_structure.msg +
373 " evaluated to false on " + detail::make_printable(c)};
376 }) // enforce legal alphabet
377 | views::char_to<alph_type>; // convert to actual target alphabet
381 } // namespace seqan3::detail
Adaptations of algorithms from the Ranges TS.
Provides alphabet adaptations for standard char types.
Provides seqan3::views::char_to.
constexpr sequenced_policy seq
Global execution policy object for sequenced execution policy.
Definition: execution.hpp:54
constexpr auto istreambuf
A view factory that returns a view over the stream buffer of an input stream.
Definition: istreambuf.hpp:113
Provides various utility functions.
Provides seqan3::fast_istreambuf_iterator and seqan3::fast_ostreambuf_iterator, as well as,...
Provides seqan3::views::istreambuf.
The main SeqAn3 namespace.
Definition: aligned_sequence_concept.hpp:29
SeqAn specific customisations in the standard namespace.
Provides character predicates for tokenisation.
Provides various utility functions.
Provides various transformation traits used by the range module.
Adaptations of concepts from the Ranges TS.
Provides seqan3::structure_file_output_options.
Provides seqan3::views::take.
Provides seqan3::views::take_line and seqan3::views::take_line_or_throw.
Provides seqan3::views::take_until and seqan3::views::take_until_or_throw.
Provides seqan3::views::to.
Provides seqan3::views::to_char.
Provides the WUSS format for RNA structure.