SeqAn3 3.2.0-rc.1
The Modern C++ library for sequence analysis.
format_sam.hpp
Go to the documentation of this file.
1// -----------------------------------------------------------------------------------------------------
2// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
3// Copyright (c) 2016-2021, 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 <iterator>
16#include <seqan3/std/ranges>
17#include <string>
18#include <vector>
19
38
39namespace seqan3
40{
41
117{
118public:
122 // construction cannot be noexcept because this class has a std::string variable as a quality string buffer.
123 format_sam() = default;
124 format_sam(format_sam const &) = default;
125 format_sam & operator=(format_sam const &) = default;
126 format_sam(format_sam &&) = default;
128 ~format_sam() = default;
129
131
134 {
135 { "sam" },
136 };
137
138protected:
139 template <typename stream_type, // constraints checked by file
140 typename seq_legal_alph_type,
141 typename stream_pos_type,
142 typename seq_type, // other constraints checked inside function
143 typename id_type,
144 typename qual_type>
145 void read_sequence_record(stream_type & stream,
147 stream_pos_type & position_buffer,
148 seq_type & sequence,
149 id_type & id,
150 qual_type & qualities);
151
152 template <typename stream_type, // constraints checked by file
153 typename seq_type, // other constraints checked inside function
154 typename id_type,
155 typename qual_type>
156 void write_sequence_record(stream_type & stream,
157 sequence_file_output_options const & SEQAN3_DOXYGEN_ONLY(options),
158 seq_type && sequence,
159 id_type && id,
160 qual_type && qualities);
161
162 template <typename stream_type, // constraints checked by file
163 typename seq_legal_alph_type,
164 typename ref_seqs_type,
165 typename ref_ids_type,
166 typename stream_pos_type,
167 typename seq_type,
168 typename id_type,
169 typename offset_type,
170 typename ref_seq_type,
171 typename ref_id_type,
172 typename ref_offset_type,
173 typename align_type,
174 typename cigar_type,
175 typename flag_type,
176 typename mapq_type,
177 typename qual_type,
178 typename mate_type,
179 typename tag_dict_type,
180 typename e_value_type,
181 typename bit_score_type>
182 void read_alignment_record(stream_type & stream,
183 sam_file_input_options<seq_legal_alph_type> const & SEQAN3_DOXYGEN_ONLY(options),
184 ref_seqs_type & ref_seqs,
186 stream_pos_type & position_buffer,
187 seq_type & seq,
188 qual_type & qual,
189 id_type & id,
190 offset_type & offset,
191 ref_seq_type & SEQAN3_DOXYGEN_ONLY(ref_seq),
192 ref_id_type & ref_id,
193 ref_offset_type & ref_offset,
194 align_type & align,
195 cigar_type & cigar_vector,
196 flag_type & flag,
197 mapq_type & mapq,
198 mate_type & mate,
199 tag_dict_type & tag_dict,
200 e_value_type & SEQAN3_DOXYGEN_ONLY(e_value),
201 bit_score_type & SEQAN3_DOXYGEN_ONLY(bit_score));
202
203 template <typename stream_type,
204 typename header_type,
205 typename seq_type,
206 typename id_type,
207 typename ref_seq_type,
208 typename ref_id_type,
209 typename align_type,
210 typename qual_type,
211 typename mate_type,
212 typename tag_dict_type,
213 typename e_value_type,
214 typename bit_score_type>
215 void write_alignment_record(stream_type & stream,
216 sam_file_output_options const & options,
217 header_type && header,
218 seq_type && seq,
219 qual_type && qual,
220 id_type && id,
221 int32_t const offset,
222 ref_seq_type && SEQAN3_DOXYGEN_ONLY(ref_seq),
223 ref_id_type && ref_id,
224 std::optional<int32_t> ref_offset,
225 align_type && align,
226 std::vector<cigar> const & cigar_vector,
227 sam_flag const flag,
228 uint8_t const mapq,
229 mate_type && mate,
230 tag_dict_type && tag_dict,
231 e_value_type && SEQAN3_DOXYGEN_ONLY(e_value),
232 bit_score_type && SEQAN3_DOXYGEN_ONLY(bit_score));
233
234private:
237
239 static constexpr std::string_view dummy{};
240
243
246
249 {
250 return dummy;
251 }
252
254 template <typename t>
255 decltype(auto) default_or(t && v) const noexcept
256 {
257 return std::forward<t>(v);
258 }
259
260 template <typename stream_view_type, arithmetic value_type>
262 stream_view_type && stream_view,
263 value_type value);
264
265 template <typename stream_view_type>
267 stream_view_type && stream_view);
268
269 template <typename stream_view_type>
270 void read_sam_dict_field(stream_view_type && stream_view, sam_tag_dictionary & target);
271
272 template <typename stream_it_t, std::ranges::forward_range field_type>
273 void write_range_or_asterisk(stream_it_t & stream_it, field_type && field_value);
274
275 template <typename stream_it_t>
276 void write_range_or_asterisk(stream_it_t & stream_it, char const * const field_value);
277
278 template <typename stream_it_t>
279 void write_tag_fields(stream_it_t & stream, sam_tag_dictionary const & tag_dict, char const separator);
280};
281
283template <typename stream_type, // constraints checked by file
284 typename seq_legal_alph_type,
285 typename stream_pos_type,
286 typename seq_type, // other constraints checked inside function
287 typename id_type,
288 typename qual_type>
289inline void format_sam::read_sequence_record(stream_type & stream,
291 stream_pos_type & position_buffer,
292 seq_type & sequence,
293 id_type & id,
294 qual_type & qualities)
295{
297
298 {
299 read_alignment_record(stream, align_options, std::ignore, default_header, position_buffer, sequence, qualities,
300 id, std::ignore, std::ignore, std::ignore, std::ignore, std::ignore, std::ignore,
301 std::ignore, std::ignore, std::ignore, std::ignore, std::ignore, std::ignore);
302 }
303
304 if constexpr (!detail::decays_to_ignore_v<seq_type>)
305 if (std::ranges::distance(sequence) == 0)
306 throw parse_error{"The sequence information must not be empty."};
307 if constexpr (!detail::decays_to_ignore_v<id_type>)
308 if (std::ranges::distance(id) == 0)
309 throw parse_error{"The id information must not be empty."};
310
311 if (options.truncate_ids)
312 id = id | detail::take_until_and_consume(is_space) | views::to<id_type>;
313}
314
316template <typename stream_type, // constraints checked by file
317 typename seq_type, // other constraints checked inside function
318 typename id_type,
319 typename qual_type>
320inline void format_sam::write_sequence_record(stream_type & stream,
321 sequence_file_output_options const & SEQAN3_DOXYGEN_ONLY(options),
322 seq_type && sequence,
323 id_type && id,
324 qual_type && qualities)
325{
327 using default_mate_t = std::tuple<std::string_view, std::optional<int32_t>, int32_t>;
328
329 sam_file_output_options output_options;
330
332 output_options,
333 /*header*/ std::ignore,
334 /*seq*/ default_or(sequence),
335 /*qual*/ default_or(qualities),
336 /*id*/ default_or(id),
337 /*offset*/ 0,
338 /*ref_seq*/ std::string_view{},
339 /*ref_id*/ std::string_view{},
340 /*ref_offset*/ -1,
341 /*align*/ default_align_t{},
342 /*cigar_vector*/ std::vector<cigar>{},
343 /*flag*/ sam_flag::none,
344 /*mapq*/ 0,
345 /*mate*/ default_mate_t{},
346 /*tag_dict*/ sam_tag_dictionary{},
347 /*e_value*/ 0,
348 /*bit_score*/ 0);
349}
350
352template <typename stream_type, // constraints checked by file
353 typename seq_legal_alph_type,
354 typename ref_seqs_type,
355 typename ref_ids_type,
356 typename stream_pos_type,
357 typename seq_type,
358 typename id_type,
359 typename offset_type,
360 typename ref_seq_type,
361 typename ref_id_type,
362 typename ref_offset_type,
363 typename align_type,
364 typename cigar_type,
365 typename flag_type,
366 typename mapq_type,
367 typename qual_type,
368 typename mate_type,
369 typename tag_dict_type,
370 typename e_value_type,
371 typename bit_score_type>
372inline void format_sam::read_alignment_record(stream_type & stream,
373 sam_file_input_options<seq_legal_alph_type> const & SEQAN3_DOXYGEN_ONLY(options),
374 ref_seqs_type & ref_seqs,
376 stream_pos_type & position_buffer,
377 seq_type & seq,
378 qual_type & qual,
379 id_type & id,
380 offset_type & offset,
381 ref_seq_type & SEQAN3_DOXYGEN_ONLY(ref_seq),
382 ref_id_type & ref_id,
383 ref_offset_type & ref_offset,
384 align_type & align,
385 cigar_type & cigar_vector,
386 flag_type & flag,
387 mapq_type & mapq,
388 mate_type & mate,
389 tag_dict_type & tag_dict,
390 e_value_type & SEQAN3_DOXYGEN_ONLY(e_value),
391 bit_score_type & SEQAN3_DOXYGEN_ONLY(bit_score))
392{
393 static_assert(detail::decays_to_ignore_v<ref_offset_type> ||
394 detail::is_type_specialisation_of_v<ref_offset_type, std::optional>,
395 "The ref_offset must be a specialisation of std::optional.");
396
397 auto stream_view = detail::istreambuf(stream);
398 auto field_view = stream_view | detail::take_until_or_throw_and_consume(is_char<'\t'>);
399
400 // these variables need to be stored to compute the ALIGNMENT
401 int32_t ref_offset_tmp{};
402 std::ranges::range_value_t<decltype(header.ref_ids())> ref_id_tmp{};
403 [[maybe_unused]] int32_t offset_tmp{};
404 [[maybe_unused]] int32_t soft_clipping_end{};
405 [[maybe_unused]] std::vector<cigar> tmp_cigar_vector{};
406 [[maybe_unused]] int32_t ref_length{0}, seq_length{0}; // length of aligned part for ref and query
407
408 // Header
409 // -------------------------------------------------------------------------------------------------------------
410 if (is_char<'@'>(*std::ranges::begin(stream_view))) // we always read the header if present
411 {
412 read_header(stream_view, header, ref_seqs);
413
414 if (std::ranges::begin(stream_view) == std::ranges::end(stream_view)) // file has no records
415 return;
416 }
417
418 // Store the current file position in the buffer.
419 position_buffer = stream.tellg();
420
421 // Fields 1-5: ID FLAG REF_ID REF_OFFSET MAPQ
422 // -------------------------------------------------------------------------------------------------------------
423 if constexpr (!detail::decays_to_ignore_v<id_type>)
424 read_forward_range_field(field_view, id);
425 else
426 detail::consume(field_view);
427
428 uint16_t flag_integral{};
429 read_arithmetic_field(field_view, flag_integral);
430 flag = sam_flag{flag_integral};
431
432 read_forward_range_field(field_view, ref_id_tmp);
433 check_and_assign_ref_id(ref_id, ref_id_tmp, header, ref_seqs);
434
435 read_arithmetic_field(field_view, ref_offset_tmp);
436 --ref_offset_tmp; // SAM format is 1-based but SeqAn operates 0-based
437
438 if (ref_offset_tmp == -1)
439 ref_offset = std::nullopt; // indicates an unmapped read -> ref_offset is not set
440 else if (ref_offset_tmp > -1)
441 ref_offset = ref_offset_tmp;
442 else if (ref_offset_tmp < -1)
443 throw format_error{"No negative values are allowed for field::ref_offset."};
444
445 if constexpr (!detail::decays_to_ignore_v<mapq_type>)
446 read_arithmetic_field(field_view, mapq);
447 else
448 detail::consume(field_view);
449
450 // Field 6: CIGAR
451 // -------------------------------------------------------------------------------------------------------------
452 if constexpr (!detail::decays_to_ignore_v<align_type> || !detail::decays_to_ignore_v<cigar_type>)
453 {
454 if (!is_char<'*'>(*std::ranges::begin(stream_view))) // no cigar information given
455 {
456 std::tie(tmp_cigar_vector, ref_length, seq_length) = detail::parse_cigar(field_view);
457 transfer_soft_clipping_to(tmp_cigar_vector, offset_tmp, soft_clipping_end);
458 // the actual cigar_vector is swapped with tmp_cigar_vector at the end to avoid copying
459 }
460 else
461 {
462 ++std::ranges::begin(field_view); // skip '*'
463 }
464 }
465 else
466 {
467 detail::consume(field_view);
468 }
469
470 offset = offset_tmp;
471
472 // Field 7-9: (RNEXT PNEXT TLEN) = MATE
473 // -------------------------------------------------------------------------------------------------------------
474 if constexpr (!detail::decays_to_ignore_v<mate_type>)
475 {
476 std::ranges::range_value_t<decltype(header.ref_ids())> tmp_mate_ref_id{};
477 read_forward_range_field(field_view, tmp_mate_ref_id); // RNEXT
478
479 if (tmp_mate_ref_id == "=") // indicates "same as ref id"
480 {
481 if constexpr (!detail::decays_to_ignore_v<ref_id_type>)
482 get<0>(mate) = ref_id;
483 else
484 check_and_assign_ref_id(get<0>(mate), ref_id_tmp, header, ref_seqs);
485 }
486 else
487 {
488 check_and_assign_ref_id(get<0>(mate), tmp_mate_ref_id, header, ref_seqs);
489 }
490
491 int32_t tmp_pnext{};
492 read_arithmetic_field(field_view, tmp_pnext); // PNEXT
493
494 if (tmp_pnext > 0)
495 get<1>(mate) = --tmp_pnext; // SAM format is 1-based but SeqAn operates 0-based.
496 else if (tmp_pnext < 0)
497 throw format_error{"No negative values are allowed at the mate mapping position."};
498 // tmp_pnext == 0 indicates an unmapped mate -> do not fill std::optional get<1>(mate)
499
500 read_arithmetic_field(field_view, get<2>(mate)); // TLEN
501 }
502 else
503 {
504 for (size_t i = 0; i < 3u; ++i)
505 {
506 detail::consume(field_view);
507 }
508 }
509
510 // Field 10: Sequence
511 // -------------------------------------------------------------------------------------------------------------
512 if (!is_char<'*'>(*std::ranges::begin(stream_view))) // sequence information is given
513 {
514 auto constexpr is_legal_alph = char_is_valid_for<seq_legal_alph_type>;
515 auto seq_stream = field_view | std::views::transform([is_legal_alph] (char const c) // enforce legal alphabet
516 {
517 if (!is_legal_alph(c))
518 throw parse_error{std::string{"Encountered an unexpected letter: "} +
519 "char_is_valid_for<" +
520 detail::type_name_as_string<seq_legal_alph_type> +
521 "> evaluated to false on " +
523 return c;
524 });
525
526 if constexpr (detail::decays_to_ignore_v<seq_type>)
527 {
528 if constexpr (!detail::decays_to_ignore_v<align_type>)
529 {
531 "If you want to read ALIGNMENT but not SEQ, the alignment"
532 " object must store a sequence container at the second (query) position.");
533
534 if (!tmp_cigar_vector.empty()) // only parse alignment if cigar information was given
535 {
536
537 auto tmp_iter = std::ranges::begin(seq_stream);
538 std::ranges::advance(tmp_iter, offset_tmp);
539
540 for (; seq_length > 0; --seq_length) // seq_length is not needed anymore
541 {
542 get<1>(align).push_back(std::ranges::range_value_t<decltype(get<1>(align))>{}.assign_char(*tmp_iter));
543 ++tmp_iter;
544 }
545
546 std::ranges::advance(tmp_iter, soft_clipping_end);
547 }
548 else
549 {
550 get<1>(align) = std::remove_reference_t<decltype(get<1>(align))>{}; // empty container
551 }
552 }
553 else
554 {
555 detail::consume(seq_stream);
556 }
557 }
558 else
559 {
560 read_forward_range_field(seq_stream, seq);
561
562 if constexpr (!detail::decays_to_ignore_v<align_type>)
563 {
564 if (!tmp_cigar_vector.empty()) // if no alignment info is given, the field::alignment should remain empty
565 {
566 assign_unaligned(get<1>(align),
567 seq | views::slice(static_cast<decltype(std::ranges::size(seq))>(offset_tmp),
568 std::ranges::size(seq) - soft_clipping_end));
569 }
570 }
571 }
572 }
573 else
574 {
575 ++std::ranges::begin(field_view); // skip '*'
576 }
577
578 // Field 11: Quality
579 // -------------------------------------------------------------------------------------------------------------
580 auto const tab_or_end = is_char<'\t'> || is_char<'\r'> || is_char<'\n'>;
581 auto qual_view = stream_view | detail::take_until_or_throw(tab_or_end);
582 if constexpr (!detail::decays_to_ignore_v<qual_type>)
583 read_forward_range_field(qual_view, qual);
584 else
585 detail::consume(qual_view);
586
587 if constexpr (!detail::decays_to_ignore_v<seq_type> && !detail::decays_to_ignore_v<qual_type>)
588 {
589 if (std::ranges::distance(seq) != 0 && std::ranges::distance(qual) != 0 &&
590 std::ranges::distance(seq) != std::ranges::distance(qual))
591 {
592 throw format_error{detail::to_string("Sequence length (", std::ranges::distance(seq),
593 ") and quality length (", std::ranges::distance(qual),
594 ") must be the same.")};
595 }
596 }
597
598 // All remaining optional fields if any: SAM tags dictionary
599 // -------------------------------------------------------------------------------------------------------------
600 while (is_char<'\t'>(*std::ranges::begin(stream_view))) // read all tags if present
601 {
602 ++std::ranges::begin(stream_view); // skip tab
603 auto stream_until_tab_or_end = stream_view | detail::take_until_or_throw(tab_or_end);
604 if constexpr (!detail::decays_to_ignore_v<tag_dict_type>)
605 read_sam_dict_field(stream_until_tab_or_end, tag_dict);
606 else
607 detail::consume(stream_until_tab_or_end);
608 }
609
610 detail::consume(stream_view | detail::take_until(!(is_char<'\r'> || is_char<'\n'>))); // consume new line
611
612 // DONE READING - wrap up
613 // -------------------------------------------------------------------------------------------------------------
614 // Alignment object construction
615 // Note that the query sequence in get<1>(align) has already been filled while reading Field 10.
616 if constexpr (!detail::decays_to_ignore_v<align_type>)
617 {
618 int32_t ref_idx{(ref_id_tmp.empty()/*unmapped read?*/) ? -1 : 0};
619
620 if constexpr (!detail::decays_to_ignore_v<ref_seqs_type>)
621 {
622 if (!ref_id_tmp.empty())
623 {
624 assert(header.ref_dict.count(ref_id_tmp) != 0); // taken care of in check_and_assign_ref_id()
625 ref_idx = header.ref_dict[ref_id_tmp]; // get index for reference sequence
626 }
627 }
628
629 construct_alignment(align, tmp_cigar_vector, ref_idx, ref_seqs, ref_offset_tmp, ref_length);
630 }
631
632 if constexpr (!detail::decays_to_ignore_v<cigar_type>)
633 std::swap(cigar_vector, tmp_cigar_vector);
634}
635
637template <typename stream_type,
638 typename header_type,
639 typename seq_type,
640 typename id_type,
641 typename ref_seq_type,
642 typename ref_id_type,
643 typename align_type,
644 typename qual_type,
645 typename mate_type,
646 typename tag_dict_type,
647 typename e_value_type,
648 typename bit_score_type>
649inline void format_sam::write_alignment_record(stream_type & stream,
650 sam_file_output_options const & options,
651 header_type && header,
652 seq_type && seq,
653 qual_type && qual,
654 id_type && id,
655 int32_t const offset,
656 ref_seq_type && SEQAN3_DOXYGEN_ONLY(ref_seq),
657 ref_id_type && ref_id,
658 std::optional<int32_t> ref_offset,
659 align_type && align,
660 std::vector<cigar> const & cigar_vector,
661 sam_flag const flag,
662 uint8_t const mapq,
663 mate_type && mate,
664 tag_dict_type && tag_dict,
665 e_value_type && SEQAN3_DOXYGEN_ONLY(e_value),
666 bit_score_type && SEQAN3_DOXYGEN_ONLY(bit_score))
667{
668 /* Note the following general things:
669 *
670 * - Given the SAM specifications, all fields may be empty
671 *
672 * - arithmetic values default to 0 while all others default to '*'
673 *
674 * - Because of the former, arithmetic values can be directly streamed
675 * into 'stream' as operator<< is defined for all arithmetic types
676 * and the default value (0) is also the SAM default.
677 *
678 * - All other non-arithmetic values need to be checked for emptiness
679 */
680
681 // ---------------------------------------------------------------------
682 // Type Requirements (as static asserts for user friendliness)
683 // ---------------------------------------------------------------------
684 static_assert((std::ranges::forward_range<seq_type> &&
686 "The seq object must be a std::ranges::forward_range over "
687 "letters that model seqan3::alphabet.");
688
689 static_assert((std::ranges::forward_range<id_type> &&
691 "The id object must be a std::ranges::forward_range over "
692 "letters that model seqan3::alphabet.");
693
694 if constexpr (!detail::decays_to_ignore_v<ref_id_type>)
695 {
696 static_assert((std::ranges::forward_range<ref_id_type> ||
697 std::integral<std::remove_reference_t<ref_id_type>> ||
698 detail::is_type_specialisation_of_v<std::remove_cvref_t<ref_id_type>, std::optional>),
699 "The ref_id object must be a std::ranges::forward_range "
700 "over letters that model seqan3::alphabet.");
701
702 if constexpr (std::integral<std::remove_cvref_t<ref_id_type>> ||
703 detail::is_type_specialisation_of_v<std::remove_cvref_t<ref_id_type>, std::optional>)
704 static_assert(!detail::decays_to_ignore_v<header_type>,
705 "If you give indices as reference id information the header must also be present.");
706 }
707
709 "The align object must be a std::pair of two ranges whose "
710 "value_type is comparable to seqan3::gap");
711
712 static_assert((std::tuple_size_v<std::remove_cvref_t<align_type>> == 2 &&
713 std::equality_comparable_with<gap, std::ranges::range_reference_t<decltype(std::get<0>(align))>> &&
714 std::equality_comparable_with<gap, std::ranges::range_reference_t<decltype(std::get<1>(align))>>),
715 "The align object must be a std::pair of two ranges whose "
716 "value_type is comparable to seqan3::gap");
717
718 static_assert((std::ranges::forward_range<qual_type> &&
720 "The qual object must be a std::ranges::forward_range "
721 "over letters that model seqan3::alphabet.");
722
724 "The mate object must be a std::tuple of size 3 with "
725 "1) a std::ranges::forward_range with a value_type modelling seqan3::alphabet, "
726 "2) a std::integral or std::optional<std::integral>, and "
727 "3) a std::integral.");
728
729 static_assert(((std::ranges::forward_range<decltype(std::get<0>(mate))> ||
730 std::integral<std::remove_cvref_t<decltype(std::get<0>(mate))>> ||
731 detail::is_type_specialisation_of_v<std::remove_cvref_t<decltype(std::get<0>(mate))>, std::optional>) &&
732 (std::integral<std::remove_cvref_t<decltype(std::get<1>(mate))>> ||
733 detail::is_type_specialisation_of_v<std::remove_cvref_t<decltype(std::get<1>(mate))>, std::optional>) &&
734 std::integral<std::remove_cvref_t<decltype(std::get<2>(mate))>>),
735 "The mate object must be a std::tuple of size 3 with "
736 "1) a std::ranges::forward_range with a value_type modelling seqan3::alphabet, "
737 "2) a std::integral or std::optional<std::integral>, and "
738 "3) a std::integral.");
739
740 if constexpr (std::integral<std::remove_cvref_t<decltype(std::get<0>(mate))>> ||
741 detail::is_type_specialisation_of_v<std::remove_cvref_t<decltype(std::get<0>(mate))>, std::optional>)
742 static_assert(!detail::decays_to_ignore_v<header_type>,
743 "If you give indices as mate reference id information the header must also be present.");
744
745 static_assert(std::same_as<std::remove_cvref_t<tag_dict_type>, sam_tag_dictionary>,
746 "The tag_dict object must be of type seqan3::sam_tag_dictionary.");
747
748 // ---------------------------------------------------------------------
749 // logical Requirements
750 // ---------------------------------------------------------------------
751 if constexpr (!detail::decays_to_ignore_v<header_type> &&
752 !detail::decays_to_ignore_v<ref_id_type> &&
753 !std::integral<std::remove_reference_t<ref_id_type>> &&
754 !detail::is_type_specialisation_of_v<std::remove_reference_t<ref_id_type>, std::optional>)
755 {
756
757 if (options.sam_require_header && !std::ranges::empty(ref_id))
758 {
759 auto id_it = header.ref_dict.end();
760
761 if constexpr (std::ranges::contiguous_range<decltype(ref_id)> &&
762 std::ranges::sized_range<decltype(ref_id)> &&
763 std::ranges::borrowed_range<decltype(ref_id)>)
764 {
765 id_it = header.ref_dict.find(std::span{std::ranges::data(ref_id), std::ranges::size(ref_id)});
766 }
767 else
768 {
769 using header_ref_id_type = std::remove_reference_t<decltype(header.ref_ids()[0])>;
770
772 "The ref_id type is not convertible to the reference id information stored in the "
773 "reference dictionary of the header object.");
774
775 id_it = header.ref_dict.find(ref_id);
776 }
777
778 if (id_it == header.ref_dict.end()) // no reference id matched
779 throw format_error{detail::to_string("The ref_id '", ref_id, "' was not in the list of references:",
780 header.ref_ids())};
781 }
782 }
783
784 if (ref_offset.has_value() && (ref_offset.value() + 1) < 0)
785 throw format_error{"The ref_offset object must be a std::integral >= 0."};
786
787 // ---------------------------------------------------------------------
788 // Writing the Header on first call
789 // ---------------------------------------------------------------------
790 if constexpr (!detail::decays_to_ignore_v<header_type>)
791 {
793 {
794 write_header(stream, options, header);
795 header_was_written = true;
796 }
797 }
798
799 // ---------------------------------------------------------------------
800 // Writing the Record
801 // ---------------------------------------------------------------------
802
803 detail::fast_ostreambuf_iterator stream_it{*stream.rdbuf()};
804 constexpr char separator{'\t'};
805
806 write_range_or_asterisk(stream_it, id);
807 *stream_it = separator;
808
809 stream_it.write_number(static_cast<uint16_t>(flag));
810 *stream_it = separator;
811
812 if constexpr (!detail::decays_to_ignore_v<ref_id_type>)
813 {
814 if constexpr (std::integral<std::remove_reference_t<ref_id_type>>)
815 {
816 write_range_or_asterisk(stream_it, (header.ref_ids())[ref_id]);
817 }
818 else if constexpr (detail::is_type_specialisation_of_v<std::remove_reference_t<ref_id_type>, std::optional>)
819 {
820 if (ref_id.has_value())
821 write_range_or_asterisk(stream_it, (header.ref_ids())[ref_id.value()]);
822 else
823 *stream_it = '*';
824 }
825 else
826 {
828 }
829 }
830 else
831 {
832 *stream_it = '*';
833 }
834
835 *stream_it = separator;
836
837 // SAM is 1 based, 0 indicates unmapped read if optional is not set
838 stream_it.write_number(ref_offset.value_or(-1) + 1);
839 *stream_it = separator;
840
841 stream_it.write_number(static_cast<unsigned>(mapq));
842 *stream_it = separator;
843
844 if (!std::ranges::empty(cigar_vector))
845 {
846 for (auto & c : cigar_vector) //TODO THIS IS PROBABLY TERRIBLE PERFORMANCE_WISE
847 stream_it.write_range(c.to_string());
848 }
849 else if (!std::ranges::empty(get<0>(align)) && !std::ranges::empty(get<1>(align)))
850 {
851 // compute possible distance from alignment end to sequence end
852 // which indicates soft clipping at the end.
853 // This should be replace by a free count_gaps function for
854 // aligned sequences which is more efficient if possible.
855 size_t off_end{std::ranges::size(seq) - offset};
856 for (auto chr : get<1>(align))
857 if (chr == gap{})
858 ++off_end;
859
860 // Might happen if get<1>(align) doesn't correspond to the reference.
861 assert(off_end >= std::ranges::size(get<1>(align)));
862 off_end -= std::ranges::size(get<1>(align));
863
864 write_range_or_asterisk(stream_it, detail::get_cigar_string(align, offset, off_end));
865 }
866 else
867 {
868 *stream_it = '*';
869 }
870
871 *stream_it = separator;
872
873 if constexpr (std::integral<std::remove_reference_t<decltype(get<0>(mate))>>)
874 {
875 write_range_or_asterisk(stream_it, (header.ref_ids())[get<0>(mate)]);
876 }
877 else if constexpr (detail::is_type_specialisation_of_v<std::remove_reference_t<decltype(get<0>(mate))>, std::optional>)
878 {
879 if (get<0>(mate).has_value())
880 write_range_or_asterisk(stream_it, header.ref_ids()[get<0>(mate).value()]);
881 else
882 *stream_it = '*';
883 }
884 else
885 {
886 write_range_or_asterisk(stream_it, get<0>(mate));
887 }
888
889 *stream_it = separator;
890
891 if constexpr (detail::is_type_specialisation_of_v<std::remove_cvref_t<decltype(get<1>(mate))>, std::optional>)
892 {
893 // SAM is 1 based, 0 indicates unmapped read if optional is not set
894 stream_it.write_number(get<1>(mate).value_or(-1) + 1);
895 *stream_it = separator;
896 }
897 else
898 {
899 stream_it.write_number(get<1>(mate));
900 *stream_it = separator;
901 }
902
903 stream_it.write_number(get<2>(mate));
904 *stream_it = separator;
905
906 write_range_or_asterisk(stream_it, seq);
907 *stream_it = separator;
908
909 write_range_or_asterisk(stream_it, qual);
910
911 write_tag_fields(stream_it, tag_dict, separator);
912
913 stream_it.write_end_of_line(options.add_carriage_return);
914}
915
916
934template <typename stream_view_type, arithmetic value_type>
936 stream_view_type && stream_view,
937 value_type value)
938{
939 std::vector<value_type> tmp_vector;
940 while (std::ranges::begin(stream_view) != ranges::end(stream_view)) // not fully consumed yet
941 {
942 read_arithmetic_field(stream_view | detail::take_until(is_char<','>), value);
943 tmp_vector.push_back(value);
944
945 if (is_char<','>(*std::ranges::begin(stream_view)))
946 ++std::ranges::begin(stream_view); // skip ','
947 }
948 variant = std::move(tmp_vector);
949}
950
964template <typename stream_view_type>
966 stream_view_type && stream_view)
967{
968 std::vector<std::byte> tmp_vector;
969 std::byte value;
970
971 while (std::ranges::begin(stream_view) != ranges::end(stream_view)) // not fully consumed yet
972 {
973 try
974 {
975 read_byte_field(stream_view | detail::take_exactly_or_throw(2), value);
976 }
977 catch (std::exception const & e)
978 {
979 throw format_error{"Hexadecimal tag has an uneven number of digits!"};
980 }
981
982 tmp_vector.push_back(value);
983 }
984
985 variant = std::move(tmp_vector);
986}
987
1005template <typename stream_view_type>
1006inline void format_sam::read_sam_dict_field(stream_view_type && stream_view, sam_tag_dictionary & target)
1007{
1008 /* Every SAM tag has the format "[TAG]:[TYPE_ID]:[VALUE]", where TAG is a two letter
1009 name tag which is converted to a unique integer identifier and TYPE_ID is one character in [A,i,Z,H,B,f]
1010 describing the type for the upcoming VALUES. If TYPE_ID=='B' it signals an array of comma separated
1011 VALUE's and the inner value type is identified by the character following ':', one of [cCsSiIf].
1012 */
1013 uint16_t tag = static_cast<uint16_t>(*std::ranges::begin(stream_view)) << 8;
1014 ++std::ranges::begin(stream_view); // skip char read before
1015 tag += static_cast<uint16_t>(*std::ranges::begin(stream_view));
1016 ++std::ranges::begin(stream_view); // skip char read before
1017 ++std::ranges::begin(stream_view); // skip ':'
1018 char type_id = *std::ranges::begin(stream_view);
1019 ++std::ranges::begin(stream_view); // skip char read before
1020 ++std::ranges::begin(stream_view); // skip ':'
1021
1022 switch (type_id)
1023 {
1024 case 'A' : // char
1025 {
1026 target[tag] = static_cast<char>(*std::ranges::begin(stream_view));
1027 ++std::ranges::begin(stream_view); // skip char that has been read
1028 break;
1029 }
1030 case 'i' : // int32_t
1031 {
1032 int32_t tmp;
1033 read_arithmetic_field(stream_view, tmp);
1034 target[tag] = tmp;
1035 break;
1036 }
1037 case 'f' : // float
1038 {
1039 float tmp;
1040 read_arithmetic_field(stream_view, tmp);
1041 target[tag] = tmp;
1042 break;
1043 }
1044 case 'Z' : // string
1045 {
1046 target[tag] = stream_view | views::to<std::string>;
1047 break;
1048 }
1049 case 'H' :
1050 {
1051 read_sam_byte_vector(target[tag], stream_view);
1052 break;
1053 }
1054 case 'B' : // Array. Value type depends on second char [cCsSiIf]
1055 {
1056 char array_value_type_id = *std::ranges::begin(stream_view);
1057 ++std::ranges::begin(stream_view); // skip char read before
1058 ++std::ranges::begin(stream_view); // skip first ','
1059
1060 switch (array_value_type_id)
1061 {
1062 case 'c' : // int8_t
1063 read_sam_dict_vector(target[tag], stream_view, int8_t{});
1064 break;
1065 case 'C' : // uint8_t
1066 read_sam_dict_vector(target[tag], stream_view, uint8_t{});
1067 break;
1068 case 's' : // int16_t
1069 read_sam_dict_vector(target[tag], stream_view, int16_t{});
1070 break;
1071 case 'S' : // uint16_t
1072 read_sam_dict_vector(target[tag], stream_view, uint16_t{});
1073 break;
1074 case 'i' : // int32_t
1075 read_sam_dict_vector(target[tag], stream_view, int32_t{});
1076 break;
1077 case 'I' : // uint32_t
1078 read_sam_dict_vector(target[tag], stream_view, uint32_t{});
1079 break;
1080 case 'f' : // float
1081 read_sam_dict_vector(target[tag], stream_view, float{});
1082 break;
1083 default:
1084 throw format_error{std::string("The first character in the numerical ") +
1085 "id of a SAM tag must be one of [cCsSiIf] but '" + array_value_type_id +
1086 "' was given."};
1087 }
1088 break;
1089 }
1090 default:
1091 throw format_error{std::string("The second character in the numerical id of a "
1092 "SAM tag must be one of [A,i,Z,H,B,f] but '") + type_id + "' was given."};
1093 }
1094}
1095
1103template <typename stream_it_t, std::ranges::forward_range field_type>
1104inline void format_sam::write_range_or_asterisk(stream_it_t & stream_it, field_type && field_value)
1105{
1106 if (std::ranges::empty(field_value))
1107 {
1108 *stream_it = '*';
1109 }
1110 else
1111 {
1112 if constexpr (std::same_as<std::remove_cvref_t<std::ranges::range_reference_t<field_type>>, char>)
1113 stream_it.write_range(field_value);
1114 else // convert from alphabets to their character representation
1115 stream_it.write_range(field_value | views::to_char);
1116 }
1117}
1118
1125template <typename stream_it_t>
1126inline void format_sam::write_range_or_asterisk(stream_it_t & stream_it, char const * const field_value)
1127{
1128 write_range_or_asterisk(stream_it, std::string_view{field_value});
1129}
1130
1138template <typename stream_it_t>
1139inline void format_sam::write_tag_fields(stream_it_t & stream_it, sam_tag_dictionary const & tag_dict, char const separator)
1140{
1141 auto const stream_variant_fn = [&stream_it] (auto && arg) // helper to print a std::variant
1142 {
1143 using T = std::remove_cvref_t<decltype(arg)>;
1144
1145 if constexpr (std::ranges::input_range<T>)
1146 {
1147 if constexpr (std::same_as<std::remove_cvref_t<std::ranges::range_reference_t<T>>, char>)
1148 {
1149 stream_it.write_range(arg);
1150 }
1151 else if constexpr (std::same_as<std::remove_cvref_t<std::ranges::range_reference_t<T>>, std::byte>)
1152 {
1153 if (!std::ranges::empty(arg))
1154 {
1155 stream_it.write_number(std::to_integer<uint8_t>(*std::ranges::begin(arg)));
1156
1157 for (auto && elem : arg | std::views::drop(1))
1158 {
1159 *stream_it = ',';
1160 stream_it.write_number(std::to_integer<uint8_t>(elem));
1161 }
1162 }
1163 }
1164 else
1165 {
1166 if (!std::ranges::empty(arg))
1167 {
1168 stream_it.write_number(*std::ranges::begin(arg));
1169
1170 for (auto && elem : arg | std::views::drop(1))
1171 {
1172 *stream_it = ',';
1173 stream_it.write_number(elem);
1174 }
1175 }
1176 }
1177 }
1178 else if constexpr (std::same_as<std::remove_cvref_t<T>, char>)
1179 {
1180 *stream_it = arg;
1181 }
1182 else // number
1183 {
1184 stream_it.write_number(arg);
1185 }
1186 };
1187
1188 for (auto & [tag, variant] : tag_dict)
1189 {
1190 *stream_it = separator;
1191
1192 char const char0 = tag / 256;
1193 char const char1 = tag % 256;
1194
1195 *stream_it = char0;
1196 *stream_it = char1;
1197 *stream_it = ':';
1198 *stream_it = detail::sam_tag_type_char[variant.index()];
1199 *stream_it = ':';
1200
1201 if (detail::sam_tag_type_char_extra[variant.index()] != '\0')
1202 {
1203 *stream_it = detail::sam_tag_type_char_extra[variant.index()];
1204 *stream_it = ',';
1205 }
1206
1207 std::visit(stream_variant_fn, variant);
1208 }
1209}
1210
1211} // namespace seqan3
Core alphabet concept and free function/type trait wrappers.
Functionally the same as std::ostreambuf_iterator, but offers writing a range more efficiently.
Definition: fast_ostreambuf_iterator.hpp:39
The alignment base format.
Definition: format_sam_base.hpp:44
void check_and_assign_ref_id(ref_id_type &ref_id, ref_id_tmp_type &ref_id_tmp, header_type &header, ref_seqs_type &)
Checks for known reference ids or adds a new reference is and assigns a reference id to ref_id.
Definition: format_sam_base.hpp:123
void read_arithmetic_field(stream_view_t &&stream_view, arithmetic_target_type &arithmetic_target)
Reads arithmetic fields using std::from_chars.
Definition: format_sam_base.hpp:328
void write_header(stream_t &stream, sam_file_output_options const &options, sam_file_header< ref_ids_type > &header)
Writes the SAM header.
Definition: format_sam_base.hpp:696
void construct_alignment(align_type &align, std::vector< cigar > &cigar_vector, int32_t rid, ref_seqs_type &ref_seqs, int32_t ref_start, size_t ref_length)
Construct the field::alignment depending on the given information.
Definition: format_sam_base.hpp:207
void transfer_soft_clipping_to(std::vector< cigar > const &cigar_vector, int32_t &sc_begin, int32_t &sc_end) const
Transfer soft clipping information from the cigar_vector to sc_begin and sc_end.
Definition: format_sam_base.hpp:165
void read_forward_range_field(stream_view_type &&stream_view, target_range_type &target)
Reads a range by copying from stream_view to target, converting values with seqan3::views::char_to.
Definition: format_sam_base.hpp:296
bool header_was_written
A variable that tracks whether the content of header has been written or not.
Definition: format_sam_base.hpp:65
void read_header(stream_view_type &&stream_view, sam_file_header< ref_ids_type > &hdr, ref_seqs_type &)
Reads the SAM header.
Definition: format_sam_base.hpp:364
void read_byte_field(stream_view_t &&stream_view, std::byte &byte_target)
Reads std::byte fields using std::from_chars.
Definition: format_sam_base.hpp:267
The SAM format (tag).
Definition: format_sam.hpp:117
sam_file_header default_header
The default header for the alignment format.
Definition: format_sam.hpp:242
std::string_view const & default_or(detail::ignore_t) const noexcept
brief Returns a reference to dummy if passed a std::ignore.
Definition: format_sam.hpp:248
~format_sam()=default
Defaulted.
format_sam & operator=(format_sam const &)=default
Defaulted.
void read_sam_byte_vector(seqan3::detail::sam_tag_variant &variant, stream_view_type &&stream_view)
Reads a list of byte pairs as it is the case for SAM tag byte arrays.
Definition: format_sam.hpp:965
static std::vector< std::string > file_extensions
The valid file extensions for this format; note that you can modify this value.
Definition: format_sam.hpp:134
void write_sequence_record(stream_type &stream, sequence_file_output_options const &options, seq_type &&sequence, id_type &&id, qual_type &&qualities)
Write the given fields to the specified stream.
Definition: format_sam.hpp:320
format_sam(format_sam &&)=default
Defaulted.
void write_tag_fields(stream_it_t &stream, sam_tag_dictionary const &tag_dict, char const separator)
Writes the optional fields of the seqan3::sam_tag_dictionary.
Definition: format_sam.hpp:1139
format_sam & operator=(format_sam &&)=default
Defaulted.
void read_sam_dict_vector(seqan3::detail::sam_tag_variant &variant, stream_view_type &&stream_view, value_type value)
Reads a list of values separated by comma as it is the case for SAM tag arrays.
Definition: format_sam.hpp:935
void read_sam_dict_field(stream_view_type &&stream_view, sam_tag_dictionary &target)
Reads the optional tag fields into the seqan3::sam_tag_dictionary.
Definition: format_sam.hpp:1006
static constexpr std::string_view dummy
An empty dummy container to pass to align_format.write() such that an empty field is written.
Definition: format_sam.hpp:239
void write_range_or_asterisk(stream_it_t &stream_it, field_type &&field_value)
Writes a field value to the stream.
Definition: format_sam.hpp:1104
bool ref_info_present_in_header
Tracks whether reference information (@SR tag) were found in the SAM header.
Definition: format_sam.hpp:245
decltype(auto) default_or(t &&v) const noexcept
brief Returns the input unchanged.
Definition: format_sam.hpp:255
void read_sequence_record(stream_type &stream, sequence_file_input_options< seq_legal_alph_type > const &options, stream_pos_type &position_buffer, seq_type &sequence, id_type &id, qual_type &qualities)
Read from the specified stream and back-insert into the given field buffers.
Definition: format_sam.hpp:289
std::string tmp_qual
Stores quality values temporarily if seq and qual information are combined (not supported by SAM yet)...
Definition: format_sam.hpp:236
void write_alignment_record(stream_type &stream, sam_file_output_options const &options, header_type &&header, seq_type &&seq, qual_type &&qual, id_type &&id, int32_t const offset, ref_seq_type &&ref_seq, ref_id_type &&ref_id, std::optional< int32_t > ref_offset, align_type &&align, std::vector< cigar > const &cigar_vector, sam_flag const flag, uint8_t const mapq, mate_type &&mate, tag_dict_type &&tag_dict, e_value_type &&e_value, bit_score_type &&bit_score)
Write the given fields to the specified stream.
Definition: format_sam.hpp:649
void read_alignment_record(stream_type &stream, sam_file_input_options< seq_legal_alph_type > const &options, ref_seqs_type &ref_seqs, sam_file_header< ref_ids_type > &header, stream_pos_type &position_buffer, seq_type &seq, qual_type &qual, id_type &id, offset_type &offset, ref_seq_type &ref_seq, ref_id_type &ref_id, ref_offset_type &ref_offset, align_type &align, cigar_type &cigar_vector, flag_type &flag, mapq_type &mapq, mate_type &mate, tag_dict_type &tag_dict, e_value_type &e_value, bit_score_type &bit_score)
Read from the specified stream and back-insert into the given field buffers.
Definition: format_sam.hpp:372
format_sam()=default
Defaulted.
format_sam(format_sam const &)=default
Defaulted.
The alphabet of a gap character '-'.
Definition: gap.hpp:39
Stores the header information of alignment files.
Definition: header.hpp:34
ref_ids_type & ref_ids()
The range of reference ids.
Definition: header.hpp:139
std::unordered_map< key_type, int32_t, key_hasher, detail::view_equality_fn > ref_dict
The mapping of reference id to position in the ref_ids() range and the ref_id_info range.
Definition: header.hpp:178
The SAM tag dictionary class that stores all optional SAM fields.
Definition: sam_tag_dictionary.hpp:336
Provides seqan3::detail::fast_ostreambuf_iterator.
Provides the seqan3::format_sam_base that can be inherited from.
auto const to_char
A view that calls seqan3::to_char() on each element in the input range.
Definition: to_char.hpp:63
constexpr void consume(rng_t &&rng)
Iterate over a range (consumes single-pass input ranges).
Definition: misc.hpp:28
std::tuple< std::vector< cigar >, int32_t, int32_t > parse_cigar(cigar_input_type &&cigar_input)
Parses a cigar string into a vector of operation-count pairs (e.g. (M, 3)).
Definition: cigar.hpp:137
sam_flag
An enum flag that describes the properties of an aligned read (given as a SAM record).
Definition: sam_flag.hpp:76
char constexpr sam_tag_type_char[12]
Each SAM tag type char identifier. Index corresponds to the seqan3::detail::sam_tag_variant types.
Definition: sam_tag_dictionary.hpp:36
std::string get_cigar_string(std::vector< cigar > const &cigar_vector)
Transforms a vector of cigar elements into a string representation.
Definition: cigar.hpp:266
char constexpr sam_tag_type_char_extra[12]
Each types SAM tag type extra char id. Index corresponds to the seqan3::detail::sam_tag_variant types...
Definition: sam_tag_dictionary.hpp:39
@ none
None of the flags below are set.
auto constexpr take_until_or_throw
A view adaptor that returns elements from the underlying range until the functor evaluates to true (t...
Definition: take_until_view.hpp:592
auto constexpr take_exactly_or_throw
A view adaptor that returns the first size elements from the underlying range and also exposes size i...
Definition: take_exactly_view.hpp:617
auto constexpr take_until_or_throw_and_consume
A view adaptor that returns elements from the underlying range until the functor evaluates to true (t...
Definition: take_until_view.hpp:620
constexpr auto istreambuf
A view factory that returns a view over the stream buffer of an input stream.
Definition: istreambuf_view.hpp:110
auto constexpr take_until_and_consume
A view adaptor that returns elements from the underlying range until the functor evaluates to true (o...
Definition: take_until_view.hpp:606
auto constexpr take_until
A view adaptor that returns elements from the underlying range until the functor evaluates to true (o...
Definition: take_until_view.hpp:578
@ flag
The alignment flag (bit information), uint16_t value.
@ ref_offset
Sequence (seqan3::field::ref_seq) relative start position (0-based), unsigned value.
@ mapq
The mapping quality of the seqan3::field::seq alignment, usually a Phred-scaled score.
@ offset
Sequence (seqan3::field::seq) relative start position (0-based), unsigned value.
@ mate
The mate pair information given as a std::tuple of reference name, offset and template length.
@ ref_id
The identifier of the (reference) sequence that seqan3::field::seq was aligned to.
@ seq
The "sequence", usually a range of nucleotides or amino acids.
@ qual
The qualities, usually in Phred score notation.
std::string make_printable(char const c)
Returns a printable value for the given character c.
Definition: pretty_print.hpp:48
auto constexpr is_space
Checks whether c is a space character.
Definition: predicate.hpp:128
typename decltype(detail::split_after< i >(list_t{}))::second_type drop
Return a seqan3::type_list of the types in the input type list, except the first n.
Definition: traits.hpp:412
decltype(detail::transform< trait_t >(list_t{})) transform
Apply a transformation trait to every type in the list and return a seqan3::type_list of the results.
Definition: traits.hpp:495
constexpr size_t size
The size of a type pack.
Definition: traits.hpp:151
constexpr auto slice
A view adaptor that returns a half-open interval on the underlying range.
Definition: slice.hpp:183
Provides the seqan3::sam_file_header class.
The generic alphabet concept that covers most data types used in ranges.
Checks whether from can be implicityly converted to to.
A more refined container concept than seqan3::container.
The generic concept for a (biological) sequence.
Whether a type behaves like a tuple.
Auxiliary functions for the alignment IO.
Provides seqan3::detail::istreambuf.
std::string to_string(value_type &&...values)
Streams all parameters via the seqan3::debug_stream and returns a concatenated string.
Definition: to_string.hpp:29
The main SeqAn3 namespace.
Definition: aligned_sequence_concept.hpp:29
T push_back(T... args)
The <ranges> header from C++20's standard library.
Provides seqan3::sam_file_input_format and auxiliary classes.
Provides seqan3::sam_file_output_options.
Provides helper data structures for the seqan3::sam_file_output.
Provides the seqan3::sam_tag_dictionary class and auxiliaries.
Provides seqan3::sequence_file_input_format and auxiliary classes.
Provides seqan3::sequence_file_output_options.
Provides seqan3::views::slice.
Thrown if information given to output format didn't match expectations.
Definition: exception.hpp:91
Thrown if there is a parse error, such as reading an unexpected character from an input stream.
Definition: exception.hpp:48
The options type defines various option members that influence the behaviour of all or some formats.
Definition: input_options.hpp:27
The options type defines various option members that influence the behavior of all or some formats.
Definition: output_options.hpp:26
bool add_carriage_return
The default plain text line-ending is "\n", but on Windows an additional carriage return is recommend...
Definition: output_options.hpp:30
bool sam_require_header
Whether to require a header for SAM files.
Definition: output_options.hpp:44
The options type defines various option members that influence the behaviour of all or some formats.
Definition: input_options.hpp:27
bool truncate_ids
Read the ID string only up until the first whitespace character.
Definition: input_options.hpp:29
The options type defines various option members that influence the behaviour of all or some formats.
Definition: output_options.hpp:26
T swap(T... args)
Provides seqan3::views::take_until and seqan3::views::take_until_or_throw.
T tie(T... args)
Provides seqan3::views::to.
Provides seqan3::views::to_char.
Provides traits to inspect some information of a type, for example its name.
Provides seqan3::tuple_like.
T visit(T... args)