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