struct
let string_to_item ?filename () =
let name = sprintf "fastq_parser:%s" Option.(value ~default:"<>" filename) in
Biocaml_lines.Transform.make_merge_error
~name ?filename ~next:(fun p ->
let open Biocaml_lines.Buffer in
if queued_lines p < 4 then
`not_ready
else (
let name_line = (next_line_exn p :> string) in
if String.length name_line = 0 || name_line.[0] <> '@'
then output_error (`wrong_name_line (current_position p, name_line))
else
let sequence = (next_line_exn p :> string) in
let comment_line = (next_line_exn p :> string) in
if String.length comment_line = 0 || comment_line.[0] <> '+'
then output_error (`wrong_comment_line (current_position p, comment_line))
else
let qualities = (next_line_exn p :> string) in
if String.length sequence <> String.length qualities
then output_error
(`sequence_and_qualities_do_not_match (current_position p,
sequence, qualities))
else (
output_ok {
name = String.sub name_line 1 (String.length name_line - 1);
comment = String.sub comment_line 1 (String.length comment_line - 1);
sequence; qualities }
))
) ()
let item_to_string_pure r =
sprintf "@%s\n%s\n+%s\n%s\n" r.name r.sequence r.comment r.qualities
let item_to_string () =
Biocaml_transform.of_function ~name:"fastq_to_string" item_to_string_pure
let trim (specification: [`beginning of int|`ending of int]) =
let items = Queue.create () in
let name =
sprintf "(fastq_trimmer %s)"
(match specification with
| `beginning i -> sprintf "B:%d" i
| `ending i -> sprintf "E:%d" i) in
Biocaml_transform.make ~name ()
~feed:(fun r -> Queue.enqueue items r)
~next:(fun stopped ->
begin match Queue.dequeue items with
| Some r ->
let rlgth = String.length r.sequence in
begin match specification with
| `beginning i when i < rlgth ->
output_ok
{ r with sequence = String.sub r.sequence ~pos:i ~len:(rlgth - i);
qualities = String.sub r.qualities ~pos:i ~len:(rlgth - i) }
| `ending i when i < rlgth ->
output_ok
{ r with sequence = String.sub r.sequence ~pos:0 ~len:(rlgth - i);
qualities = String.sub r.qualities ~pos:0 ~len:(rlgth - i) }
| _ ->
output_error (`invalid_size rlgth)
end
| None -> if stopped then `end_of_stream else `not_ready
end)
let fasta_pair_to_fastq ?(phred_score_offset=`offset33) () =
let open Result in
let module Fasta = Biocaml_fasta in
Biocaml_transform.of_function begin fun (char_item, int_item) ->
if char_item.Fasta.header = int_item.Fasta.header then
begin
begin try
List.map int_item.Fasta.sequence (fun int ->
Biocaml_phred_score.(
of_int_exn int
|> to_ascii_exn ~offset:phred_score_offset
|> Char.to_string))
|> String.concat ~sep:"" |> return
with _ ->
fail (`cannot_convert_to_phred_score int_item.Fasta.sequence)
end
>>= fun qualities ->
return {name = char_item.Fasta.header;
sequence = char_item.Fasta.sequence;
comment = char_item.Fasta.header;
qualities}
end
else
fail (`sequence_names_mismatch (char_item.Fasta.header,
int_item.Fasta.header))
end
let fastq_to_fasta_pair ?(phred_score_offset=`offset33) () =
let open Result in
Biocaml_transform.of_function begin fun {name; sequence; qualities; _} ->
begin try
let scores =
String.fold ~init:[] qualities ~f:(fun prev c ->
Biocaml_phred_score.(
of_ascii_exn ~offset:phred_score_offset c |> to_int) :: prev)
|> List.rev in
return Biocaml_fasta.({ header = name; sequence },
{ header = name; sequence = scores })
with e ->
fail (`cannot_convert_ascii_phred_score qualities)
end
end
end