894204b6b386f44b280f8070480a83ad76c147f3
[pspp] / rust / src / cooked.rs
1 use std::{
2     borrow::Cow, cell::RefCell, cmp::Ordering, collections::HashMap, iter::repeat, ops::Range,
3     rc::Rc,
4 };
5
6 use crate::{
7     dictionary::{self, Dictionary},
8     encoding::{default_encoding, get_encoding, Error as EncodingError},
9     endian::Endian,
10     format::{Error as FormatError, Spec, UncheckedSpec},
11     identifier::{Error as IdError, Identifier},
12     raw::{
13         self, LongStringMissingValueRecord, MissingValues, ProductInfoRecord, RawDocumentLine,
14         RawStr, RawString, VarDisplayRecord, VarType,
15     },
16 };
17 use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
18 use encoding_rs::{DecoderResult, Encoding};
19 use num::integer::div_ceil;
20 use ordered_float::OrderedFloat;
21 use thiserror::Error as ThisError;
22
23 pub use crate::raw::{CategoryLabels, Compression};
24
25 #[derive(ThisError, Debug)]
26 pub enum Error {
27     // XXX this is really an internal error and maybe we should change the
28     // interfaces to make it impossible
29     #[error("Missing header record")]
30     MissingHeaderRecord,
31
32     #[error("{0}")]
33     EncodingError(EncodingError),
34
35     #[error("Using default encoding {0}.")]
36     UsingDefaultEncoding(String),
37
38     #[error("Variable record from offset {:x} to {:x} specifies width {width} not in valid range [-1,255).", offsets.start, offsets.end)]
39     InvalidVariableWidth { offsets: Range<u64>, width: i32 },
40
41     #[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.")]
42     InvalidLongMissingValueFormat,
43
44     #[error("File creation date {creation_date} is not in the expected format \"DD MMM YY\" format.  Using 01 Jan 1970.")]
45     InvalidCreationDate { creation_date: String },
46
47     #[error("File creation time {creation_time} is not in the expected format \"HH:MM:SS\" format.  Using midnight.")]
48     InvalidCreationTime { creation_time: String },
49
50     #[error("{id_error}  Renaming variable to {new_name}.")]
51     InvalidVariableName {
52         id_error: IdError,
53         new_name: Identifier,
54     },
55
56     #[error(
57         "Substituting {new_spec} for invalid print format on variable {variable}.  {format_error}"
58     )]
59     InvalidPrintFormat {
60         new_spec: Spec,
61         variable: Identifier,
62         format_error: FormatError,
63     },
64
65     #[error(
66         "Substituting {new_spec} for invalid write format on variable {variable}.  {format_error}"
67     )]
68     InvalidWriteFormat {
69         new_spec: Spec,
70         variable: Identifier,
71         format_error: FormatError,
72     },
73
74     #[error("Renaming variable with duplicate name {duplicate_name} to {new_name}.")]
75     DuplicateVariableName {
76         duplicate_name: Identifier,
77         new_name: Identifier,
78     },
79
80     #[error("Dictionary index {dict_index} is outside valid range [1,{max_index}].")]
81     InvalidDictIndex { dict_index: usize, max_index: usize },
82
83     #[error("Dictionary index {0} refers to a long string continuation.")]
84     DictIndexIsContinuation(usize),
85
86     #[error("Variables associated with value label are not all of identical type.  Variable {numeric_var} is numeric, but variable {string_var} is string.")]
87     ValueLabelsDifferentTypes {
88         numeric_var: Identifier,
89         string_var: Identifier,
90     },
91
92     #[error(
93         "Value labels may not be added to long string variable {0} using record types 3 or 4."
94     )]
95     InvalidLongStringValueLabel(Identifier),
96
97     #[error("Invalid multiple response set name.  {0}")]
98     InvalidMrSetName(IdError),
99
100     #[error("Multiple response set {mr_set} includes unknown variable {short_name}.")]
101     UnknownMrSetVariable {
102         mr_set: Identifier,
103         short_name: Identifier,
104     },
105
106     #[error("Multiple response set {0} has no variables.")]
107     EmptyMrSet(Identifier),
108
109     #[error("Multiple response set {0} has only one variable.")]
110     OneVarMrSet(Identifier),
111
112     #[error("Multiple response set {0} contains both string and numeric variables.")]
113     MixedMrSet(Identifier),
114
115     #[error(
116         "Invalid numeric format for counted value {number} in multiple response set {mr_set}."
117     )]
118     InvalidMDGroupCountedValue { mr_set: Identifier, number: String },
119
120     #[error("Counted value {value} has width {width}, but it must be no wider than {max_width}, the width of the narrowest variable in multiple response set {mr_set}.")]
121     TooWideMDGroupCountedValue {
122         mr_set: Identifier,
123         value: String,
124         width: usize,
125         max_width: u16,
126     },
127
128     #[error("Long string value label for variable {name} has width {width}, which is not in the valid range [{min_width},{max_width}].")]
129     InvalidLongValueLabelWidth {
130         name: Identifier,
131         width: u32,
132         min_width: u16,
133         max_width: u16,
134     },
135
136     #[error("Invalid attribute name.  {0}")]
137     InvalidAttributeName(IdError),
138
139     #[error("Invalid short name in long variable name record.  {0}")]
140     InvalidShortName(IdError),
141
142     #[error("Invalid name in long variable name record.  {0}")]
143     InvalidLongName(IdError),
144
145     #[error("Invalid variable name in very long string record.  {0}")]
146     InvalidLongStringName(IdError),
147
148     #[error("Invalid variable name in long string value label record.  {0}")]
149     InvalidLongStringValueLabelName(IdError),
150
151     #[error("Invalid variable name in attribute record.  {0}")]
152     InvalidAttributeVariableName(IdError),
153
154     // XXX This is risky because `text` might be arbitarily long.
155     #[error("Text string contains invalid bytes for {encoding} encoding: {text}")]
156     MalformedString { encoding: String, text: String },
157
158     #[error("Details TBD")]
159     TBD,
160 }
161
162 #[derive(Clone, Debug)]
163 pub enum Record {
164     Header(HeaderRecord),
165     Variable(VariableRecord),
166     ValueLabel(ValueLabelRecord),
167     Document(DocumentRecord),
168     IntegerInfo(IntegerInfoRecord),
169     FloatInfo(FloatInfoRecord),
170     VariableSets(VariableSetRecord),
171     VarDisplay(VarDisplayRecord),
172     MultipleResponse(MultipleResponseRecord),
173     LongStringMissingValues(LongStringMissingValueRecord<String, String>),
174     LongStringValueLabels(LongStringValueLabelRecord),
175     Encoding(EncodingRecord),
176     NumberOfCases(NumberOfCasesRecord),
177     ProductInfo(ProductInfoRecord),
178     LongNames(LongNameRecord),
179     VeryLongStrings(VeryLongStringRecord),
180     FileAttributes(FileAttributeRecord),
181     VariableAttributes(VariableAttributeRecord),
182     OtherExtension(Extension),
183     //Case(Vec<Value>),
184 }
185
186 pub use crate::raw::EncodingRecord;
187 pub use crate::raw::Extension;
188 pub use crate::raw::FloatInfoRecord;
189 pub use crate::raw::IntegerInfoRecord;
190 pub use crate::raw::NumberOfCasesRecord;
191
192 type DictIndex = usize;
193
194 pub struct Variable {
195     pub dict_index: DictIndex,
196     pub short_name: Identifier,
197     pub long_name: Option<Identifier>,
198     pub width: VarWidth,
199 }
200
201 pub struct Decoder {
202     pub raw: raw::Decoder,
203     pub encoding: &'static Encoding,
204     pub variables: HashMap<DictIndex, Variable>,
205     pub var_names: HashMap<Identifier, DictIndex>,
206     pub dictionary: Dictionary,
207     n_dict_indexes: usize,
208     n_generated_names: usize,
209 }
210
211 #[derive(Default)]
212 struct Headers<'a> {
213     header: Option<raw::HeaderRecord<Cow<'a, str>>>,
214     variables: Vec<raw::VariableRecord<Cow<'a, str>, String>>,
215     value_labels: Vec<&'a raw::ValueLabelRecord<RawStr<8>, RawString>>,
216     documents: Vec<raw::DocumentRecord<Cow<'a, str>>>,
217     integer_info: Option<&'a raw::IntegerInfoRecord>,
218     float_info: Option<&'a raw::FloatInfoRecord>,
219     variable_sets: Vec<&'a raw::VariableSetRecord>,
220     var_display: Option<&'a raw::VarDisplayRecord>,
221     multiple_response: Vec<&'a raw::MultipleResponseRecord<RawString, RawString>>,
222     long_string_value_labels: Vec<&'a raw::LongStringValueLabelRecord<RawString>>,
223     long_string_missing_values: Vec<raw::LongStringMissingValueRecord<Identifier, String>>,
224     encoding: Option<&'a raw::EncodingRecord>,
225     number_of_cases: Option<&'a raw::NumberOfCasesRecord>,
226     product_info: Option<&'a raw::ProductInfoRecord>,
227     long_names: Option<&'a raw::LongNamesRecord>,
228     very_long_strings: Vec<&'a raw::VeryLongStringsRecord>,
229     file_attributes: Vec<&'a raw::FileAttributeRecord>,
230     variable_attributes: Vec<&'a raw::VariableAttributeRecord>,
231     other_extensions: Vec<&'a raw::Extension>,
232     cases: Option<&'a Rc<RefCell<raw::Cases>>>,
233 }
234
235 fn set_or_warn<T>(option: &mut Option<T>, value: T, warn: &impl Fn(Error)) {
236     if option.is_none() {
237         let _ = option.insert(value);
238     } else {
239         warn(Error::TBD);
240     }
241 }
242
243 impl<'a> Headers<'a> {
244     fn new(headers: &'a Vec<raw::Record>, decoder: &Decoder, warn: &impl Fn(Error)) -> Headers<'a> {
245         let mut h = Headers::default();
246         for header in headers {
247             match header {
248                 raw::Record::Header(record) => {
249                     set_or_warn(&mut h.header, record.decode(&decoder.raw), warn)
250                 }
251                 raw::Record::Variable(record) => h.variables.push(record.decode(&decoder.raw)),
252                 raw::Record::ValueLabel(record) => h.value_labels.push(record),
253                 raw::Record::Document(record) => h.documents.push(record.decode(&decoder.raw)),
254                 raw::Record::IntegerInfo(record) => set_or_warn(&mut h.integer_info, record, warn),
255                 raw::Record::FloatInfo(record) => set_or_warn(&mut h.float_info, record, warn),
256                 raw::Record::VariableSets(record) => h.variable_sets.push(record),
257                 raw::Record::VarDisplay(record) => set_or_warn(&mut h.var_display, record, warn),
258                 raw::Record::MultipleResponse(record) => h.multiple_response.push(record),
259                 raw::Record::LongStringValueLabels(record) => {
260                     h.long_string_value_labels.push(record)
261                 }
262                 raw::Record::LongStringMissingValues(record) => h
263                     .long_string_missing_values
264                     .push(record.decode(&decoder.raw)),
265                 raw::Record::Encoding(record) => set_or_warn(&mut h.encoding, record, warn),
266                 raw::Record::NumberOfCases(record) => {
267                     set_or_warn(&mut h.number_of_cases, record, warn)
268                 }
269                 raw::Record::ProductInfo(record) => set_or_warn(&mut h.product_info, record, warn),
270                 raw::Record::LongNames(record) => set_or_warn(&mut h.long_names, record, warn),
271                 raw::Record::VeryLongStrings(record) => h.very_long_strings.push(record),
272                 raw::Record::FileAttributes(record) => h.file_attributes.push(record),
273                 raw::Record::VariableAttributes(record) => h.variable_attributes.push(record),
274                 raw::Record::OtherExtension(record) => h.other_extensions.push(record),
275                 raw::Record::EndOfHeaders(_) => (),
276                 raw::Record::ZHeader(_) => (),
277                 raw::Record::ZTrailer(_) => (),
278                 raw::Record::Cases(record) => set_or_warn(&mut h.cases, record, warn),
279                 raw::Record::Text(_) => todo!(),
280             }
281         }
282         h
283     }
284 }
285
286 pub fn encoding_from_headers(
287     headers: &Vec<raw::Record>,
288     warn: &impl Fn(Error),
289 ) -> Result<&'static Encoding, Error> {
290     let mut encoding_record = None;
291     let mut integer_info_record = None;
292     for record in headers {
293         match record {
294             raw::Record::Encoding(record) => encoding_record = Some(record),
295             raw::Record::IntegerInfo(record) => integer_info_record = Some(record),
296             _ => (),
297         }
298     }
299     let encoding = encoding_record.map(|record| record.0.as_str());
300     let character_code = integer_info_record.map(|record| record.character_code);
301     match get_encoding(encoding, character_code) {
302         Ok(encoding) => Ok(encoding),
303         Err(err @ EncodingError::Ebcdic) => Err(Error::EncodingError(err)),
304         Err(err) => {
305             warn(Error::EncodingError(err));
306             // Warn that we're using the default encoding.
307             Ok(default_encoding())
308         }
309     }
310 }
311
312 pub fn decode(
313     headers: Vec<raw::Record>,
314     encoding: &'static Encoding,
315     warn: &impl Fn(Error),
316 ) -> Result<(Vec<Record>, Metadata), Error> {
317     let mut decoder = Decoder {
318         raw: raw::Decoder {
319             encoding,
320             warn: Box::new(|error| println!("{error}")),
321         },
322         encoding,
323         variables: HashMap::new(),
324         var_names: HashMap::new(),
325         dictionary: Dictionary::new(encoding),
326         n_dict_indexes: 0,
327         n_generated_names: 0,
328     };
329
330     let h = Headers::new(&headers, &decoder, warn);
331     let Some(header) = h.header else {
332         return Err(Error::MissingHeaderRecord);
333     };
334
335     let mut output = Vec::with_capacity(headers.len());
336
337     // Decode the records that don't use variables at all.
338     if let Some(header) = HeaderRecord::try_decode(&mut decoder, &header, warn)? {
339         output.push(Record::Header(header))
340     }
341     for document in h.documents {
342         for line in &document.lines {
343             decoder.dictionary.documents.push(line.to_string())
344         }
345     }
346     /*
347             for &raw in &h.file_attributes {
348                 let s = decoder.decode_string_cow(&raw.text.0, warn);
349                 output.push(Record::FileAttributes(FileAttributeRecord::parse(
350                     &decoder, &s, warn,
351                 )?));
352             }
353             for &raw in &h.other_extensions {
354                 output.push(Record::OtherExtension(raw.clone()));
355     }
356         */
357     // Decode the variable records, which are the basis of almost everything
358     // else.
359     for raw in &h.variables {
360         parse_variable_record(&mut decoder, raw, warn)?;
361     }
362     /*
363         // Decode value labels and weight variable.  These use indexes into the
364         // variable records, so we need to parse them before those indexes become
365         // invalidated by very long string variables.
366         for &raw in &h.value_labels {
367             if let Some(value_label) = ValueLabelRecord::try_decode(&mut decoder, raw, warn)? {
368                 output.push(Record::ValueLabel(value_label));
369             }
370         }
371         // XXX weight
372         if let Some(raw) = h.var_display {
373             output.push(Record::VarDisplay(raw.clone()));
374         }
375
376         // Decode records that use short names.
377             for &raw in &h.multiple_response {
378                 if let Some(mrr) = MultipleResponseRecord::try_decode(&mut decoder, raw, warn)? {
379                     output.push(Record::MultipleResponse(mrr))
380                 }
381             }
382         for &raw in &h.very_long_strings {
383             let s = decoder.decode_string_cow(&raw.text.0, warn);
384             output.push(Record::VeryLongStrings(VeryLongStringRecord::parse(
385                 &decoder, &s, warn,
386             )?));
387         }
388
389         // Rename variables to their long names.
390         for &raw in &h.long_names {
391             let s = decoder.decode_string_cow(&raw.text.0, warn);
392             output.push(Record::LongNames(LongNameRecord::parse(
393                 &mut decoder,
394                 &s,
395                 warn,
396             )?));
397         }
398
399         // Decode recods that use long names.
400         for &raw in &h.variable_attributes {
401             let s = decoder.decode_string_cow(&raw.text.0, warn);
402             output.push(Record::VariableAttributes(VariableAttributeRecord::parse(
403                 &decoder, &s, warn,
404             )?));
405         }
406         for &raw in &h.long_string_value_labels {
407             if let Some(mrr) = LongStringValueLabelRecord::try_decode(&mut decoder, raw, warn)? {
408                 output.push(Record::LongStringValueLabels(mrr))
409             }
410         }
411         for &raw in &h.long_string_missing_values {
412             if let Some(mrr) = LongStringMissingValuesRecord::try_decode(&mut decoder, raw, warn)? {
413                 output.push(Record::LongStringMissingValues(mrr))
414             }
415         }
416         for &raw in &h.variable_sets {
417             let s = decoder.decode_string_cow(&raw.text.0, warn);
418             output.push(Record::VariableSets(VariableSetRecord::parse(&s, warn)?));
419         }
420      */
421     let metadata = Metadata::decode(&header, h.integer_info, h.product_info, warn);
422     Ok((output, metadata))
423 }
424
425 impl Decoder {
426     fn generate_name(&mut self) -> Identifier {
427         loop {
428             self.n_generated_names += 1;
429             let name = Identifier::new(&format!("VAR{:03}", self.n_generated_names), self.encoding)
430                 .unwrap();
431             if !self.var_names.contains_key(&name) {
432                 return name;
433             }
434             assert!(self.n_generated_names < usize::MAX);
435         }
436     }
437     fn decode_string_cow<'a>(&self, input: &'a [u8], warn: &impl Fn(Error)) -> Cow<'a, str> {
438         let (output, malformed) = self.encoding.decode_without_bom_handling(input);
439         if malformed {
440             warn(Error::MalformedString {
441                 encoding: self.encoding.name().into(),
442                 text: output.clone().into(),
443             });
444         }
445         output
446     }
447     fn decode_string(&self, input: &[u8], warn: &impl Fn(Error)) -> String {
448         self.decode_string_cow(input, warn).into()
449     }
450     pub fn decode_identifier(
451         &self,
452         input: &[u8],
453         warn: &impl Fn(Error),
454     ) -> Result<Identifier, IdError> {
455         let s = self.decode_string_cow(input, warn);
456         Identifier::new(&s, self.encoding)
457     }
458     fn get_var_by_index(&self, dict_index: usize) -> Result<&Variable, Error> {
459         let max_index = self.n_dict_indexes;
460         if dict_index == 0 || dict_index > max_index {
461             return Err(Error::InvalidDictIndex {
462                 dict_index,
463                 max_index,
464             });
465         }
466         let Some(variable) = self.variables.get(&(dict_index - 1)) else {
467             return Err(Error::DictIndexIsContinuation(dict_index));
468         };
469         Ok(variable)
470     }
471
472     /// Returns `input` decoded from `self.encoding` into UTF-8 such that
473     /// re-encoding the result back into `self.encoding` will have exactly the
474     /// same length in bytes.
475     ///
476     /// XXX warn about errors?
477     fn decode_exact_length<'a>(&self, input: &'a [u8]) -> Cow<'a, str> {
478         if let (s, false) = self.encoding.decode_without_bom_handling(input) {
479             // This is the common case.  Usually there will be no errors.
480             s
481         } else {
482             // Unusual case.  Don't bother to optimize it much.
483             let mut decoder = self.encoding.new_decoder_without_bom_handling();
484             let mut output = String::with_capacity(
485                 decoder
486                     .max_utf8_buffer_length_without_replacement(input.len())
487                     .unwrap(),
488             );
489             let mut rest = input;
490             while !rest.is_empty() {
491                 match decoder.decode_to_string_without_replacement(rest, &mut output, true) {
492                     (DecoderResult::InputEmpty, _) => break,
493                     (DecoderResult::OutputFull, _) => unreachable!(),
494                     (DecoderResult::Malformed(a, b), consumed) => {
495                         let skipped = a as usize + b as usize;
496                         output.extend(repeat('?').take(skipped));
497                         rest = &rest[consumed..];
498                     }
499                 }
500             }
501             assert_eq!(self.encoding.encode(&output).0.len(), input.len());
502             output.into()
503         }
504     }
505 }
506
507 pub trait TryDecode: Sized {
508     type Input<'a>;
509     fn try_decode(
510         decoder: &mut Decoder,
511         input: &Self::Input<'_>,
512         warn: impl Fn(Error),
513     ) -> Result<Option<Self>, Error>;
514 }
515
516 pub trait Decode<Input>: Sized {
517     fn decode(decoder: &Decoder, input: &Input, warn: impl Fn(Error)) -> Self;
518 }
519
520 impl<const N: usize> Decode<RawStr<N>> for String {
521     fn decode(decoder: &Decoder, input: &RawStr<N>, warn: impl Fn(Error)) -> Self {
522         decoder.decode_string(&input.0, &warn)
523     }
524 }
525
526 #[derive(Clone, Debug)]
527 pub struct HeaderRecord {
528     pub eye_catcher: String,
529     pub weight_index: Option<usize>,
530     pub n_cases: Option<u64>,
531     pub creation: NaiveDateTime,
532     pub file_label: String,
533 }
534
535 fn trim_end_spaces(mut s: String) -> String {
536     s.truncate(s.trim_end_matches(' ').len());
537     s
538 }
539
540 /// Data file info that doesn't fit in [Dictionary].
541 pub struct Metadata {
542     creation: NaiveDateTime,
543     endian: Endian,
544     compression: Option<Compression>,
545     n_cases: Option<u64>,
546     product: String,
547     product_ext: Option<String>,
548     version: Option<(i32, i32, i32)>,
549 }
550
551 impl Metadata {
552     fn decode(
553         header: &crate::raw::HeaderRecord<Cow<str>>,
554         integer_info: Option<&IntegerInfoRecord>,
555         product_ext: Option<&ProductInfoRecord>,
556         warn: impl Fn(Error),
557     ) -> Self {
558         let creation_date = NaiveDate::parse_from_str(&header.creation_date, "%e %b %Y")
559             .unwrap_or_else(|_| {
560                 warn(Error::InvalidCreationDate {
561                     creation_date: header.creation_date.to_string(),
562                 });
563                 Default::default()
564             });
565         let creation_time = NaiveTime::parse_from_str(&header.creation_time, "%H:%M:%S")
566             .unwrap_or_else(|_| {
567                 warn(Error::InvalidCreationTime {
568                     creation_time: header.creation_time.to_string(),
569                 });
570                 Default::default()
571             });
572         let creation = NaiveDateTime::new(creation_date, creation_time);
573
574         let product = header
575             .eye_catcher
576             .trim_start_matches("@(#) SPSS DATA FILE")
577             .trim_end()
578             .to_string();
579
580         Self {
581             creation,
582             endian: header.endian,
583             compression: header.compression,
584             n_cases: header.n_cases.map(|n| n as u64),
585             product,
586             product_ext: product_ext.map(|pe| pe.0.clone()),
587             version: integer_info.map(|ii| ii.version),
588         }
589     }
590 }
591
592 impl TryDecode for HeaderRecord {
593     type Input<'a> = crate::raw::HeaderRecord<Cow<'a, str>>;
594
595     fn try_decode(
596         _decoder: &mut Decoder,
597         input: &Self::Input<'_>,
598         warn: impl Fn(Error),
599     ) -> Result<Option<Self>, Error> {
600         let eye_catcher = trim_end_spaces(input.eye_catcher.to_string());
601         let file_label = trim_end_spaces(input.file_label.to_string());
602         let creation_date = NaiveDate::parse_from_str(&input.creation_date, "%e %b %Y")
603             .unwrap_or_else(|_| {
604                 warn(Error::InvalidCreationDate {
605                     creation_date: input.creation_date.to_string(),
606                 });
607                 Default::default()
608             });
609         let creation_time = NaiveTime::parse_from_str(&input.creation_time, "%H:%M:%S")
610             .unwrap_or_else(|_| {
611                 warn(Error::InvalidCreationTime {
612                     creation_time: input.creation_time.to_string(),
613                 });
614                 Default::default()
615             });
616         Ok(Some(HeaderRecord {
617             eye_catcher,
618             weight_index: input.weight_index.map(|n| n as usize),
619             n_cases: input.n_cases.map(|n| n as u64),
620             creation: NaiveDateTime::new(creation_date, creation_time),
621             file_label,
622         }))
623     }
624 }
625
626 #[derive(Clone, Debug)]
627 pub struct VariableRecord {
628     pub width: VarWidth,
629     pub name: Identifier,
630     pub print_format: Spec,
631     pub write_format: Spec,
632     pub missing_values: MissingValues<String>,
633     pub label: Option<String>,
634 }
635
636 fn decode_format(raw: raw::Spec, width: VarWidth, warn: impl Fn(Spec, FormatError)) -> Spec {
637     UncheckedSpec::try_from(raw)
638         .and_then(Spec::try_from)
639         .and_then(|x| x.check_width_compatibility(width))
640         .unwrap_or_else(|error| {
641             let new_format = Spec::default_for_width(width);
642             warn(new_format, error);
643             new_format
644         })
645 }
646
647 fn parse_variable_record(
648     decoder: &mut Decoder,
649     input: &raw::VariableRecord<Cow<str>, String>,
650     warn: impl Fn(Error),
651 ) -> Result<(), Error> {
652     let width = match input.width {
653         0 => VarWidth::Numeric,
654         w @ 1..=255 => VarWidth::String(w as u16),
655         -1 => return Ok(()),
656         _ => {
657             return Err(Error::InvalidVariableWidth {
658                 offsets: input.offsets.clone(),
659                 width: input.width,
660             })
661         }
662     };
663     let name = trim_end_spaces(input.name.to_string());
664     let name = match Identifier::new(&name, decoder.encoding) {
665         Ok(name) => {
666             if !decoder.var_names.contains_key(&name) {
667                 name
668             } else {
669                 let new_name = decoder.generate_name();
670                 warn(Error::DuplicateVariableName {
671                     duplicate_name: name.clone(),
672                     new_name: new_name.clone(),
673                 });
674                 new_name
675             }
676         }
677         Err(id_error) => {
678             let new_name = decoder.generate_name();
679             warn(Error::InvalidVariableName {
680                 id_error,
681                 new_name: new_name.clone(),
682             });
683             new_name
684         }
685     };
686     let variable = Variable {
687         dict_index: decoder.n_dict_indexes,
688         short_name: name.clone(),
689         long_name: None,
690         width,
691     };
692     decoder.n_dict_indexes += width.n_dict_indexes();
693     assert!(decoder
694         .var_names
695         .insert(name.clone(), variable.dict_index)
696         .is_none());
697     assert!(decoder
698         .variables
699         .insert(variable.dict_index, variable)
700         .is_none());
701
702     let print_format = decode_format(input.print_format, width, |new_spec, format_error| {
703         warn(Error::InvalidPrintFormat {
704             new_spec,
705             variable: name.clone(),
706             format_error,
707         })
708     });
709     let write_format = decode_format(input.write_format, width, |new_spec, format_error| {
710         warn(Error::InvalidWriteFormat {
711             new_spec,
712             variable: name.clone(),
713             format_error,
714         })
715     });
716     let mut variable = dictionary::Variable::new(name, width);
717     variable.print_format = print_format;
718     variable.write_format = write_format;
719     variable.missing_values = input.missing_values.clone();
720     if let Some(ref label) = input.label {
721         variable.label = Some(label.to_string());
722     }
723     decoder.dictionary.add_var(variable).unwrap();
724     Ok(())
725 }
726
727 #[derive(Clone, Debug)]
728 pub struct DocumentRecord(Vec<String>);
729
730 impl TryDecode for DocumentRecord {
731     type Input<'a> = crate::raw::DocumentRecord<RawDocumentLine>;
732
733     fn try_decode(
734         decoder: &mut Decoder,
735         input: &Self::Input<'_>,
736         warn: impl Fn(Error),
737     ) -> Result<Option<Self>, Error> {
738         Ok(Some(DocumentRecord(
739             input
740                 .lines
741                 .iter()
742                 .map(|s| trim_end_spaces(decoder.decode_string(&s.0, &warn)))
743                 .collect(),
744         )))
745     }
746 }
747
748 trait TextRecord
749 where
750     Self: Sized,
751 {
752     const NAME: &'static str;
753     fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error>;
754 }
755
756 #[derive(Clone, Debug)]
757 pub struct VariableSet {
758     pub name: String,
759     pub vars: Vec<String>,
760 }
761
762 impl VariableSet {
763     fn parse(input: &str) -> Result<Self, Error> {
764         let (name, input) = input.split_once('=').ok_or(Error::TBD)?;
765         let vars = input.split_ascii_whitespace().map(String::from).collect();
766         Ok(VariableSet {
767             name: name.into(),
768             vars,
769         })
770     }
771 }
772
773 trait WarnOnError<T> {
774     fn warn_on_error<F: Fn(Error)>(self, warn: &F) -> Option<T>;
775 }
776 impl<T> WarnOnError<T> for Result<T, Error> {
777     fn warn_on_error<F: Fn(Error)>(self, warn: &F) -> Option<T> {
778         match self {
779             Ok(result) => Some(result),
780             Err(error) => {
781                 warn(error);
782                 None
783             }
784         }
785     }
786 }
787
788 #[derive(Clone, Debug)]
789 pub struct ValueLabel {
790     pub value: Value,
791     pub label: String,
792 }
793
794 #[derive(Clone, Debug)]
795 pub struct ValueLabelRecord {
796     pub var_type: VarType,
797     pub labels: Vec<ValueLabel>,
798     pub variables: Vec<Identifier>,
799 }
800
801 impl TryDecode for ValueLabelRecord {
802     type Input<'a> = crate::raw::ValueLabelRecord<RawStr<8>, RawString>;
803     fn try_decode(
804         decoder: &mut Decoder,
805         input: &Self::Input<'_>,
806         warn: impl Fn(Error),
807     ) -> Result<Option<ValueLabelRecord>, Error> {
808         let variables: Vec<&Variable> = input
809             .dict_indexes
810             .iter()
811             .filter_map(|&dict_index| {
812                 decoder
813                     .get_var_by_index(dict_index as usize)
814                     .warn_on_error(&warn)
815             })
816             .filter(|&variable| match variable.width {
817                 VarWidth::String(width) if width > 8 => {
818                     warn(Error::InvalidLongStringValueLabel(
819                         variable.short_name.clone(),
820                     ));
821                     false
822                 }
823                 _ => true,
824             })
825             .collect();
826         let mut i = variables.iter();
827         let Some(&first_var) = i.next() else {
828             return Ok(None);
829         };
830         let var_type: VarType = first_var.width.into();
831         for &variable in i {
832             let this_type: VarType = variable.width.into();
833             if var_type != this_type {
834                 let (numeric_var, string_var) = match var_type {
835                     VarType::Numeric => (first_var, variable),
836                     VarType::String => (variable, first_var),
837                 };
838                 warn(Error::ValueLabelsDifferentTypes {
839                     numeric_var: numeric_var.short_name.clone(),
840                     string_var: string_var.short_name.clone(),
841                 });
842                 return Ok(None);
843             }
844         }
845         let labels = input
846             .labels
847             .iter()
848             .map(|raw::ValueLabel { value, label }| {
849                 let label = decoder.decode_string(&label.0, &warn);
850                 let value = Value::decode(value, decoder);
851                 ValueLabel { value, label }
852             })
853             .collect();
854         let variables = variables
855             .iter()
856             .map(|&variable| variable.short_name.clone())
857             .collect();
858         Ok(Some(ValueLabelRecord {
859             var_type,
860             labels,
861             variables,
862         }))
863     }
864 }
865
866 #[derive(Clone, Debug)]
867 pub struct VariableSetRecord(Vec<VariableSet>);
868
869 impl TextRecord for VariableSetRecord {
870     const NAME: &'static str = "variable set";
871     fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
872         let mut sets = Vec::new();
873         for line in input.lines() {
874             if let Some(set) = VariableSet::parse(line).warn_on_error(&warn) {
875                 sets.push(set)
876             }
877         }
878         Ok(VariableSetRecord(sets))
879     }
880 }
881
882 #[derive(Clone, Debug)]
883 pub struct LongName {
884     pub short_name: Identifier,
885     pub long_name: Identifier,
886 }
887
888 impl LongName {
889     fn new(decoder: &mut Decoder, short_name: &str, long_name: &str) -> Result<LongName, Error> {
890         let short_name =
891             Identifier::new(short_name, decoder.encoding).map_err(Error::InvalidShortName)?;
892         let long_name =
893             Identifier::new(long_name, decoder.encoding).map_err(Error::InvalidLongName)?;
894         Ok(LongName {
895             short_name,
896             long_name,
897         })
898     }
899 }
900
901 #[derive(Clone, Debug)]
902 pub struct LongNameRecord(Vec<LongName>);
903
904 impl LongNameRecord {
905     pub fn parse(decoder: &mut Decoder, input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
906         let mut names = Vec::new();
907         for pair in input.split('\t').filter(|s| !s.is_empty()) {
908             if let Some((short_name, long_name)) = pair.split_once('=') {
909                 if let Some(long_name) =
910                     LongName::new(decoder, short_name, long_name).warn_on_error(&warn)
911                 {
912                     names.push(long_name);
913                 }
914             } else {
915                 warn(Error::TBD)
916             }
917         }
918         Ok(LongNameRecord(names))
919     }
920 }
921
922 #[derive(Clone, Debug)]
923 pub struct VeryLongString {
924     pub short_name: Identifier,
925     pub length: u16,
926 }
927
928 impl VeryLongString {
929     fn parse(decoder: &Decoder, input: &str) -> Result<VeryLongString, Error> {
930         let Some((short_name, length)) = input.split_once('=') else {
931             return Err(Error::TBD);
932         };
933         let short_name =
934             Identifier::new(short_name, decoder.encoding).map_err(Error::InvalidLongStringName)?;
935         let length: u16 = length.parse().map_err(|_| Error::TBD)?;
936         if length > VarWidth::MAX_STRING {
937             return Err(Error::TBD);
938         }
939         Ok(VeryLongString { short_name, length })
940     }
941 }
942
943 #[derive(Clone, Debug)]
944 pub struct VeryLongStringRecord(Vec<VeryLongString>);
945
946 impl VeryLongStringRecord {
947     pub fn parse(decoder: &Decoder, input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
948         let mut very_long_strings = Vec::new();
949         for tuple in input
950             .split('\0')
951             .map(|s| s.trim_end_matches('\t'))
952             .filter(|s| !s.is_empty())
953         {
954             if let Some(vls) = VeryLongString::parse(decoder, tuple).warn_on_error(&warn) {
955                 very_long_strings.push(vls)
956             }
957         }
958         Ok(VeryLongStringRecord(very_long_strings))
959     }
960 }
961
962 #[derive(Clone, Debug)]
963 pub struct Attribute {
964     pub name: Identifier,
965     pub values: Vec<String>,
966 }
967
968 impl Attribute {
969     fn parse<'a>(
970         decoder: &Decoder,
971         input: &'a str,
972         warn: &impl Fn(Error),
973     ) -> Result<(Option<Attribute>, &'a str), Error> {
974         let Some((name, mut input)) = input.split_once('(') else {
975             return Err(Error::TBD);
976         };
977         let mut values = Vec::new();
978         loop {
979             let Some((value, rest)) = input.split_once('\n') else {
980                 return Err(Error::TBD);
981             };
982             if let Some(stripped) = value
983                 .strip_prefix('\'')
984                 .and_then(|value| value.strip_suffix('\''))
985             {
986                 values.push(stripped.into());
987             } else {
988                 warn(Error::TBD);
989                 values.push(value.into());
990             }
991             if let Some(rest) = rest.strip_prefix(')') {
992                 let attribute = Identifier::new(name, decoder.encoding)
993                     .map_err(Error::InvalidAttributeName)
994                     .warn_on_error(warn)
995                     .map(|name| Attribute { name, values });
996                 return Ok((attribute, rest));
997             };
998             input = rest;
999         }
1000     }
1001 }
1002
1003 #[derive(Clone, Debug)]
1004 pub struct AttributeSet(pub Vec<Attribute>);
1005
1006 impl AttributeSet {
1007     fn parse<'a>(
1008         decoder: &Decoder,
1009         mut input: &'a str,
1010         sentinel: Option<char>,
1011         warn: &impl Fn(Error),
1012     ) -> Result<(AttributeSet, &'a str), Error> {
1013         let mut attributes = Vec::new();
1014         let rest = loop {
1015             match input.chars().next() {
1016                 None => break input,
1017                 c if c == sentinel => break &input[1..],
1018                 _ => {
1019                     let (attribute, rest) = Attribute::parse(decoder, input, &warn)?;
1020                     if let Some(attribute) = attribute {
1021                         attributes.push(attribute);
1022                     }
1023                     input = rest;
1024                 }
1025             }
1026         };
1027         Ok((AttributeSet(attributes), rest))
1028     }
1029 }
1030
1031 #[derive(Clone, Debug)]
1032 pub struct FileAttributeRecord(AttributeSet);
1033
1034 impl FileAttributeRecord {
1035     pub fn parse(decoder: &Decoder, input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
1036         let (set, rest) = AttributeSet::parse(decoder, input, None, &warn)?;
1037         if !rest.is_empty() {
1038             warn(Error::TBD);
1039         }
1040         Ok(FileAttributeRecord(set))
1041     }
1042 }
1043
1044 #[derive(Clone, Debug)]
1045 pub struct VarAttributeSet {
1046     pub long_var_name: Identifier,
1047     pub attributes: AttributeSet,
1048 }
1049
1050 impl VarAttributeSet {
1051     fn parse<'a>(
1052         decoder: &Decoder,
1053         input: &'a str,
1054         warn: &impl Fn(Error),
1055     ) -> Result<(Option<VarAttributeSet>, &'a str), Error> {
1056         let Some((long_var_name, rest)) = input.split_once(':') else {
1057             return Err(Error::TBD);
1058         };
1059         let (attributes, rest) = AttributeSet::parse(decoder, rest, Some('/'), warn)?;
1060         let var_attribute = Identifier::new(long_var_name, decoder.encoding)
1061             .map_err(Error::InvalidAttributeVariableName)
1062             .warn_on_error(warn)
1063             .map(|name| VarAttributeSet {
1064                 long_var_name: name,
1065                 attributes,
1066             });
1067         Ok((var_attribute, rest))
1068     }
1069 }
1070
1071 #[derive(Clone, Debug)]
1072 pub struct VariableAttributeRecord(Vec<VarAttributeSet>);
1073
1074 impl VariableAttributeRecord {
1075     pub fn parse(decoder: &Decoder, mut input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
1076         let mut var_attribute_sets = Vec::new();
1077         while !input.is_empty() {
1078             let Some((var_attribute, rest)) =
1079                 VarAttributeSet::parse(decoder, input, &warn).warn_on_error(&warn)
1080             else {
1081                 break;
1082             };
1083             if let Some(var_attribute) = var_attribute {
1084                 var_attribute_sets.push(var_attribute);
1085             }
1086             input = rest;
1087         }
1088         Ok(VariableAttributeRecord(var_attribute_sets))
1089     }
1090 }
1091
1092 #[derive(Clone, Debug)]
1093 pub enum MultipleResponseType {
1094     MultipleDichotomy {
1095         value: Value,
1096         labels: CategoryLabels,
1097     },
1098     MultipleCategory,
1099 }
1100
1101 impl MultipleResponseType {
1102     fn decode(
1103         decoder: &Decoder,
1104         mr_set: &Identifier,
1105         input: &raw::MultipleResponseType,
1106         min_width: VarWidth,
1107         warn: &impl Fn(Error),
1108     ) -> Result<Self, Error> {
1109         let mr_type = match input {
1110             raw::MultipleResponseType::MultipleDichotomy { value, labels } => {
1111                 let value = decoder.decode_string_cow(&value.0, warn);
1112                 let value = match min_width {
1113                     VarWidth::Numeric => {
1114                         let number: f64 = value.trim().parse().map_err(|_| {
1115                             Error::InvalidMDGroupCountedValue {
1116                                 mr_set: mr_set.clone(),
1117                                 number: value.into(),
1118                             }
1119                         })?;
1120                         Value::Number(Some(number.into()))
1121                     }
1122                     VarWidth::String(max_width) => {
1123                         let value = value.trim_end_matches(' ');
1124                         let width = value.len();
1125                         if width > max_width as usize {
1126                             return Err(Error::TooWideMDGroupCountedValue {
1127                                 mr_set: mr_set.clone(),
1128                                 value: value.into(),
1129                                 width,
1130                                 max_width,
1131                             });
1132                         };
1133                         Value::String(value.into())
1134                     }
1135                 };
1136                 MultipleResponseType::MultipleDichotomy {
1137                     value,
1138                     labels: *labels,
1139                 }
1140             }
1141             raw::MultipleResponseType::MultipleCategory => MultipleResponseType::MultipleCategory,
1142         };
1143         Ok(mr_type)
1144     }
1145 }
1146
1147 #[derive(Clone, Debug)]
1148 pub struct MultipleResponseSet {
1149     pub name: Identifier,
1150     pub min_width: VarWidth,
1151     pub max_width: VarWidth,
1152     pub label: String,
1153     pub mr_type: MultipleResponseType,
1154     pub dict_indexes: Vec<DictIndex>,
1155 }
1156
1157 impl MultipleResponseSet {
1158     fn decode(
1159         decoder: &Decoder,
1160         input: &raw::MultipleResponseSet<Identifier, Cow<str>>,
1161         warn: &impl Fn(Error),
1162     ) -> Result<Self, Error> {
1163         let mr_set_name = input.name.clone();
1164         let mut dict_indexes = Vec::with_capacity(input.short_names.len());
1165         for short_name in input.short_names.iter() {
1166             let Some(&dict_index) = decoder.var_names.get(&short_name) else {
1167                 warn(Error::UnknownMrSetVariable {
1168                     mr_set: mr_set_name.clone(),
1169                     short_name: short_name.clone(),
1170                 });
1171                 continue;
1172             };
1173             dict_indexes.push(dict_index);
1174         }
1175
1176         match dict_indexes.len() {
1177             0 => return Err(Error::EmptyMrSet(mr_set_name)),
1178             1 => return Err(Error::OneVarMrSet(mr_set_name)),
1179             _ => (),
1180         }
1181
1182         let Some((Some(min_width), Some(max_width))) = dict_indexes
1183             .iter()
1184             .map(|dict_index| decoder.variables[dict_index].width)
1185             .map(|w| (Some(w), Some(w)))
1186             .reduce(|(na, wa), (nb, wb)| (VarWidth::narrower(na, nb), VarWidth::wider(wa, wb)))
1187         else {
1188             return Err(Error::MixedMrSet(mr_set_name));
1189         };
1190
1191         let mr_type =
1192             MultipleResponseType::decode(decoder, &mr_set_name, &input.mr_type, min_width, warn)?;
1193
1194         Ok(MultipleResponseSet {
1195             name: mr_set_name,
1196             min_width,
1197             max_width,
1198             label: input.label.to_string(),
1199             mr_type,
1200             dict_indexes,
1201         })
1202     }
1203 }
1204
1205 #[derive(Clone, Debug)]
1206 pub struct MultipleResponseRecord(pub Vec<MultipleResponseSet>);
1207
1208 impl TryDecode for MultipleResponseRecord {
1209     type Input<'a> = raw::MultipleResponseRecord<Identifier, Cow<'a, str>>;
1210
1211     fn try_decode(
1212         decoder: &mut Decoder,
1213         input: &Self::Input<'_>,
1214         warn: impl Fn(Error),
1215     ) -> Result<Option<Self>, Error> {
1216         let mut sets = Vec::with_capacity(input.0.len());
1217         for set in &input.0 {
1218             match MultipleResponseSet::decode(decoder, set, &warn) {
1219                 Ok(set) => sets.push(set),
1220                 Err(error) => warn(error),
1221             }
1222         }
1223         Ok(Some(MultipleResponseRecord(sets)))
1224     }
1225 }
1226
1227 #[derive(Clone, Debug)]
1228 pub struct LongStringValueLabels {
1229     pub var_name: Identifier,
1230     pub width: VarWidth,
1231     pub labels: Vec<ValueLabel>,
1232 }
1233
1234 impl LongStringValueLabels {
1235     fn decode(
1236         decoder: &Decoder,
1237         input: &raw::LongStringValueLabels<RawString>,
1238         warn: &impl Fn(Error),
1239     ) -> Result<Self, Error> {
1240         let var_name = decoder.decode_string(&input.var_name.0, warn);
1241         let var_name = Identifier::new(var_name.trim_end(), decoder.encoding)
1242             .map_err(Error::InvalidLongStringValueLabelName)?;
1243
1244         let min_width = 9;
1245         let max_width = VarWidth::MAX_STRING;
1246         if input.width < 9 || input.width > max_width as u32 {
1247             return Err(Error::InvalidLongValueLabelWidth {
1248                 name: var_name,
1249                 width: input.width,
1250                 min_width,
1251                 max_width,
1252             });
1253         }
1254         let width = input.width as u16;
1255
1256         let mut labels = Vec::with_capacity(input.labels.len());
1257         for (value, label) in input.labels.iter() {
1258             let value = Value::String(decoder.decode_exact_length(&value.0).into());
1259             let label = decoder.decode_string(&label.0, warn);
1260             labels.push(ValueLabel { value, label });
1261         }
1262
1263         Ok(LongStringValueLabels {
1264             var_name,
1265             width: VarWidth::String(width),
1266             labels,
1267         })
1268     }
1269 }
1270
1271 #[derive(Clone, Debug)]
1272 pub struct LongStringValueLabelRecord(pub Vec<LongStringValueLabels>);
1273
1274 impl TryDecode for LongStringValueLabelRecord {
1275     type Input<'a> = raw::LongStringValueLabelRecord<RawString>;
1276
1277     fn try_decode(
1278         decoder: &mut Decoder,
1279         input: &Self::Input<'_>,
1280         warn: impl Fn(Error),
1281     ) -> Result<Option<Self>, Error> {
1282         let mut labels = Vec::with_capacity(input.0.len());
1283         for label in &input.0 {
1284             match LongStringValueLabels::decode(decoder, label, &warn) {
1285                 Ok(set) => labels.push(set),
1286                 Err(error) => warn(error),
1287             }
1288         }
1289         Ok(Some(LongStringValueLabelRecord(labels)))
1290     }
1291 }
1292
1293 #[cfg(test)]
1294 mod test {
1295     use encoding_rs::WINDOWS_1252;
1296
1297     #[test]
1298     fn test() {
1299         let mut s = String::new();
1300         s.push(char::REPLACEMENT_CHARACTER);
1301         let encoded = WINDOWS_1252.encode(&s).0;
1302         let decoded = WINDOWS_1252.decode(&encoded[..]).0;
1303         println!("{:?}", decoded);
1304     }
1305
1306     #[test]
1307     fn test2() {
1308         let charset: Vec<u8> = (0..=255).collect();
1309         println!("{}", charset.len());
1310         let decoded = WINDOWS_1252.decode(&charset[..]).0;
1311         println!("{}", decoded.len());
1312         let encoded = WINDOWS_1252.encode(&decoded[..]).0;
1313         println!("{}", encoded.len());
1314         assert_eq!(&charset[..], &encoded[..]);
1315     }
1316 }