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