/// Comma-separated values using each variable's print format (variable
/// names are written as the first line)
Csv,
-
- /// SPSS system file.
- Sav,
}
/// Convert SPSS data files into other formats.
/// Format for output file (if omitted, the intended format is inferred
/// based on file extension).
+ #[arg(short = 'O')]
output_format: Option<OutputFormat>,
/// The encoding to use.
- #[arg(long, value_parser = parse_encoding)]
+ #[arg(short = 'e', long, value_parser = parse_encoding)]
encoding: Option<&'static Encoding>,
+
+ /// Maximum number of cases to print.
+ #[arg(short = 'c', long = "cases")]
+ max_cases: Option<u64>,
+
+ #[command(flatten, next_help_heading = "Options for CSV output")]
+ csv_options: CsvOptions,
+}
+
+#[derive(Args, Clone, Debug)]
+struct CsvOptions {
+ /// Omit writing variable names as the first line of output.
+ #[arg(long)]
+ no_var_names: bool,
}
impl Convert {
None => Box::new(stdout()),
};
let mut output = csv::WriterBuilder::new().from_writer(writer);
- output.write_record(dictionary.variables.iter().map(|var| var.name.as_str()))?;
-
- if let Some(cases) = cases {
- for case in cases {
- output.write_record(case?.into_iter().zip(dictionary.variables.iter()).map(
- |(datum, variable)| {
- datum
- .display(variable.print_format, variable.encoding)
- .to_string()
- },
- ))?;
- }
+ if !self.csv_options.no_var_names {
+ output.write_record(dictionary.variables.iter().map(|var| var.name.as_str()))?;
+ }
+
+ for (_case_number, case) in (0..self.max_cases.unwrap_or(u64::MAX)).zip(cases) {
+ output.write_record(case?.into_iter().zip(dictionary.variables.iter()).map(
+ |(datum, variable)| {
+ datum
+ .display(variable.print_format, variable.encoding)
+ .to_string()
+ },
+ ))?;
}
Ok(())
}
let header = header?;
println!("{:?}", header);
}
- if let Some(cases) = reader.cases() {
- for (_index, case) in (0..max_cases).zip(cases) {
- println!("{:?}", case?);
- }
+ for (_index, case) in (0..max_cases).zip(reader.cases()) {
+ println!("{:?}", case?);
}
}
Mode::Decoded => {
}
let headers = Headers::new(decoded_records, &mut |e| eprintln!("{e}"))?;
let (dictionary, metadata, _cases) =
- headers.decode(None, encoding, |e| eprintln!("{e}"))?;
+ headers.decode(reader.cases(), encoding, |e| eprintln!("{e}"))?;
println!("{dictionary:#?}");
println!("{metadata:#?}");
}
pub fn decode(
mut self,
- mut cases: Option<Cases>,
+ mut cases: Cases,
encoding: &'static Encoding,
mut warn: impl FnMut(Error),
- ) -> Result<(Dictionary, Metadata, Option<Cases>), Error> {
+ ) -> Result<(Dictionary, Metadata, Cases), Error> {
let mut dictionary = Dictionary::new(encoding);
let file_label = fix_line_ends(self.header.file_label.trim_end_matches(' '));
variable.short_names = short_names;
variable.resize(width);
}
- cases = cases
- .take()
- .map(|cases| cases.with_widths(dictionary.variables.iter().map(|var| var.width)));
+ cases = cases.with_widths(dictionary.variables.iter().map(|var| var.width));
}
if self.long_names.is_empty() {
let metadata = Metadata::decode(&self, warn);
if let Some(n_cases) = metadata.n_cases {
- cases = cases.take().map(|cases| cases.with_expected_cases(n_cases))
+ cases = cases.with_expected_cases(n_cases);
}
Ok((dictionary, metadata, cases))
}
pub fn headers<'b>(&'b mut self) -> ReadHeaders<'a, 'b, R> {
ReadHeaders(self)
}
- pub fn cases(self) -> Option<Cases> {
- self.cases
+ pub fn cases(self) -> Cases {
+ self.cases.unwrap_or_default()
}
}
if let Some(pt) = dictionary.output_variable_sets().to_pivot_table() {
output.push(Arc::new(pt.into()));
}
- if let Some(cases) = cases {
- let variables = Group::new("Variable")
- .with_multiple(dictionary.variables.iter().map(|var| &**var));
- let mut case_numbers = Group::new("Case").with_label_shown();
- let mut data = Vec::new();
- for case in cases {
- match case {
- Ok(case) => {
- case_numbers
- .push(Value::new_integer(Some((case_numbers.len() + 1) as f64)));
- data.push(
- case.into_iter()
- .map(|datum| Value::new_datum(&datum, dictionary.encoding))
- .collect::<Vec<_>>(),
- );
- }
- Err(error) => {
- output.push(Arc::new(Item::from(Text::new_log(error.to_string()))));
- }
+ let variables =
+ Group::new("Variable").with_multiple(dictionary.variables.iter().map(|var| &**var));
+ let mut case_numbers = Group::new("Case").with_label_shown();
+ let mut data = Vec::new();
+ for case in cases {
+ match case {
+ Ok(case) => {
+ case_numbers
+ .push(Value::new_integer(Some((case_numbers.len() + 1) as f64)));
+ data.push(
+ case.into_iter()
+ .map(|datum| Value::new_datum(&datum, dictionary.encoding))
+ .collect::<Vec<_>>(),
+ );
+ }
+ Err(error) => {
+ output.push(Arc::new(Item::from(Text::new_log(error.to_string()))));
}
}
- if !data.is_empty() {
- let mut pt = PivotTable::new([
- (Axis3::X, Dimension::new(variables)),
- (Axis3::Y, Dimension::new(case_numbers)),
- ]);
- for (row_number, row) in data.into_iter().enumerate() {
- for (column_number, datum) in row.into_iter().enumerate() {
- pt.insert(&[column_number, row_number], datum);
- }
+ }
+ if !data.is_empty() {
+ let mut pt = PivotTable::new([
+ (Axis3::X, Dimension::new(variables)),
+ (Axis3::Y, Dimension::new(case_numbers)),
+ ]);
+ for (row_number, row) in data.into_iter().enumerate() {
+ for (column_number, datum) in row.into_iter().enumerate() {
+ pt.insert(&[column_number, row_number], datum);
}
- output.push(Arc::new(pt.into()));
}
+ output.push(Arc::new(pt.into()));
}
Item::new(Details::Group(output))
}