diploid_contam_estimator/
lib.rs1pub mod bedreader;
2pub mod cli;
3pub mod contamination_estimator;
4pub mod model;
5pub mod vcfreader;
6
7use bedreader::read_bed;
8use contamination_estimator::calculate_contam_hypothesis;
9use log::info;
10use model::{ContamProbResult, VariantPosition};
11use std::fs::File;
12use std::io::Write;
13use std::option::Option;
14use std::string::String;
15use std::vec::Vec;
16use vcfreader::build_variant_list;
17
18const MAX_CONTAM: usize = 400; const DECIMAL_PLACE: f64 = 0.001; pub fn write_json(filename: &str, json_string: String) -> Result<(), String> {
27 let mut output_file = File::create(filename).map_err(|e| e.to_string())?;
28 write!(output_file, "{}", json_string).unwrap();
29 info!("Written debug file at: {}", filename);
30 Ok(())
31}
32
33pub fn run(
57 vcf_file: &str,
58 loci_bed: Option<&str>,
59 snv_only_flag: bool,
60 depth_threshold: usize,
61 prob_json: Option<&str>,
62 variant_json: Option<&str>,
63) -> Result<f64, String> {
64 let regions: Vec<String> = match loci_bed {
66 Some(bed) => read_bed(bed)?,
67 _ => vec![],
68 };
69 let mut variant_vector: Vec<VariantPosition> =
70 build_variant_list(vcf_file, snv_only_flag, depth_threshold, regions)?;
71
72 let mut result_vector: Vec<ContamProbResult> = Vec::with_capacity(MAX_CONTAM); let mut best_guess: Option<ContamProbResult> = None;
75 let contamination_range_to_evaluate = (1..MAX_CONTAM).map(|x| x as f64 * DECIMAL_PLACE);
76 for hypothetical_contamination_level in contamination_range_to_evaluate {
77 let log_prob: f64 =
80 calculate_contam_hypothesis(&mut variant_vector, hypothetical_contamination_level)?;
81
82 let output: ContamProbResult = ContamProbResult {
84 contamination_level: hypothetical_contamination_level,
85 log_likelihood: log_prob,
86 };
87 result_vector.push(output);
89
90 match best_guess {
94 None => {
95 best_guess = Some(output);
96 }
97 Some(bg) => {
98 if output.log_likelihood > bg.log_likelihood {
99 best_guess = Some(output);
100 }
101 }
102 }
103 }
104 let best_guess_contam_level = best_guess
105 .ok_or("No best guess contam object")?
106 .contamination_level;
107
108 if prob_json.is_some() {
110 let json_string =
112 serde_json::to_string_pretty(&result_vector).map_err(|e| e.to_string())?;
113 write_json(prob_json.ok_or("No prob json name found")?, json_string)?
114 }
115
116 if variant_json.is_some() {
117 info!("Adding labels to the variant data json");
119 calculate_contam_hypothesis(&mut variant_vector, best_guess_contam_level)?;
120 let json_string =
122 serde_json::to_string_pretty(&variant_vector).map_err(|e| e.to_string())?;
123 write_json(
124 variant_json.ok_or("No variant json name found")?,
125 json_string,
126 )?
127 }
128
129 Ok(best_guess_contam_level)
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use assert_approx_eq::assert_approx_eq;
136 use rstest::*;
137 use serde_json::Value;
138 use std::io::Read;
139
140 #[rstest]
141 #[case(
142 false,
143 true,
144 1000,
145 Some("prob.json"),
146 Some("variants.json"),
147 0.046,
148 None
149 )]
150 #[case(false, true, 1000, None, None, 0.046, None)]
151 #[case(false, true, 10, None, None, 0.046, None)]
152 #[case(false, true, 10, None, None, 0.046, None)]
153 #[case(false, false, 1100, None, None, 0.399, None)]
154 #[case(false, true, 1100, None, None, 0.043, None)]
155 #[case(true, true, 200, None, None, 0.001, Some("data/test.bed"))] #[case(true, true, 200, None, None, 0.046, None)] fn test_run(
158 #[case] gz_input: bool,
159 #[case] snv_only_flag: bool,
160 #[case] depth_threshold: usize,
161 #[case] prob_json: Option<&str>,
162 #[case] variant_json: Option<&str>,
163 #[case] expected_out: f64,
164 #[case] bed_file: Option<&str>,
165 ) {
166 let vcf_file = match gz_input {
169 false => "data/test.vcf",
170 true => "data/test.vcf.gz",
171 };
172 let best_guess_contam_level: f64 = run(
173 vcf_file,
174 bed_file,
175 snv_only_flag,
176 depth_threshold,
177 prob_json,
178 variant_json,
179 )
180 .unwrap();
181 assert_approx_eq!(best_guess_contam_level, expected_out);
182 }
183
184 #[test]
185 #[should_panic(expected = "Fetching bed loci from non bgzipped")]
186 fn test_workflow_exception() {
187 run(
188 "data/test.vcf",
189 Some("data/test.bed"),
190 true,
191 100,
192 None,
193 None,
194 )
195 .unwrap();
196 }
197
198 #[test]
199 fn test_write_json() {
200 let json_string = "{\"data/test.vcf\":0.046 }";
201 write_json("test.json", json_string.to_string()).unwrap();
202
203 let mut file = File::open("test.json").unwrap();
204 let mut data = String::new();
205 file.read_to_string(&mut data).unwrap();
206
207 let json_data: Value = serde_json::from_str(&data).expect("Bad json data?");
208 assert_eq!(json_data["data/test.vcf"], 0.046);
209 }
210}