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