
/*!
Helper traits and functions for dumping data into CSV or JSON files.
*/

use std::slice::Iter;
use csv;
use serde_json;
use serde::Serialize;
use crate::error::DynError;

/// Write a CSV from an iterator of [serde-serializable][Serialize] items.
pub fn write_csv<I, J>(mut iter : I, filename : String) -> DynError
where I : Iterator<Item=J>,
      J : Serialize {
    let wtr = csv::WriterBuilder::new()
        .has_headers(true)
        .delimiter(b'\t')
        .from_path(filename);
    wtr.and_then(|mut w|{
        //w.write_record(self.tabledump_headers())?;
        iter.try_for_each(|item| w.serialize(item))
    })?;
    Ok(())
}

/// Write a JSON from an iterator of [serde-serializable][Serialize] items.
pub fn write_json<I, J>(iter : I, filename : String) -> DynError
where I : Iterator<Item=J>,
      J : Serialize {
    let v : Vec<J> = iter.collect();
    serde_json::to_writer_pretty(std::fs::File::create(filename)?, &v)?;
    Ok(())
}

/// Trait that should be implemented by types that can be dumped into CSV (or JSON) files.
///
/// The method that needs to be implemented is [`Self::tabledump_entries`] that prodvides an
/// iterator over the items to be dumped. Moreover, the items themselves need to be
/// [serde-serializable][`Serialize`]. Caveats about [`csv`] crate restrictions on writable
/// data apply for CSV writing.
pub trait TableDump<'a>
where <Self::Iter as Iterator>::Item : Serialize {
    /// Iterator over the items to be dumped.
    type Iter : Iterator;

    // Return the headers of the CSV file.
    //fn tabledump_headers(&'a self) -> Vec<String>;

    /// Return an iterator over the rows that would be dumped into a CSV file.
    fn tabledump_entries(&'a self) -> Self::Iter;

    /// Write a CSV file.
    fn write_csv(&'a self, f : String) -> DynError {
        write_csv(self.tabledump_entries(), f)
    }

    /// Write mapped CSV. This is a workaround to rust-csv not supporting struct flattening
    fn write_csv_mapped<D, G>(&'a self, f : String, g : G) -> DynError
    where D : Serialize,
          G : FnMut(<Self::Iter as Iterator>::Item) -> D {
        write_csv(self.tabledump_entries().map(g), f)
    }

    /// Write a JSON file.
    fn write_json(&'a self, f : String) -> DynError {
        write_json(self.tabledump_entries(), f)
    }
}

impl<'a, T : Serialize + 'a> TableDump<'a> for [T] {
    type Iter = Iter<'a, T>;

    // fn tabledump_headers(&'a self) -> Vec<String> {
    //     vec!["value".into()]
    // }

    fn tabledump_entries(&'a self) -> Self::Iter {
        self.iter()
    }
}

