diff --git a/.gitignore b/.gitignore index 5f271dc..23a12ec 100644 --- a/.gitignore +++ b/.gitignore @@ -21,5 +21,6 @@ Cargo.lock #.idea/*~ .DS_Store env/ +venv/ site/ static/ \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index ae2bc8e..c840998 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,4 @@ opt-level = 0 [profile.release] opt-level = 3 + diff --git a/heatmap/src/canvas/drawing.rs b/heatmap/src/canvas/drawing.rs index e2e0435..4f11d1b 100644 --- a/heatmap/src/canvas/drawing.rs +++ b/heatmap/src/canvas/drawing.rs @@ -1,6 +1,5 @@ -use web_sys::{CanvasRenderingContext2d, console}; use wasm_bindgen::JsValue; - +use web_sys::{console, CanvasRenderingContext2d}; pub fn draw_responsive_heatmap( context: &CanvasRenderingContext2d, @@ -10,12 +9,11 @@ pub fn draw_responsive_heatmap( canvas_width: f64, canvas_height: f64, device_pixel_ratio: f64, -) -> Result<(), JsValue> -{ +) -> Result<(), JsValue> { let rows = values.len(); let cols = values[0].len(); console::log_1(&JsValue::from_str(&format!("up in the draw function"))); - // Get canvas dimensions + // Get canvas dimensions // Calculate dynamic padding and box size let adj_canvas_width = canvas_width * device_pixel_ratio; let adj_canvas_height = canvas_height * device_pixel_ratio; @@ -24,13 +22,16 @@ pub fn draw_responsive_heatmap( let padding_bottom = adj_canvas_height * 0.05; let _padding_right = adj_canvas_width * 0.05; - // let box_width = (adj_canvas_width - padding_left - padding_right) / (cols as f64 * 1.1); - // let box_height = (adj_canvas_height - padding_top - padding_bottom) / (rows as f64 * 1.1); - + // let box_width = (adj_canvas_width - padding_left - padding_right) / (cols as f64 * 1.1); + // let box_height = (adj_canvas_height - padding_top - padding_bottom) / (rows as f64 * 1.1); + let box_width = 30.0; let box_height = 30.0; // Clear the canvas - console::log_1(&JsValue::from_str(&format!("pad left {} pad bottom {}",&padding_left, &padding_bottom))); + console::log_1(&JsValue::from_str(&format!( + "pad left {} pad bottom {}", + &padding_left, &padding_bottom + ))); context.clear_rect(0.0, 0.0, adj_canvas_width, adj_canvas_height); println!("cleared rec"); // Draw the heatmap @@ -46,7 +47,7 @@ pub fn draw_responsive_heatmap( _ => "#FFFFFF", }; //context.set_fill_style_str(&JsValue::from(color)); - context.set_fill_style_str(color); + context.set_fill_style_str(color); let x = padding_left + (col as f64 * box_width); let y = padding_top + (row as f64 * box_height); @@ -54,9 +55,9 @@ pub fn draw_responsive_heatmap( // Draw box borders //context.set_stroke_style(&JsValue::from("#FFFFFF")); - context.set_stroke_style_str("#FFFFFF"); + context.set_stroke_style_str("#FFFFFF"); context.set_line_width(2.0 / device_pixel_ratio); - + if row < rows - 1 { context.begin_path(); context.move_to(x, y + box_height); @@ -73,19 +74,22 @@ pub fn draw_responsive_heatmap( } } console::log_1(&JsValue::from_str(&format!( - "after the rows and cols padding bottom: {}, height: {}", - &padding_bottom, - &(box_height * rows as f64), - ))); + "after the rows and cols padding bottom: {}, height: {}", + &padding_bottom, + &(box_height * rows as f64), + ))); // Draw X-axis context.begin_path(); //context.set_stroke_style_str(&JsValue::from("#000000")); context.set_stroke_style_str("#000000"); context.move_to(padding_left, (box_height * rows as f64) + padding_bottom); - context.line_to((box_height * rows as f64) + padding_bottom, (box_height * rows as f64) + padding_left); + context.line_to( + (box_height * rows as f64) + padding_bottom, + (box_height * rows as f64) + padding_left, + ); context.stroke(); - + // Draw Y-axis context.begin_path(); context.move_to(padding_left, padding_top); @@ -97,11 +101,12 @@ pub fn draw_responsive_heatmap( context.set_font(&format!("{}px Arial", label_font_size)); context.set_text_align("center"); context.set_text_baseline("top"); - + for col in 0..cols { let x = padding_left + col as f64 * box_width + box_width / 2.0; - let y = (box_height * rows as f64) + padding_bottom + 5.0; // Position below the heatmap - context.fill_text(&x_labels[col], x, y) + let y = (box_height * rows as f64) + padding_bottom + 5.0; // Position below the heatmap + context + .fill_text(&x_labels[col], x, y) .map_err(|_| JsValue::from_str(&format!("Failed to draw text at column {}", col)))?; // Draw ticks @@ -114,11 +119,12 @@ pub fn draw_responsive_heatmap( // Draw Y-axis ticks and labels context.set_text_align("right"); context.set_text_baseline("middle"); - + for row in 0..rows { - let x = padding_left - 10.0; // Position to the left of the heatmap + let x = padding_left - 10.0; // Position to the left of the heatmap let y = padding_top + row as f64 * box_height + box_height / 2.0; - context.fill_text(&y_labels[row], x, y) + context + .fill_text(&y_labels[row], x, y) .map_err(|_| JsValue::from_str(&format!("Failed to draw text at row {}", row)))?; // Draw ticks @@ -128,9 +134,8 @@ pub fn draw_responsive_heatmap( context.stroke(); } console::log_1(&JsValue::from_str(&format!( - "at the end of draw funct Canvas width: {}, height: {}", - &adj_canvas_width, - &adj_canvas_height - ))); + "at the end of draw funct Canvas width: {}, height: {}", + &adj_canvas_width, &adj_canvas_height + ))); Ok(()) -} \ No newline at end of file +} diff --git a/heatmap/src/heatmap_data.rs b/heatmap/src/heatmap_data.rs index 616d4e8..5032dd0 100644 --- a/heatmap/src/heatmap_data.rs +++ b/heatmap/src/heatmap_data.rs @@ -1,12 +1,12 @@ //! This module contains the data structure for the heatmap -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Debug)] pub struct HeatmapData { - pub values: Vec>, - pub x_labels: Vec, - pub y_labels: Vec, + pub values: Vec>, + pub x_labels: Vec, + pub y_labels: Vec, } impl HeatmapData { @@ -31,4 +31,4 @@ mod tests { assert_eq!(heatmap_data.x_labels, Vec::::new()); assert_eq!(heatmap_data.y_labels, Vec::::new()); } -} \ No newline at end of file +} diff --git a/heatmap/src/lib.rs b/heatmap/src/lib.rs index 099835c..a3ca5ec 100644 --- a/heatmap/src/lib.rs +++ b/heatmap/src/lib.rs @@ -1,26 +1,26 @@ //! # A Heatmap in Rust web assembly calling d3.js //! -//! You will need to use wasm-pack to build instead of cargo +//! You will need to use wasm-pack to build instead of cargo //! wasm-pack build --target web -//! And some way of serving locally +//! And some way of serving locally //! http-server . //! It requires the index.html in the static directory //! Currently working with fixed data //! and a rusty colour theme #![allow(non_snake_case)] -pub mod heatmap_data; pub mod canvas; +pub mod heatmap_data; // internal imports use canvas::drawing::draw_responsive_heatmap; use heatmap_data::HeatmapData; // external imports +use std::rc::Rc; use wasm_bindgen::prelude::*; use wasm_bindgen::JsValue; -use web_sys::{window, HtmlCanvasElement, CanvasRenderingContext2d}; use web_sys::console; -use std::rc::Rc; +use web_sys::{window, CanvasRenderingContext2d, HtmlCanvasElement}; //returns a JsValue to javascript #[wasm_bindgen(start)] @@ -31,7 +31,9 @@ pub fn start() -> Result<(), JsValue> { let window = Rc::new(window); let window_clone = Rc::clone(&window); let document = window.document().ok_or(JsValue::from_str("no document"))?; - console::log_1(&JsValue::from_str(&format!("up in the start of the function"))); + console::log_1(&JsValue::from_str(&format!( + "up in the start of the function" + ))); // Get the canvas element let canvas = document .get_element_by_id("heatmap") @@ -39,28 +41,37 @@ pub fn start() -> Result<(), JsValue> { .dyn_into::()?; console::log_1(&JsValue::from_str(&format!("called the canvas"))); let heatmap_values = vec![ - vec![2, 1, 0, 1, 0], // row 1 - vec![1, 2, 0, 0, 1], // row 2 - vec![2, 0, 1, 2, 1], // row 3 - vec![0, 0, 0, 2, 0], // row 4 + vec![2, 1, 0, 1, 0], // row 1 + vec![1, 2, 0, 0, 1], // row 2 + vec![2, 0, 1, 2, 1], // row 3 + vec![0, 0, 0, 2, 0], // row 4 vec![1, 2, 0, 1, 1], // row 5 ]; console::log_1(&JsValue::from_str(&format!("called the heatmap vals"))); - let x_labels: Vec = vec!["A", "B", "C", "D", "E"].iter().map(|s| s.to_string()).collect(); - let y_labels: Vec = vec!["R1", "R2", "R3", "R4", "R5"].iter().map(|s| s.to_string()).collect(); - - let num_rows = heatmap_values.len(); // Should be 5 - let num_cols = heatmap_values[0].len(); // Should be 5 + let x_labels: Vec = vec!["A", "B", "C", "D", "E"] + .iter() + .map(|s| s.to_string()) + .collect(); + let y_labels: Vec = vec!["R1", "R2", "R3", "R4", "R5"] + .iter() + .map(|s| s.to_string()) + .collect(); + + let num_rows = heatmap_values.len(); // Should be 5 + let num_cols = heatmap_values[0].len(); // Should be 5 let mut heatmap_data = HeatmapData::new(); heatmap_data.values = heatmap_values.clone(); heatmap_data.x_labels = x_labels.clone(); heatmap_data.y_labels = y_labels.clone(); let box_size = 100.0; let device_pixel_ratio = window.device_pixel_ratio(); - console::log_1(&JsValue::from_str(&format!("num rows are {:?} num cols are {:?}", &num_rows, &num_cols))); - + console::log_1(&JsValue::from_str(&format!( + "num rows are {:?} num cols are {:?}", + &num_rows, &num_cols + ))); + // Dynamically set canvas size based on number of rows and columns - let canvas_width = num_cols as f64 * box_size; // 6 columns * 50px + let canvas_width = num_cols as f64 * box_size; // 6 columns * 50px let canvas_height = num_rows as f64 * box_size; // 6 rows * 50px canvas.set_width(canvas_width as u32); canvas.set_height(canvas_height as u32); @@ -68,7 +79,7 @@ pub fn start() -> Result<(), JsValue> { "Canvas width: {}, height: {}", canvas.width(), canvas.height() - ))); + ))); let context = canvas .get_context("2d")? @@ -77,24 +88,26 @@ pub fn start() -> Result<(), JsValue> { // Define the heatmap matrix (3x3) with values representing different colors context.scale(device_pixel_ratio, device_pixel_ratio)?; - + draw_responsive_heatmap( - &context, - heatmap_values.clone(), - x_labels.clone(), - y_labels.clone(), - canvas_width, - canvas_height, - device_pixel_ratio, - )?; + &context, + heatmap_values.clone(), + x_labels.clone(), + y_labels.clone(), + canvas_width, + canvas_height, + device_pixel_ratio, + )?; let handle_heatmap_resize = move || -> Result<(), JsValue> { - let new_width = window_clone.inner_width() + let new_width = window_clone + .inner_width() .map_err(|_| JsValue::from_str("error getting inner width"))? .as_f64() .ok_or(JsValue::from_str("error converting width to f64"))?; - let new_height = window_clone.inner_height() + let new_height = window_clone + .inner_height() .map_err(|_| JsValue::from_str("error getting inner height"))? .as_f64() .ok_or(JsValue::from_str("error converting height to f64"))?; @@ -105,9 +118,11 @@ pub fn start() -> Result<(), JsValue> { canvas.set_width(canvas_new_width as u32); canvas.set_height(canvas_new_height as u32); - context.set_transform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) + context + .set_transform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) .map_err(|_| JsValue::from_str("error setting transform"))?; - context.scale(device_pixel_ratio, device_pixel_ratio) + context + .scale(device_pixel_ratio, device_pixel_ratio) .map_err(|_| JsValue::from_str("error scaling context"))?; draw_responsive_heatmap( @@ -128,9 +143,9 @@ pub fn start() -> Result<(), JsValue> { console::error_1(&e); } }; - + let closure = Closure::wrap(Box::new(error_handled_heatmap_resize) as Box); - + window.add_event_listener_with_callback("resize", closure.as_ref().unchecked_ref())?; closure.forget(); diff --git a/microBioRust/Cargo.toml b/microBioRust/Cargo.toml index 109f097..6da5838 100644 --- a/microBioRust/Cargo.toml +++ b/microBioRust/Cargo.toml @@ -20,13 +20,17 @@ repository = "https://github.com/LCrossman/microBioRust" [lints.rust] unsafe_code = "forbid" +[lib] +path = "src/lib.rs" + [dependencies] paste = "1.0" -itertools = "0.10.1" +itertools = "0.14.0" protein-translate = "0.2.0" bio = "2.3.0" anyhow = "1.0" -thiserror = "1.0" +thiserror = "2.0.12" regex = "1.5" chrono = "0.4.38" clap = { version = "4.5.19", features = ["derive"] } + diff --git a/microBioRust/expand.rs b/microBioRust/expand.rs new file mode 100644 index 0000000..163e5b0 --- /dev/null +++ b/microBioRust/expand.rs @@ -0,0 +1,5981 @@ +#![feature(prelude_import)] +//! The aim of this crate is to provide Microbiology friendly Rust functions for bioinformatics. +//! +//! +//! With the genbank parser, you are able to parse a genbank format file, then write into gff3 format +//! +//! It is also possible to print the DNA sequences extracted from the coding sequences (genes, ffn format), +//! plus the protein fasta sequences (faa format). +//! +//! Additionally, you can create new features and records and save them either in genbank or gff3 format +//! +#![allow(non_snake_case)] +#[prelude_import] +use std::prelude::rust_2021::*; +#[macro_use] +extern crate std; +pub mod embl { + //! # An EMBL format to GFF parser + //! + //! + //! You are able to parse genbank and save as a GFF (gff3) format as well as extracting DNA sequences, gene DNA sequences (ffn) and protein fasta sequences (faa) + //! + //! You can also create new records and save as a embl (gbk) format + //! + //! ## Detailed Explanation + //! + //! + //! The Embl parser contains: + //! + //! Records - a top level structure which consists of either one record (single embl) or multiple instances of record (multi-embl). + //! + //! Each Record contains: + //! + //! 1. A source, ```SourceAttributes```, construct(enum) of counter (source name), start, stop [of source or contig], organism, mol_type, strain, type_material, db_xref + //! 2. Features, ```FeatureAttributes```, construct(enum) of counter (locus tag), gene (if present), product, codon start, strand, start, stop [of cds/gene] + //! 3. Sequence features, ```SequenceAttributes```, construct(enum) of counter (locus tag), sequence_ffn (DNA gene sequence) sequence_faa (protein translation), strand, codon start, start, stop [cds/gene] + //! 4. The DNA sequence of the whole record (or contig) + //! + //! Example to extract and print all the protein sequence fasta, example using getters (or get_ functionality), simplified embl! macro + //! + //!```rust + //! use clap::Parser; + //! use std::fs::File; + //! use microBioRust::embl::Reader; + //! use std::io; + //! use microBioRust::embl; + //! + //! + //! #[derive(Parser, Debug)] + //! #[clap(author, version, about)] + //! struct Arguments { + //! #[clap(short, long)] + //! filename: String, + //! } + //! + //! pub fn genbank_to_faa() -> Result<(), anyhow::Error> { + //! let args = Arguments::parse(); + //! let records = embl!(&args.filename); + //! for record in records { + //! for (k, v) in &record.cds.attributes { + //! if let Some(seq) = record.seq_features.get_sequence_faa(k) { + //! println!(">{}|{}\n{}", &record.id, &k, seq); + //! } + //! } + //! } + //! return Ok(()); + //! } + //!``` + //! + //! Example to extract protein sequence from embl file, debugging use + //!```rust + //! use clap::Parser; + //! use std::fs::File; + //! use microBioRust::embl::Reader; + //! use std::io; + //! + //! #[derive(Parser, Debug)] + //! #[clap(author, version, about)] + //! struct Arguments { + //! #[clap(short, long)] + //! filename: String, + //! } + //! + //! pub fn embl_to_faa() -> Result<(), anyhow::Error> { + //! let args = Arguments::parse(); + //! let file_embl = File::open(args.filename)?; + //! let mut reader = Reader::new(file_embl); + //! let mut records = reader.records(); + //! loop { + //! //collect from each record advancing on a next record basis, count cds records + //! match records.next() { + //! Some(Ok(mut record)) => { + //! for (k, v) in &record.cds.attributes { + //! match record.seq_features.get_sequence_faa(&k) { + //! Some(value) => { let seq_faa = value.to_string(); + //! println!(">{}|{}\n{}", &record.id, &k, seq_faa); + //! }, + //! _ => (), + //! }; + //! } + //! }, + //! Some(Err(e)) => { println!("Error encountered - an err {:?}", e); }, + //! None => break, + //! } + //! } + //! return Ok(()); + //! } + //!``` + //! + //! + //! Example to save a provided multi- or single genbank file as a GFF file (by joining any multi-genbank) + //! + //! + //! ```rust + //! use microBioRust::embl::gff_write; + //! use microBioRust::embl::Reader; + //! use microBioRust::embl::Record; + //! use std::collections::BTreeMap; + //! use std::fs::File; + //! use clap::Parser; + //! use std::io; + //! + //! #[derive(Parser, Debug)] + //! #[clap(author, version, about)] + //! struct Arguments { + //! #[clap(short, long)] + //! filename: String, + //! } + //! + //! pub fn embl_to_gff() -> io::Result<()> { + //! let args = Arguments::parse(); + //! let file_embl = File::open(&args.filename)?; + //! let prev_start: u32 = 0; + //! let mut prev_end: u32 = 0; + //! let mut reader = Reader::new(file_embl); + //! let mut records = reader.records(); + //! let mut read_counter: u32 = 0; + //! let mut seq_region: BTreeMap = BTreeMap::new(); + //! let mut record_vec: Vec = Vec::new(); + //! loop { + //! match records.next() { + //! Some(Ok(mut record)) => { + //! //println!("next record"); + //! //println!("Record id: {:?}", record.id); + //! let source = record.source_map.source_name.clone().expect("issue collecting source name"); + //! let beginning = match record.source_map.get_start(&source) { + //! Some(value) => value.get_value(), + //! _ => 0, + //! }; + //! let ending = match record.source_map.get_stop(&source) { + //! Some(value) => value.get_value(), + //! _ => 0, + //! }; + //! if ending + prev_end < beginning + prev_end { + //! println!("debug: end value smaller is than the start {:?}", beginning); + //! } + //! seq_region.insert(source, (beginning + prev_end, ending + prev_end)); + //! record_vec.push(record); + //! // Add additional fields to print if needed + //! read_counter+=1; + //! prev_end+=ending; // create the joined record if there are multiple + //! }, + //! Some(Err(e)) => { println!("theres an err {:?}", e); }, + //! None => { + //! println!("finished iteration"); + //! break; }, + //! } + //! } + //! let output_file = format!("{}.gff", &args.filename); + //! gff_write(seq_region.clone(), record_vec, &output_file, true); + //! println!("Total records processed: {}", read_counter); + //! return Ok(()); + //! } + //!``` + //! Example to create a completely new record, use of setters or set_ functionality + //! + //! To write into GFF format requires gff_write(seq_region, record_vec, filename, true or false) + //! + //! The seq_region is the region of interest to save with name and DNA coordinates such as ``` seqregion.entry("source_1".to_string(), (1,897))``` + //! This makes it possible to save the whole file or to subset it + //! + //! record_vec is a list of the records. If there is only one record, include this as a vec using ``` vec![record] ``` + //! + //! The boolean true/false describes whether the DNA sequence should be included in the GFF3 file + //! + //! To write into embl format requires embl_write(seq_region, record_vec, filename), no true or false since embl format will include the DNA sequence + //! + //! + //! ```rust + //! use microBioRust::embl::gff_write; + //! use microBioRust::embl::RangeValue; + //! use microBioRust::embl::Record; + //! use std::collections::BTreeMap; + //! + //! pub fn create_new_record() -> Result<(), anyhow::Error> { + //! let filename = format!("new_record.gff"); + //! let mut record = Record::new(); + //! let mut seq_region: BTreeMap = BTreeMap::new(); + //! //example from E.coli K12 + //! seq_region.insert("source_1".to_string(), (1,897)); + //! //Add the source into SourceAttributes + //! record.source_map + //! .set_counter("source_1".to_string()) + //! .set_start(RangeValue::Exact(1)) + //! .set_stop(RangeValue::Exact(897)) + //! .set_organism("Escherichia coli".to_string()) + //! .set_mol_type("DNA".to_string()) + //! .set_strain("K-12 substr. MG1655".to_string()) + //! .set_type_material("type strain of Escherichia coli K12".to_string()) + //! .set_db_xref("PRJNA57779".to_string()); + //! //Add the features into FeatureAttributes, here we are setting two features, i.e. coding sequences or genes + //! record.cds + //! .set_counter("b3304".to_string()) + //! .set_start(RangeValue::Exact(1)) + //! .set_stop(RangeValue::Exact(354)) + //! .set_gene("rplR".to_string()) + //! .set_product("50S ribosomal subunit protein L18".to_string()) + //! .set_codon_start(1) + //! .set_strand(-1); + //! record.cds + //! .set_counter("b3305".to_string()) + //! .set_start(RangeValue::Exact(364)) + //! .set_stop(RangeValue::Exact(897)) + //! .set_gene("rplF".to_string()) + //! .set_product("50S ribosomal subunit protein L6".to_string()) + //! .set_codon_start(1) + //! .set_strand(-1); + //! //Add the sequences for the coding sequence (CDS) into SequenceAttributes + //! record.seq_features + //! .set_counter("b3304".to_string()) + //! .set_start(RangeValue::Exact(1)) + //! .set_stop(RangeValue::Exact(354)) + //! .set_sequence_ffn("ATGGATAAGAAATCTGCTCGTATCCGTCGTGCGACCCGCGCACGCCGCAAGCTCCAGGAG + //!CTGGGCGCAACTCGCCTGGTGGTACATCGTACCCCGCGTCACATTTACGCACAGGTAATT + //!GCACCGAACGGTTCTGAAGTTCTGGTAGCTGCTTCTACTGTAGAAAAAGCTATCGCTGAA + //!CAACTGAAGTACACCGGTAACAAAGACGCGGCTGCAGCTGTGGGTAAAGCTGTCGCTGAA + //!CGCGCTCTGGAAAAAGGCATCAAAGATGTATCCTTTGACCGTTCCGGGTTCCAATATCAT + //!GGTCGTGTCCAGGCACTGGCAGATGCTGCCCGTGAAGCTGGCCTTCAGTTCTAA".to_string()) + //! .set_sequence_faa("MDKKSARIRRATRARRKLQELGATRLVVHRTPRHIYAQVIAPNGSEVLVAASTVEKAIAE + //!QLKYTGNKDAAAAVGKAVAERALEKGIKDVSFDRSGFQYHGRVQALADAAREAGLQF".to_string()) + //! .set_codon_start(1) + //! .set_strand(-1); + //! record.seq_features + //! .set_counter("bb3305".to_string()) + //! .set_start(RangeValue::Exact(364)) + //! .set_stop(RangeValue::Exact(897)) + //! .set_sequence_ffn("ATGTCTCGTGTTGCTAAAGCACCGGTCGTTGTTCCTGCCGGCGTTGACGTAAAAATCAAC + //!GGTCAGGTTATTACGATCAAAGGTAAAAACGGCGAGCTGACTCGTACTCTCAACGATGCT + //!GTTGAAGTTAAACATGCAGATAATACCCTGACCTTCGGTCCGCGTGATGGTTACGCAGAC + //!GGTTGGGCACAGGCTGGTACCGCGCGTGCCCTGCTGAACTCAATGGTTATCGGTGTTACC + //!GAAGGCTTCACTAAGAAGCTGCAGCTGGTTGGTGTAGGTTACCGTGCAGCGGTTAAAGGC + //!AATGTGATTAACCTGTCTCTGGGTTTCTCTCATCCTGTTGACCATCAGCTGCCTGCGGGT + //!ATCACTGCTGAATGTCCGACTCAGACTGAAATCGTGCTGAAAGGCGCTGATAAGCAGGTG + //!ATCGGCCAGGTTGCAGCGGATCTGCGCGCCTACCGTCGTCCTGAGCCTTATAAAGGCAAG + //!GGTGTTCGTTACGCCGACGAAGTCGTGCGTACCAAAGAGGCTAAGAAGAAGTAA".to_string()) + //! .set_sequence_faa("MSRVAKAPVVVPAGVDVKINGQVITIKGKNGELTRTLNDAVEVKHADNTLTFGPRDGYAD + //!GWAQAGTARALLNSMVIGVTEGFTKKLQLVGVGYRAAVKGNVINLSLGFSHPVDHQLPAG + //!ITAECPTQTEIVLKGADKQVIGQVAADLRAYRRPEPYKGKGVRYADEVVRTKEAKKK".to_string()) + //! .set_codon_start(1) + //! .set_strand(-1); + //! //Add the full sequence of the entire record into the record.sequence + //! record.sequence = "TTAGAACTGAAGGCCAGCTTCACGGGCAGCATCTGCCAGTGCCTGGACACGACCATGATA + //!TTGGAACCCGGAACGGTCAAAGGATACATCTTTGATGCCTTTTTCCAGAGCGCGTTCAGC + //!GACAGCTTTACCCACAGCTGCAGCCGCGTCTTTGTTACCGGTGTACTTCAGTTGTTCAGC + //!GATAGCTTTTTCTACAGTAGAAGCAGCTACCAGAACTTCAGAACCGTTCGGTGCAATTAC + //!CTGTGCGTAAATGTGACGCGGGGTACGATGTACCACCAGGCGAGTTGCGCCCAGCTCCTG + //!GAGCTTGCGGCGTGCGCGGGTCGCACGACGGATACGAGCAGATTTCTTATCCATAGTGTT + //!ACCTTACTTCTTCTTAGCCTCTTTGGTACGCACGACTTCGTCGGCGTAACGAACACCCTT + //!GCCTTTATAAGGCTCAGGACGACGGTAGGCGCGCAGATCCGCTGCAACCTGGCCGATCAC + //!CTGCTTATCAGCGCCTTTCAGCACGATTTCAGTCTGAGTCGGACATTCAGCAGTGATACC + //!CGCAGGCAGCTGATGGTCAACAGGATGAGAGAAACCCAGAGACAGGTTAATCACATTGCC + //!TTTAACCGCTGCACGGTAACCTACACCAACCAGCTGCAGCTTCTTAGTGAAGCCTTCGGT + //!AACACCGATAACCATTGAGTTCAGCAGGGCACGCGCGGTACCAGCCTGTGCCCAACCGTC + //!TGCGTAACCATCACGCGGACCGAAGGTCAGGGTATTATCTGCATGTTTAACTTCAACAGC + //!ATCGTTGAGAGTACGAGTCAGCTCGCCGTTTTTACCTTTGATCGTAATAACCTGACCGTT + //!GATTTTTACGTCAACGCCGGCAGGAACAACGACCGGTGCTTTAGCAACACGAGACAT".to_string(); + //! gff_write(seq_region, vec![record], &filename, true); + //! return Ok(()); + //! } + //!``` + //! + use std::io::{self, Write}; + use std::fs; + use regex::Regex; + use std::vec::Vec; + use std::str; + use std::convert::AsRef; + use protein_translate::translate; + use std::path::Path; + use bio::alphabets::dna::revcomp; + use anyhow::anyhow; + use std::collections::BTreeMap; + use std::fs::{OpenOptions, File}; + use anyhow::Context; + use std::collections::HashSet; + use paste::paste; + use std::convert::TryInto; + use chrono::prelude::*; + /// import macro to create get_ functions for the values + use crate::create_getters; + /// import macro to create the set_ functions for the values in a Builder format + use crate::create_builder; + /// An EMBL reader. + pub struct Records + where + B: io::BufRead, + { + reader: Reader, + error_has_occurred: bool, + } + #[automatically_derived] + impl ::core::fmt::Debug for Records + where + B: io::BufRead, + { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "Records", + "reader", + &self.reader, + "error_has_occurred", + &&self.error_has_occurred, + ) + } + } + impl Records + where + B: io::BufRead, + { + #[allow(unused_mut)] + pub fn new(mut reader: Reader) -> Self { + Records { + reader: reader, + error_has_occurred: false, + } + } + } + impl Iterator for Records + where + B: io::BufRead, + { + type Item = Result; + fn next(&mut self) -> Option> { + if self.error_has_occurred { + { + ::std::io::_print( + format_args!("error was encountered in iteration\n"), + ); + }; + None + } else { + let mut record = Record::new(); + match self.reader.read(&mut record) { + Ok(_) => if record.is_empty() { None } else { Some(Ok(record)) } + Err(err) => { + self.error_has_occurred = true; + Some( + Err( + ::anyhow::Error::msg( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("next record read error {0:?}", err), + ); + res + }), + ), + ), + ) + } + } + } + } + } + pub trait EmblRead { + fn read(&mut self, record: &mut Record) -> Result; + } + ///per line reader for the file + pub struct Reader { + reader: B, + line_buffer: String, + } + #[automatically_derived] + impl ::core::fmt::Debug for Reader { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "Reader", + "reader", + &self.reader, + "line_buffer", + &&self.line_buffer, + ) + } + } + #[automatically_derived] + impl ::core::default::Default for Reader { + #[inline] + fn default() -> Reader { + Reader { + reader: ::core::default::Default::default(), + line_buffer: ::core::default::Default::default(), + } + } + } + impl Reader> { + /// Read Embl from given file path in given format. + pub fn from_file + std::fmt::Debug>( + path: P, + ) -> anyhow::Result { + fs::File::open(&path) + .map(Reader::new) + .with_context(|| ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("Failed to read Embl from {0:#?}", path), + ); + res + })) + } + } + impl Reader> + where + R: io::Read, + { + pub fn new(reader: R) -> Self { + Reader { + reader: io::BufReader::new(reader), + line_buffer: String::new(), + } + } + } + impl Reader + where + B: io::BufRead, + { + pub fn from_bufread(bufreader: B) -> Self { + Reader { + reader: bufreader, + line_buffer: String::new(), + } + } + pub fn records(self) -> Records { + Records { + reader: self, + error_has_occurred: false, + } + } + } + ///main embl parser + impl<'a, B> EmblRead for Reader + where + B: io::BufRead, + { + #[allow(unused_mut)] + #[allow(unused_variables)] + #[allow(unused_assignments)] + fn read(&mut self, record: &mut Record) -> Result { + record.rec_clear(); + let mut sequences = String::new(); + let mut source_map = SourceAttributeBuilder::new(); + let mut cds = FeatureAttributeBuilder::new(); + let mut seq_features = SequenceAttributeBuilder::new(); + let mut cds_counter: i32 = 0; + let mut source_counter: i32 = 0; + let mut prev_end: u32 = 0; + let mut organism = String::new(); + let mut mol_type = String::new(); + let mut strain = String::new(); + let mut source_name = String::new(); + let mut type_material = String::new(); + let mut theend: u32 = 0; + let mut thestart: u32 = 0; + let mut db_xref = String::new(); + if self.line_buffer.is_empty() { + self.reader.read_line(&mut self.line_buffer)?; + if self.line_buffer.is_empty() { + return Ok(record.to_owned()); + } + } + 'outer: while !self.line_buffer.is_empty() { + if self.line_buffer.starts_with("ID") { + record.rec_clear(); + let mut header_fields: Vec<&str> = self + .line_buffer + .split_whitespace() + .collect(); + let header_len = header_fields.len(); + let mut header_iter = header_fields.iter(); + header_iter.next(); + record.id = header_iter + .next() + .ok_or_else(|| ::anyhow::__private::must_use({ + let error = ::anyhow::__private::format_err( + format_args!("missing record id"), + ); + error + }))? + .to_string(); + if record.id.ends_with(";") { + record.id.pop(); + } + header_iter.next(); + header_iter.next(); + header_iter.next(); + header_iter.next(); + header_iter.next(); + header_iter.next(); + header_iter.next(); + let lens = header_iter + .next() + .ok_or_else(|| ::anyhow::__private::must_use({ + let error = ::anyhow::__private::format_err( + format_args!("missing record length"), + ); + error + }))? + .to_string(); + record.length = lens.trim().parse::()?; + self.line_buffer.clear(); + } + if self.line_buffer.starts_with("FT source") { + let re = Regex::new(r"([0-9]+)[[:punct:]]+([0-9]+)")?; + let location = re + .captures(&self.line_buffer) + .ok_or_else(|| ::anyhow::__private::must_use({ + let error = ::anyhow::__private::format_err( + format_args!("missing location"), + ); + error + }))?; + let start = &location[1]; + let end = &location[2]; + thestart = start.trim().parse::()?; + source_counter += 1; + source_name = ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("source_{0}_{1}", record.id, source_counter), + ); + res + }) + .to_string(); + thestart += prev_end; + theend = end.trim().parse::()? + prev_end; + loop { + self.line_buffer.clear(); + self.reader.read_line(&mut self.line_buffer)?; + if self.line_buffer.starts_with("FT CDS") { + record + .source_map + .set_counter(source_name.to_string()) + .set_start(RangeValue::Exact(thestart)) + .set_stop(RangeValue::Exact(theend)) + .set_organism(organism.clone()) + .set_mol_type(mol_type.clone()) + .set_strain(strain.clone()) + .set_type_material(type_material.clone()) + .set_db_xref(db_xref.clone()); + continue 'outer; + } + if self.line_buffer.contains("/organism") { + let org: Vec<&str> = self.line_buffer.split('\"').collect(); + organism = org[1].to_string(); + } + if self.line_buffer.contains("/mol_type") { + let mol: Vec<&str> = self.line_buffer.split('\"').collect(); + mol_type = mol[1].to_string(); + } + if self.line_buffer.contains("/strain") { + let stra: Vec<&str> = self.line_buffer.split('\"').collect(); + strain = stra[1].to_string(); + } + if self.line_buffer.contains("/type_material") { + let mat: Vec<&str> = self.line_buffer.split('\"').collect(); + type_material = mat[1].to_string(); + } + if self.line_buffer.contains("/db_xref") { + let db: Vec<&str> = self.line_buffer.split('\"').collect(); + db_xref = db[1].to_string(); + } + } + } + if self.line_buffer.starts_with("FT CDS") { + let mut startiter: Vec<_> = Vec::new(); + let mut enditer: Vec<_> = Vec::new(); + let mut thestart: u32 = 0; + let mut thend: u32 = 0; + let mut joined: bool = false; + let joined = if self.line_buffer.contains("join") { + true + } else { + false + }; + let re = Regex::new(r"([0-9]+)[[:punct:]]+([0-9]+)")?; + for cap in re.captures_iter(&self.line_buffer) { + cds_counter += 1; + thestart = cap[1] + .parse() + .expect("failed to match and parse numerical start"); + theend = cap[2] + .parse() + .expect("failed to match and parse numerical end"); + startiter.push(thestart); + enditer.push(theend); + } + let mut gene = String::new(); + let mut product = String::new(); + let strand: i8 = if self.line_buffer.contains("complement") { + -1 + } else { + 1 + }; + let mut locus_tag = String::new(); + let mut codon_start: u8 = 1; + loop { + self.line_buffer.clear(); + self.reader.read_line(&mut self.line_buffer)?; + if self.line_buffer.contains("/locus_tag=") { + let loctag: Vec<&str> = self + .line_buffer + .split('\"') + .collect(); + locus_tag = loctag[1].to_string(); + } + if self.line_buffer.contains("/codon_start") { + let codstart: Vec<&str> = self + .line_buffer + .split('=') + .collect(); + let valstart = codstart[1].trim().parse::()?; + codon_start = valstart; + } + if self.line_buffer.contains("/gene=") { + let gen: Vec<&str> = self.line_buffer.split('\"').collect(); + gene = gen[1].to_string(); + } + if self.line_buffer.contains("/product") { + let prod: Vec<&str> = self.line_buffer.split('\"').collect(); + product = substitute_odd_punctuation(prod[1].to_string())?; + } + if self.line_buffer.starts_with("FT CDS") + || self.line_buffer.starts_with("SQ Sequence") + || self.line_buffer.starts_with("FT intron") + || self.line_buffer.starts_with("FT exon") + || self.line_buffer.starts_with(" misc_feature") + { + if locus_tag.is_empty() { + locus_tag = ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("CDS_{0}", cds_counter), + ); + res + }) + .to_string(); + } + if joined { + for (i, m) in startiter.iter().enumerate() { + let loc_tag = ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("{0}_{1}", locus_tag.clone(), i), + ); + res + }); + record + .cds + .set_counter(loc_tag) + .set_start(RangeValue::Exact(*m)) + .set_stop(RangeValue::Exact(enditer[i])) + .set_gene(gene.to_string()) + .set_product(product.to_string()) + .set_codon_start(codon_start) + .set_strand(strand); + } + continue 'outer; + } else { + record + .cds + .set_counter(locus_tag.clone()) + .set_start(RangeValue::Exact(thestart)) + .set_stop(RangeValue::Exact(theend)) + .set_gene(gene.to_string()) + .set_product(product.to_string()) + .set_codon_start(codon_start) + .set_strand(strand); + continue 'outer; + } + } + } + } + if self.line_buffer.starts_with("SQ Sequence") { + let mut sequences = String::new(); + let result_seq = loop { + self.line_buffer.clear(); + self.reader.read_line(&mut self.line_buffer)?; + if self.line_buffer.starts_with("//") { + break sequences; + } else { + let s: Vec<&str> = self + .line_buffer + .split_whitespace() + .collect(); + let sequence = if s.len() > 1 { + s[0..s.len() - 1].join("") + } else { + String::new() + }; + sequences.push_str(&sequence); + } + }; + record.sequence = result_seq.to_string(); + let mut iterablecount: u32 = 0; + for (key, val) in record.cds.iter_sorted() { + let ( + mut a, + mut b, + mut c, + mut d, + ): (Option, Option, Option, Option) = ( + None, + None, + None, + None, + ); + for value in val { + match value { + FeatureAttributes::Start { value } => { + a = match value { + RangeValue::Exact(v) => Some(*v), + RangeValue::LessThan(v) => Some(*v), + RangeValue::GreaterThan(v) => Some(*v), + }; + } + FeatureAttributes::Stop { value } => { + b = match value { + RangeValue::Exact(v) => Some(*v), + RangeValue::LessThan(v) => Some(*v), + RangeValue::GreaterThan(v) => Some(*v), + }; + } + FeatureAttributes::Strand { value } => { + c = match value { + value => Some(*value), + }; + } + FeatureAttributes::CodonStart { value } => { + d = match value { + value => Some(value.clone()), + }; + } + _ => {} + } + } + let sta = a + .map(|o| o as usize) + .ok_or( + ::anyhow::__private::must_use({ + let error = ::anyhow::__private::format_err( + format_args!("No value for start"), + ); + error + }), + )?; + let sto = b + .map(|t| t as usize) + .ok_or( + ::anyhow::__private::must_use({ + let error = ::anyhow::__private::format_err( + format_args!("No value for stop"), + ); + error + }), + )? - 1; + let stra = c + .map(|u| u as i8) + .ok_or( + ::anyhow::__private::must_use({ + let error = ::anyhow::__private::format_err( + format_args!("No value for strand"), + ); + error + }), + )?; + let cod = d + .map(|v| v as usize - 1) + .ok_or( + ::anyhow::__private::must_use({ + let error = ::anyhow::__private::format_err( + format_args!("No value for strand"), + ); + error + }), + )?; + let star = sta.try_into()?; + let stow = sto.try_into()?; + let codd = cod.try_into()?; + let mut sliced_sequence: &str = ""; + if stra == -1 { + if cod > 1 { + { + ::std::io::_print( + format_args!( + "reverse strand coding start more than one {0:?}\n", + &iterablecount, + ), + ); + }; + if sto + 1 <= record.sequence.len() { + sliced_sequence = &record.sequence[sta + cod..sto + 1]; + } else { + sliced_sequence = &record.sequence[sta + cod..sto]; + } + } else { + { + ::std::io::_print( + format_args!( + "record sta {0:?} sto {1:?} cod {2:?} stra {3:?} record.seq length {4:?}\n", + &sta, + &sto, + &cod, + &stra, + &record.sequence.len(), + ), + ); + }; + { + ::std::io::_print( + format_args!( + "sliced sta {0:?} sliced sto {1:?} record.id {2:?}\n", + sta, + sto, + &record.id, + ), + ); + }; + { + ::std::io::_print( + format_args!( + "iterable count is {0:?} reverse strand codon start one\n", + &iterablecount, + ), + ); + }; + { + ::std::io::_print( + format_args!( + "this is the sequence len {0:?}\n", + &record.sequence.len(), + ), + ); + }; + if sto + 1 <= record.sequence.len() { + sliced_sequence = &record.sequence[sta..sto + 1]; + } else { + sliced_sequence = &record.sequence[sta..sto]; + } + { + ::std::io::_print( + format_args!( + "iterable count after is {0:?}\n", + &iterablecount, + ), + ); + }; + } + let cds_char = sliced_sequence; + let prot_seq = translate(&revcomp(cds_char.as_bytes())); + let parts: Vec<&str> = prot_seq.split('*').collect(); + { + ::std::io::_print( + format_args!("this is the prot_seq {0:?}\n", &prot_seq), + ); + }; + record + .seq_features + .set_counter(key.to_string()) + .set_start(RangeValue::Exact(star)) + .set_stop(RangeValue::Exact(stow)) + .set_sequence_ffn(cds_char.to_string()) + .set_sequence_faa(parts[0].to_string()) + .set_codon_start(codd) + .set_strand(stra); + } else { + if cod > 1 { + sliced_sequence = &record.sequence[sta + cod - 1..sto]; + } else { + sliced_sequence = &record.sequence[sta - 1..sto]; + } + let cds_char = sliced_sequence; + let prot_seq = translate(cds_char.as_bytes()); + let parts: Vec<&str> = prot_seq.split('*').collect(); + record + .seq_features + .set_counter(key.to_string()) + .set_start(RangeValue::Exact(star)) + .set_stop(RangeValue::Exact(stow)) + .set_sequence_ffn(cds_char.to_string()) + .set_sequence_faa(parts[0].to_string()) + .set_codon_start(codd) + .set_strand(stra); + } + } + return Ok(record.to_owned()); + } + self.line_buffer.clear(); + self.reader.read_line(&mut self.line_buffer)?; + } + Ok(record.to_owned()) + } + } + ///stores a value for start or stop (end) which can be denoted as a < value or > value. + pub enum RangeValue { + Exact(u32), + LessThan(u32), + GreaterThan(u32), + } + #[automatically_derived] + impl ::core::fmt::Debug for RangeValue { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + RangeValue::Exact(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "Exact", + &__self_0, + ) + } + RangeValue::LessThan(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "LessThan", + &__self_0, + ) + } + RangeValue::GreaterThan(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "GreaterThan", + &__self_0, + ) + } + } + } + } + #[automatically_derived] + impl ::core::hash::Hash for RangeValue { + #[inline] + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + let __self_discr = ::core::intrinsics::discriminant_value(self); + ::core::hash::Hash::hash(&__self_discr, state); + match self { + RangeValue::Exact(__self_0) => ::core::hash::Hash::hash(__self_0, state), + RangeValue::LessThan(__self_0) => { + ::core::hash::Hash::hash(__self_0, state) + } + RangeValue::GreaterThan(__self_0) => { + ::core::hash::Hash::hash(__self_0, state) + } + } + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for RangeValue {} + #[automatically_derived] + impl ::core::cmp::PartialEq for RangeValue { + #[inline] + fn eq(&self, other: &RangeValue) -> bool { + let __self_discr = ::core::intrinsics::discriminant_value(self); + let __arg1_discr = ::core::intrinsics::discriminant_value(other); + __self_discr == __arg1_discr + && match (self, other) { + (RangeValue::Exact(__self_0), RangeValue::Exact(__arg1_0)) => { + __self_0 == __arg1_0 + } + (RangeValue::LessThan(__self_0), RangeValue::LessThan(__arg1_0)) => { + __self_0 == __arg1_0 + } + ( + RangeValue::GreaterThan(__self_0), + RangeValue::GreaterThan(__arg1_0), + ) => __self_0 == __arg1_0, + _ => unsafe { ::core::intrinsics::unreachable() } + } + } + } + #[automatically_derived] + impl ::core::cmp::Eq for RangeValue { + #[inline] + #[doc(hidden)] + #[coverage(off)] + fn assert_receiver_is_total_eq(&self) -> () { + let _: ::core::cmp::AssertParamIsEq; + } + } + #[automatically_derived] + impl ::core::clone::Clone for RangeValue { + #[inline] + fn clone(&self) -> RangeValue { + match self { + RangeValue::Exact(__self_0) => { + RangeValue::Exact(::core::clone::Clone::clone(__self_0)) + } + RangeValue::LessThan(__self_0) => { + RangeValue::LessThan(::core::clone::Clone::clone(__self_0)) + } + RangeValue::GreaterThan(__self_0) => { + RangeValue::GreaterThan(::core::clone::Clone::clone(__self_0)) + } + } + } + } + impl RangeValue { + pub fn get_value(&self) -> u32 { + match self { + RangeValue::Exact(value) => *value, + RangeValue::LessThan(value) => *value, + RangeValue::GreaterThan(value) => *value, + } + } + } + ///stores the details of the source features in genbank (contigs) + pub enum SourceAttributes { + Start { value: RangeValue }, + Stop { value: RangeValue }, + Organism { value: String }, + MolType { value: String }, + Strain { value: String }, + CultureCollection { value: String }, + TypeMaterial { value: String }, + DbXref { value: String }, + } + #[automatically_derived] + impl ::core::fmt::Debug for SourceAttributes { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + SourceAttributes::Start { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Start", + "value", + &__self_0, + ) + } + SourceAttributes::Stop { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Stop", + "value", + &__self_0, + ) + } + SourceAttributes::Organism { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Organism", + "value", + &__self_0, + ) + } + SourceAttributes::MolType { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "MolType", + "value", + &__self_0, + ) + } + SourceAttributes::Strain { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Strain", + "value", + &__self_0, + ) + } + SourceAttributes::CultureCollection { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "CultureCollection", + "value", + &__self_0, + ) + } + SourceAttributes::TypeMaterial { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "TypeMaterial", + "value", + &__self_0, + ) + } + SourceAttributes::DbXref { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "DbXref", + "value", + &__self_0, + ) + } + } + } + } + #[automatically_derived] + impl ::core::cmp::Eq for SourceAttributes { + #[inline] + #[doc(hidden)] + #[coverage(off)] + fn assert_receiver_is_total_eq(&self) -> () { + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for SourceAttributes {} + #[automatically_derived] + impl ::core::cmp::PartialEq for SourceAttributes { + #[inline] + fn eq(&self, other: &SourceAttributes) -> bool { + let __self_discr = ::core::intrinsics::discriminant_value(self); + let __arg1_discr = ::core::intrinsics::discriminant_value(other); + __self_discr == __arg1_discr + && match (self, other) { + ( + SourceAttributes::Start { value: __self_0 }, + SourceAttributes::Start { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SourceAttributes::Stop { value: __self_0 }, + SourceAttributes::Stop { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SourceAttributes::Organism { value: __self_0 }, + SourceAttributes::Organism { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SourceAttributes::MolType { value: __self_0 }, + SourceAttributes::MolType { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SourceAttributes::Strain { value: __self_0 }, + SourceAttributes::Strain { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SourceAttributes::CultureCollection { value: __self_0 }, + SourceAttributes::CultureCollection { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SourceAttributes::TypeMaterial { value: __self_0 }, + SourceAttributes::TypeMaterial { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SourceAttributes::DbXref { value: __self_0 }, + SourceAttributes::DbXref { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + _ => unsafe { ::core::intrinsics::unreachable() } + } + } + } + #[automatically_derived] + impl ::core::hash::Hash for SourceAttributes { + #[inline] + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + let __self_discr = ::core::intrinsics::discriminant_value(self); + ::core::hash::Hash::hash(&__self_discr, state); + match self { + SourceAttributes::Start { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SourceAttributes::Stop { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SourceAttributes::Organism { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SourceAttributes::MolType { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SourceAttributes::Strain { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SourceAttributes::CultureCollection { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SourceAttributes::TypeMaterial { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SourceAttributes::DbXref { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + } + } + } + #[automatically_derived] + impl ::core::clone::Clone for SourceAttributes { + #[inline] + fn clone(&self) -> SourceAttributes { + match self { + SourceAttributes::Start { value: __self_0 } => { + SourceAttributes::Start { + value: ::core::clone::Clone::clone(__self_0), + } + } + SourceAttributes::Stop { value: __self_0 } => { + SourceAttributes::Stop { + value: ::core::clone::Clone::clone(__self_0), + } + } + SourceAttributes::Organism { value: __self_0 } => { + SourceAttributes::Organism { + value: ::core::clone::Clone::clone(__self_0), + } + } + SourceAttributes::MolType { value: __self_0 } => { + SourceAttributes::MolType { + value: ::core::clone::Clone::clone(__self_0), + } + } + SourceAttributes::Strain { value: __self_0 } => { + SourceAttributes::Strain { + value: ::core::clone::Clone::clone(__self_0), + } + } + SourceAttributes::CultureCollection { value: __self_0 } => { + SourceAttributes::CultureCollection { + value: ::core::clone::Clone::clone(__self_0), + } + } + SourceAttributes::TypeMaterial { value: __self_0 } => { + SourceAttributes::TypeMaterial { + value: ::core::clone::Clone::clone(__self_0), + } + } + SourceAttributes::DbXref { value: __self_0 } => { + SourceAttributes::DbXref { + value: ::core::clone::Clone::clone(__self_0), + } + } + } + } + } + impl SourceAttributeBuilder { + pub fn get_start(&self, key: &str) -> Option<&RangeValue> { + self.source_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SourceAttributes::Start { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_stop(&self, key: &str) -> Option<&RangeValue> { + self.source_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SourceAttributes::Stop { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_organism(&self, key: &str) -> Option<&String> { + self.source_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SourceAttributes::Organism { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_mol_type(&self, key: &str) -> Option<&String> { + self.source_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SourceAttributes::MolType { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_strain(&self, key: &str) -> Option<&String> { + self.source_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SourceAttributes::Strain { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_type_material(&self, key: &str) -> Option<&String> { + self.source_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SourceAttributes::TypeMaterial { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_db_xref(&self, key: &str) -> Option<&String> { + self.source_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SourceAttributes::DbXref { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + } + ///builder for the source information on a per record basis + pub struct SourceAttributeBuilder { + pub source_attributes: BTreeMap>, + pub source_name: Option, + } + #[automatically_derived] + impl ::core::fmt::Debug for SourceAttributeBuilder { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "SourceAttributeBuilder", + "source_attributes", + &self.source_attributes, + "source_name", + &&self.source_name, + ) + } + } + #[automatically_derived] + impl ::core::default::Default for SourceAttributeBuilder { + #[inline] + fn default() -> SourceAttributeBuilder { + SourceAttributeBuilder { + source_attributes: ::core::default::Default::default(), + source_name: ::core::default::Default::default(), + } + } + } + #[automatically_derived] + impl ::core::clone::Clone for SourceAttributeBuilder { + #[inline] + fn clone(&self) -> SourceAttributeBuilder { + SourceAttributeBuilder { + source_attributes: ::core::clone::Clone::clone(&self.source_attributes), + source_name: ::core::clone::Clone::clone(&self.source_name), + } + } + } + impl SourceAttributeBuilder { + pub fn set_source_name(&mut self, name: String) { + self.source_name = Some(name); + } + pub fn get_source_name(&self) -> Option<&String> { + self.source_name.as_ref() + } + pub fn add_source_attribute( + &mut self, + key: String, + attribute: SourceAttributes, + ) { + self.source_attributes + .entry(key) + .or_insert_with(HashSet::new) + .insert(attribute); + } + pub fn get_source_attributes( + &self, + key: &str, + ) -> Option<&HashSet> { + self.source_attributes.get(key) + } + } + impl SourceAttributeBuilder { + pub fn new() -> Self { + SourceAttributeBuilder { + source_attributes: BTreeMap::new(), + source_name: None, + } + } + pub fn set_counter(&mut self, counter: String) -> &mut Self { + self.source_name = Some(counter); + self + } + pub fn insert_to(&mut self, value: SourceAttributes) { + if let Some(counter) = &self.source_name { + self.source_attributes + .entry(counter.to_string()) + .or_insert_with(HashSet::new) + .insert(value); + } else { + { + ::core::panicking::panic_fmt(format_args!("Counter key not set")); + }; + } + } + pub fn set_start(&mut self, value: RangeValue) -> &mut Self { + self.insert_to(SourceAttributes::Start { value }); + self + } + pub fn set_stop(&mut self, value: RangeValue) -> &mut Self { + self.insert_to(SourceAttributes::Stop { value }); + self + } + pub fn set_organism(&mut self, value: String) -> &mut Self { + self.insert_to(SourceAttributes::Organism { + value, + }); + self + } + pub fn set_mol_type(&mut self, value: String) -> &mut Self { + self.insert_to(SourceAttributes::MolType { value }); + self + } + pub fn set_strain(&mut self, value: String) -> &mut Self { + self.insert_to(SourceAttributes::Strain { value }); + self + } + pub fn set_type_material(&mut self, value: String) -> &mut Self { + self.insert_to(SourceAttributes::TypeMaterial { + value, + }); + self + } + pub fn set_db_xref(&mut self, value: String) -> &mut Self { + self.insert_to(SourceAttributes::DbXref { value }); + self + } + pub fn build(self) -> BTreeMap> { + self.source_attributes + } + pub fn iter_sorted( + &self, + ) -> std::collections::btree_map::Iter> { + self.source_attributes.iter() + } + pub fn default() -> Self { + SourceAttributeBuilder { + source_attributes: BTreeMap::new(), + source_name: None, + } + } + } + ///attributes for each feature, cds or gene + pub enum FeatureAttributes { + Start { value: RangeValue }, + Stop { value: RangeValue }, + Gene { value: String }, + Product { value: String }, + CodonStart { value: u8 }, + Strand { value: i8 }, + } + #[automatically_derived] + impl ::core::fmt::Debug for FeatureAttributes { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + FeatureAttributes::Start { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Start", + "value", + &__self_0, + ) + } + FeatureAttributes::Stop { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Stop", + "value", + &__self_0, + ) + } + FeatureAttributes::Gene { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Gene", + "value", + &__self_0, + ) + } + FeatureAttributes::Product { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Product", + "value", + &__self_0, + ) + } + FeatureAttributes::CodonStart { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "CodonStart", + "value", + &__self_0, + ) + } + FeatureAttributes::Strand { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Strand", + "value", + &__self_0, + ) + } + } + } + } + #[automatically_derived] + impl ::core::cmp::Eq for FeatureAttributes { + #[inline] + #[doc(hidden)] + #[coverage(off)] + fn assert_receiver_is_total_eq(&self) -> () { + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + } + } + #[automatically_derived] + impl ::core::hash::Hash for FeatureAttributes { + #[inline] + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + let __self_discr = ::core::intrinsics::discriminant_value(self); + ::core::hash::Hash::hash(&__self_discr, state); + match self { + FeatureAttributes::Start { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + FeatureAttributes::Stop { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + FeatureAttributes::Gene { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + FeatureAttributes::Product { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + FeatureAttributes::CodonStart { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + FeatureAttributes::Strand { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + } + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for FeatureAttributes {} + #[automatically_derived] + impl ::core::cmp::PartialEq for FeatureAttributes { + #[inline] + fn eq(&self, other: &FeatureAttributes) -> bool { + let __self_discr = ::core::intrinsics::discriminant_value(self); + let __arg1_discr = ::core::intrinsics::discriminant_value(other); + __self_discr == __arg1_discr + && match (self, other) { + ( + FeatureAttributes::Start { value: __self_0 }, + FeatureAttributes::Start { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + FeatureAttributes::Stop { value: __self_0 }, + FeatureAttributes::Stop { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + FeatureAttributes::Gene { value: __self_0 }, + FeatureAttributes::Gene { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + FeatureAttributes::Product { value: __self_0 }, + FeatureAttributes::Product { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + FeatureAttributes::CodonStart { value: __self_0 }, + FeatureAttributes::CodonStart { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + FeatureAttributes::Strand { value: __self_0 }, + FeatureAttributes::Strand { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + _ => unsafe { ::core::intrinsics::unreachable() } + } + } + } + #[automatically_derived] + impl ::core::clone::Clone for FeatureAttributes { + #[inline] + fn clone(&self) -> FeatureAttributes { + match self { + FeatureAttributes::Start { value: __self_0 } => { + FeatureAttributes::Start { + value: ::core::clone::Clone::clone(__self_0), + } + } + FeatureAttributes::Stop { value: __self_0 } => { + FeatureAttributes::Stop { + value: ::core::clone::Clone::clone(__self_0), + } + } + FeatureAttributes::Gene { value: __self_0 } => { + FeatureAttributes::Gene { + value: ::core::clone::Clone::clone(__self_0), + } + } + FeatureAttributes::Product { value: __self_0 } => { + FeatureAttributes::Product { + value: ::core::clone::Clone::clone(__self_0), + } + } + FeatureAttributes::CodonStart { value: __self_0 } => { + FeatureAttributes::CodonStart { + value: ::core::clone::Clone::clone(__self_0), + } + } + FeatureAttributes::Strand { value: __self_0 } => { + FeatureAttributes::Strand { + value: ::core::clone::Clone::clone(__self_0), + } + } + } + } + } + impl FeatureAttributeBuilder { + pub fn get_start(&self, key: &str) -> Option<&RangeValue> { + self.attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let FeatureAttributes::Start { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_stop(&self, key: &str) -> Option<&RangeValue> { + self.attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let FeatureAttributes::Stop { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_gene(&self, key: &str) -> Option<&String> { + self.attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let FeatureAttributes::Gene { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_product(&self, key: &str) -> Option<&String> { + self.attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let FeatureAttributes::Product { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_codon_start(&self, key: &str) -> Option<&u8> { + self.attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let FeatureAttributes::CodonStart { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_strand(&self, key: &str) -> Option<&i8> { + self.attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let FeatureAttributes::Strand { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + } + ///builder for the feature information on a per coding sequence (CDS) basis + pub struct FeatureAttributeBuilder { + pub attributes: BTreeMap>, + locus_tag: Option, + } + #[automatically_derived] + impl ::core::fmt::Debug for FeatureAttributeBuilder { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "FeatureAttributeBuilder", + "attributes", + &self.attributes, + "locus_tag", + &&self.locus_tag, + ) + } + } + #[automatically_derived] + impl ::core::default::Default for FeatureAttributeBuilder { + #[inline] + fn default() -> FeatureAttributeBuilder { + FeatureAttributeBuilder { + attributes: ::core::default::Default::default(), + locus_tag: ::core::default::Default::default(), + } + } + } + #[automatically_derived] + impl ::core::clone::Clone for FeatureAttributeBuilder { + #[inline] + fn clone(&self) -> FeatureAttributeBuilder { + FeatureAttributeBuilder { + attributes: ::core::clone::Clone::clone(&self.attributes), + locus_tag: ::core::clone::Clone::clone(&self.locus_tag), + } + } + } + impl FeatureAttributeBuilder { + pub fn new() -> Self { + FeatureAttributeBuilder { + attributes: BTreeMap::new(), + locus_tag: None, + } + } + pub fn set_counter(&mut self, counter: String) -> &mut Self { + self.locus_tag = Some(counter); + self + } + pub fn insert_to(&mut self, value: FeatureAttributes) { + if let Some(counter) = &self.locus_tag { + self.attributes + .entry(counter.to_string()) + .or_insert_with(HashSet::new) + .insert(value); + } else { + { + ::core::panicking::panic_fmt(format_args!("Counter key not set")); + }; + } + } + pub fn set_start(&mut self, value: RangeValue) -> &mut Self { + self.insert_to(FeatureAttributes::Start { value }); + self + } + pub fn set_stop(&mut self, value: RangeValue) -> &mut Self { + self.insert_to(FeatureAttributes::Stop { value }); + self + } + pub fn set_gene(&mut self, value: String) -> &mut Self { + self.insert_to(FeatureAttributes::Gene { value }); + self + } + pub fn set_product(&mut self, value: String) -> &mut Self { + self.insert_to(FeatureAttributes::Product { + value, + }); + self + } + pub fn set_codon_start(&mut self, value: u8) -> &mut Self { + self.insert_to(FeatureAttributes::CodonStart { + value, + }); + self + } + pub fn set_strand(&mut self, value: i8) -> &mut Self { + self.insert_to(FeatureAttributes::Strand { value }); + self + } + pub fn build(self) -> BTreeMap> { + self.attributes + } + pub fn iter_sorted( + &self, + ) -> std::collections::btree_map::Iter> { + self.attributes.iter() + } + pub fn default() -> Self { + FeatureAttributeBuilder { + attributes: BTreeMap::new(), + locus_tag: None, + } + } + } + ///stores the sequences of the coding sequences (genes) and proteins. Also stores start, stop, codon_start and strand information + pub enum SequenceAttributes { + Start { value: RangeValue }, + Stop { value: RangeValue }, + SequenceFfn { value: String }, + SequenceFaa { value: String }, + CodonStart { value: u8 }, + Strand { value: i8 }, + } + #[automatically_derived] + impl ::core::fmt::Debug for SequenceAttributes { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + SequenceAttributes::Start { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Start", + "value", + &__self_0, + ) + } + SequenceAttributes::Stop { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Stop", + "value", + &__self_0, + ) + } + SequenceAttributes::SequenceFfn { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "SequenceFfn", + "value", + &__self_0, + ) + } + SequenceAttributes::SequenceFaa { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "SequenceFaa", + "value", + &__self_0, + ) + } + SequenceAttributes::CodonStart { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "CodonStart", + "value", + &__self_0, + ) + } + SequenceAttributes::Strand { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Strand", + "value", + &__self_0, + ) + } + } + } + } + #[automatically_derived] + impl ::core::cmp::Eq for SequenceAttributes { + #[inline] + #[doc(hidden)] + #[coverage(off)] + fn assert_receiver_is_total_eq(&self) -> () { + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for SequenceAttributes {} + #[automatically_derived] + impl ::core::cmp::PartialEq for SequenceAttributes { + #[inline] + fn eq(&self, other: &SequenceAttributes) -> bool { + let __self_discr = ::core::intrinsics::discriminant_value(self); + let __arg1_discr = ::core::intrinsics::discriminant_value(other); + __self_discr == __arg1_discr + && match (self, other) { + ( + SequenceAttributes::Start { value: __self_0 }, + SequenceAttributes::Start { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SequenceAttributes::Stop { value: __self_0 }, + SequenceAttributes::Stop { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SequenceAttributes::SequenceFfn { value: __self_0 }, + SequenceAttributes::SequenceFfn { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SequenceAttributes::SequenceFaa { value: __self_0 }, + SequenceAttributes::SequenceFaa { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SequenceAttributes::CodonStart { value: __self_0 }, + SequenceAttributes::CodonStart { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SequenceAttributes::Strand { value: __self_0 }, + SequenceAttributes::Strand { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + _ => unsafe { ::core::intrinsics::unreachable() } + } + } + } + #[automatically_derived] + impl ::core::hash::Hash for SequenceAttributes { + #[inline] + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + let __self_discr = ::core::intrinsics::discriminant_value(self); + ::core::hash::Hash::hash(&__self_discr, state); + match self { + SequenceAttributes::Start { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SequenceAttributes::Stop { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SequenceAttributes::SequenceFfn { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SequenceAttributes::SequenceFaa { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SequenceAttributes::CodonStart { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SequenceAttributes::Strand { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + } + } + } + #[automatically_derived] + impl ::core::clone::Clone for SequenceAttributes { + #[inline] + fn clone(&self) -> SequenceAttributes { + match self { + SequenceAttributes::Start { value: __self_0 } => { + SequenceAttributes::Start { + value: ::core::clone::Clone::clone(__self_0), + } + } + SequenceAttributes::Stop { value: __self_0 } => { + SequenceAttributes::Stop { + value: ::core::clone::Clone::clone(__self_0), + } + } + SequenceAttributes::SequenceFfn { value: __self_0 } => { + SequenceAttributes::SequenceFfn { + value: ::core::clone::Clone::clone(__self_0), + } + } + SequenceAttributes::SequenceFaa { value: __self_0 } => { + SequenceAttributes::SequenceFaa { + value: ::core::clone::Clone::clone(__self_0), + } + } + SequenceAttributes::CodonStart { value: __self_0 } => { + SequenceAttributes::CodonStart { + value: ::core::clone::Clone::clone(__self_0), + } + } + SequenceAttributes::Strand { value: __self_0 } => { + SequenceAttributes::Strand { + value: ::core::clone::Clone::clone(__self_0), + } + } + } + } + } + impl SequenceAttributeBuilder { + pub fn get_start(&self, key: &str) -> Option<&RangeValue> { + self.seq_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SequenceAttributes::Start { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_stop(&self, key: &str) -> Option<&RangeValue> { + self.seq_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SequenceAttributes::Stop { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_sequence_ffn(&self, key: &str) -> Option<&String> { + self.seq_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SequenceAttributes::SequenceFfn { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_sequence_faa(&self, key: &str) -> Option<&String> { + self.seq_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SequenceAttributes::SequenceFaa { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_codon_start(&self, key: &str) -> Option<&u8> { + self.seq_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SequenceAttributes::CodonStart { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_strand(&self, key: &str) -> Option<&i8> { + self.seq_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SequenceAttributes::Strand { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + } + ///builder for the sequence information on a per coding sequence (CDS) basis + pub struct SequenceAttributeBuilder { + pub seq_attributes: BTreeMap>, + locus_tag: Option, + } + #[automatically_derived] + impl ::core::fmt::Debug for SequenceAttributeBuilder { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "SequenceAttributeBuilder", + "seq_attributes", + &self.seq_attributes, + "locus_tag", + &&self.locus_tag, + ) + } + } + #[automatically_derived] + impl ::core::default::Default for SequenceAttributeBuilder { + #[inline] + fn default() -> SequenceAttributeBuilder { + SequenceAttributeBuilder { + seq_attributes: ::core::default::Default::default(), + locus_tag: ::core::default::Default::default(), + } + } + } + #[automatically_derived] + impl ::core::clone::Clone for SequenceAttributeBuilder { + #[inline] + fn clone(&self) -> SequenceAttributeBuilder { + SequenceAttributeBuilder { + seq_attributes: ::core::clone::Clone::clone(&self.seq_attributes), + locus_tag: ::core::clone::Clone::clone(&self.locus_tag), + } + } + } + impl SequenceAttributeBuilder { + pub fn new() -> Self { + SequenceAttributeBuilder { + seq_attributes: BTreeMap::new(), + locus_tag: None, + } + } + pub fn set_counter(&mut self, counter: String) -> &mut Self { + self.locus_tag = Some(counter); + self + } + pub fn insert_to(&mut self, value: SequenceAttributes) { + if let Some(counter) = &self.locus_tag { + self.seq_attributes + .entry(counter.to_string()) + .or_insert_with(HashSet::new) + .insert(value); + } else { + { + ::core::panicking::panic_fmt(format_args!("Counter key not set")); + }; + } + } + pub fn set_start(&mut self, value: RangeValue) -> &mut Self { + self.insert_to(SequenceAttributes::Start { value }); + self + } + pub fn set_stop(&mut self, value: RangeValue) -> &mut Self { + self.insert_to(SequenceAttributes::Stop { value }); + self + } + pub fn set_sequence_ffn(&mut self, value: String) -> &mut Self { + self.insert_to(SequenceAttributes::SequenceFfn { + value, + }); + self + } + pub fn set_sequence_faa(&mut self, value: String) -> &mut Self { + self.insert_to(SequenceAttributes::SequenceFaa { + value, + }); + self + } + pub fn set_codon_start(&mut self, value: u8) -> &mut Self { + self.insert_to(SequenceAttributes::CodonStart { + value, + }); + self + } + pub fn set_strand(&mut self, value: i8) -> &mut Self { + self.insert_to(SequenceAttributes::Strand { + value, + }); + self + } + pub fn build(self) -> BTreeMap> { + self.seq_attributes + } + pub fn iter_sorted( + &self, + ) -> std::collections::btree_map::Iter> { + self.seq_attributes.iter() + } + pub fn default() -> Self { + SequenceAttributeBuilder { + seq_attributes: BTreeMap::new(), + locus_tag: None, + } + } + } + ///product lines can contain difficult to parse punctuation such as biochemical symbols like unclosed single quotes, superscripts, single and double brackets etc. + ///here we substitute these for an underscore + pub fn substitute_odd_punctuation(input: String) -> Result { + let re = Regex::new(r"[/?()',`]|[α-ωΑ-Ω]")?; + let cleaned = input.trim_end_matches(&['\r', '\n'][..]); + Ok(re.replace_all(cleaned, "_").to_string()) + } + ///GFF3 field9 construct + pub struct GFFInner { + id: String, + name: String, + locus_tag: String, + gene: String, + product: String, + } + #[automatically_derived] + impl ::core::fmt::Debug for GFFInner { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field5_finish( + f, + "GFFInner", + "id", + &self.id, + "name", + &self.name, + "locus_tag", + &self.locus_tag, + "gene", + &self.gene, + "product", + &&self.product, + ) + } + } + impl GFFInner { + pub fn new( + id: String, + name: String, + locus_tag: String, + gene: String, + product: String, + ) -> Self { + GFFInner { + id, + name, + locus_tag, + gene, + product, + } + } + } + ///The main GFF3 construct + pub struct GFFOuter<'a> { + seqid: String, + source: String, + type_val: String, + start: u32, + end: u32, + score: f64, + strand: String, + phase: u8, + attributes: &'a GFFInner, + } + #[automatically_derived] + impl<'a> ::core::fmt::Debug for GFFOuter<'a> { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let names: &'static _ = &[ + "seqid", + "source", + "type_val", + "start", + "end", + "score", + "strand", + "phase", + "attributes", + ]; + let values: &[&dyn ::core::fmt::Debug] = &[ + &self.seqid, + &self.source, + &self.type_val, + &self.start, + &self.end, + &self.score, + &self.strand, + &self.phase, + &&self.attributes, + ]; + ::core::fmt::Formatter::debug_struct_fields_finish( + f, + "GFFOuter", + names, + values, + ) + } + } + impl<'a> GFFOuter<'a> { + pub fn new( + seqid: String, + source: String, + type_val: String, + start: u32, + end: u32, + score: f64, + strand: String, + phase: u8, + attributes: &'a GFFInner, + ) -> Self { + GFFOuter { + seqid, + source, + type_val, + start, + end, + score, + strand, + phase, + attributes, + } + } + pub fn field9_attributes_build(&self) -> String { + let mut full_field9 = Vec::new(); + if !self.attributes.id.is_empty() { + full_field9 + .push( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("id={0}", self.attributes.id), + ); + res + }), + ); + } + if !self.attributes.name.is_empty() { + full_field9 + .push( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("name={0}", self.attributes.name), + ); + res + }), + ); + } + if !self.attributes.gene.is_empty() { + full_field9 + .push( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("gene={0}", self.attributes.gene), + ); + res + }), + ); + } + if !self.attributes.locus_tag.is_empty() { + full_field9 + .push( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("locus_tag={0}", self.attributes.locus_tag), + ); + res + }), + ); + } + if !self.attributes.product.is_empty() { + full_field9 + .push( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("product={0}", self.attributes.product), + ); + res + }), + ); + } + full_field9.join(";") + } + } + ///formats the translation string which can be mulitple lines, for embl + pub fn format_translation(translation: &str) -> String { + let mut formatted = String::new(); + let cleaned_translation = translation.replace("\n", ""); + formatted.push_str(" /translation=\""); + let line_length: usize = 60; + let final_num = line_length - 15; + formatted + .push_str( + &::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("{0}\n", &cleaned_translation[0..final_num]), + ); + res + }), + ); + for i in (47..translation.len()).step_by(60) { + let end = i + 60 - 1; + let valid_end = if end >= translation.len() { + &cleaned_translation.len() - 1 + } else { + end + }; + formatted + .push_str( + &::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!( + " {0}", + &cleaned_translation[i..valid_end], + ), + ); + res + }), + ); + { + ::std::io::_print( + format_args!( + "cleaned translation leng is {0:?}\n", + &cleaned_translation[i..valid_end].len(), + ), + ); + }; + if *&cleaned_translation[i..valid_end].len() < 59 { + formatted.push('\"'); + } else { + formatted.push('\n'); + } + } + formatted + } + ///writes the DNA sequence in gbk format with numbering + pub fn write_gbk_format_sequence(sequence: &str, file: &mut File) -> io::Result<()> { + file.write_fmt(format_args!("ORIGIN\n"))?; + let mut formatted = String::new(); + let cleaned_input = sequence.replace("\n", ""); + let mut index = 1; + for (_i, chunk) in cleaned_input.as_bytes().chunks(60).enumerate() { + formatted + .push_str( + &::alloc::__export::must_use({ + let res = ::alloc::fmt::format(format_args!("{0:>5} ", index)); + res + }), + ); + for (j, sub_chunk) in chunk.chunks(10).enumerate() { + if j > 0 { + formatted.push(' '); + } + formatted.push_str(&String::from_utf8_lossy(sub_chunk)); + } + formatted.push('\n'); + index += 60; + } + file.write_fmt(format_args!("{0:>6}\n", &formatted))?; + file.write_fmt(format_args!("//\n"))?; + Ok(()) + } + ///saves the parsed data in genbank format + pub fn gbk_write( + seq_region: BTreeMap, + record_vec: Vec, + filename: &str, + ) -> io::Result<()> { + let now = Local::now(); + let formatted_date = now.format("%d-%b-%Y").to_string().to_uppercase(); + let mut file = OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(filename)?; + for (i, (key, _val)) in seq_region.iter().enumerate() { + let strain = match &record_vec[i].source_map.get_strain(key) { + Some(value) => value.to_string(), + None => "Unknown".to_string(), + }; + let organism = match &record_vec[i].source_map.get_organism(key) { + Some(value) => value.to_string(), + None => "Unknown".to_string(), + }; + let mol_type = match &record_vec[i].source_map.get_mol_type(key) { + Some(value) => value.to_string(), + None => "Unknown".to_string(), + }; + let type_material = match &record_vec[i].source_map.get_type_material(&key) { + Some(value) => value.to_string(), + None => "Unknown".to_string(), + }; + let db_xref = match &record_vec[i].source_map.get_db_xref(key) { + Some(value) => value.to_string(), + None => "Unknown".to_string(), + }; + let source_stop = match &record_vec[i].source_map.get_stop(key) { + Some(value) => value.get_value(), + None => { + { + { + ::std::io::_print(format_args!("stop value not found\n")); + }; + None + } + .expect("stop value not received") + } + }; + file.write_fmt( + format_args!( + "LOCUS {0} {1} bp DNA linear CON {2}\n", + &key, + &record_vec[i].sequence.len(), + &formatted_date, + ), + )?; + file.write_fmt(format_args!("DEFINITION {0} {1}.\n", &organism, &strain))?; + file.write_fmt(format_args!("ACCESSION {0}\n", &key))?; + file.write_fmt(format_args!("KEYWORDS .\n"))?; + file.write_fmt(format_args!("SOURCE {0} {1}\n", &organism, &strain))?; + file.write_fmt(format_args!(" ORGANISM {0} {1}\n", &organism, &strain))?; + file.write_fmt(format_args!("FEATURES Location/Qualifiers\n"))?; + file.write_fmt(format_args!(" source 1..{0}\n", &source_stop))?; + file.write_fmt( + format_args!(" /organism=\"{0}\"\n", &strain), + )?; + file.write_fmt( + format_args!(" /mol_type=\"{0}\"\n", &mol_type), + )?; + file.write_fmt( + format_args!(" /strain=\"{0}\"\n", &strain), + )?; + if type_material != *"Unknown".to_string() { + file.write_fmt( + format_args!( + " /type_material=\"{0}\"\n", + &type_material, + ), + )?; + } + file.write_fmt( + format_args!(" /db_xref=\"{0}\"\n", &db_xref), + )?; + for (locus_tag, _value) in &record_vec[i].cds.attributes { + let start = match &record_vec[i].cds.get_start(locus_tag) { + Some(value) => value.get_value(), + None => { + { + { + ::std::io::_print(format_args!("start value not found\n")); + }; + None + } + .expect("start value not received") + } + }; + let stop = match &record_vec[i].cds.get_stop(locus_tag) { + Some(value) => value.get_value(), + None => { + { + { + ::std::io::_print(format_args!("stop value not found\n")); + }; + None + } + .expect("stop value not received") + } + }; + let product = match &record_vec[i].cds.get_product(locus_tag) { + Some(value) => value.to_string(), + None => "unknown product".to_string(), + }; + let strand = match &record_vec[i].cds.get_strand(locus_tag) { + Some(value) => **value, + None => 0, + }; + let codon_start = match &record_vec[i].cds.get_codon_start(locus_tag) { + Some(value) => **value, + None => 0, + }; + let gene = match &record_vec[i].cds.get_gene(locus_tag) { + Some(value) => value.to_string(), + None => "unknown".to_string(), + }; + let translation = match &record_vec[i] + .seq_features + .get_sequence_faa(locus_tag) + { + Some(value) => value.to_string(), + None => "unknown".to_string(), + }; + if strand == 1 { + file.write_fmt( + format_args!(" gene {0}..{1}\n", &start, &stop), + )?; + } else { + file.write_fmt( + format_args!( + " gene complement({0}..{1})\n", + &start, + &stop, + ), + )?; + } + file.write_fmt( + format_args!(" /locus_tag=\"{0}\"\n", &locus_tag), + )?; + if strand == 1 { + file.write_fmt( + format_args!(" CDS {0}..{1}\n", &start, &stop), + )?; + } else { + file.write_fmt( + format_args!( + " CDS complement({0}..{1})\n", + &start, + &stop, + ), + )?; + } + file.write_fmt( + format_args!(" /locus_tag=\"{0}\"\n", &locus_tag), + )?; + file.write_fmt( + format_args!( + " /codon_start=\"{0}\"\n", + &codon_start, + ), + )?; + if gene != "unknown" { + file.write_fmt( + format_args!(" /gene=\"{0}\"\n", &gene), + )?; + } + if translation != "unknown" { + let formatted_translation = format_translation(&translation); + file.write_fmt(format_args!("{0}\n", &formatted_translation))?; + } + file.write_fmt( + format_args!(" /product=\"{0}\"\n", &product), + )?; + } + write_gbk_format_sequence(&record_vec[i].sequence, &mut file)?; + } + Ok(()) + } + ///saves the parsed data in gff3 format + #[allow(unused_assignments)] + #[allow(unused_variables)] + pub fn gff_write( + seq_region: BTreeMap, + mut record_vec: Vec, + filename: &str, + dna: bool, + ) -> io::Result<()> { + let mut file = OpenOptions::new().append(true).create(true).open(filename)?; + if file.metadata()?.len() == 0 { + file.write_fmt(format_args!("##gff-version 3\n"))?; + } + let mut full_seq = String::new(); + let mut prev_end: u32 = 0; + for (k, v) in seq_region.iter() { + file.write_fmt( + format_args!("##sequence-region\t{0}\t{1}\t{2}\n", &k, v.0, v.1), + )?; + } + for ((source_name, (seq_start, seq_end)), record) in seq_region + .iter() + .zip(record_vec.drain(..)) + { + if dna == true { + full_seq.push_str(&record.sequence); + } + for (locus_tag, _valu) in &record.cds.attributes { + let start = match record.cds.get_start(&locus_tag) { + Some(value) => value.get_value(), + None => { + { + { + ::std::io::_print(format_args!("start value not found\n")); + }; + None + } + .expect("start value not received") + } + }; + let stop = match record.cds.get_stop(&locus_tag) { + Some(value) => value.get_value(), + None => { + { + { + ::std::io::_print(format_args!("stop value not found\n")); + }; + None + } + .expect("stop value not received") + } + }; + let gene = match record.cds.get_gene(&locus_tag) { + Some(value) => value.to_string(), + None => "unknown".to_string(), + }; + let product = match record.cds.get_product(&locus_tag) { + Some(value) => value.to_string(), + None => "unknown product".to_string(), + }; + let strand = match record.cds.get_strand(&locus_tag) { + Some(valu) => { + match valu { + 1 => "+".to_string(), + -1 => "-".to_string(), + _ => { + { + ::std::io::_print( + format_args!( + "unexpected strand value {0} for locus_tag {1}\n", + valu, + &locus_tag, + ), + ); + }; + "unknownstrand".to_string() + } + } + } + None => "unknownvalue".to_string(), + }; + let phase = match record.cds.get_codon_start(&locus_tag) { + Some(valuer) => { + match valuer { + 1 => 0, + 2 => 1, + 3 => 2, + _ => { + { + ::std::io::_print( + format_args!( + "unexpected phase value {0} in the bagging area for locus_tag {1}\n", + valuer, + &locus_tag, + ), + ); + }; + 1 + } + } + } + None => 1, + }; + let gff_inner = GFFInner::new( + locus_tag.to_string(), + source_name.clone(), + locus_tag.to_string(), + gene, + product, + ); + let gff_outer = GFFOuter::new( + source_name.clone(), + ".".to_string(), + "CDS".to_string(), + start + prev_end, + stop + prev_end, + 0.0, + strand, + phase, + &gff_inner, + ); + let field9_attributes = gff_outer.field9_attributes_build(); + file.write_fmt( + format_args!( + "{0}\t{1}\t{2}\t{3:?}\t{4:?}\t{5}\t{6}\t{7}\t{8}\n", + gff_outer.seqid, + gff_outer.source, + gff_outer.type_val, + gff_outer.start, + gff_outer.end, + gff_outer.score, + gff_outer.strand, + gff_outer.phase, + field9_attributes, + ), + )?; + } + prev_end = *seq_end; + } + if dna { + file.write_fmt(format_args!("##FASTA\n"))?; + file.write_fmt(format_args!("{0}\n", full_seq))?; + } + Ok(()) + } + ///internal record containing data from a single source or contig. Has multiple features. + pub struct Record { + pub id: String, + pub length: u32, + pub sequence: String, + pub start: usize, + pub end: usize, + pub strand: i32, + pub cds: FeatureAttributeBuilder, + pub source_map: SourceAttributeBuilder, + pub seq_features: SequenceAttributeBuilder, + } + #[automatically_derived] + impl ::core::fmt::Debug for Record { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let names: &'static _ = &[ + "id", + "length", + "sequence", + "start", + "end", + "strand", + "cds", + "source_map", + "seq_features", + ]; + let values: &[&dyn ::core::fmt::Debug] = &[ + &self.id, + &self.length, + &self.sequence, + &self.start, + &self.end, + &self.strand, + &self.cds, + &self.source_map, + &&self.seq_features, + ]; + ::core::fmt::Formatter::debug_struct_fields_finish( + f, + "Record", + names, + values, + ) + } + } + #[automatically_derived] + impl ::core::clone::Clone for Record { + #[inline] + fn clone(&self) -> Record { + Record { + id: ::core::clone::Clone::clone(&self.id), + length: ::core::clone::Clone::clone(&self.length), + sequence: ::core::clone::Clone::clone(&self.sequence), + start: ::core::clone::Clone::clone(&self.start), + end: ::core::clone::Clone::clone(&self.end), + strand: ::core::clone::Clone::clone(&self.strand), + cds: ::core::clone::Clone::clone(&self.cds), + source_map: ::core::clone::Clone::clone(&self.source_map), + seq_features: ::core::clone::Clone::clone(&self.seq_features), + } + } + } + impl Record { + /// Create a new instance. + pub fn new() -> Self { + Record { + id: "".to_owned(), + length: 0, + sequence: "".to_owned(), + start: 0, + end: 0, + strand: 0, + source_map: SourceAttributeBuilder::new(), + cds: FeatureAttributeBuilder::new(), + seq_features: SequenceAttributeBuilder::new(), + } + } + pub fn is_empty(&mut self) -> bool { + self.id.is_empty() && self.length == 0 + } + pub fn check(&mut self) -> Result<(), &str> { + if self.id().is_empty() { + return Err("Expecting id for Embl record."); + } + Ok(()) + } + pub fn id(&mut self) -> &str { + &self.id + } + pub fn length(&mut self) -> u32 { + self.length + } + pub fn sequence(&mut self) -> &str { + &self.sequence + } + pub fn start(&mut self) -> u32 { + self.start.try_into().unwrap() + } + pub fn end(&mut self) -> u32 { + self.end.try_into().unwrap() + } + pub fn strand(&mut self) -> i32 { + self.strand + } + pub fn cds(&mut self) -> FeatureAttributeBuilder { + self.cds.clone() + } + pub fn source_map(&mut self) -> SourceAttributeBuilder { + self.source_map.clone() + } + pub fn seq_features(&mut self) -> SequenceAttributeBuilder { + self.seq_features.clone() + } + fn rec_clear(&mut self) { + self.id.clear(); + self.length = 0; + self.sequence.clear(); + self.start = 0; + self.end = 0; + self.strand = 0; + self.source_map = SourceAttributeBuilder::new(); + self.cds = FeatureAttributeBuilder::new(); + self.seq_features = SequenceAttributeBuilder::new(); + } + } + impl Default for Record { + fn default() -> Self { + Self::new() + } + } + #[allow(dead_code)] + pub struct Config { + filename: String, + } + impl Config { + pub fn new(args: &[String]) -> Result { + if args.len() < 2 { + { + ::core::panicking::panic_fmt( + format_args!("not enough arguments, please provide filename"), + ); + }; + } + let filename = args[1].clone(); + Ok(Config { filename }) + } + } +} +pub mod gbk { + //! # A Genbank to GFF parser + //! + //! + //! You are able to parse genbank and save as a GFF (gff3) format as well as extracting DNA sequences, gene DNA sequences (ffn) and protein fasta sequences (faa) + //! + //! You can also create new records and save as a genbank (gbk) format + //! + //! ## Detailed Explanation + //! + //! + //! The Genbank parser contains: + //! + //! Records - a top level structure which consists of either one record (single genbank) or multiple instances of record (multi-genbank). + //! + //! Each Record contains: + //! + //! 1. A source, ```SourceAttributes```, construct(enum) of counter (source name), start, stop [of source or contig], organism, mol_type, strain, type_material, db_xref + //! 2. Features, ```FeatureAttributes```, construct(enum) of counter (locus tag), gene (if present), product, codon start, strand, start, stop [of cds/gene] + //! 3. Sequence features, ```SequenceAttributes```, construct(enum) of counter (locus tag), sequence_ffn (DNA gene sequence) sequence_faa (protein translation), strand, codon start, start, stop [cds/gene] + //! 4. The DNA sequence of the whole record (or contig) + //! + //! Example to extract and print all the protein sequence fasta, example using getters or get_ functionality + //! + //! + //!```rust + //! use clap::Parser; + //! use std::fs::File; + //! use microBioRust::gbk::Reader; + //! use std::io; + //! + //! #[derive(Parser, Debug)] + //! #[clap(author, version, about)] + //! struct Arguments { + //! #[clap(short, long)] + //! filename: String, + //! } + //! + //! pub fn genbank_to_faa() -> Result<(), anyhow::Error> { + //! let args = Arguments::parse(); + //! let file_gbk = File::open(args.filename)?; + //! let mut reader = Reader::new(file_gbk); + //! let mut records = reader.records(); + //! loop { + //! //collect from each record advancing on a next record basis, count cds records + //! match records.next() { + //! Some(Ok(mut record)) => { + //! for (k, v) in &record.cds.attributes { + //! match record.seq_features.get_sequence_faa(&k) { + //! Some(value) => { let seq_faa = value.to_string(); + //! println!(">{}|{}\n{}", &record.id, &k, seq_faa); + //! }, + //! _ => (), + //! }; + //! } + //! }, + //! Some(Err(e)) => { println!("Error encountered - an err {:?}", e); }, + //! None => break, + //! } + //! } + //! return Ok(()); + //! } + //!``` + //! + //! Example to extract the protein sequences with simplified genbank! macro use + //! + //!```rust + //! use clap::Parser; + //! use std::fs::File; + //! use microBioRust::gbk::Reader; + //! use std::io; + //! use microBioRust::genbank; + //! + //! + //! #[derive(Parser, Debug)] + //! #[clap(author, version, about)] + //! struct Arguments { + //! #[clap(short, long)] + //! filename: String, + //! } + //! + //! pub fn genbank_to_faa() -> Result<(), anyhow::Error> { + //! let args = Arguments::parse(); + //! let records = genbank!(&args.filename); + //! for record in records { + //! for (k, v) in &record.cds.attributes { + //! if let Some(seq) = record.seq_features.get_sequence_faa(k) { + //! println!(">{}|{}\n{}", &record.id, &k, seq); + //! } + //! } + //! } + //! return Ok(()); + //! } + //! + //!``` + //! Example to save a provided multi- or single genbank file as a GFF file (by joining any multi-genbank) + //! + //! ```rust + //! use microBioRust::gbk::gff_write; + //! use microBioRust::gbk::Reader; + //! use microBioRust::gbk::Record; + //! use std::collections::BTreeMap; + //! use std::fs::File; + //! use clap::Parser; + //! use std::io; + //! + //! #[derive(Parser, Debug)] + //! #[clap(author, version, about)] + //! struct Arguments { + //! #[clap(short, long)] + //! filename: String, + //! } + //! + //! pub fn genbank_to_gff() -> io::Result<()> { + //! let args = Arguments::parse(); + //! let file_gbk = File::open(&args.filename)?; + //! let prev_start: u32 = 0; + //! let mut prev_end: u32 = 0; + //! let mut reader = Reader::new(file_gbk); + //! let mut records = reader.records(); + //! let mut read_counter: u32 = 0; + //! let mut seq_region: BTreeMap = BTreeMap::new(); + //! let mut record_vec: Vec = Vec::new(); + //! loop { + //! match records.next() { + //! Some(Ok(mut record)) => { + //! println!("next record"); + //! println!("Record id: {:?}", record.id); + //! let source = record.source_map.source_name.clone().expect("issue collecting source name"); + //! let beginning = match record.source_map.get_start(&source) { + //! Some(value) => value.get_value(), + //! _ => 0, + //! }; + //! let ending = match record.source_map.get_stop(&source) { + //! Some(value) => value.get_value(), + //! _ => 0, + //! }; + //! if ending + prev_end < beginning + prev_end { + //! println!("debug: end value smaller is than the start {:?}", beginning); + //! } + //! seq_region.insert(source, (beginning + prev_end, ending + prev_end)); + //! record_vec.push(record); + //! // Add additional fields to print if needed + //! read_counter+=1; + //! prev_end+=ending; // create the joined record if there are multiple + //! }, + //! Some(Err(e)) => { println!("theres an err {:?}", e); }, + //! None => { + //! println!("finished iteration"); + //! break; }, + //! } + //! } + //! let output_file = format!("{}.gff", &args.filename); + //! if std::path::Path::new(&output_file).exists() { + //! println!("Deleting existing file: {}", &output_file); + //! std::fs::remove_file(&output_file).expect("NOOO"); + //! } + //! gff_write(seq_region.clone(), record_vec, &output_file, true); + //! println!("Total records processed: {}", read_counter); + //! return Ok(()); + //! } + //!``` + //! Example to create a completely new record, use of setters or set_ functionality + //! + //! To write into GFF format requires gff_write(seq_region, record_vec, filename, true or false) + //! + //! The seq_region is the region of interest to save with name and DNA coordinates such as ``` seqregion.entry("source_1".to_string(), (1,897))``` + //! This makes it possible to save the whole file or to subset it + //! + //! record_vec is a list of the records. If there is only one record, include this as a vec using ``` vec![record] ``` + //! + //! The boolean true/false describes whether the DNA sequence should be included in the GFF3 file + //! + //! To write into genbank format requires gbk_write(seq_region, record_vec, filename), no true or false since genbank format will include the DNA sequence + //! + //! + //! ```rust + //! use microBioRust::gbk::gff_write; + //! use microBioRust::gbk::RangeValue; + //! use microBioRust::gbk::Record; + //! use std::fs::File; + //! use std::collections::BTreeMap; + //! + //! pub fn create_new_record() -> Result<(), anyhow::Error> { + //! let filename = format!("new_record.gff"); + //! if std::path::Path::new(&filename).exists() { + //! std::fs::remove_file(&filename)?; + //! } + //! let mut record = Record::new(); + //! let mut seq_region: BTreeMap = BTreeMap::new(); + //! //example from E.coli K12 + //! seq_region.insert("source_1".to_string(), (1,897)); + //! //Add the source into SourceAttributes + //! record.source_map + //! .set_counter("source_1".to_string()) + //! .set_start(RangeValue::Exact(1)) + //! .set_stop(RangeValue::Exact(897)) + //! .set_organism("Escherichia coli".to_string()) + //! .set_mol_type("DNA".to_string()) + //! .set_strain("K-12 substr. MG1655".to_string()) + //! .set_type_material("type strain of Escherichia coli K12".to_string()) + //! .set_db_xref("PRJNA57779".to_string()); + //! //Add the features into FeatureAttributes, here we are setting two features, i.e. coding sequences or genes + //! record.cds + //! .set_counter("b3304".to_string()) + //! .set_start(RangeValue::Exact(1)) + //! .set_stop(RangeValue::Exact(354)) + //! .set_gene("rplR".to_string()) + //! .set_product("50S ribosomal subunit protein L18".to_string()) + //! .set_codon_start(1) + //! .set_strand(-1); + //! record.cds + //! .set_counter("b3305".to_string()) + //! .set_start(RangeValue::Exact(364)) + //! .set_stop(RangeValue::Exact(897)) + //! .set_gene("rplF".to_string()) + //! .set_product("50S ribosomal subunit protein L6".to_string()) + //! .set_codon_start(1) + //! .set_strand(-1); + //! //Add the sequences for the coding sequence (CDS) into SequenceAttributes + //! record.seq_features + //! .set_counter("b3304".to_string()) + //! .set_start(RangeValue::Exact(1)) + //! .set_stop(RangeValue::Exact(354)) + //! .set_sequence_ffn("ATGGATAAGAAATCTGCTCGTATCCGTCGTGCGACCCGCGCACGCCGCAAGCTCCAGGAG + //!CTGGGCGCAACTCGCCTGGTGGTACATCGTACCCCGCGTCACATTTACGCACAGGTAATT + //!GCACCGAACGGTTCTGAAGTTCTGGTAGCTGCTTCTACTGTAGAAAAAGCTATCGCTGAA + //!CAACTGAAGTACACCGGTAACAAAGACGCGGCTGCAGCTGTGGGTAAAGCTGTCGCTGAA + //!CGCGCTCTGGAAAAAGGCATCAAAGATGTATCCTTTGACCGTTCCGGGTTCCAATATCAT + //!GGTCGTGTCCAGGCACTGGCAGATGCTGCCCGTGAAGCTGGCCTTCAGTTCTAA".to_string()) + //! .set_sequence_faa("MDKKSARIRRATRARRKLQELGATRLVVHRTPRHIYAQVIAPNGSEVLVAASTVEKAIAE + //!QLKYTGNKDAAAAVGKAVAERALEKGIKDVSFDRSGFQYHGRVQALADAAREAGLQF".to_string()) + //! .set_codon_start(1) + //! .set_strand(-1); + //! record.seq_features + //! .set_counter("bb3305".to_string()) + //! .set_start(RangeValue::Exact(364)) + //! .set_stop(RangeValue::Exact(897)) + //! .set_sequence_ffn("ATGTCTCGTGTTGCTAAAGCACCGGTCGTTGTTCCTGCCGGCGTTGACGTAAAAATCAAC + //!GGTCAGGTTATTACGATCAAAGGTAAAAACGGCGAGCTGACTCGTACTCTCAACGATGCT + //!GTTGAAGTTAAACATGCAGATAATACCCTGACCTTCGGTCCGCGTGATGGTTACGCAGAC + //!GGTTGGGCACAGGCTGGTACCGCGCGTGCCCTGCTGAACTCAATGGTTATCGGTGTTACC + //!GAAGGCTTCACTAAGAAGCTGCAGCTGGTTGGTGTAGGTTACCGTGCAGCGGTTAAAGGC + //!AATGTGATTAACCTGTCTCTGGGTTTCTCTCATCCTGTTGACCATCAGCTGCCTGCGGGT + //!ATCACTGCTGAATGTCCGACTCAGACTGAAATCGTGCTGAAAGGCGCTGATAAGCAGGTG + //!ATCGGCCAGGTTGCAGCGGATCTGCGCGCCTACCGTCGTCCTGAGCCTTATAAAGGCAAG + //!GGTGTTCGTTACGCCGACGAAGTCGTGCGTACCAAAGAGGCTAAGAAGAAGTAA".to_string()) + //! .set_sequence_faa("MSRVAKAPVVVPAGVDVKINGQVITIKGKNGELTRTLNDAVEVKHADNTLTFGPRDGYAD + //!GWAQAGTARALLNSMVIGVTEGFTKKLQLVGVGYRAAVKGNVINLSLGFSHPVDHQLPAG + //!ITAECPTQTEIVLKGADKQVIGQVAADLRAYRRPEPYKGKGVRYADEVVRTKEAKKK".to_string()) + //! .set_codon_start(1) + //! .set_strand(-1); + //! //Add the full sequence of the entire record into the record.sequence + //! record.sequence = "TTAGAACTGAAGGCCAGCTTCACGGGCAGCATCTGCCAGTGCCTGGACACGACCATGATA + //!TTGGAACCCGGAACGGTCAAAGGATACATCTTTGATGCCTTTTTCCAGAGCGCGTTCAGC + //!GACAGCTTTACCCACAGCTGCAGCCGCGTCTTTGTTACCGGTGTACTTCAGTTGTTCAGC + //!GATAGCTTTTTCTACAGTAGAAGCAGCTACCAGAACTTCAGAACCGTTCGGTGCAATTAC + //!CTGTGCGTAAATGTGACGCGGGGTACGATGTACCACCAGGCGAGTTGCGCCCAGCTCCTG + //!GAGCTTGCGGCGTGCGCGGGTCGCACGACGGATACGAGCAGATTTCTTATCCATAGTGTT + //!ACCTTACTTCTTCTTAGCCTCTTTGGTACGCACGACTTCGTCGGCGTAACGAACACCCTT + //!GCCTTTATAAGGCTCAGGACGACGGTAGGCGCGCAGATCCGCTGCAACCTGGCCGATCAC + //!CTGCTTATCAGCGCCTTTCAGCACGATTTCAGTCTGAGTCGGACATTCAGCAGTGATACC + //!CGCAGGCAGCTGATGGTCAACAGGATGAGAGAAACCCAGAGACAGGTTAATCACATTGCC + //!TTTAACCGCTGCACGGTAACCTACACCAACCAGCTGCAGCTTCTTAGTGAAGCCTTCGGT + //!AACACCGATAACCATTGAGTTCAGCAGGGCACGCGCGGTACCAGCCTGTGCCCAACCGTC + //!TGCGTAACCATCACGCGGACCGAAGGTCAGGGTATTATCTGCATGTTTAACTTCAACAGC + //!ATCGTTGAGAGTACGAGTCAGCTCGCCGTTTTTACCTTTGATCGTAATAACCTGACCGTT + //!GATTTTTACGTCAACGCCGGCAGGAACAACGACCGGTGCTTTAGCAACACGAGACAT".to_string(); + //! gff_write(seq_region, vec![record], &filename, true); + //! return Ok(()); + //! } + //!``` + //! + use std::io::{self, Write}; + use std::fs; + use regex::Regex; + use itertools::Itertools; + use std::vec::Vec; + use std::str; + use std::convert::AsRef; + use protein_translate::translate; + use std::path::Path; + use bio::alphabets::dna::revcomp; + use anyhow::anyhow; + use std::collections::BTreeMap; + use std::fs::{OpenOptions, File}; + use anyhow::Context; + use std::collections::HashSet; + use paste::paste; + use std::convert::TryInto; + use chrono::prelude::*; + /// A Gbk reader. + #[allow(unused_mut)] + pub struct Records + where + B: io::BufRead, + { + reader: Reader, + error_has_occurred: bool, + } + #[automatically_derived] + #[allow(unused_mut)] + impl ::core::fmt::Debug for Records + where + B: io::BufRead, + { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "Records", + "reader", + &self.reader, + "error_has_occurred", + &&self.error_has_occurred, + ) + } + } + impl Records + where + B: io::BufRead, + { + #[allow(unused_mut)] + pub fn new(mut reader: Reader) -> Self { + Records { + reader: reader, + error_has_occurred: false, + } + } + } + impl Iterator for Records + where + B: io::BufRead, + { + type Item = Result; + fn next(&mut self) -> Option { + if self.error_has_occurred { + { + ::std::io::_print( + format_args!("error was encountered in iteration\n"), + ); + }; + None + } else { + let mut record = Record::new(); + match self.reader.read(&mut record) { + Ok(_) => if record.is_empty() { None } else { Some(Ok(record)) } + Err(err) => { + self.error_has_occurred = true; + Some( + Err( + ::anyhow::Error::msg( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("next record read error {0:?}", err), + ); + res + }), + ), + ), + ) + } + } + } + } + } + pub trait GbkRead { + fn read(&mut self, record: &mut Record) -> Result; + } + ///per line reader for the file + pub struct Reader { + reader: B, + line_buffer: String, + } + #[automatically_derived] + impl ::core::fmt::Debug for Reader { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "Reader", + "reader", + &self.reader, + "line_buffer", + &&self.line_buffer, + ) + } + } + #[automatically_derived] + impl ::core::default::Default for Reader { + #[inline] + fn default() -> Reader { + Reader { + reader: ::core::default::Default::default(), + line_buffer: ::core::default::Default::default(), + } + } + } + impl Reader> { + /// Read Gbk from given file path in given format. + pub fn from_file + std::fmt::Debug>( + path: P, + ) -> anyhow::Result { + fs::File::open(&path) + .map(Reader::new) + .with_context(|| ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("Failed to read Gbk from {0:#?}", path), + ); + res + })) + } + } + impl Reader> + where + R: io::Read, + { + pub fn new(reader: R) -> Self { + Reader { + reader: io::BufReader::new(reader), + line_buffer: String::new(), + } + } + } + impl Reader + where + B: io::BufRead, + { + pub fn from_bufread(bufreader: B) -> Self { + Reader { + reader: bufreader, + line_buffer: String::new(), + } + } + pub fn records(self) -> Records { + Records { + reader: self, + error_has_occurred: false, + } + } + } + ///main gbk parser + impl<'a, B> GbkRead for Reader + where + B: io::BufRead, + { + #[allow(unused_mut)] + #[allow(unused_variables)] + #[allow(unused_assignments)] + fn read(&mut self, record: &mut Record) -> Result { + record.rec_clear(); + let mut sequences = String::new(); + let mut source_map = SourceAttributeBuilder::new(); + let mut cds = FeatureAttributeBuilder::new(); + let mut seq_features = SequenceAttributeBuilder::new(); + let mut cds_counter: i32 = 0; + let mut source_counter: i32 = 0; + let mut prev_end: u32 = 0; + let mut organism = String::new(); + let mut mol_type = String::new(); + let mut strain = String::new(); + let mut source_name = String::new(); + let mut type_material = String::new(); + let mut theend: u32 = 0; + let mut thestart: u32 = 0; + let mut db_xref = String::new(); + if self.line_buffer.is_empty() { + self.reader.read_line(&mut self.line_buffer)?; + if self.line_buffer.is_empty() { + return Ok(record.to_owned()); + } + } + 'outer: while !self.line_buffer.is_empty() { + if self.line_buffer.starts_with("LOCUS") { + record.rec_clear(); + let mut header_fields: Vec<&str> = self + .line_buffer + .split_whitespace() + .collect(); + let mut header_iter = header_fields.iter(); + header_iter.next(); + record.id = header_iter + .next() + .ok_or_else(|| ::anyhow::__private::must_use({ + let error = ::anyhow::__private::format_err( + format_args!("missing record id"), + ); + error + }))? + .to_string(); + let lens = header_iter + .next() + .ok_or_else(|| ::anyhow::__private::must_use({ + let error = ::anyhow::__private::format_err( + format_args!("missing record length"), + ); + error + }))? + .to_string(); + record.length = lens.trim().parse::()?; + self.line_buffer.clear(); + } + if self.line_buffer.starts_with(" source") { + let re = Regex::new(r"([0-9]+)[[:punct:]]+([0-9]+)")?; + let location = re + .captures(&self.line_buffer) + .ok_or_else(|| ::anyhow::__private::must_use({ + let error = ::anyhow::__private::format_err( + format_args!("missing location"), + ); + error + }))?; + let start = &location[1]; + let end = &location[2]; + thestart = start.trim().parse::()?; + source_counter += 1; + source_name = ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("source_{0}_{1}", record.id, source_counter), + ); + res + }) + .to_string(); + thestart += prev_end; + theend = end.trim().parse::()? + prev_end; + loop { + self.line_buffer.clear(); + self.reader.read_line(&mut self.line_buffer)?; + if self.line_buffer.starts_with(" CDS") { + record + .source_map + .set_counter(source_name.to_string()) + .set_start(RangeValue::Exact(thestart)) + .set_stop(RangeValue::Exact(theend)) + .set_organism(organism.clone()) + .set_mol_type(mol_type.clone()) + .set_strain(strain.clone()) + .set_type_material(type_material.clone()) + .set_db_xref(db_xref.clone()); + continue 'outer; + } + if self.line_buffer.contains("/organism") { + let org: Vec<&str> = self.line_buffer.split('\"').collect(); + organism = org[1].to_string(); + } + if self.line_buffer.contains("/mol_type") { + let mol: Vec<&str> = self.line_buffer.split('\"').collect(); + mol_type = mol[1].to_string(); + } + if self.line_buffer.contains("/strain") { + let stra: Vec<&str> = self.line_buffer.split('\"').collect(); + strain = stra[1].to_string(); + } + if self.line_buffer.contains("/type_material") { + let mat: Vec<&str> = self.line_buffer.split('\"').collect(); + type_material = mat[1].to_string(); + } + if self.line_buffer.contains("/db_xref") { + let db: Vec<&str> = self.line_buffer.split('\"').collect(); + db_xref = db[1].to_string(); + } + } + } + if self.line_buffer.starts_with(" CDS") { + let mut startiter: Vec<_> = Vec::new(); + let mut enditer: Vec<_> = Vec::new(); + let mut thestart: u32 = 0; + let mut thend: u32 = 0; + let mut joined: bool = false; + let joined = if self.line_buffer.contains("join") { + true + } else { + false + }; + let re = Regex::new(r"([0-9]+)[[:punct:]]+([0-9]+)")?; + for cap in re.captures_iter(&self.line_buffer) { + cds_counter += 1; + thestart = cap[1] + .parse() + .expect("failed to match and parse numerical start"); + theend = cap[2] + .parse() + .expect("failed to match and parse numerical end"); + startiter.push(thestart); + enditer.push(theend); + } + let mut gene = String::new(); + let mut product = String::new(); + let strand: i8 = if self.line_buffer.contains("complement") { + -1 + } else { + 1 + }; + let mut locus_tag = String::new(); + let mut codon_start: u8 = 1; + loop { + self.line_buffer.clear(); + self.reader.read_line(&mut self.line_buffer)?; + if self.line_buffer.contains("/locus_tag=") { + let loctag: Vec<&str> = self + .line_buffer + .split('\"') + .collect(); + locus_tag = loctag[1].to_string(); + } + if self.line_buffer.contains("/codon_start") { + let codstart: Vec<&str> = self + .line_buffer + .split('=') + .collect(); + let valstart = codstart[1].trim().parse::()?; + codon_start = valstart; + } + if self.line_buffer.contains("/gene=") { + let gen: Vec<&str> = self.line_buffer.split('\"').collect(); + gene = gen[1].to_string(); + } + if self.line_buffer.contains("/product") { + let prod: Vec<&str> = self.line_buffer.split('\"').collect(); + product = substitute_odd_punctuation(prod[1].to_string())?; + } + if self.line_buffer.starts_with(" CDS") + || self.line_buffer.starts_with("ORIGIN") + || self.line_buffer.starts_with(" gene") + || self.line_buffer.starts_with(" misc_feature") + { + if locus_tag.is_empty() { + locus_tag = ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("CDS_{0}", cds_counter), + ); + res + }) + .to_string(); + } + if joined { + for (i, m) in startiter.iter().enumerate() { + let loc_tag = ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("{0}_{1}", locus_tag.clone(), i), + ); + res + }); + record + .cds + .set_counter(loc_tag) + .set_start(RangeValue::Exact(*m)) + .set_stop(RangeValue::Exact(enditer[i])) + .set_gene(gene.to_string()) + .set_product(product.to_string()) + .set_codon_start(codon_start) + .set_strand(strand); + } + continue 'outer; + } else { + record + .cds + .set_counter(locus_tag.clone()) + .set_start(RangeValue::Exact(thestart)) + .set_stop(RangeValue::Exact(theend)) + .set_gene(gene.to_string()) + .set_product(product.to_string()) + .set_codon_start(codon_start) + .set_strand(strand); + continue 'outer; + } + } + } + } + if self.line_buffer.starts_with("ORIGIN") { + let mut sequences = String::new(); + let result_seq = loop { + self.line_buffer.clear(); + self.reader.read_line(&mut self.line_buffer)?; + if self.line_buffer.starts_with("//") { + break sequences; + } else { + let s: Vec<&str> = self + .line_buffer + .split_whitespace() + .collect(); + let s = &s[1..]; + let sequence = s.iter().join(""); + sequences.push_str(&sequence); + } + }; + record.sequence = result_seq.to_string(); + let mut iterablecount: u32 = 0; + for (key, val) in record.cds.iter_sorted() { + let ( + mut a, + mut b, + mut c, + mut d, + ): (Option, Option, Option, Option) = ( + None, + None, + None, + None, + ); + for value in val { + match value { + FeatureAttributes::Start { value } => { + a = match value { + RangeValue::Exact(v) => Some(*v), + RangeValue::LessThan(v) => Some(*v), + RangeValue::GreaterThan(v) => Some(*v), + }; + } + FeatureAttributes::Stop { value } => { + b = match value { + RangeValue::Exact(v) => Some(*v), + RangeValue::LessThan(v) => Some(*v), + RangeValue::GreaterThan(v) => Some(*v), + }; + } + FeatureAttributes::Strand { value } => { + c = match value { + value => Some(*value), + }; + } + FeatureAttributes::CodonStart { value } => { + d = match value { + value => Some(value.clone()), + }; + } + _ => {} + } + } + let sta = a + .map(|o| o as usize) + .ok_or( + ::anyhow::__private::must_use({ + let error = ::anyhow::__private::format_err( + format_args!("No value for start"), + ); + error + }), + )?; + let sto = b + .map(|t| t as usize) + .ok_or( + ::anyhow::__private::must_use({ + let error = ::anyhow::__private::format_err( + format_args!("No value for stop"), + ); + error + }), + )? - 1; + let stra = c + .map(|u| u as i8) + .ok_or( + ::anyhow::__private::must_use({ + let error = ::anyhow::__private::format_err( + format_args!("No value for strand"), + ); + error + }), + )?; + let cod = d + .map(|v| v as usize - 1) + .ok_or( + ::anyhow::__private::must_use({ + let error = ::anyhow::__private::format_err( + format_args!("No value for strand"), + ); + error + }), + )?; + let star = sta.try_into()?; + let stow = sto.try_into()?; + let codd = cod.try_into()?; + let mut sliced_sequence: &str = ""; + if stra == -1 { + if cod > 1 { + if sto + 1 <= record.sequence.len() { + sliced_sequence = &record.sequence[sta + cod..sto + 1]; + } else { + sliced_sequence = &record.sequence[sta + cod..sto]; + } + } else { + if sto + 1 <= record.sequence.len() { + sliced_sequence = &record.sequence[sta..sto + 1]; + } else { + sliced_sequence = &record.sequence[sta..sto]; + } + } + let cds_char = sliced_sequence; + let prot_seq = translate(&revcomp(cds_char.as_bytes())); + let parts: Vec<&str> = prot_seq.split('*').collect(); + record + .seq_features + .set_counter(key.to_string()) + .set_start(RangeValue::Exact(star)) + .set_stop(RangeValue::Exact(stow)) + .set_sequence_ffn(cds_char.to_string()) + .set_sequence_faa(parts[0].to_string()) + .set_codon_start(codd) + .set_strand(stra); + } else { + if cod > 1 { + sliced_sequence = &record.sequence[sta + cod - 1..sto]; + } else { + sliced_sequence = &record.sequence[sta - 1..sto]; + } + let cds_char = sliced_sequence; + let prot_seq = translate(cds_char.as_bytes()); + let parts: Vec<&str> = prot_seq.split('*').collect(); + record + .seq_features + .set_counter(key.to_string()) + .set_start(RangeValue::Exact(star)) + .set_stop(RangeValue::Exact(stow)) + .set_sequence_ffn(cds_char.to_string()) + .set_sequence_faa(parts[0].to_string()) + .set_codon_start(codd) + .set_strand(stra); + } + } + return Ok(record.to_owned()); + } + self.line_buffer.clear(); + self.reader.read_line(&mut self.line_buffer)?; + } + Ok(record.to_owned()) + } + } + ///stores a value for start or stop (end) which can be denoted as a < value or > value. + pub enum RangeValue { + Exact(u32), + LessThan(u32), + GreaterThan(u32), + } + #[automatically_derived] + impl ::core::fmt::Debug for RangeValue { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + RangeValue::Exact(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "Exact", + &__self_0, + ) + } + RangeValue::LessThan(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "LessThan", + &__self_0, + ) + } + RangeValue::GreaterThan(__self_0) => { + ::core::fmt::Formatter::debug_tuple_field1_finish( + f, + "GreaterThan", + &__self_0, + ) + } + } + } + } + #[automatically_derived] + impl ::core::hash::Hash for RangeValue { + #[inline] + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + let __self_discr = ::core::intrinsics::discriminant_value(self); + ::core::hash::Hash::hash(&__self_discr, state); + match self { + RangeValue::Exact(__self_0) => ::core::hash::Hash::hash(__self_0, state), + RangeValue::LessThan(__self_0) => { + ::core::hash::Hash::hash(__self_0, state) + } + RangeValue::GreaterThan(__self_0) => { + ::core::hash::Hash::hash(__self_0, state) + } + } + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for RangeValue {} + #[automatically_derived] + impl ::core::cmp::PartialEq for RangeValue { + #[inline] + fn eq(&self, other: &RangeValue) -> bool { + let __self_discr = ::core::intrinsics::discriminant_value(self); + let __arg1_discr = ::core::intrinsics::discriminant_value(other); + __self_discr == __arg1_discr + && match (self, other) { + (RangeValue::Exact(__self_0), RangeValue::Exact(__arg1_0)) => { + __self_0 == __arg1_0 + } + (RangeValue::LessThan(__self_0), RangeValue::LessThan(__arg1_0)) => { + __self_0 == __arg1_0 + } + ( + RangeValue::GreaterThan(__self_0), + RangeValue::GreaterThan(__arg1_0), + ) => __self_0 == __arg1_0, + _ => unsafe { ::core::intrinsics::unreachable() } + } + } + } + #[automatically_derived] + impl ::core::cmp::Eq for RangeValue { + #[inline] + #[doc(hidden)] + #[coverage(off)] + fn assert_receiver_is_total_eq(&self) -> () { + let _: ::core::cmp::AssertParamIsEq; + } + } + #[automatically_derived] + impl ::core::clone::Clone for RangeValue { + #[inline] + fn clone(&self) -> RangeValue { + match self { + RangeValue::Exact(__self_0) => { + RangeValue::Exact(::core::clone::Clone::clone(__self_0)) + } + RangeValue::LessThan(__self_0) => { + RangeValue::LessThan(::core::clone::Clone::clone(__self_0)) + } + RangeValue::GreaterThan(__self_0) => { + RangeValue::GreaterThan(::core::clone::Clone::clone(__self_0)) + } + } + } + } + impl RangeValue { + pub fn get_value(&self) -> u32 { + match self { + RangeValue::Exact(value) => *value, + RangeValue::LessThan(value) => *value, + RangeValue::GreaterThan(value) => *value, + } + } + } + pub enum SourceAttributes { + Start { value: RangeValue }, + Stop { value: RangeValue }, + Organism { value: String }, + MolType { value: String }, + Strain { value: String }, + CultureCollection { value: String }, + TypeMaterial { value: String }, + DbXref { value: String }, + } + #[automatically_derived] + impl ::core::fmt::Debug for SourceAttributes { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + SourceAttributes::Start { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Start", + "value", + &__self_0, + ) + } + SourceAttributes::Stop { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Stop", + "value", + &__self_0, + ) + } + SourceAttributes::Organism { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Organism", + "value", + &__self_0, + ) + } + SourceAttributes::MolType { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "MolType", + "value", + &__self_0, + ) + } + SourceAttributes::Strain { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Strain", + "value", + &__self_0, + ) + } + SourceAttributes::CultureCollection { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "CultureCollection", + "value", + &__self_0, + ) + } + SourceAttributes::TypeMaterial { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "TypeMaterial", + "value", + &__self_0, + ) + } + SourceAttributes::DbXref { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "DbXref", + "value", + &__self_0, + ) + } + } + } + } + #[automatically_derived] + impl ::core::cmp::Eq for SourceAttributes { + #[inline] + #[doc(hidden)] + #[coverage(off)] + fn assert_receiver_is_total_eq(&self) -> () { + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for SourceAttributes {} + #[automatically_derived] + impl ::core::cmp::PartialEq for SourceAttributes { + #[inline] + fn eq(&self, other: &SourceAttributes) -> bool { + let __self_discr = ::core::intrinsics::discriminant_value(self); + let __arg1_discr = ::core::intrinsics::discriminant_value(other); + __self_discr == __arg1_discr + && match (self, other) { + ( + SourceAttributes::Start { value: __self_0 }, + SourceAttributes::Start { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SourceAttributes::Stop { value: __self_0 }, + SourceAttributes::Stop { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SourceAttributes::Organism { value: __self_0 }, + SourceAttributes::Organism { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SourceAttributes::MolType { value: __self_0 }, + SourceAttributes::MolType { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SourceAttributes::Strain { value: __self_0 }, + SourceAttributes::Strain { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SourceAttributes::CultureCollection { value: __self_0 }, + SourceAttributes::CultureCollection { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SourceAttributes::TypeMaterial { value: __self_0 }, + SourceAttributes::TypeMaterial { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SourceAttributes::DbXref { value: __self_0 }, + SourceAttributes::DbXref { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + _ => unsafe { ::core::intrinsics::unreachable() } + } + } + } + #[automatically_derived] + impl ::core::hash::Hash for SourceAttributes { + #[inline] + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + let __self_discr = ::core::intrinsics::discriminant_value(self); + ::core::hash::Hash::hash(&__self_discr, state); + match self { + SourceAttributes::Start { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SourceAttributes::Stop { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SourceAttributes::Organism { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SourceAttributes::MolType { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SourceAttributes::Strain { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SourceAttributes::CultureCollection { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SourceAttributes::TypeMaterial { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SourceAttributes::DbXref { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + } + } + } + #[automatically_derived] + impl ::core::clone::Clone for SourceAttributes { + #[inline] + fn clone(&self) -> SourceAttributes { + match self { + SourceAttributes::Start { value: __self_0 } => { + SourceAttributes::Start { + value: ::core::clone::Clone::clone(__self_0), + } + } + SourceAttributes::Stop { value: __self_0 } => { + SourceAttributes::Stop { + value: ::core::clone::Clone::clone(__self_0), + } + } + SourceAttributes::Organism { value: __self_0 } => { + SourceAttributes::Organism { + value: ::core::clone::Clone::clone(__self_0), + } + } + SourceAttributes::MolType { value: __self_0 } => { + SourceAttributes::MolType { + value: ::core::clone::Clone::clone(__self_0), + } + } + SourceAttributes::Strain { value: __self_0 } => { + SourceAttributes::Strain { + value: ::core::clone::Clone::clone(__self_0), + } + } + SourceAttributes::CultureCollection { value: __self_0 } => { + SourceAttributes::CultureCollection { + value: ::core::clone::Clone::clone(__self_0), + } + } + SourceAttributes::TypeMaterial { value: __self_0 } => { + SourceAttributes::TypeMaterial { + value: ::core::clone::Clone::clone(__self_0), + } + } + SourceAttributes::DbXref { value: __self_0 } => { + SourceAttributes::DbXref { + value: ::core::clone::Clone::clone(__self_0), + } + } + } + } + } + impl SourceAttributeBuilder { + pub fn get_start(&self, key: &str) -> Option<&RangeValue> { + self.source_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SourceAttributes::Start { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_stop(&self, key: &str) -> Option<&RangeValue> { + self.source_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SourceAttributes::Stop { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_organism(&self, key: &str) -> Option<&String> { + self.source_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SourceAttributes::Organism { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_mol_type(&self, key: &str) -> Option<&String> { + self.source_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SourceAttributes::MolType { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_strain(&self, key: &str) -> Option<&String> { + self.source_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SourceAttributes::Strain { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_type_material(&self, key: &str) -> Option<&String> { + self.source_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SourceAttributes::TypeMaterial { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_db_xref(&self, key: &str) -> Option<&String> { + self.source_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SourceAttributes::DbXref { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + } + ///builder for the source information on a per record basis + pub struct SourceAttributeBuilder { + pub source_attributes: BTreeMap>, + pub source_name: Option, + } + #[automatically_derived] + impl ::core::fmt::Debug for SourceAttributeBuilder { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "SourceAttributeBuilder", + "source_attributes", + &self.source_attributes, + "source_name", + &&self.source_name, + ) + } + } + #[automatically_derived] + impl ::core::default::Default for SourceAttributeBuilder { + #[inline] + fn default() -> SourceAttributeBuilder { + SourceAttributeBuilder { + source_attributes: ::core::default::Default::default(), + source_name: ::core::default::Default::default(), + } + } + } + #[automatically_derived] + impl ::core::clone::Clone for SourceAttributeBuilder { + #[inline] + fn clone(&self) -> SourceAttributeBuilder { + SourceAttributeBuilder { + source_attributes: ::core::clone::Clone::clone(&self.source_attributes), + source_name: ::core::clone::Clone::clone(&self.source_name), + } + } + } + impl SourceAttributeBuilder { + pub fn set_source_name(&mut self, name: String) { + self.source_name = Some(name); + } + pub fn get_source_name(&self) -> Option<&String> { + self.source_name.as_ref() + } + pub fn add_source_attribute( + &mut self, + key: String, + attribute: SourceAttributes, + ) { + self.source_attributes + .entry(key) + .or_insert_with(HashSet::new) + .insert(attribute); + } + pub fn get_source_attributes( + &self, + key: &str, + ) -> Option<&HashSet> { + self.source_attributes.get(key) + } + } + impl SourceAttributeBuilder { + pub fn new() -> Self { + SourceAttributeBuilder { + source_attributes: BTreeMap::new(), + source_name: None, + } + } + pub fn set_counter(&mut self, counter: String) -> &mut Self { + self.source_name = Some(counter); + self + } + pub fn insert_to(&mut self, value: SourceAttributes) { + if let Some(counter) = &self.source_name { + self.source_attributes + .entry(counter.to_string()) + .or_insert_with(HashSet::new) + .insert(value); + } else { + { + ::core::panicking::panic_fmt(format_args!("Counter key not set")); + }; + } + } + pub fn set_start(&mut self, value: RangeValue) -> &mut Self { + self.insert_to(SourceAttributes::Start { value }); + self + } + pub fn set_stop(&mut self, value: RangeValue) -> &mut Self { + self.insert_to(SourceAttributes::Stop { value }); + self + } + pub fn set_organism(&mut self, value: String) -> &mut Self { + self.insert_to(SourceAttributes::Organism { + value, + }); + self + } + pub fn set_mol_type(&mut self, value: String) -> &mut Self { + self.insert_to(SourceAttributes::MolType { value }); + self + } + pub fn set_strain(&mut self, value: String) -> &mut Self { + self.insert_to(SourceAttributes::Strain { value }); + self + } + pub fn set_type_material(&mut self, value: String) -> &mut Self { + self.insert_to(SourceAttributes::TypeMaterial { + value, + }); + self + } + pub fn set_db_xref(&mut self, value: String) -> &mut Self { + self.insert_to(SourceAttributes::DbXref { value }); + self + } + pub fn build(self) -> BTreeMap> { + self.source_attributes + } + pub fn iter_sorted( + &self, + ) -> std::collections::btree_map::Iter> { + self.source_attributes.iter() + } + pub fn default() -> Self { + SourceAttributeBuilder { + source_attributes: BTreeMap::new(), + source_name: None, + } + } + } + ///attributes for each feature, cds or gene + pub enum FeatureAttributes { + Start { value: RangeValue }, + Stop { value: RangeValue }, + Gene { value: String }, + Product { value: String }, + CodonStart { value: u8 }, + Strand { value: i8 }, + } + #[automatically_derived] + impl ::core::fmt::Debug for FeatureAttributes { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + FeatureAttributes::Start { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Start", + "value", + &__self_0, + ) + } + FeatureAttributes::Stop { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Stop", + "value", + &__self_0, + ) + } + FeatureAttributes::Gene { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Gene", + "value", + &__self_0, + ) + } + FeatureAttributes::Product { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Product", + "value", + &__self_0, + ) + } + FeatureAttributes::CodonStart { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "CodonStart", + "value", + &__self_0, + ) + } + FeatureAttributes::Strand { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Strand", + "value", + &__self_0, + ) + } + } + } + } + #[automatically_derived] + impl ::core::cmp::Eq for FeatureAttributes { + #[inline] + #[doc(hidden)] + #[coverage(off)] + fn assert_receiver_is_total_eq(&self) -> () { + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + } + } + #[automatically_derived] + impl ::core::hash::Hash for FeatureAttributes { + #[inline] + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + let __self_discr = ::core::intrinsics::discriminant_value(self); + ::core::hash::Hash::hash(&__self_discr, state); + match self { + FeatureAttributes::Start { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + FeatureAttributes::Stop { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + FeatureAttributes::Gene { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + FeatureAttributes::Product { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + FeatureAttributes::CodonStart { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + FeatureAttributes::Strand { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + } + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for FeatureAttributes {} + #[automatically_derived] + impl ::core::cmp::PartialEq for FeatureAttributes { + #[inline] + fn eq(&self, other: &FeatureAttributes) -> bool { + let __self_discr = ::core::intrinsics::discriminant_value(self); + let __arg1_discr = ::core::intrinsics::discriminant_value(other); + __self_discr == __arg1_discr + && match (self, other) { + ( + FeatureAttributes::Start { value: __self_0 }, + FeatureAttributes::Start { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + FeatureAttributes::Stop { value: __self_0 }, + FeatureAttributes::Stop { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + FeatureAttributes::Gene { value: __self_0 }, + FeatureAttributes::Gene { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + FeatureAttributes::Product { value: __self_0 }, + FeatureAttributes::Product { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + FeatureAttributes::CodonStart { value: __self_0 }, + FeatureAttributes::CodonStart { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + FeatureAttributes::Strand { value: __self_0 }, + FeatureAttributes::Strand { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + _ => unsafe { ::core::intrinsics::unreachable() } + } + } + } + #[automatically_derived] + impl ::core::clone::Clone for FeatureAttributes { + #[inline] + fn clone(&self) -> FeatureAttributes { + match self { + FeatureAttributes::Start { value: __self_0 } => { + FeatureAttributes::Start { + value: ::core::clone::Clone::clone(__self_0), + } + } + FeatureAttributes::Stop { value: __self_0 } => { + FeatureAttributes::Stop { + value: ::core::clone::Clone::clone(__self_0), + } + } + FeatureAttributes::Gene { value: __self_0 } => { + FeatureAttributes::Gene { + value: ::core::clone::Clone::clone(__self_0), + } + } + FeatureAttributes::Product { value: __self_0 } => { + FeatureAttributes::Product { + value: ::core::clone::Clone::clone(__self_0), + } + } + FeatureAttributes::CodonStart { value: __self_0 } => { + FeatureAttributes::CodonStart { + value: ::core::clone::Clone::clone(__self_0), + } + } + FeatureAttributes::Strand { value: __self_0 } => { + FeatureAttributes::Strand { + value: ::core::clone::Clone::clone(__self_0), + } + } + } + } + } + impl FeatureAttributeBuilder { + pub fn get_start(&self, key: &str) -> Option<&RangeValue> { + self.attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let FeatureAttributes::Start { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_stop(&self, key: &str) -> Option<&RangeValue> { + self.attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let FeatureAttributes::Stop { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_gene(&self, key: &str) -> Option<&String> { + self.attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let FeatureAttributes::Gene { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_product(&self, key: &str) -> Option<&String> { + self.attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let FeatureAttributes::Product { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_codon_start(&self, key: &str) -> Option<&u8> { + self.attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let FeatureAttributes::CodonStart { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_strand(&self, key: &str) -> Option<&i8> { + self.attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let FeatureAttributes::Strand { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + } + ///builder for the feature information on a per coding sequence (CDS) basis + pub struct FeatureAttributeBuilder { + pub attributes: BTreeMap>, + locus_tag: Option, + } + #[automatically_derived] + impl ::core::fmt::Debug for FeatureAttributeBuilder { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "FeatureAttributeBuilder", + "attributes", + &self.attributes, + "locus_tag", + &&self.locus_tag, + ) + } + } + #[automatically_derived] + impl ::core::default::Default for FeatureAttributeBuilder { + #[inline] + fn default() -> FeatureAttributeBuilder { + FeatureAttributeBuilder { + attributes: ::core::default::Default::default(), + locus_tag: ::core::default::Default::default(), + } + } + } + #[automatically_derived] + impl ::core::clone::Clone for FeatureAttributeBuilder { + #[inline] + fn clone(&self) -> FeatureAttributeBuilder { + FeatureAttributeBuilder { + attributes: ::core::clone::Clone::clone(&self.attributes), + locus_tag: ::core::clone::Clone::clone(&self.locus_tag), + } + } + } + impl FeatureAttributeBuilder { + pub fn new() -> Self { + FeatureAttributeBuilder { + attributes: BTreeMap::new(), + locus_tag: None, + } + } + pub fn set_counter(&mut self, counter: String) -> &mut Self { + self.locus_tag = Some(counter); + self + } + pub fn insert_to(&mut self, value: FeatureAttributes) { + if let Some(counter) = &self.locus_tag { + self.attributes + .entry(counter.to_string()) + .or_insert_with(HashSet::new) + .insert(value); + } else { + { + ::core::panicking::panic_fmt(format_args!("Counter key not set")); + }; + } + } + pub fn set_start(&mut self, value: RangeValue) -> &mut Self { + self.insert_to(FeatureAttributes::Start { value }); + self + } + pub fn set_stop(&mut self, value: RangeValue) -> &mut Self { + self.insert_to(FeatureAttributes::Stop { value }); + self + } + pub fn set_gene(&mut self, value: String) -> &mut Self { + self.insert_to(FeatureAttributes::Gene { value }); + self + } + pub fn set_product(&mut self, value: String) -> &mut Self { + self.insert_to(FeatureAttributes::Product { + value, + }); + self + } + pub fn set_codon_start(&mut self, value: u8) -> &mut Self { + self.insert_to(FeatureAttributes::CodonStart { + value, + }); + self + } + pub fn set_strand(&mut self, value: i8) -> &mut Self { + self.insert_to(FeatureAttributes::Strand { value }); + self + } + pub fn build(self) -> BTreeMap> { + self.attributes + } + pub fn iter_sorted( + &self, + ) -> std::collections::btree_map::Iter> { + self.attributes.iter() + } + pub fn default() -> Self { + FeatureAttributeBuilder { + attributes: BTreeMap::new(), + locus_tag: None, + } + } + } + ///stores the sequences of the coding sequences (genes) and proteins. Also stores start, stop, codon_start and strand information + pub enum SequenceAttributes { + Start { value: RangeValue }, + Stop { value: RangeValue }, + SequenceFfn { value: String }, + SequenceFaa { value: String }, + CodonStart { value: u8 }, + Strand { value: i8 }, + } + #[automatically_derived] + impl ::core::fmt::Debug for SequenceAttributes { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + SequenceAttributes::Start { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Start", + "value", + &__self_0, + ) + } + SequenceAttributes::Stop { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Stop", + "value", + &__self_0, + ) + } + SequenceAttributes::SequenceFfn { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "SequenceFfn", + "value", + &__self_0, + ) + } + SequenceAttributes::SequenceFaa { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "SequenceFaa", + "value", + &__self_0, + ) + } + SequenceAttributes::CodonStart { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "CodonStart", + "value", + &__self_0, + ) + } + SequenceAttributes::Strand { value: __self_0 } => { + ::core::fmt::Formatter::debug_struct_field1_finish( + f, + "Strand", + "value", + &__self_0, + ) + } + } + } + } + #[automatically_derived] + impl ::core::cmp::Eq for SequenceAttributes { + #[inline] + #[doc(hidden)] + #[coverage(off)] + fn assert_receiver_is_total_eq(&self) -> () { + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for SequenceAttributes {} + #[automatically_derived] + impl ::core::cmp::PartialEq for SequenceAttributes { + #[inline] + fn eq(&self, other: &SequenceAttributes) -> bool { + let __self_discr = ::core::intrinsics::discriminant_value(self); + let __arg1_discr = ::core::intrinsics::discriminant_value(other); + __self_discr == __arg1_discr + && match (self, other) { + ( + SequenceAttributes::Start { value: __self_0 }, + SequenceAttributes::Start { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SequenceAttributes::Stop { value: __self_0 }, + SequenceAttributes::Stop { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SequenceAttributes::SequenceFfn { value: __self_0 }, + SequenceAttributes::SequenceFfn { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SequenceAttributes::SequenceFaa { value: __self_0 }, + SequenceAttributes::SequenceFaa { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SequenceAttributes::CodonStart { value: __self_0 }, + SequenceAttributes::CodonStart { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + ( + SequenceAttributes::Strand { value: __self_0 }, + SequenceAttributes::Strand { value: __arg1_0 }, + ) => __self_0 == __arg1_0, + _ => unsafe { ::core::intrinsics::unreachable() } + } + } + } + #[automatically_derived] + impl ::core::hash::Hash for SequenceAttributes { + #[inline] + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + let __self_discr = ::core::intrinsics::discriminant_value(self); + ::core::hash::Hash::hash(&__self_discr, state); + match self { + SequenceAttributes::Start { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SequenceAttributes::Stop { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SequenceAttributes::SequenceFfn { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SequenceAttributes::SequenceFaa { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SequenceAttributes::CodonStart { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + SequenceAttributes::Strand { value: __self_0 } => { + ::core::hash::Hash::hash(__self_0, state) + } + } + } + } + #[automatically_derived] + impl ::core::clone::Clone for SequenceAttributes { + #[inline] + fn clone(&self) -> SequenceAttributes { + match self { + SequenceAttributes::Start { value: __self_0 } => { + SequenceAttributes::Start { + value: ::core::clone::Clone::clone(__self_0), + } + } + SequenceAttributes::Stop { value: __self_0 } => { + SequenceAttributes::Stop { + value: ::core::clone::Clone::clone(__self_0), + } + } + SequenceAttributes::SequenceFfn { value: __self_0 } => { + SequenceAttributes::SequenceFfn { + value: ::core::clone::Clone::clone(__self_0), + } + } + SequenceAttributes::SequenceFaa { value: __self_0 } => { + SequenceAttributes::SequenceFaa { + value: ::core::clone::Clone::clone(__self_0), + } + } + SequenceAttributes::CodonStart { value: __self_0 } => { + SequenceAttributes::CodonStart { + value: ::core::clone::Clone::clone(__self_0), + } + } + SequenceAttributes::Strand { value: __self_0 } => { + SequenceAttributes::Strand { + value: ::core::clone::Clone::clone(__self_0), + } + } + } + } + } + impl SequenceAttributeBuilder { + pub fn get_start(&self, key: &str) -> Option<&RangeValue> { + self.seq_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SequenceAttributes::Start { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_stop(&self, key: &str) -> Option<&RangeValue> { + self.seq_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SequenceAttributes::Stop { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_sequence_ffn(&self, key: &str) -> Option<&String> { + self.seq_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SequenceAttributes::SequenceFfn { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_sequence_faa(&self, key: &str) -> Option<&String> { + self.seq_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SequenceAttributes::SequenceFaa { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_codon_start(&self, key: &str) -> Option<&u8> { + self.seq_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SequenceAttributes::CodonStart { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + pub fn get_strand(&self, key: &str) -> Option<&i8> { + self.seq_attributes + .get(key) + .and_then(|set| { + set.iter() + .find_map(|attr| { + if let SequenceAttributes::Strand { value } = attr { + Some(value) + } else { + None + } + }) + }) + } + } + ///builder for the sequence information on a per coding sequence (CDS) basis + pub struct SequenceAttributeBuilder { + pub seq_attributes: BTreeMap>, + pub locus_tag: Option, + } + #[automatically_derived] + impl ::core::fmt::Debug for SequenceAttributeBuilder { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish( + f, + "SequenceAttributeBuilder", + "seq_attributes", + &self.seq_attributes, + "locus_tag", + &&self.locus_tag, + ) + } + } + #[automatically_derived] + impl ::core::default::Default for SequenceAttributeBuilder { + #[inline] + fn default() -> SequenceAttributeBuilder { + SequenceAttributeBuilder { + seq_attributes: ::core::default::Default::default(), + locus_tag: ::core::default::Default::default(), + } + } + } + #[automatically_derived] + impl ::core::clone::Clone for SequenceAttributeBuilder { + #[inline] + fn clone(&self) -> SequenceAttributeBuilder { + SequenceAttributeBuilder { + seq_attributes: ::core::clone::Clone::clone(&self.seq_attributes), + locus_tag: ::core::clone::Clone::clone(&self.locus_tag), + } + } + } + impl SequenceAttributeBuilder { + pub fn new() -> Self { + SequenceAttributeBuilder { + seq_attributes: BTreeMap::new(), + locus_tag: None, + } + } + pub fn set_counter(&mut self, counter: String) -> &mut Self { + self.locus_tag = Some(counter); + self + } + pub fn insert_to(&mut self, value: SequenceAttributes) { + if let Some(counter) = &self.locus_tag { + self.seq_attributes + .entry(counter.to_string()) + .or_insert_with(HashSet::new) + .insert(value); + } else { + { + ::core::panicking::panic_fmt(format_args!("Counter key not set")); + }; + } + } + pub fn set_start(&mut self, value: RangeValue) -> &mut Self { + self.insert_to(SequenceAttributes::Start { value }); + self + } + pub fn set_stop(&mut self, value: RangeValue) -> &mut Self { + self.insert_to(SequenceAttributes::Stop { value }); + self + } + pub fn set_sequence_ffn(&mut self, value: String) -> &mut Self { + self.insert_to(SequenceAttributes::SequenceFfn { + value, + }); + self + } + pub fn set_sequence_faa(&mut self, value: String) -> &mut Self { + self.insert_to(SequenceAttributes::SequenceFaa { + value, + }); + self + } + pub fn set_codon_start(&mut self, value: u8) -> &mut Self { + self.insert_to(SequenceAttributes::CodonStart { + value, + }); + self + } + pub fn set_strand(&mut self, value: i8) -> &mut Self { + self.insert_to(SequenceAttributes::Strand { + value, + }); + self + } + pub fn build(self) -> BTreeMap> { + self.seq_attributes + } + pub fn iter_sorted( + &self, + ) -> std::collections::btree_map::Iter> { + self.seq_attributes.iter() + } + pub fn default() -> Self { + SequenceAttributeBuilder { + seq_attributes: BTreeMap::new(), + locus_tag: None, + } + } + } + ///product lines can contain difficult to parse punctuation such as biochemical symbols like unclosed single quotes, superscripts, single and double brackets etc. + ///here we substitute these for an underscore + pub fn substitute_odd_punctuation(input: String) -> Result { + let re = Regex::new(r"[/?()',`]|[α-ωΑ-Ω]")?; + let cleaned = input.trim_end_matches(&['\r', '\n'][..]); + Ok(re.replace_all(cleaned, "_").to_string()) + } + ///GFF3 field9 construct + pub struct GFFInner { + pub id: String, + pub name: String, + pub locus_tag: String, + pub gene: String, + pub product: String, + } + #[automatically_derived] + impl ::core::fmt::Debug for GFFInner { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field5_finish( + f, + "GFFInner", + "id", + &self.id, + "name", + &self.name, + "locus_tag", + &self.locus_tag, + "gene", + &self.gene, + "product", + &&self.product, + ) + } + } + impl GFFInner { + pub fn new( + id: String, + name: String, + locus_tag: String, + gene: String, + product: String, + ) -> Self { + GFFInner { + id, + name, + locus_tag, + gene, + product, + } + } + } + ///The main GFF3 construct + pub struct GFFOuter<'a> { + pub seqid: String, + pub source: String, + pub type_val: String, + pub start: u32, + pub end: u32, + pub score: f64, + pub strand: String, + pub phase: u8, + pub attributes: &'a GFFInner, + } + #[automatically_derived] + impl<'a> ::core::fmt::Debug for GFFOuter<'a> { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let names: &'static _ = &[ + "seqid", + "source", + "type_val", + "start", + "end", + "score", + "strand", + "phase", + "attributes", + ]; + let values: &[&dyn ::core::fmt::Debug] = &[ + &self.seqid, + &self.source, + &self.type_val, + &self.start, + &self.end, + &self.score, + &self.strand, + &self.phase, + &&self.attributes, + ]; + ::core::fmt::Formatter::debug_struct_fields_finish( + f, + "GFFOuter", + names, + values, + ) + } + } + impl<'a> GFFOuter<'a> { + pub fn new( + seqid: String, + source: String, + type_val: String, + start: u32, + end: u32, + score: f64, + strand: String, + phase: u8, + attributes: &'a GFFInner, + ) -> Self { + GFFOuter { + seqid, + source, + type_val, + start, + end, + score, + strand, + phase, + attributes, + } + } + pub fn field9_attributes_build(&self) -> String { + let mut full_field9 = Vec::new(); + if !self.attributes.id.is_empty() { + full_field9 + .push( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("id={0}", self.attributes.id), + ); + res + }), + ); + } + if !self.attributes.name.is_empty() { + full_field9 + .push( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("name={0}", self.attributes.name), + ); + res + }), + ); + } + if !self.attributes.gene.is_empty() { + full_field9 + .push( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("gene={0}", self.attributes.gene), + ); + res + }), + ); + } + if !self.attributes.locus_tag.is_empty() { + full_field9 + .push( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("locus_tag={0}", self.attributes.locus_tag), + ); + res + }), + ); + } + if !self.attributes.product.is_empty() { + full_field9 + .push( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("product={0}", self.attributes.product), + ); + res + }), + ); + } + full_field9.join(";") + } + } + ///formats the translation string which can be multiple lines, for gbk + pub fn format_translation(translation: &str) -> String { + let mut formatted = String::new(); + let cleaned_translation = translation.replace("\n", ""); + formatted.push_str(" /translation=\""); + let line_length: usize = 60; + let final_num = line_length - 15; + formatted + .push_str( + &::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("{0}\n", &cleaned_translation[0..final_num]), + ); + res + }), + ); + for i in (47..translation.len()).step_by(60) { + let end = i + 60 - 1; + let valid_end = if end >= translation.len() { + &cleaned_translation.len() - 1 + } else { + end + }; + formatted + .push_str( + &::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!( + " {0}", + &cleaned_translation[i..valid_end], + ), + ); + res + }), + ); + { + ::std::io::_print( + format_args!( + "cleaned translation leng is {0:?}\n", + &cleaned_translation[i..valid_end].len(), + ), + ); + }; + if *&cleaned_translation[i..valid_end].len() < 59 { + formatted.push('\"'); + } else { + formatted.push('\n'); + } + } + formatted + } + ///writes the DNA sequence in gbk format with numbering + pub fn write_gbk_format_sequence(sequence: &str, file: &mut File) -> io::Result<()> { + file.write_fmt(format_args!("ORIGIN\n"))?; + let mut formatted = String::new(); + let cleaned_input = sequence.replace("\n", ""); + let mut index = 1; + for (_i, chunk) in cleaned_input.as_bytes().chunks(60).enumerate() { + formatted + .push_str( + &::alloc::__export::must_use({ + let res = ::alloc::fmt::format(format_args!("{0:>5} ", index)); + res + }), + ); + for (j, sub_chunk) in chunk.chunks(10).enumerate() { + if j > 0 { + formatted.push(' '); + } + formatted.push_str(&String::from_utf8_lossy(sub_chunk)); + } + formatted.push('\n'); + index += 60; + } + file.write_fmt(format_args!("{0:>6}\n", &formatted))?; + file.write_fmt(format_args!("//\n"))?; + Ok(()) + } + ///saves the parsed data in genbank format + pub fn gbk_write( + seq_region: BTreeMap, + record_vec: Vec, + filename: &str, + ) -> io::Result<()> { + let now = Local::now(); + let formatted_date = now.format("%d-%b-%Y").to_string().to_uppercase(); + let mut file = OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(filename)?; + for (i, (key, _val)) in seq_region.iter().enumerate() { + let strain = match &record_vec[i].source_map.get_strain(key) { + Some(value) => value.to_string(), + None => "Unknown".to_string(), + }; + let organism = match &record_vec[i].source_map.get_organism(key) { + Some(value) => value.to_string(), + None => "Unknown".to_string(), + }; + let mol_type = match &record_vec[i].source_map.get_mol_type(key) { + Some(value) => value.to_string(), + None => "Unknown".to_string(), + }; + let type_material = match &record_vec[i].source_map.get_type_material(&key) { + Some(value) => value.to_string(), + None => "Unknown".to_string(), + }; + let db_xref = match &record_vec[i].source_map.get_db_xref(key) { + Some(value) => value.to_string(), + None => "Unknown".to_string(), + }; + let source_stop = match &record_vec[i].source_map.get_stop(key) { + Some(value) => value.get_value(), + None => { + { + { + ::std::io::_print(format_args!("stop value not found\n")); + }; + None + } + .expect("stop value not received") + } + }; + file.write_fmt( + format_args!( + "LOCUS {0} {1} bp DNA linear CON {2}\n", + &key, + &record_vec[i].sequence.len(), + &formatted_date, + ), + )?; + file.write_fmt(format_args!("DEFINITION {0} {1}.\n", &organism, &strain))?; + file.write_fmt(format_args!("ACCESSION {0}\n", &key))?; + file.write_fmt(format_args!("KEYWORDS .\n"))?; + file.write_fmt(format_args!("SOURCE {0} {1}\n", &organism, &strain))?; + file.write_fmt(format_args!(" ORGANISM {0} {1}\n", &organism, &strain))?; + file.write_fmt(format_args!("FEATURES Location/Qualifiers\n"))?; + file.write_fmt(format_args!(" source 1..{0}\n", &source_stop))?; + file.write_fmt( + format_args!(" /organism=\"{0}\"\n", &strain), + )?; + file.write_fmt( + format_args!(" /mol_type=\"{0}\"\n", &mol_type), + )?; + file.write_fmt( + format_args!(" /strain=\"{0}\"\n", &strain), + )?; + if type_material != *"Unknown".to_string() { + file.write_fmt( + format_args!( + " /type_material=\"{0}\"\n", + &type_material, + ), + )?; + } + file.write_fmt( + format_args!(" /db_xref=\"{0}\"\n", &db_xref), + )?; + for (locus_tag, _value) in &record_vec[i].cds.attributes { + let start = match &record_vec[i].cds.get_start(locus_tag) { + Some(value) => value.get_value(), + None => { + { + { + ::std::io::_print(format_args!("start value not found\n")); + }; + None + } + .expect("start value not received") + } + }; + let stop = match &record_vec[i].cds.get_stop(locus_tag) { + Some(value) => value.get_value(), + None => { + { + { + ::std::io::_print(format_args!("stop value not found\n")); + }; + None + } + .expect("stop value not received") + } + }; + let product = match &record_vec[i].cds.get_product(locus_tag) { + Some(value) => value.to_string(), + None => "unknown product".to_string(), + }; + let strand = match &record_vec[i].cds.get_strand(locus_tag) { + Some(value) => **value, + None => 0, + }; + let codon_start = match &record_vec[i].cds.get_codon_start(locus_tag) { + Some(value) => **value, + None => 0, + }; + let gene = match &record_vec[i].cds.get_gene(locus_tag) { + Some(value) => value.to_string(), + None => "unknown".to_string(), + }; + let translation = match &record_vec[i] + .seq_features + .get_sequence_faa(locus_tag) + { + Some(value) => value.to_string(), + None => "unknown".to_string(), + }; + if strand == 1 { + file.write_fmt( + format_args!(" gene {0}..{1}\n", &start, &stop), + )?; + } else { + file.write_fmt( + format_args!( + " gene complement({0}..{1})\n", + &start, + &stop, + ), + )?; + } + file.write_fmt( + format_args!(" /locus_tag=\"{0}\"\n", &locus_tag), + )?; + if strand == 1 { + file.write_fmt( + format_args!(" CDS {0}..{1}\n", &start, &stop), + )?; + } else { + file.write_fmt( + format_args!( + " CDS complement({0}..{1})\n", + &start, + &stop, + ), + )?; + } + file.write_fmt( + format_args!(" /locus_tag=\"{0}\"\n", &locus_tag), + )?; + file.write_fmt( + format_args!( + " /codon_start=\"{0}\"\n", + &codon_start, + ), + )?; + if gene != "unknown" { + file.write_fmt( + format_args!(" /gene=\"{0}\"\n", &gene), + )?; + } + if translation != "unknown" { + let formatted_translation = format_translation(&translation); + file.write_fmt(format_args!("{0}\n", &formatted_translation))?; + } + file.write_fmt( + format_args!(" /product=\"{0}\"\n", &product), + )?; + } + write_gbk_format_sequence(&record_vec[i].sequence, &mut file)?; + } + Ok(()) + } + ///saves the parsed data in gff3 format + #[allow(unused_assignments)] + #[allow(unused_variables)] + pub fn gff_write( + seq_region: BTreeMap, + mut record_vec: Vec, + filename: &str, + dna: bool, + ) -> io::Result<()> { + let mut file = OpenOptions::new().append(true).create(true).open(filename)?; + if file.metadata()?.len() == 0 { + file.write_fmt(format_args!("##gff-version 3\n"))?; + } + let mut full_seq = String::new(); + let mut prev_end: u32 = 0; + for (k, v) in seq_region.iter() { + file.write_fmt( + format_args!("##sequence-region\t{0}\t{1}\t{2}\n", &k, v.0, v.1), + )?; + } + for ((source_name, (seq_start, seq_end)), record) in seq_region + .iter() + .zip(record_vec.drain(..)) + { + if dna == true { + full_seq.push_str(&record.sequence); + } + for (locus_tag, _valu) in &record.cds.attributes { + let start = match record.cds.get_start(locus_tag) { + Some(value) => value.get_value(), + None => { + { + { + ::std::io::_print(format_args!("start value not found\n")); + }; + None + } + .expect("start value not received") + } + }; + let stop = match record.cds.get_stop(locus_tag) { + Some(value) => value.get_value(), + None => { + { + { + ::std::io::_print(format_args!("stop value not found\n")); + }; + None + } + .expect("stop value not received") + } + }; + let gene = match record.cds.get_gene(locus_tag) { + Some(value) => value.to_string(), + None => "unknown".to_string(), + }; + let product = match record.cds.get_product(locus_tag) { + Some(value) => value.to_string(), + None => "unknown product".to_string(), + }; + let strand = match record.cds.get_strand(locus_tag) { + Some(valu) => { + match valu { + 1 => "+".to_string(), + -1 => "-".to_string(), + _ => { + { + ::std::io::_print( + format_args!( + "unexpected strand value {0} for locus_tag {1}\n", + valu, + locus_tag, + ), + ); + }; + "unknownstrand".to_string() + } + } + } + None => "unknownvalue".to_string(), + }; + let phase = match record.cds.get_codon_start(locus_tag) { + Some(valuer) => { + match valuer { + 1 => 0, + 2 => 1, + 3 => 2, + _ => { + { + ::std::io::_print( + format_args!( + "unexpected phase value {0} in the bagging area for locus_tag {1}\n", + valuer, + locus_tag, + ), + ); + }; + 1 + } + } + } + None => 1, + }; + let gff_inner = GFFInner::new( + locus_tag.to_string(), + source_name.clone(), + locus_tag.to_string(), + gene, + product, + ); + let gff_outer = GFFOuter::new( + source_name.clone(), + ".".to_string(), + "CDS".to_string(), + start + prev_end, + stop + prev_end, + 0.0, + strand, + phase, + &gff_inner, + ); + let field9_attributes = gff_outer.field9_attributes_build(); + file.write_fmt( + format_args!( + "{0}\t{1}\t{2}\t{3:?}\t{4:?}\t{5}\t{6}\t{7}\t{8}\n", + gff_outer.seqid, + gff_outer.source, + gff_outer.type_val, + gff_outer.start, + gff_outer.end, + gff_outer.score, + gff_outer.strand, + gff_outer.phase, + field9_attributes, + ), + )?; + } + prev_end = *seq_end; + } + if dna { + file.write_fmt(format_args!("##FASTA\n"))?; + file.write_fmt(format_args!("{0}\n", full_seq))?; + } + Ok(()) + } + ///saves the parsed data in gff3 format + #[allow(unused_assignments)] + pub fn orig_gff_write( + seq_region: BTreeMap, + record_vec: Vec, + filename: &str, + dna: bool, + ) -> io::Result<()> { + let mut file = OpenOptions::new().append(true).create(true).open(filename)?; + if file.metadata()?.len() == 0 { + file.write_fmt(format_args!("##gff-version 3\n"))?; + } + let mut source_name = String::new(); + let mut full_seq = String::new(); + let mut prev_end: u32 = 0; + for (k, v) in seq_region.iter() { + file.write_fmt( + format_args!("##sequence-region\t{0}\t{1}\t{2}\n", &k, v.0, v.1), + )?; + } + for (i, (key, val)) in seq_region.iter().enumerate() { + source_name = key.to_string(); + if dna == true { + full_seq.push_str(&record_vec[i].sequence); + } + for (locus_tag, _valu) in &record_vec[i].cds.attributes { + let start = match record_vec[i].cds.get_start(locus_tag) { + Some(value) => value.get_value(), + None => { + { + { + ::std::io::_print(format_args!("start value not found\n")); + }; + None + } + .expect("start value not received") + } + }; + let stop = match record_vec[i].cds.get_stop(locus_tag) { + Some(value) => value.get_value(), + None => { + { + { + ::std::io::_print(format_args!("stop value not found\n")); + }; + None + } + .expect("stop value not received") + } + }; + let gene = match record_vec[i].cds.get_gene(locus_tag) { + Some(value) => value.to_string(), + None => "unknown".to_string(), + }; + let product = match record_vec[i].cds.get_product(locus_tag) { + Some(value) => value.to_string(), + None => "unknown product".to_string(), + }; + let strand = match record_vec[i].cds.get_strand(locus_tag) { + Some(valu) => { + match valu { + 1 => "+".to_string(), + -1 => "-".to_string(), + _ => { + { + ::std::io::_print( + format_args!( + "unexpected strand value {0} for locus_tag {1}\n", + valu, + locus_tag, + ), + ); + }; + "unknownstrand".to_string() + } + } + } + None => "unknownvalue".to_string(), + }; + let phase = match record_vec[i].cds.get_codon_start(locus_tag) { + Some(valuer) => { + match valuer { + 1 => 0, + 2 => 1, + 3 => 2, + _ => { + { + ::std::io::_print( + format_args!( + "unexpected phase value {0} in the bagging area for locus_tag {1}\n", + valuer, + locus_tag, + ), + ); + }; + 1 + } + } + } + None => 1, + }; + let gff_inner = GFFInner::new( + locus_tag.to_string(), + source_name.clone(), + locus_tag.to_string(), + gene, + product, + ); + let gff_outer = GFFOuter::new( + source_name.clone(), + ".".to_string(), + "CDS".to_string(), + start + prev_end, + stop + prev_end, + 0.0, + strand, + phase, + &gff_inner, + ); + let field9_attributes = gff_outer.field9_attributes_build(); + file.write_fmt( + format_args!( + "{0}\t{1}\t{2}\t{3:?}\t{4:?}\t{5}\t{6}\t{7}\t{8}\n", + gff_outer.seqid, + gff_outer.source, + gff_outer.type_val, + gff_outer.start, + gff_outer.end, + gff_outer.score, + gff_outer.strand, + gff_outer.phase, + field9_attributes, + ), + )?; + } + prev_end = val.1; + } + if dna { + file.write_fmt(format_args!("##FASTA\n"))?; + file.write_fmt(format_args!("{0}\n", full_seq))?; + } + Ok(()) + } + ///internal record containing data from a single source or contig. Has multiple features. + pub struct Record { + pub id: String, + pub length: u32, + pub sequence: String, + pub start: usize, + pub end: usize, + pub strand: i32, + pub cds: FeatureAttributeBuilder, + pub source_map: SourceAttributeBuilder, + pub seq_features: SequenceAttributeBuilder, + } + #[automatically_derived] + impl ::core::fmt::Debug for Record { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let names: &'static _ = &[ + "id", + "length", + "sequence", + "start", + "end", + "strand", + "cds", + "source_map", + "seq_features", + ]; + let values: &[&dyn ::core::fmt::Debug] = &[ + &self.id, + &self.length, + &self.sequence, + &self.start, + &self.end, + &self.strand, + &self.cds, + &self.source_map, + &&self.seq_features, + ]; + ::core::fmt::Formatter::debug_struct_fields_finish( + f, + "Record", + names, + values, + ) + } + } + #[automatically_derived] + impl ::core::clone::Clone for Record { + #[inline] + fn clone(&self) -> Record { + Record { + id: ::core::clone::Clone::clone(&self.id), + length: ::core::clone::Clone::clone(&self.length), + sequence: ::core::clone::Clone::clone(&self.sequence), + start: ::core::clone::Clone::clone(&self.start), + end: ::core::clone::Clone::clone(&self.end), + strand: ::core::clone::Clone::clone(&self.strand), + cds: ::core::clone::Clone::clone(&self.cds), + source_map: ::core::clone::Clone::clone(&self.source_map), + seq_features: ::core::clone::Clone::clone(&self.seq_features), + } + } + } + impl Record { + /// Create a new instance. + pub fn new() -> Self { + Record { + id: "".to_owned(), + length: 0, + sequence: "".to_owned(), + start: 0, + end: 0, + strand: 0, + source_map: SourceAttributeBuilder::new(), + cds: FeatureAttributeBuilder::new(), + seq_features: SequenceAttributeBuilder::new(), + } + } + pub fn is_empty(&mut self) -> bool { + self.id.is_empty() && self.length == 0 + } + pub fn check(&mut self) -> Result<(), &str> { + if self.id().is_empty() { + return Err("Expecting id for Gbk record."); + } + Ok(()) + } + pub fn id(&mut self) -> &str { + &self.id + } + pub fn length(&mut self) -> u32 { + self.length + } + pub fn sequence(&mut self) -> &str { + &self.sequence + } + pub fn start(&mut self) -> u32 { + self.start.try_into().unwrap() + } + pub fn end(&mut self) -> u32 { + self.end.try_into().unwrap() + } + pub fn strand(&mut self) -> i32 { + self.strand + } + pub fn cds(&mut self) -> FeatureAttributeBuilder { + self.cds.clone() + } + pub fn source_map(&mut self) -> SourceAttributeBuilder { + self.source_map.clone() + } + pub fn seq_features(&mut self) -> SequenceAttributeBuilder { + self.seq_features.clone() + } + fn rec_clear(&mut self) { + self.id.clear(); + self.length = 0; + self.sequence.clear(); + self.start = 0; + self.end = 0; + self.strand = 0; + self.source_map = SourceAttributeBuilder::new(); + self.cds = FeatureAttributeBuilder::new(); + self.seq_features = SequenceAttributeBuilder::new(); + } + } + impl Default for Record { + fn default() -> Self { + Self::new() + } + } + #[allow(dead_code)] + pub struct Config { + filename: String, + } + impl Config { + pub fn new(args: &[String]) -> Result { + if args.len() < 2 { + { + ::core::panicking::panic_fmt( + format_args!("not enough arguments, please provide filename"), + ); + }; + } + let filename = args[1].clone(); + Ok(Config { filename }) + } + } +} diff --git a/microBioRust/src/embl.rs b/microBioRust/src/embl.rs index 0330792..5d9228e 100644 --- a/microBioRust/src/embl.rs +++ b/microBioRust/src/embl.rs @@ -1394,221 +1394,3 @@ impl Config { Ok(Config { filename }) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - #[allow(unused_mut)] - #[allow(unused_variables)] - #[allow(dead_code)] - pub fn embl_to_gff() -> io::Result<()> { - let file_embl = fs::File::open("example.embl")?; - let prev_start: u32 = 0; - let mut prev_end: u32 = 0; - let mut reader = Reader::new(file_embl); - let mut records = reader.records(); - let mut read_counter: u32 = 0; - let mut seq_region: BTreeMap = BTreeMap::new(); - let mut record_vec: Vec = Vec::new(); - loop { - match records.next() { - Some(Ok(mut record)) => { - //println!("next record"); - //println!("Record id: {:?}", record.id); - let sour = record.source_map.source_name.clone().expect("issue collecting source name"); - let beginning = match record.source_map.get_start(&sour) { - Some(value) => value.get_value(), - _ => 0, - }; - let ending = match record.source_map.get_stop(&sour) { - Some(value) => value.get_value(), - _ => 0, - }; - if ending + prev_end < beginning + prev_end { - println!("debug since the end value smaller is than the start {:?}", beginning); - } - seq_region.insert(sour, (beginning + prev_end, ending + prev_end)); - record_vec.push(record); - // Add additional fields to print if needed - read_counter+=1; - prev_end+=ending; - }, - Some(Err(e)) => { println!("theres an err {:?}", e); }, - None => { - println!("finished iteration"); - break; }, - } - } - let output_file = format!("test_output_embl.gff"); - gff_write(seq_region.clone(), record_vec, &output_file, true)?; - println!("Total records processed: {}", read_counter); - return Ok(()); - } - #[test] - #[allow(unused_mut)] - #[allow(unused_variables)] - pub fn embl_to_faa() -> Result<(), anyhow::Error> { - let file_embl = fs::File::open("example.embl")?; - let mut reader = Reader::new(file_embl); - let mut records = reader.records(); - let mut read_counter: u32 = 0; - loop { - match records.next() { - Some(Ok(mut record)) => { - //println!("next record"); - //println!("Record id: {:?}", record.id); - for (k, _v) in &record.cds.attributes { - match record.seq_features.get_sequence_faa(&k) { - Some(value) => { let seq_faa = value.to_string(); - println!(">{}|{}\n{}", &record.id, &k, seq_faa); - }, - _ => (), - }; - - } - read_counter+=1; - }, - Some(Err(e)) => { println!("theres an err {:?}", e); }, - None => { - println!("finished iteration"); - break; }, - } - } - println!("Total records processed: {}", read_counter); - return Ok(()); - } - #[test] - #[allow(unused_mut)] - #[allow(unused_variables)] - pub fn embl_to_ffn() -> Result<(), anyhow::Error> { - let file_embl = fs::File::open("example.embl")?; - let mut reader = Reader::new(file_embl); - let mut records = reader.records(); - let mut read_counter: u32 = 0; - loop { - match records.next() { - Some(Ok(mut record)) => { - //println!("next record"); - //println!("Record id: {:?}", record.id); - for (k, v) in &record.cds.attributes { - match record.seq_features.get_sequence_ffn(&k) { - Some(value) => { let seq_ffn = value.to_string(); - println!(">{}|{}\n{}", &record.id, &k, seq_ffn); - }, - _ => (), - }; - - } - read_counter+=1; - }, - Some(Err(e)) => { println!("theres an err {:?}", e); }, - None => { - println!("finished iteration"); - break; }, - } - } - println!("Total records processed: {}", read_counter); - return Ok(()); - } - #[test] - /// Test to create a new record - /// We require a source, features, sequence features and a sequence - /// The source is top level, a single genbank file has one source, multi-genbank has one per contig - /// The SourceAttributes construct has a name (counter), start, stop, organism, moltype, strain, type material and db_xref - /// The FeatureAttributes construct has a locus tag (counter), gene, product, start, stop, codon start, strand - /// SourceAttribute start and stop are the coordinates of the source feature or per contig, FeatureAttributes start and stop are per coding sequence (CDS) - /// The SequenceAttributes construct has a locus tag (counter), start, stop, sequence_ffn, sequence_faa, codon start, and strand - /// SequenceAttribute start and stop, codon start and strand are duplicates of those in the FeatureAttributes - /// To add an entry requires using the set_ values such as set_start, set_stop, set_counter, set_strand - /// To write in GFF format requires gff_write(seq_region, record_vec, filename and true/false - /// The seq_region is the region of interest with name and DNA coordinates such as ``` "source_1".to_string(), (1,897) ``` - /// record_vec is a list of the records. If there is only one record ``` vec![record] ``` will suffice - /// filename is the required filename string, true/false is whether the DNA sequence should be included in the GFF3 file - /// Some GFF3 files have the DNA sequence, whilst others do not. Some tools require the DNA sequence included. - #[allow(unused_mut)] - #[allow(unused_variables)] - pub fn create_new_record() -> Result<(), anyhow::Error> { - let filename = format!("new_record.gff"); - let mut record = Record::new(); - let mut seq_region: BTreeMap = BTreeMap::new(); - seq_region.insert("source_1".to_string(), (1,910)); - record.source_map - .set_counter("source_1".to_string()) - .set_start(RangeValue::Exact(1)) - .set_stop(RangeValue::Exact(910)) - .set_organism("Escherichia coli".to_string()) - .set_mol_type("DNA".to_string()) - .set_strain("K-12 substr. MG1655".to_string()) - // culture_collection.clone() - .set_type_material("type strain of Escherichia coli K12".to_string()) - .set_db_xref("PRJNA57779".to_string()); - record.cds - .set_counter("b3304".to_string()) - .set_start(RangeValue::Exact(1)) - .set_stop(RangeValue::Exact(354)) - .set_gene("rplR".to_string()) - .set_product("50S ribosomal subunit protein L18".to_string()) - .set_codon_start(1) - .set_strand(-1); - record.cds - .set_counter("b3305".to_string()) - .set_start(RangeValue::Exact(364)) - .set_stop(RangeValue::Exact(897)) - .set_gene("rplF".to_string()) - .set_product("50S ribosomal subunit protein L6".to_string()) - .set_codon_start(1) - .set_strand(-1); - record.seq_features - .set_counter("b3304".to_string()) - .set_start(RangeValue::Exact(1)) - .set_stop(RangeValue::Exact(354)) - .set_sequence_ffn("ATGGATAAGAAATCTGCTCGTATCCGTCGTGCGACCCGCGCACGCCGCAAGCTCCAGGAG -CTGGGCGCAACTCGCCTGGTGGTACATCGTACCCCGCGTCACATTTACGCACAGGTAATT -GCACCGAACGGTTCTGAAGTTCTGGTAGCTGCTTCTACTGTAGAAAAAGCTATCGCTGAA -CAACTGAAGTACACCGGTAACAAAGACGCGGCTGCAGCTGTGGGTAAAGCTGTCGCTGAA -CGCGCTCTGGAAAAAGGCATCAAAGATGTATCCTTTGACCGTTCCGGGTTCCAATATCAT -GGTCGTGTCCAGGCACTGGCAGATGCTGCCCGTGAAGCTGGCCTTCAGTTCTAA".to_string()) - .set_sequence_faa("MDKKSARIRRATRARRKLQELGATRLVVHRTPRHIYAQVIAPNGSEVLVAASTVEKAIAE -QLKYTGNKDAAAAVGKAVAERALEKGIKDVSFDRSGFQYHGRVQALADAAREAGLQF".to_string()) - .set_codon_start(1) - .set_strand(-1); - record.seq_features - .set_counter("b3305".to_string()) - .set_start(RangeValue::Exact(364)) - .set_stop(RangeValue::Exact(897)) - .set_sequence_ffn("ATGTCTCGTGTTGCTAAAGCACCGGTCGTTGTTCCTGCCGGCGTTGACGTAAAAATCAAC -GGTCAGGTTATTACGATCAAAGGTAAAAACGGCGAGCTGACTCGTACTCTCAACGATGCT -GTTGAAGTTAAACATGCAGATAATACCCTGACCTTCGGTCCGCGTGATGGTTACGCAGAC -GGTTGGGCACAGGCTGGTACCGCGCGTGCCCTGCTGAACTCAATGGTTATCGGTGTTACC -GAAGGCTTCACTAAGAAGCTGCAGCTGGTTGGTGTAGGTTACCGTGCAGCGGTTAAAGGC -AATGTGATTAACCTGTCTCTGGGTTTCTCTCATCCTGTTGACCATCAGCTGCCTGCGGGT -ATCACTGCTGAATGTCCGACTCAGACTGAAATCGTGCTGAAAGGCGCTGATAAGCAGGTG -ATCGGCCAGGTTGCAGCGGATCTGCGCGCCTACCGTCGTCCTGAGCCTTATAAAGGCAAG -GGTGTTCGTTACGCCGACGAAGTCGTGCGTACCAAAGAGGCTAAGAAGAAGTAA".to_string()) - .set_sequence_faa("MSRVAKAPVVVPAGVDVKINGQVITIKGKNGELTRTLNDAVEVKHADNTLTFGPRDGYAD -GWAQAGTARALLNSMVIGVTEGFTKKLQLVGVGYRAAVKGNVINLSLGFSHPVDHQLPAG -ITAECPTQTEIVLKGADKQVIGQVAADLRAYRRPEPYKGKGVRYADEVVRTKEAKKK".to_string()) - .set_codon_start(1) - .set_strand(-1); - record.sequence = "TTAGAACTGAAGGCCAGCTTCACGGGCAGCATCTGCCAGTGCCTGGACACGACCATGATA -TTGGAACCCGGAACGGTCAAAGGATACATCTTTGATGCCTTTTTCCAGAGCGCGTTCAGC -GACAGCTTTACCCACAGCTGCAGCCGCGTCTTTGTTACCGGTGTACTTCAGTTGTTCAGC -GATAGCTTTTTCTACAGTAGAAGCAGCTACCAGAACTTCAGAACCGTTCGGTGCAATTAC -CTGTGCGTAAATGTGACGCGGGGTACGATGTACCACCAGGCGAGTTGCGCCCAGCTCCTG -GAGCTTGCGGCGTGCGCGGGTCGCACGACGGATACGAGCAGATTTCTTATCCATAGTGTT -ACCTTACTTCTTCTTAGCCTCTTTGGTACGCACGACTTCGTCGGCGTAACGAACACCCTT -GCCTTTATAAGGCTCAGGACGACGGTAGGCGCGCAGATCCGCTGCAACCTGGCCGATCAC -CTGCTTATCAGCGCCTTTCAGCACGATTTCAGTCTGAGTCGGACATTCAGCAGTGATACC -CGCAGGCAGCTGATGGTCAACAGGATGAGAGAAACCCAGAGACAGGTTAATCACATTGCC -TTTAACCGCTGCACGGTAACCTACACCAACCAGCTGCAGCTTCTTAGTGAAGCCTTCGGT -AACACCGATAACCATTGAGTTCAGCAGGGCACGCGCGGTACCAGCCTGTGCCCAACCGTC -TGCGTAACCATCACGCGGACCGAAGGTCAGGGTATTATCTGCATGTTTAACTTCAACAGC -ATCGTTGAGAGTACGAGTCAGCTCGCCGTTTTTACCTTTGATCGTAATAACCTGACCGTT -GATTTTTACGTCAACGCCGGCAGGAACAACGACCGGTGCTTTAGCAACACGAGACA".to_string(); - gff_write(seq_region.clone(), vec![record.clone()], "new_output_embl.gff", true)?; - gbk_write(seq_region, vec![record], "new_output_embl.gbk")?; - return Ok(()); - } -} diff --git a/microBioRust/src/gbk.rs b/microBioRust/src/gbk.rs index 4a965c9..ac1cf00 100644 --- a/microBioRust/src/gbk.rs +++ b/microBioRust/src/gbk.rs @@ -431,7 +431,7 @@ where { type Item = Result; - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option { if self.error_has_occurred { println!("error was encountered in iteration"); None @@ -1564,225 +1564,3 @@ impl Config { Ok(Config { filename }) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - #[allow(unused_mut)] - #[allow(unused_variables)] - #[allow(dead_code)] - #[allow(unused_assignments)] - pub fn genbank_to_gff() -> io::Result<()> { - let file_gbk = fs::File::open("test_output.gbk")?; - let prev_start: u32 = 0; - let mut prev_end: u32 = 0; - let mut reader = Reader::new(file_gbk); - let mut records = reader.records(); - let mut read_counter: u32 = 0; - let mut seq_region: BTreeMap = BTreeMap::new(); - let mut record_vec: Vec = Vec::new(); - loop { - match records.next() { - Some(Ok(mut record)) => { - //println!("next record"); - //println!("Record id: {:?}", record.id); - let sour = record.source_map.source_name.clone().expect("issue collecting source name"); - let beginning = match record.source_map.get_start(&sour) { - Some(value) => value.get_value(), - _ => 0, - }; - let ending = match record.source_map.get_stop(&sour) { - Some(value) => value.get_value(), - _ => 0, - }; - if ending + prev_end < beginning + prev_end { - println!("debug since the end value smaller is than the start {:?}", beginning); - } - seq_region.insert(sour, (beginning + prev_end, ending + prev_end)); - record_vec.push(record); - // Add additional fields to print if needed - read_counter+=1; - prev_end+=ending; - }, - Some(Err(e)) => { println!("theres an err {:?}", e); }, - None => { - println!("finished iteration"); - break; }, - } - } - let output_file = format!("test_output.gff"); - gff_write(seq_region.clone(), record_vec, &output_file, true)?; - println!("Total records processed: {}", read_counter); - return Ok(()); - } - #[test] - #[allow(unused_mut)] - #[allow(unused_variables)] - #[allow(unused_assignments)] - pub fn genbank_to_faa() -> Result<(), anyhow::Error> { - let file_gbk = fs::File::open("test_output.gbk")?; - let mut reader = Reader::new(file_gbk); - let mut records = reader.records(); - let mut read_counter: u32 = 0; - loop { - match records.next() { - Some(Ok(mut record)) => { - //println!("next record"); - //println!("Record id: {:?}", record.id); - for (k, _v) in &record.cds.attributes { - match record.seq_features.get_sequence_faa(&k) { - Some(value) => { let seq_faa = value.to_string(); - println!(">{}|{}\n{}", &record.id, &k, seq_faa); - }, - _ => (), - }; - - } - read_counter+=1; - }, - Some(Err(e)) => { println!("theres an err {:?}", e); }, - None => { - println!("finished iteration"); - break; }, - } - } - println!("Total records processed: {}", read_counter); - return Ok(()); - } - #[test] - #[allow(unused_mut)] - #[allow(unused_assignments)] - #[allow(unused_variables)] - pub fn genbank_to_ffn() -> Result<(), anyhow::Error> { - let file_gbk = fs::File::open("test_output.gbk")?; - let mut reader = Reader::new(file_gbk); - let mut records = reader.records(); - let mut read_counter: u32 = 0; - loop { - match records.next() { - Some(Ok(mut record)) => { - //println!("next record"); - //println!("Record id: {:?}", record.id); - for (k, v) in &record.cds.attributes { - match record.seq_features.get_sequence_ffn(&k) { - Some(value) => { let seq_ffn = value.to_string(); - println!(">{}|{}\n{}", &record.id, &k, seq_ffn); - }, - _ => (), - }; - - } - read_counter+=1; - }, - Some(Err(e)) => { println!("theres an err {:?}", e); }, - None => { - println!("finished iteration"); - break; }, - } - } - println!("Total records processed: {}", read_counter); - return Ok(()); - } - #[test] - /// Test to create a new record - /// We require a source, features, sequence features and a sequence - /// The source is top level, a single genbank file has one source, multi-genbank has one per contig - /// The SourceAttributes construct has a name (counter), start, stop, organism, moltype, strain, type material and db_xref - /// The FeatureAttributes construct has a locus tag (counter), gene, product, start, stop, codon start, strand - /// SourceAttribute start and stop are the coordinates of the source feature or per contig, FeatureAttributes start and stop are per coding sequence (CDS) - /// The SequenceAttributes construct has a locus tag (counter), start, stop, sequence_ffn, sequence_faa, codon start, and strand - /// SequenceAttribute start and stop, codon start and strand are duplicates of those in the FeatureAttributes - /// To add an entry requires using the set_ values such as set_start, set_stop, set_counter, set_strand - /// To write in GFF format requires gff_write(seq_region, record_vec, filename and true/false - /// The seq_region is the region of interest with name and DNA coordinates such as ``` "source_1".to_string(), (1,897) ``` - /// record_vec is a list of the records. If there is only one record ``` vec![record] ``` will suffice - /// filename is the required filename string, true/false is whether the DNA sequence should be included in the GFF3 file - /// Some GFF3 files have the DNA sequence, whilst others do not. Some tools require the DNA sequence included. - #[allow(unused_mut)] - #[allow(unused_assignments)] - #[allow(unused_variables)] - pub fn create_new_record() -> Result<(), anyhow::Error> { - let filename = format!("new_record.gff"); - let mut record = Record::new(); - let mut seq_region: BTreeMap = BTreeMap::new(); - seq_region.insert("source_1".to_string(), (1,910)); - record.source_map - .set_counter("source_1".to_string()) - .set_start(RangeValue::Exact(1)) - .set_stop(RangeValue::Exact(910)) - .set_organism("Escherichia coli".to_string()) - .set_mol_type("DNA".to_string()) - .set_strain("K-12 substr. MG1655".to_string()) - // culture_collection.clone() - .set_type_material("type strain of Escherichia coli K12".to_string()) - .set_db_xref("PRJNA57779".to_string()); - record.cds - .set_counter("b3304".to_string()) - .set_start(RangeValue::Exact(1)) - .set_stop(RangeValue::Exact(354)) - .set_gene("rplR".to_string()) - .set_product("50S ribosomal subunit protein L18".to_string()) - .set_codon_start(1) - .set_strand(-1); - record.cds - .set_counter("b3305".to_string()) - .set_start(RangeValue::Exact(364)) - .set_stop(RangeValue::Exact(897)) - .set_gene("rplF".to_string()) - .set_product("50S ribosomal subunit protein L6".to_string()) - .set_codon_start(1) - .set_strand(-1); - record.seq_features - .set_counter("b3304".to_string()) - .set_start(RangeValue::Exact(1)) - .set_stop(RangeValue::Exact(354)) - .set_sequence_ffn("ATGGATAAGAAATCTGCTCGTATCCGTCGTGCGACCCGCGCACGCCGCAAGCTCCAGGAG -CTGGGCGCAACTCGCCTGGTGGTACATCGTACCCCGCGTCACATTTACGCACAGGTAATT -GCACCGAACGGTTCTGAAGTTCTGGTAGCTGCTTCTACTGTAGAAAAAGCTATCGCTGAA -CAACTGAAGTACACCGGTAACAAAGACGCGGCTGCAGCTGTGGGTAAAGCTGTCGCTGAA -CGCGCTCTGGAAAAAGGCATCAAAGATGTATCCTTTGACCGTTCCGGGTTCCAATATCAT -GGTCGTGTCCAGGCACTGGCAGATGCTGCCCGTGAAGCTGGCCTTCAGTTCTAA".to_string()) - .set_sequence_faa("MDKKSARIRRATRARRKLQELGATRLVVHRTPRHIYAQVIAPNGSEVLVAASTVEKAIAE -QLKYTGNKDAAAAVGKAVAERALEKGIKDVSFDRSGFQYHGRVQALADAAREAGLQF".to_string()) - .set_codon_start(1) - .set_strand(-1); - record.seq_features - .set_counter("b3305".to_string()) - .set_start(RangeValue::Exact(364)) - .set_stop(RangeValue::Exact(897)) - .set_sequence_ffn("ATGTCTCGTGTTGCTAAAGCACCGGTCGTTGTTCCTGCCGGCGTTGACGTAAAAATCAAC -GGTCAGGTTATTACGATCAAAGGTAAAAACGGCGAGCTGACTCGTACTCTCAACGATGCT -GTTGAAGTTAAACATGCAGATAATACCCTGACCTTCGGTCCGCGTGATGGTTACGCAGAC -GGTTGGGCACAGGCTGGTACCGCGCGTGCCCTGCTGAACTCAATGGTTATCGGTGTTACC -GAAGGCTTCACTAAGAAGCTGCAGCTGGTTGGTGTAGGTTACCGTGCAGCGGTTAAAGGC -AATGTGATTAACCTGTCTCTGGGTTTCTCTCATCCTGTTGACCATCAGCTGCCTGCGGGT -ATCACTGCTGAATGTCCGACTCAGACTGAAATCGTGCTGAAAGGCGCTGATAAGCAGGTG -ATCGGCCAGGTTGCAGCGGATCTGCGCGCCTACCGTCGTCCTGAGCCTTATAAAGGCAAG -GGTGTTCGTTACGCCGACGAAGTCGTGCGTACCAAAGAGGCTAAGAAGAAGTAA".to_string()) - .set_sequence_faa("MSRVAKAPVVVPAGVDVKINGQVITIKGKNGELTRTLNDAVEVKHADNTLTFGPRDGYAD -GWAQAGTARALLNSMVIGVTEGFTKKLQLVGVGYRAAVKGNVINLSLGFSHPVDHQLPAG -ITAECPTQTEIVLKGADKQVIGQVAADLRAYRRPEPYKGKGVRYADEVVRTKEAKKK".to_string()) - .set_codon_start(1) - .set_strand(-1); - record.sequence = "TTAGAACTGAAGGCCAGCTTCACGGGCAGCATCTGCCAGTGCCTGGACACGACCATGATA -TTGGAACCCGGAACGGTCAAAGGATACATCTTTGATGCCTTTTTCCAGAGCGCGTTCAGC -GACAGCTTTACCCACAGCTGCAGCCGCGTCTTTGTTACCGGTGTACTTCAGTTGTTCAGC -GATAGCTTTTTCTACAGTAGAAGCAGCTACCAGAACTTCAGAACCGTTCGGTGCAATTAC -CTGTGCGTAAATGTGACGCGGGGTACGATGTACCACCAGGCGAGTTGCGCCCAGCTCCTG -GAGCTTGCGGCGTGCGCGGGTCGCACGACGGATACGAGCAGATTTCTTATCCATAGTGTT -ACCTTACTTCTTCTTAGCCTCTTTGGTACGCACGACTTCGTCGGCGTAACGAACACCCTT -GCCTTTATAAGGCTCAGGACGACGGTAGGCGCGCAGATCCGCTGCAACCTGGCCGATCAC -CTGCTTATCAGCGCCTTTCAGCACGATTTCAGTCTGAGTCGGACATTCAGCAGTGATACC -CGCAGGCAGCTGATGGTCAACAGGATGAGAGAAACCCAGAGACAGGTTAATCACATTGCC -TTTAACCGCTGCACGGTAACCTACACCAACCAGCTGCAGCTTCTTAGTGAAGCCTTCGGT -AACACCGATAACCATTGAGTTCAGCAGGGCACGCGCGGTACCAGCCTGTGCCCAACCGTC -TGCGTAACCATCACGCGGACCGAAGGTCAGGGTATTATCTGCATGTTTAACTTCAACAGC -ATCGTTGAGAGTACGAGTCAGCTCGCCGTTTTTACCTTTGATCGTAATAACCTGACCGTT -GATTTTTACGTCAACGCCGGCAGGAACAACGACCGGTGCTTTAGCAACACGAGACA".to_string(); - gff_write(seq_region.clone(), vec![record.clone()], "new_output.gff", true)?; - gbk_write(seq_region, vec![record], "new_output.gbk")?; - return Ok(()); - } -} diff --git a/microBioRust/src/lib.rs b/microBioRust/src/lib.rs index 12a4804..31eace6 100644 --- a/microBioRust/src/lib.rs +++ b/microBioRust/src/lib.rs @@ -9,5 +9,5 @@ //! Additionally, you can create new features and records and save them either in genbank or gff3 format //! #![allow(non_snake_case)] -pub mod gbk; pub mod embl; +pub mod gbk; diff --git a/microBioRust/src/main.rs b/microBioRust/src/main.rs index 82d8bdc..6b40f17 100644 --- a/microBioRust/src/main.rs +++ b/microBioRust/src/main.rs @@ -1,23 +1,22 @@ use clap::Parser; use microBioRust::genbank; - #[derive(Parser, Debug)] #[clap(author, version, about)] struct Arguments { -#[clap(short, long)] -filename: String, + #[clap(short, long)] + filename: String, } fn main() -> Result<(), anyhow::Error> { - let args = Arguments::parse(); - let records = genbank!(&args.filename); - for record in records.iter() { - for (k, _v) in &record.cds.attributes { - if let Some(seq) = record.seq_features.get_sequence_faa(k) { - println!(">{}|{}\n{}", &record.id, &k, seq); - } - } + let args = Arguments::parse(); + let records = genbank!(&args.filename); + for record in records.iter() { + for (k, _v) in &record.cds.attributes { + if let Some(seq) = record.seq_features.get_sequence_faa(k) { + println!(">{}|{}\n{}", &record.id, &k, seq); } - return Ok(()); + } + } + return Ok(()); } diff --git a/microBioRust/tests/create_new_record.rs b/microBioRust/tests/create_new_record.rs new file mode 100644 index 0000000..1fedcec --- /dev/null +++ b/microBioRust/tests/create_new_record.rs @@ -0,0 +1,125 @@ +use microBioRust::embl::{gbk_write, gff_write, RangeValue, Record}; +use std::collections::BTreeMap; + + + /// Test to create a new record + /// We require a source, features, sequence features and a sequence + /// The source is top level, a single genbank file has one source, multi-genbank has one per contig + /// The SourceAttributes construct has a name (counter), start, stop, organism, moltype, strain, type material and db_xref + /// The FeatureAttributes construct has a locus tag (counter), gene, product, start, stop, codon start, strand + /// SourceAttribute start and stop are the coordinates of the source feature or per contig, FeatureAttributes start and stop are per coding sequence (CDS) + /// The SequenceAttributes construct has a locus tag (counter), start, stop, sequence_ffn, sequence_faa, codon start, and strand + /// SequenceAttribute start and stop, codon start and strand are duplicates of those in the FeatureAttributes + /// To add an entry requires using the set_ values such as set_start, set_stop, set_counter, set_strand + /// To write in GFF format requires gff_write(seq_region, record_vec, filename and true/false + /// The seq_region is the region of interest with name and DNA coordinates such as ``` "source_1".to_string(), (1,897) ``` + /// record_vec is a list of the records. If there is only one record ``` vec![record] ``` will suffice + /// filename is the required filename string, true/false is whether the DNA sequence should be included in the GFF3 file + /// Some GFF3 files have the DNA sequence, whilst others do not. Some tools require the DNA sequence included. + +#[test] +fn create_new_record() -> Result<(), anyhow::Error> { + //let filename = format!("new_record.gff"); + let mut record = Record::new(); + let mut seq_region: BTreeMap = BTreeMap::new(); + seq_region.insert("source_1".to_string(), (1, 910)); + record + .source_map + .set_counter("source_1".to_string()) + .set_start(RangeValue::Exact(1)) + .set_stop(RangeValue::Exact(910)) + .set_organism("Escherichia coli".to_string()) + .set_mol_type("DNA".to_string()) + .set_strain("K-12 substr. MG1655".to_string()) + // culture_collection.clone() + .set_type_material("type strain of Escherichia coli K12".to_string()) + .set_db_xref("PRJNA57779".to_string()); + record + .cds + .set_counter("b3304".to_string()) + .set_start(RangeValue::Exact(1)) + .set_stop(RangeValue::Exact(354)) + .set_gene("rplR".to_string()) + .set_product("50S ribosomal subunit protein L18".to_string()) + .set_codon_start(1) + .set_strand(-1); + record + .cds + .set_counter("b3305".to_string()) + .set_start(RangeValue::Exact(364)) + .set_stop(RangeValue::Exact(897)) + .set_gene("rplF".to_string()) + .set_product("50S ribosomal subunit protein L6".to_string()) + .set_codon_start(1) + .set_strand(-1); + record + .seq_features + .set_counter("b3304".to_string()) + .set_start(RangeValue::Exact(1)) + .set_stop(RangeValue::Exact(354)) + .set_sequence_ffn( + "ATGGATAAGAAATCTGCTCGTATCCGTCGTGCGACCCGCGCACGCCGCAAGCTCCAGGAG +CTGGGCGCAACTCGCCTGGTGGTACATCGTACCCCGCGTCACATTTACGCACAGGTAATT +GCACCGAACGGTTCTGAAGTTCTGGTAGCTGCTTCTACTGTAGAAAAAGCTATCGCTGAA +CAACTGAAGTACACCGGTAACAAAGACGCGGCTGCAGCTGTGGGTAAAGCTGTCGCTGAA +CGCGCTCTGGAAAAAGGCATCAAAGATGTATCCTTTGACCGTTCCGGGTTCCAATATCAT +GGTCGTGTCCAGGCACTGGCAGATGCTGCCCGTGAAGCTGGCCTTCAGTTCTAA" + .to_string(), + ) + .set_sequence_faa( + "MDKKSARIRRATRARRKLQELGATRLVVHRTPRHIYAQVIAPNGSEVLVAASTVEKAIAE +QLKYTGNKDAAAAVGKAVAERALEKGIKDVSFDRSGFQYHGRVQALADAAREAGLQF" + .to_string(), + ) + .set_codon_start(1) + .set_strand(-1); + record + .seq_features + .set_counter("b3305".to_string()) + .set_start(RangeValue::Exact(364)) + .set_stop(RangeValue::Exact(897)) + .set_sequence_ffn( + "ATGTCTCGTGTTGCTAAAGCACCGGTCGTTGTTCCTGCCGGCGTTGACGTAAAAATCAAC +GGTCAGGTTATTACGATCAAAGGTAAAAACGGCGAGCTGACTCGTACTCTCAACGATGCT +GTTGAAGTTAAACATGCAGATAATACCCTGACCTTCGGTCCGCGTGATGGTTACGCAGAC +GGTTGGGCACAGGCTGGTACCGCGCGTGCCCTGCTGAACTCAATGGTTATCGGTGTTACC +GAAGGCTTCACTAAGAAGCTGCAGCTGGTTGGTGTAGGTTACCGTGCAGCGGTTAAAGGC +AATGTGATTAACCTGTCTCTGGGTTTCTCTCATCCTGTTGACCATCAGCTGCCTGCGGGT +ATCACTGCTGAATGTCCGACTCAGACTGAAATCGTGCTGAAAGGCGCTGATAAGCAGGTG +ATCGGCCAGGTTGCAGCGGATCTGCGCGCCTACCGTCGTCCTGAGCCTTATAAAGGCAAG +GGTGTTCGTTACGCCGACGAAGTCGTGCGTACCAAAGAGGCTAAGAAGAAGTAA" + .to_string(), + ) + .set_sequence_faa( + "MSRVAKAPVVVPAGVDVKINGQVITIKGKNGELTRTLNDAVEVKHADNTLTFGPRDGYAD +GWAQAGTARALLNSMVIGVTEGFTKKLQLVGVGYRAAVKGNVINLSLGFSHPVDHQLPAG +ITAECPTQTEIVLKGADKQVIGQVAADLRAYRRPEPYKGKGVRYADEVVRTKEAKKK" + .to_string(), + ) + .set_codon_start(1) + .set_strand(-1); + record.sequence = "TTAGAACTGAAGGCCAGCTTCACGGGCAGCATCTGCCAGTGCCTGGACACGACCATGATA +TTGGAACCCGGAACGGTCAAAGGATACATCTTTGATGCCTTTTTCCAGAGCGCGTTCAGC +GACAGCTTTACCCACAGCTGCAGCCGCGTCTTTGTTACCGGTGTACTTCAGTTGTTCAGC +GATAGCTTTTTCTACAGTAGAAGCAGCTACCAGAACTTCAGAACCGTTCGGTGCAATTAC +CTGTGCGTAAATGTGACGCGGGGTACGATGTACCACCAGGCGAGTTGCGCCCAGCTCCTG +GAGCTTGCGGCGTGCGCGGGTCGCACGACGGATACGAGCAGATTTCTTATCCATAGTGTT +ACCTTACTTCTTCTTAGCCTCTTTGGTACGCACGACTTCGTCGGCGTAACGAACACCCTT +GCCTTTATAAGGCTCAGGACGACGGTAGGCGCGCAGATCCGCTGCAACCTGGCCGATCAC +CTGCTTATCAGCGCCTTTCAGCACGATTTCAGTCTGAGTCGGACATTCAGCAGTGATACC +CGCAGGCAGCTGATGGTCAACAGGATGAGAGAAACCCAGAGACAGGTTAATCACATTGCC +TTTAACCGCTGCACGGTAACCTACACCAACCAGCTGCAGCTTCTTAGTGAAGCCTTCGGT +AACACCGATAACCATTGAGTTCAGCAGGGCACGCGCGGTACCAGCCTGTGCCCAACCGTC +TGCGTAACCATCACGCGGACCGAAGGTCAGGGTATTATCTGCATGTTTAACTTCAACAGC +ATCGTTGAGAGTACGAGTCAGCTCGCCGTTTTTACCTTTGATCGTAATAACCTGACCGTT +GATTTTTACGTCAACGCCGGCAGGAACAACGACCGGTGCTTTAGCAACACGAGACA" + .to_string(); + gff_write( + seq_region.clone(), + vec![record.clone()], + "new_output_embl.gff", + true, + )?; + gbk_write(seq_region, vec![record], "new_output_embl.gbk")?; + return Ok(()); +} diff --git a/microBioRust/tests/embl_to_faa.rs b/microBioRust/tests/embl_to_faa.rs new file mode 100644 index 0000000..86933c7 --- /dev/null +++ b/microBioRust/tests/embl_to_faa.rs @@ -0,0 +1,36 @@ +use microBioRust::embl::Reader; +use std::fs; +#[test] +fn embl_to_faa() -> Result<(), anyhow::Error> { + let file_embl = fs::File::open("example.embl")?; + let reader = Reader::new(file_embl); + let mut records = reader.records(); + let mut read_counter: u32 = 0; + loop { + match records.next() { + Some(Ok(record)) => { + //println!("next record"); + //println!("Record id: {:?}", record.id); + for (k, _v) in &record.cds.attributes { + match record.seq_features.get_sequence_faa(&k) { + Some(value) => { + let seq_faa = value.to_string(); + println!(">{}|{}\n{}", &record.id, &k, seq_faa); + } + _ => (), + }; + } + read_counter += 1; + } + Some(Err(e)) => { + println!("theres an err {:?}", e); + } + None => { + println!("finished iteration"); + break; + } + } + } + println!("Total records processed: {}", read_counter); + return Ok(()); +} diff --git a/microBioRust/tests/embl_to_ffn.rs b/microBioRust/tests/embl_to_ffn.rs new file mode 100644 index 0000000..70a6f7a --- /dev/null +++ b/microBioRust/tests/embl_to_ffn.rs @@ -0,0 +1,36 @@ +use microBioRust::embl::Reader; +use std::fs; +#[test] +pub fn embl_to_ffn() -> Result<(), anyhow::Error> { + let file_embl = fs::File::open("example.embl")?; + let reader = Reader::new(file_embl); + let mut records = reader.records(); + let mut read_counter: u32 = 0; + loop { + match records.next() { + Some(Ok(record)) => { + //println!("next record"); + //println!("Record id: {:?}", record.id); + for (k, _v) in &record.cds.attributes { + match record.seq_features.get_sequence_ffn(&k) { + Some(value) => { + let seq_ffn = value.to_string(); + println!(">{}|{}\n{}", &record.id, &k, seq_ffn); + } + _ => (), + }; + } + read_counter += 1; + } + Some(Err(e)) => { + println!("theres an err {:?}", e); + } + None => { + println!("finished iteration"); + break; + } + } + } + println!("Total records processed: {}", read_counter); + return Ok(()); +} diff --git a/microBioRust/tests/embl_to_gff.rs b/microBioRust/tests/embl_to_gff.rs new file mode 100644 index 0000000..099cf6a --- /dev/null +++ b/microBioRust/tests/embl_to_gff.rs @@ -0,0 +1,51 @@ +use microBioRust::embl::{gff_write, Reader, Record}; +use std::collections::BTreeMap; +use std::fs; + +#[test] +fn test_embl_to_gff() -> std::io::Result<()> { + let file_embl = fs::File::open("example.embl")?; + let reader = Reader::new(file_embl); + let mut records = reader.records(); + let mut read_counter: u32 = 0; + let mut prev_end: u32 = 0; + let mut seq_region: BTreeMap = BTreeMap::new(); + let mut record_vec: Vec = Vec::new(); + + while let Some(record_result) = records.next() { + match record_result { + Ok(record) => { + let sour = record + .source_map + .source_name + .clone() + .expect("Missing source name"); + let beginning = record + .source_map + .get_start(&sour) + .map(|v| v.get_value()) + .unwrap_or(0); + let ending = record + .source_map + .get_stop(&sour) + .map(|v| v.get_value()) + .unwrap_or(0); + + if ending + prev_end < beginning + prev_end { + println!("start > end: {:?}", beginning); + } + + seq_region.insert(sour, (beginning + prev_end, ending + prev_end)); + record_vec.push(record); + read_counter += 1; + prev_end += ending; + } + Err(e) => eprintln!("Error: {:?}", e), + } + } + + let output_file = "test_output_embl.gff"; + gff_write(seq_region.clone(), record_vec, output_file, true)?; + println!("Total records processed: {}", read_counter); + Ok(()) +} diff --git a/microBioRust/tests/genbank_to_faa.rs b/microBioRust/tests/genbank_to_faa.rs new file mode 100644 index 0000000..c399c33 --- /dev/null +++ b/microBioRust/tests/genbank_to_faa.rs @@ -0,0 +1,36 @@ +use microBioRust::gbk::Reader; +use std::fs; +#[test] +pub fn genbank_to_faa() -> Result<(), anyhow::Error> { + let file_gbk = fs::File::open("test_output.gbk")?; + let reader = Reader::new(file_gbk); + let mut records = reader.records(); + let mut read_counter: u32 = 0; + loop { + match records.next() { + Some(Ok(record)) => { + //println!("next record"); + //println!("Record id: {:?}", record.id); + for (k, _v) in &record.cds.attributes { + match record.seq_features.get_sequence_faa(&k) { + Some(value) => { + let seq_faa = value.to_string(); + println!(">{}|{}\n{}", &record.id, &k, seq_faa); + } + _ => (), + }; + } + read_counter += 1; + } + Some(Err(e)) => { + println!("theres an err {:?}", e); + } + None => { + println!("finished iteration"); + break; + } + } + } + println!("Total records processed: {}", read_counter); + return Ok(()); +} diff --git a/microBioRust/tests/genbank_to_ffn.rs b/microBioRust/tests/genbank_to_ffn.rs new file mode 100644 index 0000000..e0892ce --- /dev/null +++ b/microBioRust/tests/genbank_to_ffn.rs @@ -0,0 +1,36 @@ +use microBioRust::gbk::Reader; +use std::fs; +#[test] +pub fn genbank_to_ffn() -> Result<(), anyhow::Error> { + let file_gbk = fs::File::open("test_output.gbk")?; + let reader = Reader::new(file_gbk); + let mut records = reader.records(); + let mut read_counter: u32 = 0; + loop { + match records.next() { + Some(Ok(record)) => { + //println!("next record"); + //println!("Record id: {:?}", record.id); + for (k, _v) in &record.cds.attributes { + match record.seq_features.get_sequence_ffn(&k) { + Some(value) => { + let seq_ffn = value.to_string(); + println!(">{}|{}\n{}", &record.id, &k, seq_ffn); + } + _ => (), + }; + } + read_counter += 1; + } + Some(Err(e)) => { + println!("theres an err {:?}", e); + } + None => { + println!("finished iteration"); + break; + } + } + } + println!("Total records processed: {}", read_counter); + return Ok(()); +} diff --git a/microBioRust/tests/genbank_to_gff.rs b/microBioRust/tests/genbank_to_gff.rs new file mode 100644 index 0000000..5968e11 --- /dev/null +++ b/microBioRust/tests/genbank_to_gff.rs @@ -0,0 +1,58 @@ +use microBioRust::gbk::{gff_write, Reader, Record}; +use std::collections::BTreeMap; +use std::fs; +use std::io; +#[test] +pub fn genbank_to_gff() -> io::Result<()> { + let file_gbk = fs::File::open("test_output.gbk")?; + let _prev_start: u32 = 0; + let mut prev_end: u32 = 0; + let reader = Reader::new(file_gbk); + let mut records = reader.records(); + let mut read_counter: u32 = 0; + let mut seq_region: BTreeMap = BTreeMap::new(); + let mut record_vec: Vec = Vec::new(); + loop { + match records.next() { + Some(Ok(record)) => { + //println!("next record"); + //println!("Record id: {:?}", record.id); + let sour = record + .source_map + .source_name + .clone() + .expect("issue collecting source name"); + let beginning = match record.source_map.get_start(&sour) { + Some(value) => value.get_value(), + _ => 0, + }; + let ending = match record.source_map.get_stop(&sour) { + Some(value) => value.get_value(), + _ => 0, + }; + if ending + prev_end < beginning + prev_end { + println!( + "debug since the end value smaller is than the start {:?}", + beginning + ); + } + seq_region.insert(sour, (beginning + prev_end, ending + prev_end)); + record_vec.push(record); + // Add additional fields to print if needed + read_counter += 1; + prev_end += ending; + } + Some(Err(e)) => { + println!("theres an err {:?}", e); + } + None => { + println!("finished iteration"); + break; + } + } + } + let output_file = format!("test_output.gff"); + gff_write(seq_region.clone(), record_vec, &output_file, true)?; + println!("Total records processed: {}", read_counter); + return Ok(()); +} diff --git a/microbiorust-py/src/lib.rs b/microbiorust-py/src/lib.rs index 891e005..a750e29 100644 --- a/microbiorust-py/src/lib.rs +++ b/microbiorust-py/src/lib.rs @@ -20,7 +20,6 @@ //! Other pyfunctions that can be run include gbk_to_faa, embl_to_faa, gbk_to_gff and embl_to_gff -#![allow(unused_imports)] use pyo3::{ prelude::*, types::PyModule, @@ -35,110 +34,127 @@ use microBioRust::gbk::{Record, Reader, RangeValue, gff_write}; use microBioRust::embl; use microBioRust::embl::gff_write as embl_gff_write; - - #[pyfunction] fn gbk_to_faa(filename: &str) -> PyResult> { - let records = genbank!(&filename); - let mut result = Vec::new(); - for record in records { - for (k, _v) in &record.cds.attributes { - if let Some(seq) = record.seq_features.get_sequence_faa(k) { - result.push(format!(">{}|{}\n{}", &record.id, &k, seq)); - } - } - } - Ok(result) - } + let records = genbank!(&filename); + let mut result = Vec::new(); + for record in records { + for (k, _v) in &record.cds.attributes { + if let Some(seq) = record.seq_features.get_sequence_faa(k) { + result.push(format!(">{}|{}\n{}", &record.id, &k, seq)); + } + } + } + Ok(result) +} #[pyfunction] fn embl_to_faa(filename: &str) -> PyResult> { - let records = genbank!(&filename); - let mut result = Vec::new(); - for record in records { - for (k, _v) in &record.cds.attributes { - if let Some(seq) = record.seq_features.get_sequence_faa(k) { - result.push(format!(">{}|{}\n{}", &record.id, &k, seq)); - } - } - } - Ok(result) - } - - + let records = genbank!(&filename); + let mut result = Vec::new(); + for record in records { + for (k, _v) in &record.cds.attributes { + if let Some(seq) = record.seq_features.get_sequence_faa(k) { + result.push(format!(">{}|{}\n{}", &record.id, &k, seq)); + } + } + } + Ok(result) +} #[allow(unused_variables)] #[pyfunction] fn gbk_to_gff(filename: &str, dna: bool) -> PyResult<()> { - let records = genbank!(&filename); - let prev_start: u32 = 0; - let mut prev_end: u32 = 0; - let mut seq_region: BTreeMap = BTreeMap::new(); - let mut record_vec = Vec::new(); - let mut read_counter = 0; - for record in records { - if let Some(ref source) = record.source_map.source_name { - let beginning = record.source_map.get_start(&source).map_or(0, |v| v.get_value()); - let ending = record.source_map.get_stop(&source).map_or(0, |v| v.get_value()); - if (ending + prev_end) < (beginning + prev_end) { - println!("debug: end value is smaller than the start value at {:?}", beginning); - } - seq_region.insert(source.to_string(), (beginning + prev_end, ending + prev_end)); - record_vec.push(record); - read_counter+=1; - prev_end+=ending; // this is to create the joined record if there are multiple - } - else { - println!("missing record source name, skipping"); - } - } - let output_file = format!("{}.gff", &filename); - if std::path::Path::new(&output_file).exists() { - println!("deleting existing file {:?}", &output_file); - std::fs::remove_file(&output_file).expect("Issue deleting output filename"); - } - let _ = gff_write(seq_region.clone(), record_vec, &output_file, dna); - println!("total records processed: {}", read_counter); - return Ok(()); + let records = genbank!(&filename); + let prev_start: u32 = 0; + let mut prev_end: u32 = 0; + let mut seq_region: BTreeMap = BTreeMap::new(); + let mut record_vec = Vec::new(); + let mut read_counter = 0; + for record in records { + if let Some(ref source) = record.source_map.source_name { + let beginning = record + .source_map + .get_start(&source) + .map_or(0, |v| v.get_value()); + let ending = record + .source_map + .get_stop(&source) + .map_or(0, |v| v.get_value()); + if (ending + prev_end) < (beginning + prev_end) { + println!( + "debug: end value is smaller than the start value at {:?}", + beginning + ); + } + seq_region.insert( + source.to_string(), + (beginning + prev_end, ending + prev_end), + ); + record_vec.push(record); + read_counter += 1; + prev_end += ending; // this is to create the joined record if there are multiple + } else { + println!("missing record source name, skipping"); + } + } + let output_file = format!("{}.gff", &filename); + if std::path::Path::new(&output_file).exists() { + println!("deleting existing file {:?}", &output_file); + std::fs::remove_file(&output_file).expect("Issue deleting output filename"); + } + let _ = gff_write(seq_region.clone(), record_vec, &output_file, dna); + println!("total records processed: {}", read_counter); + return Ok(()); } #[allow(unused_imports)] #[allow(unused_variables)] #[pyfunction] fn embl_to_gff(filename: &str, dna: bool) -> PyResult<()> { - let records = embl!(&filename); - let prev_start: u32 = 0; - let mut prev_end: u32 = 0; - let mut seq_region: BTreeMap = BTreeMap::new(); - let mut record_vec = Vec::new(); - let mut read_counter = 0; - for record in records { - if let Some(ref source) = record.source_map.source_name { - let beginning = record.source_map.get_start(&source).map_or(0, |v| v.get_value()); - let ending = record.source_map.get_stop(&source).map_or(0, |v| v.get_value()); - if (ending + prev_end) < (beginning + prev_end) { - println!("debug: end value is smaller than the start value at {:?}", beginning); - } - seq_region.insert(source.to_string(), (beginning + prev_end, ending + prev_end)); - record_vec.push(record); - read_counter+=1; - prev_end+=ending; // this is to create the joined record if there are multiple - } - else { - println!("missing record source name, skipping"); - } - } - let output_file = format!("{}.gff", &filename); - if std::path::Path::new(&output_file).exists() { - println!("deleting existing file {:?}", &output_file); - std::fs::remove_file(&output_file).expect("Issue deleting output filename"); - } - let _ = embl_gff_write(seq_region.clone(), record_vec, &output_file, dna); - println!("total records processed: {}", read_counter); - return Ok(()); + let records = embl!(&filename); + let prev_start: u32 = 0; + let mut prev_end: u32 = 0; + let mut seq_region: BTreeMap = BTreeMap::new(); + let mut record_vec = Vec::new(); + let mut read_counter = 0; + for record in records { + if let Some(ref source) = record.source_map.source_name { + let beginning = record + .source_map + .get_start(&source) + .map_or(0, |v| v.get_value()); + let ending = record + .source_map + .get_stop(&source) + .map_or(0, |v| v.get_value()); + if (ending + prev_end) < (beginning + prev_end) { + println!( + "debug: end value is smaller than the start value at {:?}", + beginning + ); + } + seq_region.insert( + source.to_string(), + (beginning + prev_end, ending + prev_end), + ); + record_vec.push(record); + read_counter += 1; + prev_end += ending; // this is to create the joined record if there are multiple + } else { + println!("missing record source name, skipping"); + } + } + let output_file = format!("{}.gff", &filename); + if std::path::Path::new(&output_file).exists() { + println!("deleting existing file {:?}", &output_file); + std::fs::remove_file(&output_file).expect("Issue deleting output filename"); + } + let _ = embl_gff_write(seq_region.clone(), record_vec, &output_file, dna); + println!("total records processed: {}", read_counter); + return Ok(()); } - #[pymodule] fn microbiorust(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(gbk_to_faa, m)?)?; @@ -148,21 +164,20 @@ fn microbiorust(m: &Bound<'_, PyModule>) -> PyResult<()> { Ok(()) } - #[cfg(test)] mod tests { - use pyo3::prelude::*; - use pyo3::types::PyModule; - use crate::microbiorust; - - #[test] - fn test_functions_are_registered() { - Python::with_gil(|py| { - let m = PyModule::new(py, "microbiorust").unwrap(); - microbiorust(&m).unwrap(); - for func in &["gbk_to_faa", "embl_to_faa", "gbk_to_gff", "embl_to_gff"] { - assert!(m.getattr(func).is_ok(), "Function {} not found", func); - } - }); + use crate::microbiorust; + use pyo3::prelude::*; + use pyo3::types::PyModule; + + #[test] + fn test_functions_are_registered() { + Python::with_gil(|py| { + let m = PyModule::new(py, "microbiorust").unwrap(); + microbiorust(&m).unwrap(); + for func in &["gbk_to_faa", "embl_to_faa", "gbk_to_gff", "embl_to_gff"] { + assert!(m.getattr(func).is_ok(), "Function {} not found", func); + } + }); } -} \ No newline at end of file +} diff --git a/seqmetrics/src/hamming.rs b/seqmetrics/src/hamming.rs index c3fb64d..71af7a6 100644 --- a/seqmetrics/src/hamming.rs +++ b/seqmetrics/src/hamming.rs @@ -8,27 +8,27 @@ pub fn hamming_distance(seq1: &str, seq2: &str) -> usize { seq2.len(), "Sequences must be of equal length for Hamming distance" ); - seq1 - .as_bytes() - .iter() - .zip(seq2.as_bytes()) - .filter(|(a, b)| a !=b) - .count() + seq1.as_bytes() + .iter() + .zip(seq2.as_bytes()) + .filter(|(a, b)| a != b) + .count() } pub async fn hamming_matrix(sequences: &Vec) -> Result>, anyhow::Error> { - assert!( - sequences.windows(2).all(|w| w[0].len() == w[1].len()), - "all sequences must be the same length, i.e an alignment"); - println!("all sequences are the same length, proceeding"); - let n = sequences.len(); - let mut distances = vec![vec![0usize; n]; n]; - for i in 0..n { - for j in i + 1..n { - let d = hamming_distance(&sequences[i], &sequences[j]); - distances[i][j] = d; - distances[j][i] = d; - } - } - Ok(distances) + assert!( + sequences.windows(2).all(|w| w[0].len() == w[1].len()), + "all sequences must be the same length, i.e an alignment" + ); + println!("all sequences are the same length, proceeding"); + let n = sequences.len(); + let mut distances = vec![vec![0usize; n]; n]; + for i in 0..n { + for j in i + 1..n { + let d = hamming_distance(&sequences[i], &sequences[j]); + distances[i][j] = d; + distances[j][i] = d; + } + } + Ok(distances) } diff --git a/seqmetrics/src/lib.rs b/seqmetrics/src/lib.rs index 78c8134..4ced829 100644 --- a/seqmetrics/src/lib.rs +++ b/seqmetrics/src/lib.rs @@ -9,6 +9,6 @@ //! Additionally, you can create new features and records and save them either in genbank or gff3 format //! #![allow(non_snake_case)] +pub mod hamming; pub mod metrics; pub mod write_dst_csv; -pub mod hamming; diff --git a/seqmetrics/src/metrics.rs b/seqmetrics/src/metrics.rs index 998622e..5d4f7fb 100644 --- a/seqmetrics/src/metrics.rs +++ b/seqmetrics/src/metrics.rs @@ -191,7 +191,7 @@ //! It is one of several string metrics for measuring the edit distance between two sequences. //! It does not encompass or take into account any biology //! It is named after the American Mathematician Richard Hamming (wikipedia) -//! This is aimed essentially at protein fasta sequences +//! This is aimed essentially at protein fasta sequences //! //! //! ``` @@ -221,18 +221,17 @@ //! let distances = hamming_matrix(&sequences).await?; //! write_distances_csv(ids, distances, "hamming_dists.csv").await?; //! -//! Ok(()) +//! Ok(()) //! } //! ``` #![allow(unused_imports)] -use microBioRust::gbk::Reader; -use std::fs::File; -use std::collections::HashMap; use crate::hamming::hamming_matrix; use crate::write_dst_csv::write_distances_csv; use bio::io::fasta; - +use microBioRust::gbk::Reader; +use std::collections::HashMap; +use std::fs::File; // Define a macro to generate the getters for each amino acid #[macro_export] @@ -292,55 +291,55 @@ pub struct MolWeights { #[allow(dead_code)] impl MolWeights { fn new() -> Self { - Self { - //masses from NIST chemistry webbook US Dept of commerce - Alanine: 89.0932, //C3H7NO2 - Arginine: 174.2010, //C6H14N4O2 - Asparagine: 132.1179, //C4H8N2O3 - Aspartate: 133.1027, //C4H7NO4 - Cysteine: 121.158, //C3H7NO2S - Glutamate: 147.1293, //C5H9NO4 - Glutamine: 146.1445, //C5H10N2O3 - Glycine: 75.0666, //C2H5NO2 - Histidine: 155.1546, //C6H9N3O2 - Isoleucine: 131.1729, //C6H13NO2 - Leucine: 131.1729, //C6H13NO2 - Lysine: 146.1876, //C6H14N2O2 - Methionine: 149.211, //C5H11NO2S - Phenylalanine: 165.1891, //C9H11NO2 - Proline: 115.1305, //C5H9NO2 - Serine: 105.0926, //C3H7NO2 - Threonine: 119.1192, //C4H9NO3 - Tryptophan: 204.2252, //C11H12N2O2 - Tyrosine: 181.1885, //C9H11NO3 - Valine: 117.1463, //C5H11NO2 - } - } + Self { + //masses from NIST chemistry webbook US Dept of commerce + Alanine: 89.0932, //C3H7NO2 + Arginine: 174.2010, //C6H14N4O2 + Asparagine: 132.1179, //C4H8N2O3 + Aspartate: 133.1027, //C4H7NO4 + Cysteine: 121.158, //C3H7NO2S + Glutamate: 147.1293, //C5H9NO4 + Glutamine: 146.1445, //C5H10N2O3 + Glycine: 75.0666, //C2H5NO2 + Histidine: 155.1546, //C6H9N3O2 + Isoleucine: 131.1729, //C6H13NO2 + Leucine: 131.1729, //C6H13NO2 + Lysine: 146.1876, //C6H14N2O2 + Methionine: 149.211, //C5H11NO2S + Phenylalanine: 165.1891, //C9H11NO2 + Proline: 115.1305, //C5H9NO2 + Serine: 105.0926, //C3H7NO2 + Threonine: 119.1192, //C4H9NO3 + Tryptophan: 204.2252, //C11H12N2O2 + Tyrosine: 181.1885, //C9H11NO3 + Valine: 117.1463, //C5H11NO2 + } + } } - -amino_acid_getters!(MolWeights, - (Alanine, alanine, Ala, A), - (Arginine, arginine, Arg, R), - (Asparagine, asparagine, Asn, N), - (Aspartate, aspartate, Asp, D), - (Cysteine, cysteine, Cys, C), - (Glutamine, glutamine, Gln, Q), - (Glutamate, glutamate, Glu, E), - (Glycine, glycine, Gly, G), - (Histidine, histidine, His, H), - (Isoleucine, isoleucine, Ile, I), - (Leucine, leucine, Leu, L), - (Lysine, lysine, Lys, K), - (Methionine, methionine, Met, M), - (Phenylalanine, phenylalanine, Phe, F), - (Proline, proline, Pro, P), - (Serine, serine, Ser, S), - (Threonine, threonine, Thr, T), - (Tryptophan, tryptophan, Trp, W), - (Tyrosine, tyrosine, Tyr, Y), - (Valine, valine, Val, V) - ); +amino_acid_getters!( + MolWeights, + (Alanine, alanine, Ala, A), + (Arginine, arginine, Arg, R), + (Asparagine, asparagine, Asn, N), + (Aspartate, aspartate, Asp, D), + (Cysteine, cysteine, Cys, C), + (Glutamine, glutamine, Gln, Q), + (Glutamate, glutamate, Glu, E), + (Glycine, glycine, Gly, G), + (Histidine, histidine, His, H), + (Isoleucine, isoleucine, Ile, I), + (Leucine, leucine, Leu, L), + (Lysine, lysine, Lys, K), + (Methionine, methionine, Met, M), + (Phenylalanine, phenylalanine, Phe, F), + (Proline, proline, Pro, P), + (Serine, serine, Ser, S), + (Threonine, threonine, Thr, T), + (Tryptophan, tryptophan, Trp, W), + (Tyrosine, tyrosine, Tyr, Y), + (Valine, valine, Val, V) +); #[allow(non_snake_case)] #[allow(dead_code)] @@ -349,32 +348,32 @@ pub fn molecular_weight(protein_seq: &str) -> f64 { let amino_weights: MolWeights = MolWeights::new(); let mut total_weight = 0.0; for ch in protein_seq.chars() { - match ch { - 'A' => total_weight += amino_weights.A(), - 'R' => total_weight += amino_weights.R(), - 'N' => total_weight += amino_weights.N(), - 'D' => total_weight += amino_weights.D(), - 'C' => total_weight += amino_weights.C(), - 'Q' => total_weight += amino_weights.Q(), - 'E' => total_weight += amino_weights.E(), - 'G' => total_weight += amino_weights.G(), - 'H' => total_weight += amino_weights.H(), - 'I' => total_weight += amino_weights.I(), - 'L' => total_weight += amino_weights.L(), - 'K' => total_weight += amino_weights.K(), - 'M' => total_weight += amino_weights.M(), - 'F' => total_weight += amino_weights.F(), - 'P' => total_weight += amino_weights.P(), - 'S' => total_weight += amino_weights.S(), - 'T' => total_weight += amino_weights.T(), - 'W' => total_weight += amino_weights.W(), - 'Y' => total_weight += amino_weights.Y(), - 'V' => total_weight += amino_weights.V(), - _ => continue, - } - } - let result_weight = total_weight - ((protein_seq.len() - 1) as f64 * 18.02); - result_weight + match ch { + 'A' => total_weight += amino_weights.A(), + 'R' => total_weight += amino_weights.R(), + 'N' => total_weight += amino_weights.N(), + 'D' => total_weight += amino_weights.D(), + 'C' => total_weight += amino_weights.C(), + 'Q' => total_weight += amino_weights.Q(), + 'E' => total_weight += amino_weights.E(), + 'G' => total_weight += amino_weights.G(), + 'H' => total_weight += amino_weights.H(), + 'I' => total_weight += amino_weights.I(), + 'L' => total_weight += amino_weights.L(), + 'K' => total_weight += amino_weights.K(), + 'M' => total_weight += amino_weights.M(), + 'F' => total_weight += amino_weights.F(), + 'P' => total_weight += amino_weights.P(), + 'S' => total_weight += amino_weights.S(), + 'T' => total_weight += amino_weights.T(), + 'W' => total_weight += amino_weights.W(), + 'Y' => total_weight += amino_weights.Y(), + 'V' => total_weight += amino_weights.V(), + _ => continue, + } + } + let result_weight = total_weight - ((protein_seq.len() - 1) as f64 * 18.02); + result_weight } #[allow(non_snake_case)] @@ -405,54 +404,55 @@ pub struct Hydrophobicity { impl Hydrophobicity { #[allow(non_snake_case)] fn new_KD() -> Self { - Self { - //Kyte-Doolittle values from the Qiagen resources website - Alanine: 1.80, - Arginine: -4.50, - Asparagine: -3.50, - Aspartate: -3.50, - Cysteine: 2.50, - Glutamate: -3.50, - Glutamine: -3.50, - Glycine: -0.40, - Histidine: -3.20, - Isoleucine: 4.50, - Leucine: 3.80, - Lysine: -3.90, - Methionine: 1.90, - Phenylalanine: 2.80, - Proline: -1.60, - Serine: -0.80, - Threonine: -0.70, - Tryptophan: -0.90, - Tyrosine: -1.30, - Valine: 4.20, - } - } + Self { + //Kyte-Doolittle values from the Qiagen resources website + Alanine: 1.80, + Arginine: -4.50, + Asparagine: -3.50, + Aspartate: -3.50, + Cysteine: 2.50, + Glutamate: -3.50, + Glutamine: -3.50, + Glycine: -0.40, + Histidine: -3.20, + Isoleucine: 4.50, + Leucine: 3.80, + Lysine: -3.90, + Methionine: 1.90, + Phenylalanine: 2.80, + Proline: -1.60, + Serine: -0.80, + Threonine: -0.70, + Tryptophan: -0.90, + Tyrosine: -1.30, + Valine: 4.20, + } + } } -amino_acid_getters!(Hydrophobicity, - (Alanine, alanine, Ala, A), - (Arginine, arginine, Arg, R), - (Asparagine, asparagine, Asn, N), - (Aspartate, aspartate, Asp, D), - (Cysteine, cysteine, Cys, C), - (Glutamine, glutamine, Gln, Q), - (Glutamate, glutamate, Glu, E), - (Glycine, glycine, Gly, G), - (Histidine, histidine, His, H), - (Isoleucine, isoleucine, Ile, I), - (Leucine, leucine, Leu, L), - (Lysine, lysine, Lys, K), - (Methionine, methionine, Met, M), - (Phenylalanine, phenylalanine, Phe, F), - (Proline, proline, Pro, P), - (Serine, serine, Ser, S), - (Threonine, threonine, Thr, T), - (Tryptophan, trytophan, Trp, W), - (Tyrosine, tyrosine, Tyr, Y), - (Valine, valine, Val, V) - ); +amino_acid_getters!( + Hydrophobicity, + (Alanine, alanine, Ala, A), + (Arginine, arginine, Arg, R), + (Asparagine, asparagine, Asn, N), + (Aspartate, aspartate, Asp, D), + (Cysteine, cysteine, Cys, C), + (Glutamine, glutamine, Gln, Q), + (Glutamate, glutamate, Glu, E), + (Glycine, glycine, Gly, G), + (Histidine, histidine, His, H), + (Isoleucine, isoleucine, Ile, I), + (Leucine, leucine, Leu, L), + (Lysine, lysine, Lys, K), + (Methionine, methionine, Met, M), + (Phenylalanine, phenylalanine, Phe, F), + (Proline, proline, Pro, P), + (Serine, serine, Ser, S), + (Threonine, threonine, Thr, T), + (Tryptophan, trytophan, Trp, W), + (Tyrosine, tyrosine, Tyr, Y), + (Valine, valine, Val, V) +); #[allow(non_snake_case)] #[allow(dead_code)] @@ -463,48 +463,47 @@ pub fn hydrophobicity(protein_seq: &str, window_size: usize) -> Vec { let mut total_hydrophobicity: Vec = Vec::new(); let mut window_values: f64 = 0.0; let mut windows: Vec = protein_seq - .chars() - .collect::>() - .windows(window_size) - .map(|window| window.iter().collect()) - .collect(); + .chars() + .collect::>() + .windows(window_size) + .map(|window| window.iter().collect()) + .collect(); for (index, window) in windows.iter().enumerate() { - for ch in window.chars() { - match ch { - 'A' => window_values += hydrophobicity.A(), - 'R' => window_values += hydrophobicity.R(), - 'N' => window_values += hydrophobicity.N(), - 'D' => window_values += hydrophobicity.D(), - 'C' => window_values += hydrophobicity.C(), - 'Q' => window_values += hydrophobicity.Q(), - 'E' => window_values += hydrophobicity.E(), - 'G' => window_values += hydrophobicity.G(), - 'H' => window_values += hydrophobicity.H(), - 'I' => window_values += hydrophobicity.I(), - 'L' => window_values += hydrophobicity.L(), - 'K' => window_values += hydrophobicity.K(), - 'M' => window_values += hydrophobicity.M(), - 'F' => window_values += hydrophobicity.F(), - 'P' => window_values += hydrophobicity.P(), - 'S' => window_values += hydrophobicity.S(), - 'T' => window_values += hydrophobicity.T(), - 'W' => window_values += hydrophobicity.W(), - 'Y' => window_values += hydrophobicity.Y(), - 'V' => window_values += hydrophobicity.V(), - _ => continue, - } - } - total_hydrophobicity.push(window_values); - } - total_hydrophobicity + for ch in window.chars() { + match ch { + 'A' => window_values += hydrophobicity.A(), + 'R' => window_values += hydrophobicity.R(), + 'N' => window_values += hydrophobicity.N(), + 'D' => window_values += hydrophobicity.D(), + 'C' => window_values += hydrophobicity.C(), + 'Q' => window_values += hydrophobicity.Q(), + 'E' => window_values += hydrophobicity.E(), + 'G' => window_values += hydrophobicity.G(), + 'H' => window_values += hydrophobicity.H(), + 'I' => window_values += hydrophobicity.I(), + 'L' => window_values += hydrophobicity.L(), + 'K' => window_values += hydrophobicity.K(), + 'M' => window_values += hydrophobicity.M(), + 'F' => window_values += hydrophobicity.F(), + 'P' => window_values += hydrophobicity.P(), + 'S' => window_values += hydrophobicity.S(), + 'T' => window_values += hydrophobicity.T(), + 'W' => window_values += hydrophobicity.W(), + 'Y' => window_values += hydrophobicity.Y(), + 'V' => window_values += hydrophobicity.V(), + _ => continue, + } + } + total_hydrophobicity.push(window_values); + } + total_hydrophobicity } - pub fn amino_counts(protein_seq: &str) -> HashMap { let mut counts: HashMap = HashMap::new(); for c in protein_seq.chars() { - *counts.entry(c).or_insert(0) +=1; - } + *counts.entry(c).or_insert(0) += 1; + } counts } @@ -516,180 +515,198 @@ pub fn amino_percentage(protein_seq: &str) -> HashMap { let mut percentages: HashMap = HashMap::new(); let counts = amino_counts(protein_seq); let seq_len: f64 = (protein_seq.len() as f64) as f64; - percentages = counts.iter().map(|(k, &value)| { - let percentage = (value as f64 / seq_len) * 100.0; - (k.clone(), percentage) - }).collect(); + percentages = counts + .iter() + .map(|(k, &value)| { + let percentage = (value as f64 / seq_len) * 100.0; + (k.clone(), percentage) + }) + .collect(); percentages } - #[cfg(test)] mod tests { use super::*; - #[test] - #[allow(unused_mut)] - #[allow(dead_code)] - #[allow(unused_variables)] - pub fn suggest_transmembrane_domains() -> Result<(), anyhow::Error> { - let file_gbk = File::open("test_output.gbk")?; - let mut reader = Reader::new(file_gbk); - let mut records = reader.records(); - loop { - match records.next() { - Some(Ok(mut record)) => { - //println!("next record"); - //println!("Record id: {:?}", record.id); - for (k, v) in &record.cds.attributes { - match record.seq_features.get_sequence_faa(&k) { - Some(value) => { let seq_faa = value.to_string(); - println!("{:?}", &seq_faa); - let hydro_values = hydrophobicity(&seq_faa, 21); - let mut result = String::new(); - for hydro in hydro_values { - if hydro > 1.6 { - println!("possible transmembrane region - score {}",&hydro); - } - else { - () - } - } - }, - _ => (), - }; - - } - }, - Some(Err(e)) => { println!("theres an err {:?}", e); }, - None => { - println!("finished iteration"); - break; }, + #[test] + #[allow(unused_mut)] + #[allow(dead_code)] + #[allow(unused_variables)] + pub fn suggest_transmembrane_domains() -> Result<(), anyhow::Error> { + let file_gbk = File::open("test_output.gbk")?; + let mut reader = Reader::new(file_gbk); + let mut records = reader.records(); + loop { + match records.next() { + Some(Ok(mut record)) => { + //println!("next record"); + //println!("Record id: {:?}", record.id); + for (k, v) in &record.cds.attributes { + match record.seq_features.get_sequence_faa(&k) { + Some(value) => { + let seq_faa = value.to_string(); + println!("{:?}", &seq_faa); + let hydro_values = hydrophobicity(&seq_faa, 21); + let mut result = String::new(); + for hydro in hydro_values { + if hydro > 1.6 { + println!( + "possible transmembrane region - score {}", + &hydro + ); + } else { + () + } + } + } + _ => (), + }; } - } - return Ok(()); - } - - #[test] - #[allow(unused_mut)] - #[allow(dead_code)] - #[allow(unused_variables)] - #[allow(unused_assignments)] - pub fn collect_molecular_weight() -> Result<(), anyhow::Error> { - let file_gbk = File::open("test_output.gbk")?; - let mut reader = Reader::new(file_gbk); - let mut records = reader.records(); - let mut molecular_weight_total: f64 = 0.0; - loop { - match records.next() { - Some(Ok(mut record)) => { - //println!("next record"); - //println!("Record id: {:?}", record.id); - for (k, v) in &record.cds.attributes { - match record.seq_features.get_sequence_faa(&k) { - Some(value) => { let seq_faa = value.to_string(); - println!("id: {:?}", &k); - molecular_weight_total = molecular_weight(&seq_faa); - println!(">{}|{}\n{}", &record.id, &k, molecular_weight_total); - }, - _ => (), - }; - - } - }, - Some(Err(e)) => { println!("theres an err {:?}", e); }, - None => { - println!("finished iteration"); - break; }, + } + Some(Err(e)) => { + println!("theres an err {:?}", e); + } + None => { + println!("finished iteration"); + break; + } + } + } + return Ok(()); + } + + #[test] + #[allow(unused_mut)] + #[allow(dead_code)] + #[allow(unused_variables)] + #[allow(unused_assignments)] + pub fn collect_molecular_weight() -> Result<(), anyhow::Error> { + let file_gbk = File::open("test_output.gbk")?; + let mut reader = Reader::new(file_gbk); + let mut records = reader.records(); + let mut molecular_weight_total: f64 = 0.0; + loop { + match records.next() { + Some(Ok(mut record)) => { + //println!("next record"); + //println!("Record id: {:?}", record.id); + for (k, v) in &record.cds.attributes { + match record.seq_features.get_sequence_faa(&k) { + Some(value) => { + let seq_faa = value.to_string(); + println!("id: {:?}", &k); + molecular_weight_total = molecular_weight(&seq_faa); + println!(">{}|{}\n{}", &record.id, &k, molecular_weight_total); + } + _ => (), + }; } - } - return Ok(()); - } + } + Some(Err(e)) => { + println!("theres an err {:?}", e); + } + None => { + println!("finished iteration"); + break; + } + } + } + return Ok(()); + } - #[test] - #[allow(unused_mut)] - #[allow(unused_variables)] - #[allow(unused_assignments)] - pub fn count_aminos() -> Result<(), anyhow::Error> { - let file_gbk = File::open("test_output.gbk")?; - let mut reader = Reader::new(file_gbk); - let mut records = reader.records(); - let mut results: HashMap = HashMap::new(); - loop { - match records.next() { - Some(Ok(record)) => { - for (k, _v) in &record.cds.attributes { - match record.seq_features.get_sequence_faa(&k) { - Some(value) => { let seq_faa = value.to_string(); - println!("id: {:?}", &k); - results = amino_counts(&seq_faa); - println!(">{}|{}\n{:?}", &record.id, &k, results); - }, - _ => (), - }; - - } - }, - Some(Err(e)) => { println!("theres an err {:?}", e); }, - None => { - println!("finished iteration"); - break; }, + #[test] + #[allow(unused_mut)] + #[allow(unused_variables)] + #[allow(unused_assignments)] + pub fn count_aminos() -> Result<(), anyhow::Error> { + let file_gbk = File::open("test_output.gbk")?; + let mut reader = Reader::new(file_gbk); + let mut records = reader.records(); + let mut results: HashMap = HashMap::new(); + loop { + match records.next() { + Some(Ok(record)) => { + for (k, _v) in &record.cds.attributes { + match record.seq_features.get_sequence_faa(&k) { + Some(value) => { + let seq_faa = value.to_string(); + println!("id: {:?}", &k); + results = amino_counts(&seq_faa); + println!(">{}|{}\n{:?}", &record.id, &k, results); + } + _ => (), + }; } - } - return Ok(()); - } - - #[test] - #[allow(dead_code)] - #[allow(unused_mut)] - #[allow(unused_variables)] - #[allow(unused_assignments)] - pub fn aromaticity() -> Result<(), anyhow::Error> { + } + Some(Err(e)) => { + println!("theres an err {:?}", e); + } + None => { + println!("finished iteration"); + break; + } + } + } + return Ok(()); + } + + #[test] + #[allow(dead_code)] + #[allow(unused_mut)] + #[allow(unused_variables)] + #[allow(unused_assignments)] + pub fn aromaticity() -> Result<(), anyhow::Error> { // calculated as in biopython with aromaticity according to Lobry, 1994 as the relative freq of Phe+Trp+Tyr let file_gbk = File::open("test_output.gbk")?; - let reader = Reader::new(file_gbk); - let mut records = reader.records(); - let mut results: HashMap = HashMap::new(); - loop { - match records.next() { - Some(Ok(record)) => { - for (k, _v) in &record.cds.attributes { - match record.seq_features.get_sequence_faa(&k) { - Some(value) => { let seq_faa = value.to_string(); - results = amino_percentage(&seq_faa); - let aromatic_aas = vec!['Y','W','F']; - let aromaticity: f64 = aromatic_aas.iter() - .filter_map(|&amino| results.get(&amino)) - .map(|&perc| perc / 100.0) - .sum(); - println!("aromaticity for {} {} is {}",&record.id, &k, &aromaticity); - }, - _ => (), - }; - } - }, - Some(Err(e)) => { println!("theres an error {:?}", e); }, - None => { println!("finished iteration"); - break; - }, - } - } - return Ok(()); - } - #[tokio::test] - pub async fn main() -> Result<(), anyhow::Error> { - let reader = fasta::Reader::new(File::open("test_hamming.aln")?); - let records: Vec<_> = reader.records().collect::>()?; - let sequences: Vec = records - .iter() - .map(|rec| String::from_utf8_lossy(rec.seq()).to_string()) - .collect(); - let ids: Vec = records - .iter() - .map(|rec| rec.id().to_string()) - .collect(); - let distances = hamming_matrix(&sequences).await?; - let _ = write_distances_csv(ids, distances, "hamming_dists.csv"); - Ok(()) - } + let reader = Reader::new(file_gbk); + let mut records = reader.records(); + let mut results: HashMap = HashMap::new(); + loop { + match records.next() { + Some(Ok(record)) => { + for (k, _v) in &record.cds.attributes { + match record.seq_features.get_sequence_faa(&k) { + Some(value) => { + let seq_faa = value.to_string(); + results = amino_percentage(&seq_faa); + let aromatic_aas = vec!['Y', 'W', 'F']; + let aromaticity: f64 = aromatic_aas + .iter() + .filter_map(|&amino| results.get(&amino)) + .map(|&perc| perc / 100.0) + .sum(); + println!( + "aromaticity for {} {} is {}", + &record.id, &k, &aromaticity + ); + } + _ => (), + }; + } + } + Some(Err(e)) => { + println!("theres an error {:?}", e); + } + None => { + println!("finished iteration"); + break; + } + } + } + return Ok(()); + } + #[tokio::test] + pub async fn main() -> Result<(), anyhow::Error> { + let reader = fasta::Reader::new(File::open("test_hamming.aln")?); + let records: Vec<_> = reader.records().collect::>()?; + let sequences: Vec = records + .iter() + .map(|rec| String::from_utf8_lossy(rec.seq()).to_string()) + .collect(); + let ids: Vec = records.iter().map(|rec| rec.id().to_string()).collect(); + let distances = hamming_matrix(&sequences).await?; + let _ = write_distances_csv(ids, distances, "hamming_dists.csv"); + Ok(()) + } } diff --git a/seqmetrics/src/write_dst_csv.rs b/seqmetrics/src/write_dst_csv.rs index 55f0920..e5a4eed 100644 --- a/seqmetrics/src/write_dst_csv.rs +++ b/seqmetrics/src/write_dst_csv.rs @@ -1,28 +1,30 @@ +use anyhow::Result; use tokio::fs::File; use tokio::io::{AsyncWriteExt, BufWriter}; -use anyhow::Result; - -pub async fn write_distances_csv(ids: Vec, distances: Vec>, filename: &str) -> Result<(), anyhow::Error> { +pub async fn write_distances_csv( + ids: Vec, + distances: Vec>, + filename: &str, +) -> Result<(), anyhow::Error> { let file = File::create(filename).await?; let mut writer = BufWriter::new(file); let mut header = String::from("id"); for id in &ids { - header.push(','); - header.push_str(id); - } + header.push(','); + header.push_str(id); + } header.push('\n'); writer.write_all(header.as_bytes()).await?; // Write each row for (i, row) in distances.iter().enumerate() { let mut line = format!("{}", ids[i]); for val in row { - line.push_str(&format!(",{:.3}",val)); + line.push_str(&format!(",{:.3}", val)); } - line.push('\n'); - writer.write_all(line.as_bytes()).await?; + line.push('\n'); + writer.write_all(line.as_bytes()).await?; } writer.flush().await?; Ok(()) } -