use std::io::{self, BufRead, BufReader, Stdin, stdin};
use std::fs::File;
use std::path::Path;
use std::collections::HashMap;
use error::*;
pub type Record = HashMap<String, String>;
pub type FieldGroupCount = HashMap<String, i32>;
pub enum LineReader {
Stdin(Stdin),
FileIn(BufReader<File>),
}
impl LineReader {
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
match *self {
LineReader::Stdin(ref mut r) => r.read_line(buf),
LineReader::FileIn(ref mut r) => r.read_line(buf),
}
}
}
pub fn open_file(name: &str) -> Result<LineReader, Error> {
match name {
"-" => Ok(LineReader::Stdin(stdin())),
_ => {
let f = File::open(&Path::new(name))?;
Ok(LineReader::FileIn(BufReader::new(f)))
}
}
}
pub fn parse_head(input: &mut LineReader) -> Result<Record, Error> {
let found: String;
loop {
let mut line = String::new();
match input.read_line(&mut line).map_err(Error::Io) {
Ok(0) | Ok(1) => continue,
Ok(_) => {
found = line;
break;
}
Err(err) => return Err(err),
}
}
let mut record = Record::new();
for item in found.split('\t').collect::<Vec<&str>>().into_iter() {
let v = item.splitn(2, ':').collect::<Vec<&str>>();
match v.len() {
0 | 1 => {
return Err(ParseError { msg: format!("invalid ltsv item: {}", item) })
.map_err(Error::Parse);
}
2 => record.insert(v[0].to_string(), v[1].to_string()),
_ => {
return Err(ParseError { msg: format!("unreachable error: {}", item) })
.map_err(Error::Parse);
}
};
}
Ok(record)
}
pub fn each_record<F>(reader: &mut LineReader, f: F) -> Result<(), Error>
where F: Fn(&Record)
{
loop {
let mut line = String::new();
match reader.read_line(&mut line) {
Err(err) => return Err(err).map_err(Error::Io),
Ok(0) => break, Ok(_) => {
line.pop(); if line.len() == 0 {
continue;
}
let mut record = Record::new();
for item in line.split('\t').collect::<Vec<&str>>().into_iter() {
let v = item.splitn(2, ':').collect::<Vec<&str>>();
match v.len() {
0 | 1 => {
return Err(ParseError { msg: format!("invalid ltsv item: {}", item) })
.map_err(Error::Parse);
}
2 => record.insert(v[0].to_string(), v[1].to_string()),
_ => {
return Err(ParseError { msg: format!("unreachable error: {}", item) })
.map_err(Error::Parse);
}
};
}
f(&record);
}
}
}
Ok(())
}
pub fn group_by(reader: &mut LineReader, label: &String) -> Result<FieldGroupCount, Error> {
let mut group = FieldGroupCount::new();
loop {
let mut line = String::new();
match reader.read_line(&mut line) {
Err(err) => return Err(err).map_err(Error::Io),
Ok(0) => break, Ok(_) => {
line.pop(); if line.len() == 0 {
continue;
}
for item in line.split('\t').collect::<Vec<&str>>().into_iter() {
let v = item.splitn(2, ':').collect::<Vec<&str>>();
match v.len() {
0 | 1 => {
return Err(ParseError { msg: format!("invalid ltsv item: {}", item) })
.map_err(Error::Parse);
}
2 => {
if label != &v[0] {
continue;
}
let count = group.entry(v[1].to_string()).or_insert(0);
*count += 1;
}
_ => {
return Err(ParseError { msg: format!("unreachable error: {}", item) })
.map_err(Error::Parse);
}
}
}
}
}
}
Ok(group)
}
pub fn order_by(reader: &mut LineReader, label: &String) -> Result<Vec<String>, Error> {
let mut lines = Vec::new();
loop {
let mut line = String::new();
match reader.read_line(&mut line) {
Err(err) => return Err(err).map_err(Error::Io),
Ok(0) => break, Ok(_) => lines.push(line),
}
}
lines.sort_by(|a, b| {
let av = match line2record(a) {
None => "".to_string(),
Some(record) => {
match record.get(label) {
Some(v) => v.to_string(),
None => "".to_string(),
}
}
};
let bv = match line2record(b) {
None => "".to_string(),
Some(record) => {
match record.get(label) {
Some(v) => v.to_string(),
None => "".to_string(),
}
}
};
av.cmp(&bv)
});
Ok(lines)
}
fn line2record(line: &String) -> Option<Record> {
let mut record = Record::new();
for field in line.split('\t').collect::<Vec<_>>().into_iter() {
let v = field.splitn(2, ':').collect::<Vec<_>>();
match v.len() {
2 => record.insert(v[0].to_string(), v[1].to_string()),
_ => return None,
};
}
Some(record)
}
#[cfg(test)]
mod test {
#[test]
fn test_parse_head() {}
}