multiple response
[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, 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(#[from] 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("Invalid numeric format for counted value {number} in multiple response set {mr_set}.")]
97     InvalidMDGroupCountedValue { mr_set: Identifier, number: String },
98
99     #[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}.")]
100     TooWideMDGroupCountedValue { mr_set: Identifier, value: String, width: usize, max_width: u16 },
101
102     #[error("Details TBD")]
103     TBD,
104 }
105
106 #[derive(Clone, Debug)]
107 pub enum Record {
108     Header(HeaderRecord),
109     Variable(VariableRecord),
110     ValueLabel(ValueLabelRecord),
111     Document(DocumentRecord),
112     IntegerInfo(IntegerInfoRecord),
113     FloatInfo(FloatInfoRecord),
114     VariableSets(VariableSetRecord),
115     VarDisplay(VarDisplayRecord),
116     MultipleResponse(MultipleResponseRecord),
117     //LongStringValueLabels(LongStringValueLabelRecord),
118     Encoding(EncodingRecord),
119     NumberOfCases(NumberOfCasesRecord),
120     ProductInfo(ProductInfoRecord),
121     //LongNames(UnencodedString),
122     //LongStrings(UnencodedString),
123     //FileAttributes(UnencodedString),
124     //VariableAttributes(UnencodedString),
125     //OtherExtension(Extension),
126     //EndOfHeaders(u32),
127     //ZHeader(ZHeader),
128     //ZTrailer(ZTrailer),
129     //Case(Vec<Value>),
130 }
131
132 pub use crate::raw::EncodingRecord;
133 pub use crate::raw::FloatInfoRecord;
134 pub use crate::raw::IntegerInfoRecord;
135 pub use crate::raw::NumberOfCasesRecord;
136
137 type DictIndex = usize;
138
139 pub struct Variable {
140     pub dict_index: DictIndex,
141     pub short_name: Identifier,
142     pub long_name: Option<Identifier>,
143     pub width: VarWidth,
144 }
145
146 pub struct Decoder {
147     pub compression: Option<Compression>,
148     pub endian: Endian,
149     pub encoding: &'static Encoding,
150     pub variables: HashMap<DictIndex, Variable>,
151     pub var_names: HashMap<Identifier, DictIndex>,
152     n_dict_indexes: usize,
153     n_generated_names: usize,
154 }
155
156 impl Decoder {
157     fn generate_name(&mut self) -> Identifier {
158         loop {
159             self.n_generated_names += 1;
160             let name = Identifier::new(&format!("VAR{:03}", self.n_generated_names), self.encoding)
161                 .unwrap();
162             if !self.var_names.contains_key(&name) {
163                 return name;
164             }
165             assert!(self.n_generated_names < usize::MAX);
166         }
167     }
168     fn decode_string<'a>(&self, input: &'a [u8], warn: &impl Fn(Error)) -> Cow<'a, str> {
169         let (output, malformed) = self.encoding.decode_without_bom_handling(input);
170         if malformed {
171             warn(Error::TBD);
172         }
173         output
174     }
175     pub fn decode_identifier(
176         &self,
177         input: &[u8],
178         warn: &impl Fn(Error),
179     ) -> Result<Identifier, IdError> {
180         let s = self.decode_string(input, warn);
181         Identifier::new(&s, self.encoding)
182     }
183     fn get_var_by_index(&self, dict_index: usize) -> Result<&Variable, Error> {
184         let max_index = self.n_dict_indexes - 1;
185         if dict_index == 0 || dict_index as usize > max_index {
186             return Err(Error::InvalidDictIndex {
187                 dict_index,
188                 max_index,
189             });
190         }
191         let Some(variable) = self.variables.get(&dict_index) else {
192             return Err(Error::DictIndexIsContinuation(dict_index));
193         };
194         Ok(variable)
195     }
196
197     /// Returns `input` decoded from `self.encoding` into UTF-8 such that
198     /// re-encoding the result back into `self.encoding` will have exactly the
199     /// same length in bytes.
200     ///
201     /// XXX warn about errors?
202     fn decode_exact_length<'a>(&self, input: &'a [u8]) -> Cow<'a, str> {
203         if let (s, false) = self.encoding.decode_without_bom_handling(input) {
204             // This is the common case.  Usually there will be no errors.
205             s.into()
206         } else {
207             // Unusual case.  Don't bother to optimize it much.
208             let mut decoder = self.encoding.new_decoder_without_bom_handling();
209             let mut output = String::with_capacity(
210                 decoder
211                     .max_utf8_buffer_length_without_replacement(input.len())
212                     .unwrap(),
213             );
214             let mut rest = input;
215             while !rest.is_empty() {
216                 match decoder.decode_to_string_without_replacement(rest, &mut output, true) {
217                     (DecoderResult::InputEmpty, _) => break,
218                     (DecoderResult::OutputFull, _) => unreachable!(),
219                     (DecoderResult::Malformed(a, b), consumed) => {
220                         let skipped = a as usize + b as usize;
221                         output.extend(repeat('?').take(skipped));
222                         rest = &rest[consumed..];
223                     }
224                 }
225             }
226             assert_eq!(self.encoding.encode(&output).0.len(), input.len());
227             output.into()
228         }
229     }
230 }
231
232 pub trait Decode: Sized {
233     type Input;
234     fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Result<Self, Error>;
235 }
236
237 #[derive(Clone, Debug)]
238 pub struct HeaderRecord {
239     pub eye_catcher: String,
240     pub weight_index: Option<usize>,
241     pub n_cases: Option<u64>,
242     pub creation: NaiveDateTime,
243     pub file_label: String,
244 }
245
246 impl Decode for HeaderRecord {
247     type Input = crate::raw::HeaderRecord;
248
249     fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Result<Self, Error> {
250         let eye_catcher = decoder.decode_string(&input.eye_catcher.0, &warn);
251         let file_label = decoder.decode_string(&input.file_label.0, &warn);
252         let creation_date = decoder.decode_string(&input.creation_date.0, &warn);
253         let creation_date = NaiveDate::parse_from_str(&creation_date, "%v").unwrap_or_else(|_| {
254             warn(Error::InvalidCreationDate {
255                 creation_date: creation_date.into(),
256             });
257             Default::default()
258         });
259         let creation_time = decoder.decode_string(&input.creation_time.0, &warn);
260         let creation_time =
261             NaiveTime::parse_from_str(&creation_time, "%H:%M:%S").unwrap_or_else(|_| {
262                 warn(Error::InvalidCreationTime {
263                     creation_time: creation_time.into(),
264                 });
265                 Default::default()
266             });
267         Ok(HeaderRecord {
268             eye_catcher: eye_catcher.into(),
269             weight_index: input.weight_index.map(|n| n as usize),
270             n_cases: input.n_cases.map(|n| n as u64),
271             creation: NaiveDateTime::new(creation_date, creation_time),
272             file_label: file_label.into(),
273         })
274     }
275 }
276
277 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
278 pub enum VarWidth {
279     Numeric,
280     String(u16),
281 }
282
283 impl PartialOrd for VarWidth {
284     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
285         match (self, other) {
286             (VarWidth::Numeric, VarWidth::Numeric) => Some(Ordering::Equal),
287             (VarWidth::String(a), VarWidth::String(b)) => Some(a.cmp(b)),
288             _ => None,
289         }
290     }
291 }
292
293 impl VarWidth {
294     fn n_dict_indexes(self) -> usize {
295         match self {
296             VarWidth::Numeric => 1,
297             VarWidth::String(w) => div_ceil(w as usize, 8),
298         }
299     }
300
301     fn width_predicate(
302         a: Option<VarWidth>,
303         b: Option<VarWidth>,
304         f: impl Fn(u16, u16) -> u16,
305     ) -> Option<VarWidth> {
306         match (a, b) {
307             (Some(VarWidth::Numeric), Some(VarWidth::Numeric)) => Some(VarWidth::Numeric),
308             (Some(VarWidth::String(a)), Some(VarWidth::String(b))) => {
309                 Some(VarWidth::String(f(a, b)))
310             }
311             _ => None,
312         }
313     }
314
315     /// Returns the wider of `self` and `other`:
316     /// - Numerical variable widths are equally wide.
317     /// - Longer strings are wider than shorter strings.
318     /// - Numerical and string types are incomparable, so result in `None`.
319     /// - Any `None` in the input yields `None` in the output.
320     pub fn wider(a: Option<VarWidth>, b: Option<VarWidth>) -> Option<VarWidth> {
321         Self::width_predicate(a, b, |a, b| a.max(b))
322     }
323
324     /// Returns the narrower of `self` and `other` (see [`Self::wider`]).
325     pub fn narrower(a: Option<VarWidth>, b: Option<VarWidth>) -> Option<VarWidth> {
326         Self::width_predicate(a, b, |a, b| a.min(b))
327     }
328 }
329
330 impl From<VarWidth> for VarType {
331     fn from(source: VarWidth) -> Self {
332         match source {
333             VarWidth::Numeric => VarType::Numeric,
334             VarWidth::String(_) => VarType::String,
335         }
336     }
337 }
338
339 #[derive(Clone, Debug)]
340 pub struct VariableRecord {
341     pub width: VarWidth,
342     pub name: Identifier,
343     pub print_format: Spec,
344     pub write_format: Spec,
345     pub missing_values: MissingValues,
346     pub label: Option<String>,
347 }
348
349 fn decode_format(raw: raw::Spec, width: VarWidth, warn: impl Fn(Spec, FormatError)) -> Spec {
350     UncheckedSpec::try_from(raw)
351         .and_then(Spec::try_from)
352         .and_then(|x| x.check_width_compatibility(width))
353         .unwrap_or_else(|error| {
354             let new_format = Spec::default_for_width(width);
355             warn(new_format, error);
356             new_format
357         })
358 }
359
360 impl VariableRecord {
361     pub fn decode(
362         decoder: &mut Decoder,
363         input: &crate::raw::VariableRecord,
364         warn: impl Fn(Error),
365     ) -> Result<Option<VariableRecord>, Error> {
366         let width = match input.width {
367             0 => VarWidth::Numeric,
368             w @ 1..=255 => VarWidth::String(w as u16),
369             -1 => return Ok(None),
370             _ => {
371                 return Err(Error::InvalidVariableWidth {
372                     offset: input.offset,
373                     width: input.width,
374                 })
375             }
376         };
377         let name = match decoder.decode_identifier(&input.name.0, &warn) {
378             Ok(name) => {
379                 if !decoder.var_names.contains_key(&name) {
380                     name
381                 } else {
382                     let new_name = decoder.generate_name();
383                     warn(Error::DuplicateVariableName {
384                         duplicate_name: name.clone(),
385                         new_name: new_name.clone(),
386                     });
387                     new_name
388                 }
389             }
390             Err(id_error) => {
391                 let new_name = decoder.generate_name();
392                 warn(Error::InvalidVariableName {
393                     id_error,
394                     new_name: new_name.clone(),
395                 });
396                 new_name
397             }
398         };
399         let variable = Variable {
400             dict_index: decoder.n_dict_indexes,
401             short_name: name.clone(),
402             long_name: None,
403             width,
404         };
405         decoder.n_dict_indexes += width.n_dict_indexes();
406         assert!(decoder
407             .var_names
408             .insert(name.clone(), variable.dict_index)
409             .is_none());
410         assert!(decoder
411             .variables
412             .insert(variable.dict_index, variable)
413             .is_none());
414
415         let print_format = decode_format(input.print_format, width, |new_spec, format_error| {
416             warn(Error::InvalidPrintFormat {
417                 new_spec,
418                 variable: name.clone(),
419                 format_error,
420             })
421         });
422         let write_format = decode_format(input.write_format, width, |new_spec, format_error| {
423             warn(Error::InvalidWriteFormat {
424                 new_spec,
425                 variable: name.clone(),
426                 format_error,
427             })
428         });
429         let label = input
430             .label
431             .as_ref()
432             .map(|label| decoder.decode_string(&label.0, &warn).into());
433         Ok(Some(VariableRecord {
434             width,
435             name,
436             print_format,
437             write_format,
438             missing_values: input.missing_values.clone(),
439             label,
440         }))
441     }
442 }
443
444 #[derive(Clone, Debug)]
445 pub struct DocumentRecord(Vec<String>);
446
447 impl Decode for DocumentRecord {
448     type Input = crate::raw::DocumentRecord;
449
450     fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Result<Self, Error> {
451         Ok(DocumentRecord(
452             input
453                 .lines
454                 .iter()
455                 .map(|s| decoder.decode_string(&s.0, &warn).into())
456                 .collect(),
457         ))
458     }
459 }
460
461 trait TextRecord
462 where
463     Self: Sized,
464 {
465     const NAME: &'static str;
466     fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error>;
467 }
468
469 #[derive(Clone, Debug)]
470 pub struct VariableSet {
471     pub name: String,
472     pub vars: Vec<String>,
473 }
474
475 impl VariableSet {
476     fn parse(input: &str) -> Result<Self, Error> {
477         let (name, input) = input.split_once('=').ok_or(Error::TBD)?;
478         let vars = input.split_ascii_whitespace().map(String::from).collect();
479         Ok(VariableSet {
480             name: name.into(),
481             vars,
482         })
483     }
484 }
485
486 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
487 pub enum Value {
488     Number(Option<OrderedFloat<f64>>),
489     String(String),
490 }
491
492 impl Value {
493     pub fn decode(raw: raw::Value, decoder: &Decoder) -> Self {
494         match raw {
495             raw::Value::Number(x) => Value::Number(x.map(|x| x.into())),
496             raw::Value::String(s) => Value::String(decoder.decode_exact_length(&s.0).into()),
497         }
498     }
499 }
500
501 #[derive(Clone, Debug)]
502 pub struct ValueLabelRecord {
503     pub var_type: VarType,
504     pub labels: Vec<(Value, String)>,
505     pub variables: Vec<Identifier>,
506 }
507
508 trait WarnOnError<T> {
509     fn warn_on_error<F: Fn(Error)>(self, warn: &F) -> Option<T>;
510 }
511 impl<T> WarnOnError<T> for Result<T, Error> {
512     fn warn_on_error<F: Fn(Error)>(self, warn: &F) -> Option<T> {
513         match self {
514             Ok(result) => Some(result),
515             Err(error) => {
516                 warn(error);
517                 None
518             }
519         }
520     }
521 }
522
523 impl ValueLabelRecord {
524     pub fn decode(
525         decoder: &mut Decoder,
526         raw_value_label: &crate::raw::ValueLabelRecord,
527         dict_indexes: &crate::raw::VarIndexRecord,
528         warn: impl Fn(Error),
529     ) -> Result<Option<ValueLabelRecord>, Error> {
530         let variables: Vec<&Variable> = dict_indexes
531             .dict_indexes
532             .iter()
533             .filter_map(|&dict_index| {
534                 decoder
535                     .get_var_by_index(dict_index as usize)
536                     .warn_on_error(&warn)
537             })
538             .filter(|&variable| match variable.width {
539                 VarWidth::String(width) if width > 8 => {
540                     warn(Error::InvalidLongStringValueLabel(
541                         variable.short_name.clone(),
542                     ));
543                     false
544                 }
545                 _ => true,
546             })
547             .collect();
548         let mut i = variables.iter();
549         let Some(&first_var) = i.next() else {
550             return Ok(None);
551         };
552         let var_type: VarType = first_var.width.into();
553         for &variable in i {
554             let this_type: VarType = variable.width.into();
555             if var_type != this_type {
556                 let (numeric_var, string_var) = match var_type {
557                     VarType::Numeric => (first_var, variable),
558                     VarType::String => (variable, first_var),
559                 };
560                 warn(Error::ValueLabelsDifferentTypes {
561                     numeric_var: numeric_var.short_name.clone(),
562                     string_var: string_var.short_name.clone(),
563                 });
564                 return Ok(None);
565             }
566         }
567         let labels = raw_value_label
568             .labels
569             .iter()
570             .map(|(value, label)| {
571                 let label = decoder.decode_string(&label.0, &warn);
572                 let value = Value::decode(
573                     raw::Value::from_raw(*value, var_type, decoder.endian),
574                     &decoder,
575                 );
576                 (value, label.into())
577             })
578             .collect();
579         let variables = variables
580             .iter()
581             .map(|&variable| variable.short_name.clone())
582             .collect();
583         Ok(Some(ValueLabelRecord {
584             var_type,
585             labels,
586             variables,
587         }))
588     }
589 }
590
591 #[derive(Clone, Debug)]
592 pub struct VariableSetRecord(Vec<VariableSet>);
593
594 impl TextRecord for VariableSetRecord {
595     const NAME: &'static str = "variable set";
596     fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
597         let mut sets = Vec::new();
598         for line in input.lines() {
599             if let Some(set) = VariableSet::parse(line).warn_on_error(&warn) {
600                 sets.push(set)
601             }
602         }
603         Ok(VariableSetRecord(sets))
604     }
605 }
606
607 #[derive(Clone, Debug)]
608 pub struct ProductInfoRecord(pub String);
609
610 impl TextRecord for ProductInfoRecord {
611     const NAME: &'static str = "extra product info";
612     fn parse(input: &str, _warn: impl Fn(Error)) -> Result<Self, Error> {
613         Ok(ProductInfoRecord(input.into()))
614     }
615 }
616
617 pub struct LongVariableName {
618     pub short_name: String,
619     pub long_name: String,
620 }
621
622 pub struct LongVariableNameRecord(Vec<LongVariableName>);
623
624 impl TextRecord for LongVariableNameRecord {
625     const NAME: &'static str = "long variable names";
626     fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
627         let mut names = Vec::new();
628         for pair in input.split('\t').filter(|s| !s.is_empty()) {
629             if let Some((short_name, long_name)) = pair.split_once('=') {
630                 let name = LongVariableName {
631                     short_name: short_name.into(),
632                     long_name: long_name.into(),
633                 };
634                 names.push(name);
635             } else {
636                 warn(Error::TBD)
637             }
638         }
639         Ok(LongVariableNameRecord(names))
640     }
641 }
642
643 pub struct VeryLongString {
644     pub short_name: String,
645     pub length: usize,
646 }
647
648 impl VeryLongString {
649     fn parse(input: &str) -> Result<VeryLongString, Error> {
650         let Some((short_name, length)) = input.split_once('=') else {
651             return Err(Error::TBD);
652         };
653         let length: usize = length.parse().map_err(|_| Error::TBD)?;
654         Ok(VeryLongString {
655             short_name: short_name.into(),
656             length,
657         })
658     }
659 }
660
661 pub struct VeryLongStringRecord(Vec<VeryLongString>);
662
663 impl TextRecord for VeryLongStringRecord {
664     const NAME: &'static str = "very long strings";
665     fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
666         let mut very_long_strings = Vec::new();
667         for tuple in input
668             .split('\0')
669             .map(|s| s.trim_end_matches('\t'))
670             .filter(|s| !s.is_empty())
671         {
672             if let Some(vls) = VeryLongString::parse(tuple).warn_on_error(&warn) {
673                 very_long_strings.push(vls)
674             }
675         }
676         Ok(VeryLongStringRecord(very_long_strings))
677     }
678 }
679
680 pub struct Attribute {
681     pub name: String,
682     pub values: Vec<String>,
683 }
684
685 impl Attribute {
686     fn parse<'a>(input: &'a str, warn: &impl Fn(Error)) -> Result<(Attribute, &'a str), Error> {
687         let Some((name, mut input)) = input.split_once('(') else {
688             return Err(Error::TBD);
689         };
690         let mut values = Vec::new();
691         loop {
692             let Some((value, rest)) = input.split_once('\n') else {
693                 return Err(Error::TBD);
694             };
695             if let Some(stripped) = value
696                 .strip_prefix('\'')
697                 .and_then(|value| value.strip_suffix('\''))
698             {
699                 values.push(stripped.into());
700             } else {
701                 warn(Error::TBD);
702                 values.push(value.into());
703             }
704             if let Some(rest) = rest.strip_prefix(')') {
705                 return Ok((
706                     Attribute {
707                         name: name.into(),
708                         values,
709                     },
710                     rest,
711                 ));
712             }
713             input = rest;
714         }
715     }
716 }
717
718 pub struct AttributeSet(pub Vec<Attribute>);
719
720 impl AttributeSet {
721     fn parse<'a>(
722         mut input: &'a str,
723         sentinel: Option<char>,
724         warn: &impl Fn(Error),
725     ) -> Result<(AttributeSet, &'a str), Error> {
726         let mut attributes = Vec::new();
727         let rest = loop {
728             match input.chars().next() {
729                 None => break input,
730                 c if c == sentinel => break &input[1..],
731                 _ => {
732                     let (attribute, rest) = Attribute::parse(input, &warn)?;
733                     attributes.push(attribute);
734                     input = rest;
735                 }
736             }
737         };
738         Ok((AttributeSet(attributes), rest))
739     }
740 }
741
742 pub struct FileAttributeRecord(AttributeSet);
743
744 impl TextRecord for FileAttributeRecord {
745     const NAME: &'static str = "data file attributes";
746     fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
747         let (set, rest) = AttributeSet::parse(input, None, &warn)?;
748         if !rest.is_empty() {
749             warn(Error::TBD);
750         }
751         Ok(FileAttributeRecord(set))
752     }
753 }
754
755 pub struct VarAttributeSet {
756     pub long_var_name: String,
757     pub attributes: AttributeSet,
758 }
759
760 impl VarAttributeSet {
761     fn parse<'a>(
762         input: &'a str,
763         warn: &impl Fn(Error),
764     ) -> Result<(VarAttributeSet, &'a str), Error> {
765         let Some((long_var_name, rest)) = input.split_once(':') else {
766             return Err(Error::TBD);
767         };
768         let (attributes, rest) = AttributeSet::parse(rest, Some('/'), warn)?;
769         Ok((
770             VarAttributeSet {
771                 long_var_name: long_var_name.into(),
772                 attributes,
773             },
774             rest,
775         ))
776     }
777 }
778
779 pub struct VariableAttributeRecord(Vec<VarAttributeSet>);
780
781 impl TextRecord for VariableAttributeRecord {
782     const NAME: &'static str = "variable attributes";
783     fn parse(mut input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
784         let mut var_attribute_sets = Vec::new();
785         while !input.is_empty() {
786             let Some((var_attribute, rest)) =
787                 VarAttributeSet::parse(input, &warn).warn_on_error(&warn)
788             else {
789                 break;
790             };
791             var_attribute_sets.push(var_attribute);
792             input = rest;
793         }
794         Ok(VariableAttributeRecord(var_attribute_sets))
795     }
796 }
797
798 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
799 pub enum Measure {
800     Nominal,
801     Ordinal,
802     Scale,
803 }
804
805 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
806 pub enum Alignment {
807     Left,
808     Right,
809     Center,
810 }
811
812 #[derive(Clone, Debug)]
813 pub struct VarDisplay {
814     pub measure: Option<Measure>,
815     pub width: u32,
816     pub align: Option<Alignment>,
817 }
818
819 #[derive(Clone, Debug)]
820 pub struct VarDisplayRecord(pub Vec<VarDisplay>);
821
822 #[derive(Clone, Debug)]
823 pub enum MultipleResponseType {
824     MultipleDichotomy {
825         value: Value,
826         labels: CategoryLabels,
827     },
828     MultipleCategory,
829 }
830
831 impl MultipleResponseType {
832     fn decode(
833         decoder: &Decoder,
834         mr_set: &Identifier,
835         input: &raw::MultipleResponseType,
836         min_width: VarWidth,
837         warn: &impl Fn(Error),
838     ) -> Result<Self, Error> {
839         let mr_type = match input {
840             raw::MultipleResponseType::MultipleDichotomy { value, labels } => {
841                 let value = decoder.decode_string(&value.0, warn);
842                 let value = match min_width {
843                     VarWidth::Numeric => {
844                         let number: f64 = value.trim().parse()
845                             .map_err(|_| Error::InvalidMDGroupCountedValue { mr_set: mr_set.clone(), number: value.into() })?;
846                         Value::Number(Some(number.into()))
847                     },
848                     VarWidth::String(max_width) => {
849                         let value = value.trim_end_matches(' ');
850                         let width = value.len();
851                         if width > max_width as usize {
852                             return Err(Error::TooWideMDGroupCountedValue { mr_set: mr_set.clone(), value: value.into(), width, max_width });
853                         };
854                         Value::String(value.into())
855                     }
856                 };
857                 MultipleResponseType::MultipleDichotomy { value, labels: *labels }
858             },
859             raw::MultipleResponseType::MultipleCategory => MultipleResponseType::MultipleCategory,
860         };
861         Ok(mr_type)
862     }
863 }
864
865 #[derive(Clone, Debug)]
866 pub struct MultipleResponseSet {
867     pub name: Identifier,
868     pub min_width: VarWidth,
869     pub max_width: VarWidth,
870     pub label: String,
871     pub mr_type: MultipleResponseType,
872     pub dict_indexes: Vec<DictIndex>,
873 }
874
875 impl MultipleResponseSet {
876     fn decode(
877         decoder: &Decoder,
878         input: &raw::MultipleResponseSet,
879         warn: &impl Fn(Error),
880     ) -> Result<Self, Error> {
881         let mr_set_name = decoder
882             .decode_identifier(&input.name.0, warn)
883             .map_err(|error| Error::InvalidMrSetName(error))?;
884
885         let label = decoder.decode_string(&input.label.0, warn).into();
886
887         let mut dict_indexes = Vec::with_capacity(input.short_names.len());
888         for short_name in input.short_names.iter() {
889             let short_name = match decoder.decode_identifier(&short_name.0, warn) {
890                 Ok(name) => name,
891                 Err(error) => {
892                     warn(Error::InvalidMrSetName(error));
893                     continue;
894                 }
895             };
896             let Some(&dict_index) = decoder.var_names.get(&short_name) else {
897                 warn(Error::UnknownMrSetVariable {
898                     mr_set: mr_set_name.clone(),
899                     short_name: short_name.clone(),
900                 });
901                 continue;
902             };
903             dict_indexes.push(dict_index);
904         }
905
906         match dict_indexes.len() {
907             0 => return Err(Error::EmptyMrSet(mr_set_name)),
908             1 => return Err(Error::OneVarMrSet(mr_set_name)),
909             _ => (),
910         }
911
912         let Some((Some(min_width), Some(max_width))) = dict_indexes
913             .iter()
914             .map(|dict_index| decoder.variables[dict_index].width)
915             .map(|w| (Some(w), Some(w)))
916             .reduce(|(na, wa), (nb, wb)| (VarWidth::narrower(na, nb), VarWidth::wider(wa, wb)))
917         else {
918             return Err(Error::MixedMrSet(mr_set_name));
919         };
920
921         let mr_type = MultipleResponseType::decode(decoder, &mr_set_name, &input.mr_type, min_width, warn)?;
922
923         Ok(MultipleResponseSet {
924             name: mr_set_name,
925             min_width,
926             max_width,
927             label,
928             mr_type,
929             dict_indexes,
930         })
931     }
932 }
933
934 #[derive(Clone, Debug)]
935 pub struct MultipleResponseRecord(pub Vec<MultipleResponseSet>);
936
937 impl Decode for MultipleResponseRecord {
938     type Input = raw::MultipleResponseRecord;
939
940     fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Result<Self, Error> {
941         let mut sets = Vec::with_capacity(input.0.len());
942         for set in &input.0 {
943             match MultipleResponseSet::decode(decoder, set, &warn) {
944                 Ok(set) => sets.push(set),
945                 Err(error) => warn(error),
946             }
947         }
948         Ok(MultipleResponseRecord(sets))
949     }
950 }
951
952 #[cfg(test)]
953 mod test {
954     use encoding_rs::WINDOWS_1252;
955
956     #[test]
957     fn test() {
958         let mut s = String::new();
959         s.push(char::REPLACEMENT_CHARACTER);
960         let encoded = WINDOWS_1252.encode(&s).0;
961         let decoded = WINDOWS_1252.decode(&encoded[..]).0;
962         println!("{:?}", decoded);
963     }
964
965     #[test]
966     fn test2() {
967         let charset: Vec<u8> = (0..=255).collect();
968         println!("{}", charset.len());
969         let decoded = WINDOWS_1252.decode(&charset[..]).0;
970         println!("{}", decoded.len());
971         let encoded = WINDOWS_1252.encode(&decoded[..]).0;
972         println!("{}", encoded.len());
973         assert_eq!(&charset[..], &encoded[..]);
974     }
975 }