1 use std::{borrow::Cow, collections::HashSet};
3 use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
4 use encoding_rs::Encoding;
8 identifier::{Identifier, Error as IdError},
9 {endian::Endian, CategoryLabels, Compression},
11 use thiserror::Error as ThisError;
13 #[derive(ThisError, Debug)]
15 #[error("Variable record at offset {offset:#x} specifies width {width} not in valid range [-1,255).")]
16 BadVariableWidth { offset: u64, width: i32 },
18 #[error("This file has corrupted metadata written by a buggy version of PSPP. To ensure that other software can read it correctly, save a new copy of the file.")]
19 BadLongMissingValueFormat,
21 #[error("File creation date {creation_date} is not in the expected format \"DD MMM YY\" format. Using 01 Jan 1970.")]
22 InvalidCreationDate { creation_date: String },
24 #[error("File creation time {creation_time} is not in the expected format \"HH:MM:SS\" format. Using midnight.")]
25 InvalidCreationTime { creation_time: String },
27 #[error("Invalid variable name: {0}")]
28 BadIdentifier(#[from] IdError),
30 #[error("Details TBD")]
35 pub compression: Option<Compression>,
37 pub encoding: &'static Encoding,
38 pub var_names: HashSet<Identifier>,
39 n_generated_names: usize,
43 fn take_name(&mut self, id: Identifier) -> bool {
44 self.var_names.insert(id)
46 fn generate_name(&mut self) -> Identifier {
48 self.n_generated_names += 1;
49 let name = Identifier::new(&format!("VAR{:03}", self.n_generated_names), self.encoding).unwrap();
50 if self.take_name(name.clone()) {
53 assert!(self.n_generated_names < usize::MAX);
56 fn decode_string<'a>(&self, input: &'a [u8], warn: &impl Fn(Error)) -> Cow<'a, str> {
57 let (output, malformed) = self.encoding.decode_without_bom_handling(input);
65 pub trait Decode: Sized {
67 fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Result<Self, Error>;
72 pub eye_catcher: String,
73 pub weight_index: Option<usize>,
74 pub n_cases: Option<u64>,
75 pub creation: NaiveDateTime,
76 pub file_label: String,
79 impl Decode for Header {
80 type Input = crate::raw::Header;
82 fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Result<Self, Error> {
83 let eye_catcher = decoder.decode_string(&input.eye_catcher, &warn);
84 let file_label = decoder.decode_string(&input.file_label, &warn);
85 let creation_date = decoder.decode_string(&input.creation_date, &warn);
86 let creation_date = NaiveDate::parse_from_str(&creation_date, "%v").unwrap_or_else(|_| {
87 warn(Error::InvalidCreationDate {
88 creation_date: creation_date.into(),
92 let creation_time = decoder.decode_string(&input.creation_time, &warn);
94 NaiveTime::parse_from_str(&creation_time, "%H:%M:%S").unwrap_or_else(|_| {
95 warn(Error::InvalidCreationTime {
96 creation_time: creation_time.into(),
101 eye_catcher: eye_catcher.into(),
102 weight_index: input.weight_index.map(|n| n as usize),
103 n_cases: input.n_cases.map(|n| n as u64),
104 creation: NaiveDateTime::new(creation_date, creation_time),
105 file_label: file_label.into(),
110 pub struct Variable {
112 pub name: Identifier,
113 pub print_format: Spec,
114 pub write_format: Spec,
118 decoder: &mut Decoder,
119 input: &crate::raw::Variable,
120 warn: impl Fn(Error),
121 ) -> Result<Option<Variable>, Error> {
124 -1 => return Ok(None),
126 return Err(Error::BadVariableWidth {
127 offset: input.offset,
132 let name = decoder.decode_string(&input.name, &warn);
133 let name = match Identifier::new(&name, decoder.encoding) {
135 if !decoder.take_name(name) {
136 decoder.generate_name()
143 decoder.generate_name()
149 pub struct Document(Vec<String>);
151 impl Decode for Document {
152 type Input = crate::raw::Document;
154 fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Result<Self, Error> {
159 .map(|s| decoder.decode_string(s, &warn).into())
165 pub use crate::raw::FloatInfo;
166 pub use crate::raw::IntegerInfo;
168 #[derive(Clone, Debug)]
169 pub enum MultipleResponseType {
172 labels: CategoryLabels,
176 #[derive(Clone, Debug)]
177 pub struct MultipleResponseSet {
180 pub mr_type: MultipleResponseType,
181 pub vars: Vec<String>,
184 #[derive(Clone, Debug)]
185 pub struct MultipleResponseRecord(Vec<MultipleResponseSet>);
187 #[derive(Clone, Debug)]
188 pub struct ProductInfo(String);
202 pub struct VarDisplay {
203 pub measure: Option<Measure>,
205 pub align: Option<Alignment>,
208 pub struct VarDisplayRecord(pub Vec<VarDisplay>);