+impl TryDecode for VarDisplayRecord {
+ type Input = raw::VarDisplayRecord;
+ fn try_decode(
+ decoder: &mut Decoder,
+ input: &Self::Input,
+ warn: impl Fn(Error),
+ ) -> Result<Option<Self>, Error> {
+ let n_vars = decoder.variables.len();
+ let n_per_var = if input.0.len() == 3 * n_vars {
+ 3
+ } else if input.0.len() == 2 * n_vars {
+ 2
+ } else {
+ return Err(Error::TBD);
+ };
+
+ let var_displays = input
+ .0
+ .chunks(n_per_var)
+ .map(|chunk| {
+ let (measure, width, alignment) = match n_per_var == 3 {
+ true => (chunk[0], Some(chunk[1]), chunk[2]),
+ false => (chunk[0], None, chunk[1]),
+ };
+ let measure = Measure::try_decode(measure).warn_on_error(&warn).flatten();
+ let alignment = Alignment::try_decode(alignment)
+ .warn_on_error(&warn)
+ .flatten();
+ VarDisplay {
+ measure,
+ width,
+ alignment,
+ }
+ })
+ .collect();
+ Ok(Some(VarDisplayRecord(var_displays)))
+ }
+}
+
+#[derive(Clone, Debug)]
+pub enum MultipleResponseType {
+ MultipleDichotomy {
+ value: Value,
+ labels: CategoryLabels,
+ },
+ MultipleCategory,
+}
+
+impl MultipleResponseType {
+ fn decode(
+ decoder: &Decoder,
+ mr_set: &Identifier,
+ input: &raw::MultipleResponseType,
+ min_width: VarWidth,
+ warn: &impl Fn(Error),
+ ) -> Result<Self, Error> {
+ let mr_type = match input {
+ raw::MultipleResponseType::MultipleDichotomy { value, labels } => {
+ let value = decoder.decode_string_cow(&value.0, warn);
+ let value = match min_width {
+ VarWidth::Numeric => {
+ let number: f64 = value.trim().parse().map_err(|_| {
+ Error::InvalidMDGroupCountedValue {
+ mr_set: mr_set.clone(),
+ number: value.into(),
+ }
+ })?;
+ Value::Number(Some(number.into()))
+ }
+ VarWidth::String(max_width) => {
+ let value = value.trim_end_matches(' ');
+ let width = value.len();
+ if width > max_width as usize {
+ return Err(Error::TooWideMDGroupCountedValue {
+ mr_set: mr_set.clone(),
+ value: value.into(),
+ width,
+ max_width,
+ });
+ };
+ Value::String(value.into())
+ }
+ };
+ MultipleResponseType::MultipleDichotomy {
+ value,
+ labels: *labels,
+ }
+ }
+ raw::MultipleResponseType::MultipleCategory => MultipleResponseType::MultipleCategory,
+ };
+ Ok(mr_type)
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct MultipleResponseSet {
+ pub name: Identifier,
+ pub min_width: VarWidth,
+ pub max_width: VarWidth,
+ pub label: String,
+ pub mr_type: MultipleResponseType,
+ pub dict_indexes: Vec<DictIndex>,
+}
+
+impl MultipleResponseSet {
+ fn decode(
+ decoder: &Decoder,
+ input: &raw::MultipleResponseSet,
+ warn: &impl Fn(Error),
+ ) -> Result<Self, Error> {
+ let mr_set_name = decoder
+ .decode_identifier(&input.name.0, warn)
+ .map_err(Error::InvalidMrSetName)?;
+
+ let label = decoder.decode_string(&input.label.0, warn);
+
+ let mut dict_indexes = Vec::with_capacity(input.short_names.len());
+ for short_name in input.short_names.iter() {
+ let short_name = match decoder.decode_identifier(&short_name.0, warn) {
+ Ok(name) => name,
+ Err(error) => {
+ warn(Error::InvalidMrSetName(error));
+ continue;
+ }
+ };
+ let Some(&dict_index) = decoder.var_names.get(&short_name) else {
+ warn(Error::UnknownMrSetVariable {
+ mr_set: mr_set_name.clone(),
+ short_name: short_name.clone(),
+ });
+ continue;
+ };
+ dict_indexes.push(dict_index);
+ }
+
+ match dict_indexes.len() {
+ 0 => return Err(Error::EmptyMrSet(mr_set_name)),
+ 1 => return Err(Error::OneVarMrSet(mr_set_name)),
+ _ => (),
+ }
+
+ let Some((Some(min_width), Some(max_width))) = dict_indexes
+ .iter()
+ .map(|dict_index| decoder.variables[dict_index].width)
+ .map(|w| (Some(w), Some(w)))
+ .reduce(|(na, wa), (nb, wb)| (VarWidth::narrower(na, nb), VarWidth::wider(wa, wb)))
+ else {
+ return Err(Error::MixedMrSet(mr_set_name));
+ };
+
+ let mr_type =
+ MultipleResponseType::decode(decoder, &mr_set_name, &input.mr_type, min_width, warn)?;
+
+ Ok(MultipleResponseSet {
+ name: mr_set_name,
+ min_width,
+ max_width,
+ label,
+ mr_type,
+ dict_indexes,
+ })
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct MultipleResponseRecord(pub Vec<MultipleResponseSet>);
+
+impl TryDecode for MultipleResponseRecord {
+ type Input = raw::MultipleResponseRecord;
+
+ fn try_decode(
+ decoder: &mut Decoder,
+ input: &Self::Input,
+ warn: impl Fn(Error),
+ ) -> Result<Option<Self>, Error> {
+ let mut sets = Vec::with_capacity(input.0.len());
+ for set in &input.0 {
+ match MultipleResponseSet::decode(decoder, set, &warn) {
+ Ok(set) => sets.push(set),
+ Err(error) => warn(error),
+ }
+ }
+ Ok(Some(MultipleResponseRecord(sets)))
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct LongStringMissingValues {
+ /// Variable name.
+ pub var_name: Identifier,
+
+ /// Missing values.
+ pub missing_values: MissingValues,
+}
+
+impl LongStringMissingValues {
+ fn decode(
+ decoder: &Decoder,
+ input: &raw::LongStringMissingValues,
+ warn: &impl Fn(Error),
+ ) -> Result<Self, Error> {
+ let var_name = decoder.decode_string(&input.var_name.0, warn);
+ let var_name = Identifier::new(var_name.trim_end(), decoder.encoding)
+ .map_err(Error::InvalidLongStringValueLabelName)?;
+
+ let missing_values = MissingValues::decode(decoder, &input.missing_values, warn);
+
+ Ok(LongStringMissingValues {
+ var_name,
+ missing_values
+ })
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct LongStringMissingValuesRecord(Vec<LongStringMissingValues>);
+
+impl TryDecode for LongStringMissingValuesRecord {
+ type Input = raw::LongStringMissingValueSet;
+
+ fn try_decode(
+ decoder: &mut Decoder,
+ input: &Self::Input,
+ warn: impl Fn(Error),
+ ) -> Result<Option<Self>, Error> {
+ let mut labels = Vec::with_capacity(input.0.len());
+ for label in &input.0 {
+ match LongStringMissingValues::decode(decoder, label, &warn) {
+ Ok(set) => labels.push(set),
+ Err(error) => warn(error),
+ }
+ }
+ Ok(Some(LongStringMissingValuesRecord(labels)))
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct LongStringValueLabels {
+ pub var_name: Identifier,
+ pub width: VarWidth,
+ pub labels: Vec<ValueLabel>,
+}
+
+impl LongStringValueLabels {
+ fn decode(
+ decoder: &Decoder,
+ input: &raw::LongStringValueLabels,
+ warn: &impl Fn(Error),
+ ) -> Result<Self, Error> {
+ let var_name = decoder.decode_string(&input.var_name.0, warn);
+ let var_name = Identifier::new(var_name.trim_end(), decoder.encoding)
+ .map_err(Error::InvalidLongStringValueLabelName)?;
+
+ let min_width = 9;
+ let max_width = VarWidth::MAX_STRING;
+ if input.width < 9 || input.width > max_width as u32 {
+ return Err(Error::InvalidLongValueLabelWidth {
+ name: var_name,
+ width: input.width,
+ min_width,
+ max_width,
+ });
+ }
+ let width = input.width as u16;
+
+ let mut labels = Vec::with_capacity(input.labels.len());
+ for (value, label) in input.labels.iter() {
+ let value = Value::String(decoder.decode_exact_length(&value.0).into());
+ let label = decoder.decode_string(&label.0, warn);
+ labels.push(ValueLabel { value, label });
+ }
+
+ Ok(LongStringValueLabels {
+ var_name,
+ width: VarWidth::String(width),
+ labels,
+ })
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct LongStringValueLabelRecord(pub Vec<LongStringValueLabels>);
+
+impl TryDecode for LongStringValueLabelRecord {
+ type Input = raw::LongStringValueLabelRecord;
+
+ fn try_decode(
+ decoder: &mut Decoder,
+ input: &Self::Input,
+ warn: impl Fn(Error),
+ ) -> Result<Option<Self>, Error> {
+ let mut labels = Vec::with_capacity(input.0.len());
+ for label in &input.0 {
+ match LongStringValueLabels::decode(decoder, label, &warn) {
+ Ok(set) => labels.push(set),
+ Err(error) => warn(error),
+ }
+ }
+ Ok(Some(LongStringValueLabelRecord(labels)))
+ }
+}
+