variables parsed
[pspp] / rust / src / raw.rs
index 0250520afc3b3ffdfee4fc5da9951d3f538952bb..e8a279f5e848418e0fdb846333cc80f5e0a60ce4 100644 (file)
@@ -46,6 +46,12 @@ pub enum Error {
     #[error("At offset {offset:#x}, unrecognized record type {rec_type}.")]
     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,
+    },
+
     #[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 {
         start_offset: u64,
@@ -287,7 +293,7 @@ impl Record {
         }
     }
 
-    pub fn decode(&self, decoder: &Decoder) -> Result<DecodedRecord, Error> {
+    pub fn decode(self, decoder: &Decoder) -> Result<DecodedRecord, Error> {
         Ok(match self {
             Record::Header(record) => record.decode(decoder),
             Record::Variable(record) => record.decode(decoder),
@@ -307,7 +313,7 @@ impl Record {
             Record::NumberOfCases(record) => DecodedRecord::NumberOfCases(record.clone()),
             Record::Text(record) => record.decode(decoder),
             Record::OtherExtension(record) => DecodedRecord::OtherExtension(record.clone()),
-            Record::EndOfHeaders(record) => DecodedRecord::EndOfHeaders(*record),
+            Record::EndOfHeaders(record) => DecodedRecord::EndOfHeaders(record),
             Record::ZHeader(record) => DecodedRecord::ZHeader(record.clone()),
             Record::ZTrailer(record) => DecodedRecord::ZTrailer(record.clone()),
             Record::Cases(record) => DecodedRecord::Cases(record.clone()),
@@ -495,7 +501,7 @@ impl HeaderRecord<RawString> {
         })
     }
 
-    pub fn decode(&self, decoder: &Decoder) -> DecodedRecord {
+    pub fn decode(self, decoder: &Decoder) -> DecodedRecord {
         let eye_catcher = decoder.decode(&self.eye_catcher).to_string();
         let file_label = decoder.decode(&self.file_label).to_string();
         let creation_date = decoder.decode(&self.creation_date).to_string();
@@ -823,9 +829,9 @@ impl RawValue {
         Ok(Some(values))
     }
 
-    fn decode(&self, decoder: &Decoder) -> Value<String> {
+    fn decode(self, decoder: &Decoder) -> Value<String> {
         match self {
-            Self::Number(x) => Value::Number(*x),
+            Self::Number(x) => Value::Number(x),
             Self::String(s) => Value::String(decoder.decode_exact_length(&s.0).into()),
         }
     }
@@ -1298,6 +1304,9 @@ impl VariableRecord<RawString, RawStr<8>> {
     fn read<R: Read + Seek>(r: &mut R, endian: Endian) -> Result<Record, Error> {
         let start_offset = r.stream_position()?;
         let width: i32 = endian.parse(read_bytes(r)?);
+        if !(-1..=255).contains(&width) {
+            return Err(Error::BadVariableWidth { start_offset, width });
+        }
         let code_offset = r.stream_position()?;
         let has_variable_label: u32 = endian.parse(read_bytes(r)?);
         let missing_value_code: i32 = endian.parse(read_bytes(r)?);
@@ -1342,7 +1351,7 @@ impl VariableRecord<RawString, RawStr<8>> {
         }))
     }
 
-    pub fn decode(&self, decoder: &Decoder) -> DecodedRecord {
+    pub fn decode(self, decoder: &Decoder) -> DecodedRecord {
         DecodedRecord::Variable(VariableRecord {
             offsets: self.offsets.clone(),
             width: self.width,
@@ -1596,7 +1605,7 @@ impl ValueLabelRecord<RawStr<8>, RawString> {
         })))
     }
 
-    fn decode(&self, decoder: &Decoder) -> ValueLabelRecord<RawStr<8>, String> {
+    fn decode(self, decoder: &Decoder) -> ValueLabelRecord<RawStr<8>, String> {
         let labels = self
             .labels
             .iter()
@@ -1621,7 +1630,8 @@ where
 {
     pub offsets: Range<u64>,
 
-    /// The document, as an array of 80-byte lines.
+    /// The document, as an array of lines.  Raw lines are exactly 80 bytes long
+    /// and are right-padded with spaces without any new-line termination.
     pub lines: Vec<S>,
 }
 
@@ -1659,7 +1669,7 @@ impl DocumentRecord<RawDocumentLine> {
         }
     }
 
-    pub fn decode(&self, decoder: &Decoder) -> DecodedRecord {
+    pub fn decode(self, decoder: &Decoder) -> DecodedRecord {
         DecodedRecord::Document(DocumentRecord {
             offsets: self.offsets.clone(),
             lines: self
@@ -1906,7 +1916,7 @@ impl ExtensionRecord for MultipleResponseRecord<RawString, RawString> {
 }
 
 impl MultipleResponseRecord<RawString, RawString> {
-    fn decode(&self, decoder: &Decoder) -> DecodedRecord {
+    fn decode(self, decoder: &Decoder) -> DecodedRecord {
         let mut sets = Vec::new();
         for set in self.0.iter() {
             if let Some(set) = set.decode(decoder).issue_warning(&decoder.warn) {
@@ -2129,7 +2139,7 @@ impl ExtensionRecord for LongStringMissingValueRecord<RawString, RawStr<8>> {
 }
 
 impl LongStringMissingValueRecord<RawString, RawStr<8>> {
-    pub fn decode(&self, decoder: &Decoder) -> LongStringMissingValueRecord<Identifier, String> {
+    pub fn decode(self, decoder: &Decoder) -> LongStringMissingValueRecord<Identifier, String> {
         let mut mvs = Vec::with_capacity(self.0.len());
         for mv in self.0.iter() {
             if let Some(mv) = mv
@@ -2219,25 +2229,25 @@ impl TextRecord {
             text: extension.data.into(),
         }
     }
-    pub fn decode(&self, decoder: &Decoder) -> DecodedRecord {
+    pub fn decode(self, decoder: &Decoder) -> DecodedRecord {
         match self.rec_type {
             TextRecordType::VariableSets => {
-                DecodedRecord::VariableSets(VariableSetRecord::decode(self, decoder))
+                DecodedRecord::VariableSets(VariableSetRecord::decode(&self, decoder))
             }
             TextRecordType::ProductInfo => {
-                DecodedRecord::ProductInfo(ProductInfoRecord::decode(self, decoder))
+                DecodedRecord::ProductInfo(ProductInfoRecord::decode(&self, decoder))
             }
             TextRecordType::LongNames => {
-                DecodedRecord::LongNames(LongNamesRecord::decode(self, decoder))
+                DecodedRecord::LongNames(LongNamesRecord::decode(&self, decoder))
             }
             TextRecordType::VeryLongStrings => {
-                DecodedRecord::VeryLongStrings(VeryLongStringsRecord::decode(self, decoder))
+                DecodedRecord::VeryLongStrings(VeryLongStringsRecord::decode(&self, decoder))
             }
             TextRecordType::FileAttributes => {
-                DecodedRecord::FileAttributes(FileAttributeRecord::decode(self, decoder))
+                DecodedRecord::FileAttributes(FileAttributeRecord::decode(&self, decoder))
             }
             TextRecordType::VariableAttributes => {
-                DecodedRecord::VariableAttributes(VariableAttributeRecord::decode(self, decoder))
+                DecodedRecord::VariableAttributes(VariableAttributeRecord::decode(&self, decoder))
             }
         }
     }
@@ -2346,7 +2356,7 @@ impl AttributeSet {
 }
 
 #[derive(Clone, Debug, Default)]
-pub struct FileAttributeRecord(AttributeSet);
+pub struct FileAttributeRecord(pub AttributeSet);
 
 impl FileAttributeRecord {
     fn decode(source: &TextRecord, decoder: &Decoder) -> Self {
@@ -2863,7 +2873,7 @@ impl ExtensionRecord for LongStringValueLabelRecord<RawString, RawString> {
 }
 
 impl LongStringValueLabelRecord<RawString, RawString> {
-    fn decode(&self, decoder: &Decoder) -> LongStringValueLabelRecord<Identifier, String> {
+    fn decode(self, decoder: &Decoder) -> LongStringValueLabelRecord<Identifier, String> {
         let mut labels = Vec::with_capacity(self.0.len());
         for label in &self.0 {
             match label.decode(decoder) {