use std::io;
use std::collections::HashMap;
use csv;
use inflector::Inflector;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error
{
Msg(String),
}
impl<'a, T: ToString> From<T> for Error
{
fn from(t: T) -> Self
{
Error::Msg(t.to_string())
}
}
#[derive(Debug)]
struct RecordType
{
fields: Vec<Field>
}
impl RecordType
{
fn with(fields: Vec<Field>) -> Self
{
Self
{
fields
}
}
}
#[derive(Debug, Clone)]
struct Field
{
name: String,
kind: FieldKind,
}
impl Field
{
fn with(name: String, kind: FieldKind) -> Self
{
Self
{
name,
kind,
}
}
}
#[derive(Debug, Clone)]
enum FieldKind
{
Integer,
Real,
Factor(String),
Empty,
}
impl FieldKind
{
fn parse(f: &str) -> Self
{
if f == ""
{
FieldKind::Empty
}
else
{
f.parse::<i32>().map(|_i| FieldKind::Integer)
.or(f.parse::<f32>().map(|_i| FieldKind::Real))
.unwrap_or(FieldKind::Factor(f.to_string()))
}
}
}
#[derive(Debug)]
struct Index
{
inner: Vec<(String, Vec<FieldKind>)>,
}
impl Index
{
fn new() -> Self
{
Self { inner: Vec::new() }
}
fn add(&mut self, rt: RecordType)
{
for field in rt.fields.iter()
{
match self.inner.iter_mut().find(|i| i.0 == field.name)
{
Some((_s, ref mut v)) =>
v.push(field.kind.clone()),
None =>
self.inner.push(
(field.name.clone(), vec![field.kind.clone()])
),
}
}
}
fn to_struct_defs(self)
{
println!("#[derive(Debug, Clone, Copy, Eq)]");
print!("pub struct Record {{\n");
let mut factor_defs = Vec::new();
for (name, kinds) in self.inner.into_iter()
{
print!(" pub {}: ", name);
let test_empty =
kinds.iter()
.any(|k| match k { FieldKind::Empty => true, _ => false });
let test_factor =
kinds.iter()
.filter_map(|k| match k { FieldKind::Empty => None, _ => Some(k) })
.any(|k| match k { FieldKind::Factor(_) => true, _ => false });
let test_real =
kinds.iter()
.filter_map(|k| match k { FieldKind::Empty => None, _ => Some(k) })
.any(|k| match k { FieldKind::Integer => false, _ => true });
let mut type_name = String::new();
if test_factor
{
type_name = name.to_pascal_case();
let mut factor_def = "#[derive(Debug, Clone, Copy, Eq)]\n".to_string();
factor_def.extend(format!("pub enum {} {{\n", type_name).chars());
kinds.into_iter()
.filter_map(|k| match k { FieldKind::Factor(s) => Some(s), _ => None })
.for_each(|level|
factor_def.extend(
format!(" {},\n", level.to_pascal_case()).chars()
)
);
factor_def.extend("}\n\n".chars());
factor_defs.push(factor_def);
}
else if test_real
{
type_name = String::from("f32");
}
else
{
type_name = String::from("i32");
}
if test_empty
{
type_name = format!("Option<{}>", type_name);
}
print!("{},\n", type_name);
}
print!("}}\n\n");
for factor_def in factor_defs.into_iter()
{
print!("{}", factor_def);
}
}
}
fn main() -> Result<()> {
let mut rdr = csv::Reader::from_reader(io::stdin());
let headers: Vec<String> =
rdr.headers().unwrap().iter()
.map(|h| h.to_string())
.collect();
let mut index = Index::new();
for result in rdr.deserialize()
{
let record: HashMap<String, String> = result?;
let mut fields = Vec::new();
for header in &headers
{
fields.push(
Field::with(
header.to_string(),
FieldKind::parse(record.get(header).unwrap())
)
);
}
index.add(RecordType::with(fields));
}
index.to_struct_defs();
Ok(())
}