1cb878f120d6768cff919e86c60c368a9c8347a5
[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
287 pub fn decode(
288     headers: Vec<raw::Record>,
289     encoding: &'static Encoding,
290     warn: &impl Fn(Error),
291 ) -> Result<(Vec<Record>, Metadata), Error> {
292     let mut decoder = Decoder {
293         raw: raw::Decoder {
294             encoding,
295             warn: Box::new(|error| println!("{error}")),
296         },
297         encoding,
298         variables: HashMap::new(),
299         var_names: HashMap::new(),
300         dictionary: Dictionary::new(encoding),
301         n_dict_indexes: 0,
302         n_generated_names: 0,
303     };
304
305     let h = Headers::new(&headers, &decoder, warn);
306     let Some(header) = h.header else {
307         return Err(Error::MissingHeaderRecord);
308     };
309
310     let mut output = Vec::with_capacity(headers.len());
311
312     // Decode the records that don't use variables at all.
313     if let Some(header) = HeaderRecord::try_decode(&mut decoder, &header, warn)? {
314         output.push(Record::Header(header))
315     }
316     for document in h.documents {
317         for line in &document.lines {
318             decoder.dictionary.documents.push(line.to_string())
319         }
320     }
321     /*
322             for &raw in &h.file_attributes {
323                 let s = decoder.decode_string_cow(&raw.text.0, warn);
324                 output.push(Record::FileAttributes(FileAttributeRecord::parse(
325                     &decoder, &s, warn,
326                 )?));
327             }
328             for &raw in &h.other_extensions {
329                 output.push(Record::OtherExtension(raw.clone()));
330     }
331         */
332     // Decode the variable records, which are the basis of almost everything
333     // else.
334     for raw in &h.variables {
335         parse_variable_record(&mut decoder, raw, warn)?;
336     }
337     /*
338         // Decode value labels and weight variable.  These use indexes into the
339         // variable records, so we need to parse them before those indexes become
340         // invalidated by very long string variables.
341         for &raw in &h.value_labels {
342             if let Some(value_label) = ValueLabelRecord::try_decode(&mut decoder, raw, warn)? {
343                 output.push(Record::ValueLabel(value_label));
344             }
345         }
346         // XXX weight
347         if let Some(raw) = h.var_display {
348             output.push(Record::VarDisplay(raw.clone()));
349         }
350
351         // Decode records that use short names.
352             for &raw in &h.multiple_response {
353                 if let Some(mrr) = MultipleResponseRecord::try_decode(&mut decoder, raw, warn)? {
354                     output.push(Record::MultipleResponse(mrr))
355                 }
356             }
357         for &raw in &h.very_long_strings {
358             let s = decoder.decode_string_cow(&raw.text.0, warn);
359             output.push(Record::VeryLongStrings(VeryLongStringRecord::parse(
360                 &decoder, &s, warn,
361             )?));
362         }
363
364         // Rename variables to their long names.
365         for &raw in &h.long_names {
366             let s = decoder.decode_string_cow(&raw.text.0, warn);
367             output.push(Record::LongNames(LongNameRecord::parse(
368                 &mut decoder,
369                 &s,
370                 warn,
371             )?));
372         }
373
374         // Decode recods that use long names.
375         for &raw in &h.variable_attributes {
376             let s = decoder.decode_string_cow(&raw.text.0, warn);
377             output.push(Record::VariableAttributes(VariableAttributeRecord::parse(
378                 &decoder, &s, warn,
379             )?));
380         }
381         for &raw in &h.long_string_value_labels {
382             if let Some(mrr) = LongStringValueLabelRecord::try_decode(&mut decoder, raw, warn)? {
383                 output.push(Record::LongStringValueLabels(mrr))
384             }
385         }
386         for &raw in &h.long_string_missing_values {
387             if let Some(mrr) = LongStringMissingValuesRecord::try_decode(&mut decoder, raw, warn)? {
388                 output.push(Record::LongStringMissingValues(mrr))
389             }
390         }
391         for &raw in &h.variable_sets {
392             let s = decoder.decode_string_cow(&raw.text.0, warn);
393             output.push(Record::VariableSets(VariableSetRecord::parse(&s, warn)?));
394         }
395      */
396     let metadata = Metadata::decode(&header, h.integer_info, h.product_info, warn);
397     Ok((output, metadata))
398 }
399
400 impl Decoder {
401     fn generate_name(&mut self) -> Identifier {
402         loop {
403             self.n_generated_names += 1;
404             let name = Identifier::new(&format!("VAR{:03}", self.n_generated_names), self.encoding)
405                 .unwrap();
406             if !self.var_names.contains_key(&name) {
407                 return name;
408             }
409             assert!(self.n_generated_names < usize::MAX);
410         }
411     }
412     fn decode_string_cow<'a>(&self, input: &'a [u8], warn: &impl Fn(Error)) -> Cow<'a, str> {
413         let (output, malformed) = self.encoding.decode_without_bom_handling(input);
414         if malformed {
415             warn(Error::MalformedString {
416                 encoding: self.encoding.name().into(),
417                 text: output.clone().into(),
418             });
419         }
420         output
421     }
422     fn decode_string(&self, input: &[u8], warn: &impl Fn(Error)) -> String {
423         self.decode_string_cow(input, warn).into()
424     }
425     pub fn decode_identifier(
426         &self,
427         input: &[u8],
428         warn: &impl Fn(Error),
429     ) -> Result<Identifier, IdError> {
430         let s = self.decode_string_cow(input, warn);
431         Identifier::new(&s, self.encoding)
432     }
433     fn get_var_by_index(&self, dict_index: usize) -> Result<&Variable, Error> {
434         let max_index = self.n_dict_indexes;
435         if dict_index == 0 || dict_index > max_index {
436             return Err(Error::InvalidDictIndex {
437                 dict_index,
438                 max_index,
439             });
440         }
441         let Some(variable) = self.variables.get(&(dict_index - 1)) else {
442             return Err(Error::DictIndexIsContinuation(dict_index));
443         };
444         Ok(variable)
445     }
446
447     /// Returns `input` decoded from `self.encoding` into UTF-8 such that
448     /// re-encoding the result back into `self.encoding` will have exactly the
449     /// same length in bytes.
450     ///
451     /// XXX warn about errors?
452     fn decode_exact_length<'a>(&self, input: &'a [u8]) -> Cow<'a, str> {
453         if let (s, false) = self.encoding.decode_without_bom_handling(input) {
454             // This is the common case.  Usually there will be no errors.
455             s
456         } else {
457             // Unusual case.  Don't bother to optimize it much.
458             let mut decoder = self.encoding.new_decoder_without_bom_handling();
459             let mut output = String::with_capacity(
460                 decoder
461                     .max_utf8_buffer_length_without_replacement(input.len())
462                     .unwrap(),
463             );
464             let mut rest = input;
465             while !rest.is_empty() {
466                 match decoder.decode_to_string_without_replacement(rest, &mut output, true) {
467                     (DecoderResult::InputEmpty, _) => break,
468                     (DecoderResult::OutputFull, _) => unreachable!(),
469                     (DecoderResult::Malformed(a, b), consumed) => {
470                         let skipped = a as usize + b as usize;
471                         output.extend(repeat('?').take(skipped));
472                         rest = &rest[consumed..];
473                     }
474                 }
475             }
476             assert_eq!(self.encoding.encode(&output).0.len(), input.len());
477             output.into()
478         }
479     }
480 }
481
482 pub trait TryDecode: Sized {
483     type Input<'a>;
484     fn try_decode(
485         decoder: &mut Decoder,
486         input: &Self::Input<'_>,
487         warn: impl Fn(Error),
488     ) -> Result<Option<Self>, Error>;
489 }
490
491 pub trait Decode<Input>: Sized {
492     fn decode(decoder: &Decoder, input: &Input, warn: impl Fn(Error)) -> Self;
493 }
494
495 impl<const N: usize> Decode<RawStr<N>> for String {
496     fn decode(decoder: &Decoder, input: &RawStr<N>, warn: impl Fn(Error)) -> Self {
497         decoder.decode_string(&input.0, &warn)
498     }
499 }
500
501 #[derive(Clone, Debug)]
502 pub struct HeaderRecord {
503     pub eye_catcher: String,
504     pub weight_index: Option<usize>,
505     pub n_cases: Option<u64>,
506     pub creation: NaiveDateTime,
507     pub file_label: String,
508 }
509
510 fn trim_end_spaces(mut s: String) -> String {
511     s.truncate(s.trim_end_matches(' ').len());
512     s
513 }
514
515 /// Data file info that doesn't fit in [Dictionary].
516 pub struct Metadata {
517     creation: NaiveDateTime,
518     endian: Endian,
519     compression: Option<Compression>,
520     n_cases: Option<u64>,
521     product: String,
522     product_ext: Option<String>,
523     version: Option<(i32, i32, i32)>,
524 }
525
526 impl Metadata {
527     fn decode(
528         header: &crate::raw::HeaderRecord<Cow<str>>,
529         integer_info: Option<&IntegerInfoRecord>,
530         product_ext: Option<&ProductInfoRecord>,
531         warn: impl Fn(Error),
532     ) -> Self {
533         let creation_date = NaiveDate::parse_from_str(&header.creation_date, "%e %b %Y")
534             .unwrap_or_else(|_| {
535                 warn(Error::InvalidCreationDate {
536                     creation_date: header.creation_date.to_string(),
537                 });
538                 Default::default()
539             });
540         let creation_time = NaiveTime::parse_from_str(&header.creation_time, "%H:%M:%S")
541             .unwrap_or_else(|_| {
542                 warn(Error::InvalidCreationTime {
543                     creation_time: header.creation_time.to_string(),
544                 });
545                 Default::default()
546             });
547         let creation = NaiveDateTime::new(creation_date, creation_time);
548
549         let product = header
550             .eye_catcher
551             .trim_start_matches("@(#) SPSS DATA FILE")
552             .trim_end()
553             .to_string();
554
555         Self {
556             creation,
557             endian: header.endian,
558             compression: header.compression,
559             n_cases: header.n_cases.map(|n| n as u64),
560             product,
561             product_ext: product_ext.map(|pe| pe.0.clone()),
562             version: integer_info.map(|ii| ii.version),
563         }
564     }
565 }
566
567 impl TryDecode for HeaderRecord {
568     type Input<'a> = crate::raw::HeaderRecord<Cow<'a, str>>;
569
570     fn try_decode(
571         _decoder: &mut Decoder,
572         input: &Self::Input<'_>,
573         warn: impl Fn(Error),
574     ) -> Result<Option<Self>, Error> {
575         let eye_catcher = trim_end_spaces(input.eye_catcher.to_string());
576         let file_label = trim_end_spaces(input.file_label.to_string());
577         let creation_date = NaiveDate::parse_from_str(&input.creation_date, "%e %b %Y")
578             .unwrap_or_else(|_| {
579                 warn(Error::InvalidCreationDate {
580                     creation_date: input.creation_date.to_string(),
581                 });
582                 Default::default()
583             });
584         let creation_time = NaiveTime::parse_from_str(&input.creation_time, "%H:%M:%S")
585             .unwrap_or_else(|_| {
586                 warn(Error::InvalidCreationTime {
587                     creation_time: input.creation_time.to_string(),
588                 });
589                 Default::default()
590             });
591         Ok(Some(HeaderRecord {
592             eye_catcher,
593             weight_index: input.weight_index.map(|n| n as usize),
594             n_cases: input.n_cases.map(|n| n as u64),
595             creation: NaiveDateTime::new(creation_date, creation_time),
596             file_label,
597         }))
598     }
599 }
600
601 #[derive(Clone, Debug)]
602 pub struct VariableRecord {
603     pub width: VarWidth,
604     pub name: Identifier,
605     pub print_format: Spec,
606     pub write_format: Spec,
607     pub missing_values: MissingValues<String>,
608     pub label: Option<String>,
609 }
610
611 fn decode_format(raw: raw::Spec, width: VarWidth, warn: impl Fn(Spec, FormatError)) -> Spec {
612     UncheckedSpec::try_from(raw)
613         .and_then(Spec::try_from)
614         .and_then(|x| x.check_width_compatibility(width))
615         .unwrap_or_else(|error| {
616             let new_format = Spec::default_for_width(width);
617             warn(new_format, error);
618             new_format
619         })
620 }
621
622 fn parse_variable_record(
623     decoder: &mut Decoder,
624     input: &raw::VariableRecord<Cow<str>, String>,
625     warn: impl Fn(Error),
626 ) -> Result<(), Error> {
627     let width = match input.width {
628         0 => VarWidth::Numeric,
629         w @ 1..=255 => VarWidth::String(w as u16),
630         -1 => return Ok(()),
631         _ => {
632             return Err(Error::InvalidVariableWidth {
633                 offsets: input.offsets.clone(),
634                 width: input.width,
635             })
636         }
637     };
638     let name = trim_end_spaces(input.name.to_string());
639     let name = match Identifier::new(&name, decoder.encoding) {
640         Ok(name) => {
641             if !decoder.var_names.contains_key(&name) {
642                 name
643             } else {
644                 let new_name = decoder.generate_name();
645                 warn(Error::DuplicateVariableName {
646                     duplicate_name: name.clone(),
647                     new_name: new_name.clone(),
648                 });
649                 new_name
650             }
651         }
652         Err(id_error) => {
653             let new_name = decoder.generate_name();
654             warn(Error::InvalidVariableName {
655                 id_error,
656                 new_name: new_name.clone(),
657             });
658             new_name
659         }
660     };
661     let variable = Variable {
662         dict_index: decoder.n_dict_indexes,
663         short_name: name.clone(),
664         long_name: None,
665         width,
666     };
667     decoder.n_dict_indexes += width.n_dict_indexes();
668     assert!(decoder
669         .var_names
670         .insert(name.clone(), variable.dict_index)
671         .is_none());
672     assert!(decoder
673         .variables
674         .insert(variable.dict_index, variable)
675         .is_none());
676
677     let print_format = decode_format(input.print_format, width, |new_spec, format_error| {
678         warn(Error::InvalidPrintFormat {
679             new_spec,
680             variable: name.clone(),
681             format_error,
682         })
683     });
684     let write_format = decode_format(input.write_format, width, |new_spec, format_error| {
685         warn(Error::InvalidWriteFormat {
686             new_spec,
687             variable: name.clone(),
688             format_error,
689         })
690     });
691     let mut variable = dictionary::Variable::new(name, width);
692     variable.print_format = print_format;
693     variable.write_format = write_format;
694     variable.missing_values = input.missing_values.clone();
695     if let Some(ref label) = input.label {
696         variable.label = Some(label.to_string());
697     }
698     decoder.dictionary.add_var(variable).unwrap();
699     Ok(())
700 }
701
702 #[derive(Clone, Debug)]
703 pub struct DocumentRecord(Vec<String>);
704
705 impl TryDecode for DocumentRecord {
706     type Input<'a> = crate::raw::DocumentRecord<RawDocumentLine>;
707
708     fn try_decode(
709         decoder: &mut Decoder,
710         input: &Self::Input<'_>,
711         warn: impl Fn(Error),
712     ) -> Result<Option<Self>, Error> {
713         Ok(Some(DocumentRecord(
714             input
715                 .lines
716                 .iter()
717                 .map(|s| trim_end_spaces(decoder.decode_string(&s.0, &warn)))
718                 .collect(),
719         )))
720     }
721 }
722
723 trait TextRecord
724 where
725     Self: Sized,
726 {
727     const NAME: &'static str;
728     fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error>;
729 }
730
731 #[derive(Clone, Debug)]
732 pub struct VariableSet {
733     pub name: String,
734     pub vars: Vec<String>,
735 }
736
737 impl VariableSet {
738     fn parse(input: &str) -> Result<Self, Error> {
739         let (name, input) = input.split_once('=').ok_or(Error::TBD)?;
740         let vars = input.split_ascii_whitespace().map(String::from).collect();
741         Ok(VariableSet {
742             name: name.into(),
743             vars,
744         })
745     }
746 }
747
748 trait WarnOnError<T> {
749     fn warn_on_error<F: Fn(Error)>(self, warn: &F) -> Option<T>;
750 }
751 impl<T> WarnOnError<T> for Result<T, Error> {
752     fn warn_on_error<F: Fn(Error)>(self, warn: &F) -> Option<T> {
753         match self {
754             Ok(result) => Some(result),
755             Err(error) => {
756                 warn(error);
757                 None
758             }
759         }
760     }
761 }
762
763 #[derive(Clone, Debug)]
764 pub struct ValueLabel {
765     pub value: Value,
766     pub label: String,
767 }
768
769 #[derive(Clone, Debug)]
770 pub struct ValueLabelRecord {
771     pub var_type: VarType,
772     pub labels: Vec<ValueLabel>,
773     pub variables: Vec<Identifier>,
774 }
775
776 impl TryDecode for ValueLabelRecord {
777     type Input<'a> = crate::raw::ValueLabelRecord<RawStr<8>, RawString>;
778     fn try_decode(
779         decoder: &mut Decoder,
780         input: &Self::Input<'_>,
781         warn: impl Fn(Error),
782     ) -> Result<Option<ValueLabelRecord>, Error> {
783         let variables: Vec<&Variable> = input
784             .dict_indexes
785             .iter()
786             .filter_map(|&dict_index| {
787                 decoder
788                     .get_var_by_index(dict_index as usize)
789                     .warn_on_error(&warn)
790             })
791             .filter(|&variable| match variable.width {
792                 VarWidth::String(width) if width > 8 => {
793                     warn(Error::InvalidLongStringValueLabel(
794                         variable.short_name.clone(),
795                     ));
796                     false
797                 }
798                 _ => true,
799             })
800             .collect();
801         let mut i = variables.iter();
802         let Some(&first_var) = i.next() else {
803             return Ok(None);
804         };
805         let var_type: VarType = first_var.width.into();
806         for &variable in i {
807             let this_type: VarType = variable.width.into();
808             if var_type != this_type {
809                 let (numeric_var, string_var) = match var_type {
810                     VarType::Numeric => (first_var, variable),
811                     VarType::String => (variable, first_var),
812                 };
813                 warn(Error::ValueLabelsDifferentTypes {
814                     numeric_var: numeric_var.short_name.clone(),
815                     string_var: string_var.short_name.clone(),
816                 });
817                 return Ok(None);
818             }
819         }
820         let labels = input
821             .labels
822             .iter()
823             .map(|raw::ValueLabel { value, label }| {
824                 let label = decoder.decode_string(&label.0, &warn);
825                 let value = Value::decode(value, decoder);
826                 ValueLabel { value, label }
827             })
828             .collect();
829         let variables = variables
830             .iter()
831             .map(|&variable| variable.short_name.clone())
832             .collect();
833         Ok(Some(ValueLabelRecord {
834             var_type,
835             labels,
836             variables,
837         }))
838     }
839 }
840
841 #[derive(Clone, Debug)]
842 pub struct VariableSetRecord(Vec<VariableSet>);
843
844 impl TextRecord for VariableSetRecord {
845     const NAME: &'static str = "variable set";
846     fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
847         let mut sets = Vec::new();
848         for line in input.lines() {
849             if let Some(set) = VariableSet::parse(line).warn_on_error(&warn) {
850                 sets.push(set)
851             }
852         }
853         Ok(VariableSetRecord(sets))
854     }
855 }
856
857 #[derive(Clone, Debug)]
858 pub struct LongName {
859     pub short_name: Identifier,
860     pub long_name: Identifier,
861 }
862
863 impl LongName {
864     fn new(decoder: &mut Decoder, short_name: &str, long_name: &str) -> Result<LongName, Error> {
865         let short_name =
866             Identifier::new(short_name, decoder.encoding).map_err(Error::InvalidShortName)?;
867         let long_name =
868             Identifier::new(long_name, decoder.encoding).map_err(Error::InvalidLongName)?;
869         Ok(LongName {
870             short_name,
871             long_name,
872         })
873     }
874 }
875
876 #[derive(Clone, Debug)]
877 pub struct LongNameRecord(Vec<LongName>);
878
879 impl LongNameRecord {
880     pub fn parse(decoder: &mut Decoder, input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
881         let mut names = Vec::new();
882         for pair in input.split('\t').filter(|s| !s.is_empty()) {
883             if let Some((short_name, long_name)) = pair.split_once('=') {
884                 if let Some(long_name) =
885                     LongName::new(decoder, short_name, long_name).warn_on_error(&warn)
886                 {
887                     names.push(long_name);
888                 }
889             } else {
890                 warn(Error::TBD)
891             }
892         }
893         Ok(LongNameRecord(names))
894     }
895 }
896
897 #[derive(Clone, Debug)]
898 pub struct VeryLongString {
899     pub short_name: Identifier,
900     pub length: u16,
901 }
902
903 impl VeryLongString {
904     fn parse(decoder: &Decoder, input: &str) -> Result<VeryLongString, Error> {
905         let Some((short_name, length)) = input.split_once('=') else {
906             return Err(Error::TBD);
907         };
908         let short_name =
909             Identifier::new(short_name, decoder.encoding).map_err(Error::InvalidLongStringName)?;
910         let length: u16 = length.parse().map_err(|_| Error::TBD)?;
911         if length > VarWidth::MAX_STRING {
912             return Err(Error::TBD);
913         }
914         Ok(VeryLongString { short_name, length })
915     }
916 }
917
918 #[derive(Clone, Debug)]
919 pub struct VeryLongStringRecord(Vec<VeryLongString>);
920
921 impl VeryLongStringRecord {
922     pub fn parse(decoder: &Decoder, input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
923         let mut very_long_strings = Vec::new();
924         for tuple in input
925             .split('\0')
926             .map(|s| s.trim_end_matches('\t'))
927             .filter(|s| !s.is_empty())
928         {
929             if let Some(vls) = VeryLongString::parse(decoder, tuple).warn_on_error(&warn) {
930                 very_long_strings.push(vls)
931             }
932         }
933         Ok(VeryLongStringRecord(very_long_strings))
934     }
935 }
936
937 #[derive(Clone, Debug)]
938 pub struct Attribute {
939     pub name: Identifier,
940     pub values: Vec<String>,
941 }
942
943 impl Attribute {
944     fn parse<'a>(
945         decoder: &Decoder,
946         input: &'a str,
947         warn: &impl Fn(Error),
948     ) -> Result<(Option<Attribute>, &'a str), Error> {
949         let Some((name, mut input)) = input.split_once('(') else {
950             return Err(Error::TBD);
951         };
952         let mut values = Vec::new();
953         loop {
954             let Some((value, rest)) = input.split_once('\n') else {
955                 return Err(Error::TBD);
956             };
957             if let Some(stripped) = value
958                 .strip_prefix('\'')
959                 .and_then(|value| value.strip_suffix('\''))
960             {
961                 values.push(stripped.into());
962             } else {
963                 warn(Error::TBD);
964                 values.push(value.into());
965             }
966             if let Some(rest) = rest.strip_prefix(')') {
967                 let attribute = Identifier::new(name, decoder.encoding)
968                     .map_err(Error::InvalidAttributeName)
969                     .warn_on_error(warn)
970                     .map(|name| Attribute { name, values });
971                 return Ok((attribute, rest));
972             };
973             input = rest;
974         }
975     }
976 }
977
978 #[derive(Clone, Debug)]
979 pub struct AttributeSet(pub Vec<Attribute>);
980
981 impl AttributeSet {
982     fn parse<'a>(
983         decoder: &Decoder,
984         mut input: &'a str,
985         sentinel: Option<char>,
986         warn: &impl Fn(Error),
987     ) -> Result<(AttributeSet, &'a str), Error> {
988         let mut attributes = Vec::new();
989         let rest = loop {
990             match input.chars().next() {
991                 None => break input,
992                 c if c == sentinel => break &input[1..],
993                 _ => {
994                     let (attribute, rest) = Attribute::parse(decoder, input, &warn)?;
995                     if let Some(attribute) = attribute {
996                         attributes.push(attribute);
997                     }
998                     input = rest;
999                 }
1000             }
1001         };
1002         Ok((AttributeSet(attributes), rest))
1003     }
1004 }
1005
1006 #[derive(Clone, Debug)]
1007 pub struct FileAttributeRecord(AttributeSet);
1008
1009 impl FileAttributeRecord {
1010     pub fn parse(decoder: &Decoder, input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
1011         let (set, rest) = AttributeSet::parse(decoder, input, None, &warn)?;
1012         if !rest.is_empty() {
1013             warn(Error::TBD);
1014         }
1015         Ok(FileAttributeRecord(set))
1016     }
1017 }
1018
1019 #[derive(Clone, Debug)]
1020 pub struct VarAttributeSet {
1021     pub long_var_name: Identifier,
1022     pub attributes: AttributeSet,
1023 }
1024
1025 impl VarAttributeSet {
1026     fn parse<'a>(
1027         decoder: &Decoder,
1028         input: &'a str,
1029         warn: &impl Fn(Error),
1030     ) -> Result<(Option<VarAttributeSet>, &'a str), Error> {
1031         let Some((long_var_name, rest)) = input.split_once(':') else {
1032             return Err(Error::TBD);
1033         };
1034         let (attributes, rest) = AttributeSet::parse(decoder, rest, Some('/'), warn)?;
1035         let var_attribute = Identifier::new(long_var_name, decoder.encoding)
1036             .map_err(Error::InvalidAttributeVariableName)
1037             .warn_on_error(warn)
1038             .map(|name| VarAttributeSet {
1039                 long_var_name: name,
1040                 attributes,
1041             });
1042         Ok((var_attribute, rest))
1043     }
1044 }
1045
1046 #[derive(Clone, Debug)]
1047 pub struct VariableAttributeRecord(Vec<VarAttributeSet>);
1048
1049 impl VariableAttributeRecord {
1050     pub fn parse(decoder: &Decoder, mut input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
1051         let mut var_attribute_sets = Vec::new();
1052         while !input.is_empty() {
1053             let Some((var_attribute, rest)) =
1054                 VarAttributeSet::parse(decoder, input, &warn).warn_on_error(&warn)
1055             else {
1056                 break;
1057             };
1058             if let Some(var_attribute) = var_attribute {
1059                 var_attribute_sets.push(var_attribute);
1060             }
1061             input = rest;
1062         }
1063         Ok(VariableAttributeRecord(var_attribute_sets))
1064     }
1065 }
1066
1067 #[derive(Clone, Debug)]
1068 pub enum MultipleResponseType {
1069     MultipleDichotomy {
1070         value: Value,
1071         labels: CategoryLabels,
1072     },
1073     MultipleCategory,
1074 }
1075
1076 impl MultipleResponseType {
1077     fn decode(
1078         decoder: &Decoder,
1079         mr_set: &Identifier,
1080         input: &raw::MultipleResponseType,
1081         min_width: VarWidth,
1082         warn: &impl Fn(Error),
1083     ) -> Result<Self, Error> {
1084         let mr_type = match input {
1085             raw::MultipleResponseType::MultipleDichotomy { value, labels } => {
1086                 let value = decoder.decode_string_cow(&value.0, warn);
1087                 let value = match min_width {
1088                     VarWidth::Numeric => {
1089                         let number: f64 = value.trim().parse().map_err(|_| {
1090                             Error::InvalidMDGroupCountedValue {
1091                                 mr_set: mr_set.clone(),
1092                                 number: value.into(),
1093                             }
1094                         })?;
1095                         Value::Number(Some(number.into()))
1096                     }
1097                     VarWidth::String(max_width) => {
1098                         let value = value.trim_end_matches(' ');
1099                         let width = value.len();
1100                         if width > max_width as usize {
1101                             return Err(Error::TooWideMDGroupCountedValue {
1102                                 mr_set: mr_set.clone(),
1103                                 value: value.into(),
1104                                 width,
1105                                 max_width,
1106                             });
1107                         };
1108                         Value::String(value.into())
1109                     }
1110                 };
1111                 MultipleResponseType::MultipleDichotomy {
1112                     value,
1113                     labels: *labels,
1114                 }
1115             }
1116             raw::MultipleResponseType::MultipleCategory => MultipleResponseType::MultipleCategory,
1117         };
1118         Ok(mr_type)
1119     }
1120 }
1121
1122 #[derive(Clone, Debug)]
1123 pub struct MultipleResponseSet {
1124     pub name: Identifier,
1125     pub min_width: VarWidth,
1126     pub max_width: VarWidth,
1127     pub label: String,
1128     pub mr_type: MultipleResponseType,
1129     pub dict_indexes: Vec<DictIndex>,
1130 }
1131
1132 impl MultipleResponseSet {
1133     fn decode(
1134         decoder: &Decoder,
1135         input: &raw::MultipleResponseSet<Identifier, Cow<str>>,
1136         warn: &impl Fn(Error),
1137     ) -> Result<Self, Error> {
1138         let mr_set_name = input.name.clone();
1139         let mut dict_indexes = Vec::with_capacity(input.short_names.len());
1140         for short_name in input.short_names.iter() {
1141             let Some(&dict_index) = decoder.var_names.get(&short_name) else {
1142                 warn(Error::UnknownMrSetVariable {
1143                     mr_set: mr_set_name.clone(),
1144                     short_name: short_name.clone(),
1145                 });
1146                 continue;
1147             };
1148             dict_indexes.push(dict_index);
1149         }
1150
1151         match dict_indexes.len() {
1152             0 => return Err(Error::EmptyMrSet(mr_set_name)),
1153             1 => return Err(Error::OneVarMrSet(mr_set_name)),
1154             _ => (),
1155         }
1156
1157         let Some((Some(min_width), Some(max_width))) = dict_indexes
1158             .iter()
1159             .map(|dict_index| decoder.variables[dict_index].width)
1160             .map(|w| (Some(w), Some(w)))
1161             .reduce(|(na, wa), (nb, wb)| (VarWidth::narrower(na, nb), VarWidth::wider(wa, wb)))
1162         else {
1163             return Err(Error::MixedMrSet(mr_set_name));
1164         };
1165
1166         let mr_type =
1167             MultipleResponseType::decode(decoder, &mr_set_name, &input.mr_type, min_width, warn)?;
1168
1169         Ok(MultipleResponseSet {
1170             name: mr_set_name,
1171             min_width,
1172             max_width,
1173             label: input.label.to_string(),
1174             mr_type,
1175             dict_indexes,
1176         })
1177     }
1178 }
1179
1180 #[derive(Clone, Debug)]
1181 pub struct MultipleResponseRecord(pub Vec<MultipleResponseSet>);
1182
1183 impl TryDecode for MultipleResponseRecord {
1184     type Input<'a> = raw::MultipleResponseRecord<Identifier, Cow<'a, str>>;
1185
1186     fn try_decode(
1187         decoder: &mut Decoder,
1188         input: &Self::Input<'_>,
1189         warn: impl Fn(Error),
1190     ) -> Result<Option<Self>, Error> {
1191         let mut sets = Vec::with_capacity(input.0.len());
1192         for set in &input.0 {
1193             match MultipleResponseSet::decode(decoder, set, &warn) {
1194                 Ok(set) => sets.push(set),
1195                 Err(error) => warn(error),
1196             }
1197         }
1198         Ok(Some(MultipleResponseRecord(sets)))
1199     }
1200 }
1201
1202 #[derive(Clone, Debug)]
1203 pub struct LongStringValueLabels {
1204     pub var_name: Identifier,
1205     pub width: VarWidth,
1206     pub labels: Vec<ValueLabel>,
1207 }
1208
1209 impl LongStringValueLabels {
1210     fn decode(
1211         decoder: &Decoder,
1212         input: &raw::LongStringValueLabels<RawString>,
1213         warn: &impl Fn(Error),
1214     ) -> Result<Self, Error> {
1215         let var_name = decoder.decode_string(&input.var_name.0, warn);
1216         let var_name = Identifier::new(var_name.trim_end(), decoder.encoding)
1217             .map_err(Error::InvalidLongStringValueLabelName)?;
1218
1219         let min_width = 9;
1220         let max_width = VarWidth::MAX_STRING;
1221         if input.width < 9 || input.width > max_width as u32 {
1222             return Err(Error::InvalidLongValueLabelWidth {
1223                 name: var_name,
1224                 width: input.width,
1225                 min_width,
1226                 max_width,
1227             });
1228         }
1229         let width = input.width as u16;
1230
1231         let mut labels = Vec::with_capacity(input.labels.len());
1232         for (value, label) in input.labels.iter() {
1233             let value = Value::String(decoder.decode_exact_length(&value.0).into());
1234             let label = decoder.decode_string(&label.0, warn);
1235             labels.push(ValueLabel { value, label });
1236         }
1237
1238         Ok(LongStringValueLabels {
1239             var_name,
1240             width: VarWidth::String(width),
1241             labels,
1242         })
1243     }
1244 }
1245
1246 #[derive(Clone, Debug)]
1247 pub struct LongStringValueLabelRecord(pub Vec<LongStringValueLabels>);
1248
1249 impl TryDecode for LongStringValueLabelRecord {
1250     type Input<'a> = raw::LongStringValueLabelRecord<RawString>;
1251
1252     fn try_decode(
1253         decoder: &mut Decoder,
1254         input: &Self::Input<'_>,
1255         warn: impl Fn(Error),
1256     ) -> Result<Option<Self>, Error> {
1257         let mut labels = Vec::with_capacity(input.0.len());
1258         for label in &input.0 {
1259             match LongStringValueLabels::decode(decoder, label, &warn) {
1260                 Ok(set) => labels.push(set),
1261                 Err(error) => warn(error),
1262             }
1263         }
1264         Ok(Some(LongStringValueLabelRecord(labels)))
1265     }
1266 }
1267
1268 #[cfg(test)]
1269 mod test {
1270     use encoding_rs::WINDOWS_1252;
1271
1272     #[test]
1273     fn test() {
1274         let mut s = String::new();
1275         s.push(char::REPLACEMENT_CHARACTER);
1276         let encoded = WINDOWS_1252.encode(&s).0;
1277         let decoded = WINDOWS_1252.decode(&encoded[..]).0;
1278         println!("{:?}", decoded);
1279     }
1280
1281     #[test]
1282     fn test2() {
1283         let charset: Vec<u8> = (0..=255).collect();
1284         println!("{}", charset.len());
1285         let decoded = WINDOWS_1252.decode(&charset[..]).0;
1286         println!("{}", decoded.len());
1287         let encoded = WINDOWS_1252.encode(&decoded[..]).0;
1288         println!("{}", encoded.len());
1289         assert_eq!(&charset[..], &encoded[..]);
1290     }
1291 }