use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::path::PathBuf;
use std::sync::Arc;
use std::fmt;
use super::ZipBascetShardReader;
use super::TirpBascetShardReader;
use super::DetectedFileformat;
pub type CellID = String;
pub type CellUMI = Vec<u8>;
type ListReadWithBarcode = Arc<(CellID,Arc<Vec<ReadPair>>)>;
#[derive(Debug,Clone)]
pub struct ReadPair {
pub r1: Vec<u8>,
pub r2: Vec<u8>,
pub q1: Vec<u8>,
pub q2: Vec<u8>,
pub umi: Vec<u8>
}
impl fmt::Display for ReadPair {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {}, {})",
String::from_utf8_lossy(self.r1.as_slice()),
String::from_utf8_lossy(self.r2.as_slice()) ,
String::from_utf8_lossy(self.umi.as_slice())
)
}
}
pub trait ConstructFromPath<R> where Self: Clone { fn new_from_path(&self, fname: &PathBuf) -> anyhow::Result<R> where Self: Sized;
}
pub trait ReadPairWriter {
fn write_reads_for_cell(
&mut self,
cell_id: &CellID,
list_reads: &Arc<Vec<ReadPair>>
);
fn writing_done(&mut self) -> anyhow::Result<()>;
}
pub trait ReadPairReader {
fn get_reads_for_cell(
&mut self,
cell_id: &CellID
) -> anyhow::Result<Arc<Vec<ReadPair>>>;
}
pub trait StreamingReadPairReader {
fn get_reads_for_next_cell(
&mut self
) -> anyhow::Result<Option<ListReadWithBarcode>>;
}
pub trait ShardCellDictionary {
fn get_cell_ids(&mut self) -> anyhow::Result<Vec<CellID>>;
fn has_cell(&mut self, cellid: &CellID) -> bool;
}
pub trait ShardRandomFileExtractor {
fn extract_to_outdir (
&mut self,
cell_id: &CellID,
needed_files: &Vec<String>,
fail_if_missing: bool,
out_directory: &PathBuf
) -> anyhow::Result<bool>;
fn get_files_for_cell(
&mut self,
cell_id: &CellID
) -> anyhow::Result<Vec<String>>;
fn extract_as(
&mut self,
cell_id: &String,
file_name: &String,
path_outfile: &PathBuf
) -> anyhow::Result<()>;
}
pub trait ShardStreamingFileExtractor {
fn next_cell (
&mut self,
) -> anyhow::Result<Option<CellID>>;
fn extract_to_outdir (
&mut self,
needed_files: &Vec<String>,
fail_if_missing: bool,
out_directory: &PathBuf
) -> anyhow::Result<bool>;
fn get_files_for_cell(
&mut self
) -> anyhow::Result<Vec<String>>;
}
pub enum DynShardReader {
TirpBascetShardReader(TirpBascetShardReader),
ZipBascetShardReader(ZipBascetShardReader)
}
impl ShardCellDictionary for DynShardReader {
fn get_cell_ids(&mut self) -> anyhow::Result<Vec<CellID>> {
match self {
DynShardReader::TirpBascetShardReader(r) => r.get_cell_ids(),
DynShardReader::ZipBascetShardReader(r) => r.get_cell_ids()
}
}
fn has_cell(&mut self, cellid: &CellID) -> bool {
match self {
DynShardReader::TirpBascetShardReader(r) => r.has_cell(&cellid),
DynShardReader::ZipBascetShardReader(r) => r.has_cell(&cellid)
}
}
}
impl ShardRandomFileExtractor for DynShardReader {
fn extract_to_outdir (
&mut self,
cell_id: &CellID,
needed_files: &Vec<String>,
fail_if_missing: bool,
out_directory: &PathBuf
) -> anyhow::Result<bool> {
match self {
DynShardReader::TirpBascetShardReader(r) => r.extract_to_outdir(&cell_id, &needed_files, fail_if_missing, &out_directory),
DynShardReader::ZipBascetShardReader(r) => r.extract_to_outdir(&cell_id, &needed_files, fail_if_missing, &out_directory),
}
}
fn get_files_for_cell(
&mut self,
cell_id: &CellID
) -> anyhow::Result<Vec<String>> {
match self {
DynShardReader::TirpBascetShardReader(r) => r.get_files_for_cell(&cell_id),
DynShardReader::ZipBascetShardReader(r) => r.get_files_for_cell(&cell_id)
}
}
fn extract_as(
&mut self,
cell_id: &String,
file_name: &String,
path_outfile: &PathBuf
) -> anyhow::Result<()> {
match self {
DynShardReader::TirpBascetShardReader(r) => r.extract_as(&cell_id, &file_name, &path_outfile),
DynShardReader::ZipBascetShardReader(r) => r.extract_as(&cell_id, &file_name, &path_outfile),
}
}
}
pub fn get_shard_reader_for_path(p: &PathBuf) -> anyhow::Result<DynShardReader> {
match crate::fileformat::detect_shard_format(&p) {
DetectedFileformat::TIRP => {
Ok(DynShardReader::TirpBascetShardReader(TirpBascetShardReader::new(p).expect(format!("Failed to read {}",p.display()).as_str())))
},
DetectedFileformat::ZIP => {
Ok(DynShardReader::ZipBascetShardReader(ZipBascetShardReader::new(p).expect(format!("Failed to read {}",p.display()).as_str())))
},
_ => {
anyhow::bail!("File format for {} does not support listing of cell IDs", p.display())
}
}
}
pub fn get_dyn_celldict(
p: &PathBuf
) -> anyhow::Result<Box<dyn ShardCellDictionary>> {
match crate::fileformat::detect_shard_format(&p) {
DetectedFileformat::TIRP => {
Ok(Box::new(TirpBascetShardReader::new(p).expect(format!("Unable to read cell list for {}",p.display()).as_str())))
},
DetectedFileformat::ZIP => {
Ok(Box::new(ZipBascetShardReader::new(p).expect(format!("Unable to read cell list for {}",p.display()).as_str())))
},
_ => {
anyhow::bail!("File format for {} does not support listing of cell IDs", p.display())
}
}
}
pub fn try_get_cells_in_file(
p: &PathBuf
) -> anyhow::Result<Option<Vec<CellID>>> {
let mut cell_dict = get_dyn_celldict(p).
expect(format!("Unable to read cell list for {}",p.display()).as_str());
Ok(Some(cell_dict.get_cell_ids().unwrap()))
}
#[derive(Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
struct BarcodeHistogramRow {
bc: String,
cnt: u64
}
pub struct BarcodeHistogram {
histogram: HashMap<CellID, u64>
}
impl BarcodeHistogram {
pub fn new() -> BarcodeHistogram {
BarcodeHistogram {
histogram: HashMap::new()
}
}
pub fn inc(
&mut self,
cellid: &CellID
){
let counter = self.histogram.entry(cellid.clone()).or_insert(0);
*counter += 1;
}
pub fn inc_by(
&mut self,
cellid: &CellID,
cnt: &u64
){
let counter = self.histogram.entry(cellid.clone()).or_insert(0);
*counter += cnt;
}
pub fn add_histogram(
&mut self,
other: &BarcodeHistogram
) {
for (cellid,v) in other.histogram.iter() {
let counter = self.histogram.entry(cellid.clone()).or_insert(0);
*counter += v;
}
}
pub fn from_file(
fname: &PathBuf
) -> anyhow::Result<BarcodeHistogram> {
let file = File::open(fname)?;
let reader= BufReader::new(file);
let mut hist = BarcodeHistogram::new();
let mut reader = csv::ReaderBuilder::new()
.delimiter(b'\t')
.from_reader(reader);
for result in reader.deserialize() {
let record: BarcodeHistogramRow = result.unwrap();
hist.histogram.insert(record.bc, record.cnt);
}
Ok(hist)
}
pub fn write_file(
&self,
fname: &PathBuf
) -> anyhow::Result<()> {
let mut writer = csv::WriterBuilder::new()
.delimiter(b'\t')
.from_path(fname)
.expect("Could not open histogram file for writing");
for (bc, cnt) in self.histogram.iter() {
let _ = writer.serialize(BarcodeHistogramRow {
bc: bc.to_string(),
cnt: *cnt
});
}
let _ = writer.flush();
Ok(())
}
}