work on value labels rust
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 3 Mar 2024 22:55:15 +0000 (14:55 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 3 Mar 2024 22:55:15 +0000 (14:55 -0800)
rust/src/cooked.rs
rust/src/dictionary.rs
rust/src/identifier.rs
rust/src/raw.rs

index ee66890027040eba699b7de61c04894279a7eaf2..71d65adddd6cfa0ddd50e458d2df9259f7140535 100644 (file)
@@ -1,4 +1,4 @@
-use std::{cell::RefCell, ops::Range, rc::Rc, collections::HashMap};
+use std::{cell::RefCell, collections::HashMap, ops::Range, rc::Rc};
 
 use crate::{
     dictionary::{Dictionary, VarWidth, Variable},
@@ -85,17 +85,23 @@ pub enum Error {
     #[error("Dictionary index {0} refers to a long string continuation.")]
     DictIndexIsContinuation(usize),
 
+    #[error("At offset {offset:#x}, one or more variable indexes for value labels referred to long string continuation records: {indexes:?}")]
+    LongStringContinuationIndexes { offset: u64, indexes: Vec<u32> },
+
+    #[error(
+        "At offsets {:#x}...{:#x}, record types 3 and 4 may not add value labels to one or more long string variables: {variables:?}", .offsets.start, .offsets.end
+    )]
+    InvalidLongStringValueLabels {
+        offsets: Range<u64>,
+        variables: Vec<Identifier>,
+    },
+
     #[error("Variables associated with value label are not all of identical type.  Variable {numeric_var} is numeric, but variable {string_var} is string.")]
     ValueLabelsDifferentTypes {
         numeric_var: Identifier,
         string_var: Identifier,
     },
 
-    #[error(
-        "Value labels may not be added to long string variable {0} using record types 3 or 4."
-    )]
-    InvalidLongStringValueLabel(Identifier),
-
     #[error("Invalid multiple response set name.  {0}")]
     InvalidMrSetName(IdError),
 
@@ -512,6 +518,36 @@ pub fn decode(
         assert_eq!(var_index_map.insert(value_index, dict_index), None);
     }
 
+    for record in headers.value_label.drain(..) {
+        let mut dict_indexes = Vec::with_capacity(record.dict_indexes.len());
+        let mut continuation_indexes = Vec::new();
+        let mut long_string_variables = Vec::new();
+        for value_index in record.dict_indexes.iter() {
+            if let Some(dict_index) = var_index_map.get(&(*value_index as usize - 1)) {
+                let variable = &dictionary.variables[*dict_index];
+                if variable.width.is_long_string() {
+                    long_string_variables.push(variable.name.clone());
+                } else {
+                    dict_indexes.push(*dict_index);
+                }
+            } else {
+                continuation_indexes.push(*value_index);
+            }
+        }
+        if !continuation_indexes.is_empty() {
+            warn(Error::LongStringContinuationIndexes {
+                offset: record.offsets.start,
+                indexes: continuation_indexes,
+            });
+        }
+        if !long_string_variables.is_empty() {
+            warn(Error::InvalidLongStringValueLabels {
+                offsets: record.offsets.clone(),
+                variables: long_string_variables,
+            });
+        }
+    }
+
     let metadata = Metadata::decode(&headers, warn);
     Ok((dictionary, metadata))
 }
index 042a294452a2e572bd4dc95ed8b890b77269a839..8d28ff329bceedca534bd59b852fef6f0d78b4d6 100644 (file)
@@ -87,6 +87,14 @@ impl VarWidth {
             _ => Err(()),
         }
     }
+
+    pub fn is_long_string(&self) -> bool {
+        if let Self::String(width) = self {
+            *width > 8
+        } else {
+            false
+        }
+    }
 }
 
 impl From<VarWidth> for VarType {
index 70fbc00aa1173c3c5852531c2d6161f1f4580735..1108a46a7e1f5463a32b1277672b9bf05351de89 100644 (file)
@@ -3,6 +3,7 @@ use std::{
     cmp::Ordering,
     fmt::{Debug, Display, Formatter, Result as FmtResult},
     hash::{Hash, Hasher},
+    ops::Deref,
 };
 
 use encoding_rs::{EncoderResult, Encoding, UTF_8};
@@ -237,3 +238,14 @@ where
         Self(self.0.clone())
     }
 }
+
+impl<T> Deref for ByIdentifier<T>
+where
+    T: HasIdentifier + Clone,
+{
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
index e8a279f5e848418e0fdb846333cc80f5e0a60ce4..f492710378de48c1a7a641b31110559e6353d853 100644 (file)
@@ -47,10 +47,7 @@ pub enum Error {
     BadRecordType { offset: u64, rec_type: u32 },
 
     #[error("In variable record starting at offset {start_offset:#x}, variable width is not in the valid range -1 to 255.")]
-    BadVariableWidth {
-        start_offset: u64,
-        width: i32,
-    },
+    BadVariableWidth { start_offset: u64, width: i32 },
 
     #[error("In variable record starting at offset {start_offset:#x}, variable label code {code} at offset {code_offset:#x} is not 0 or 1.")]
     BadVariableLabelCode {
@@ -1544,7 +1541,7 @@ impl ValueLabelRecord<RawStr<8>, RawString> {
                 max: Self::MAX_INDEXES,
             });
         }
-
+        
         let index_offset = r.stream_position()?;
         let mut dict_indexes = Vec::with_capacity(n as usize);
         let mut invalid_indexes = Vec::new();