work
[pspp] / rust / src / raw.rs
index bae59656f05474b0c24874fe2401b97b563b0626..ccd1e6662ba4b28ff683ee89547fcd51c77e018a 100644 (file)
@@ -1,5 +1,5 @@
 use crate::endian::{Endian, Parse, ToBytes};
-use crate::Error;
+use crate::{CategoryLabels, Compression, Error};
 
 use flate2::read::ZlibDecoder;
 use num::Integer;
@@ -14,19 +14,13 @@ use std::{
 
 use self::state::State;
 
-#[derive(Copy, Clone, Debug)]
-pub enum Compression {
-    Simple,
-    ZLib,
-}
-
 #[derive(Clone, Debug)]
 pub enum Record {
     Header(Header),
-    Document(Document),
     Variable(Variable),
     ValueLabel(ValueLabel),
     VarIndexes(VarIndexes),
+    Document(Document),
     IntegerInfo(IntegerInfo),
     FloatInfo(FloatInfo),
     VariableSets(UnencodedString),
@@ -41,7 +35,7 @@ pub enum Record {
     FileAttributes(UnencodedString),
     VariableAttributes(UnencodedString),
     TextExtension(TextExtension),
-    Extension(Extension),
+    OtherExtension(Extension),
     EndOfHeaders(u32),
     ZHeader(ZHeader),
     ZTrailer(ZTrailer),
@@ -72,10 +66,7 @@ fn fallback_encode<'a>(s: &'a [u8]) -> Cow<'a, str> {
     if let Ok(s) = from_utf8(s) {
         s.into()
     } else {
-        let s: String = s
-            .iter()
-            .map(|c| char::from(*c))
-            .collect();
+        let s: String = s.iter().map(|c| char::from(*c)).collect();
         s.into()
     }
 }
@@ -639,7 +630,7 @@ impl Debug for Format {
     }
 }
 
-fn format_name(type_: u32) -> &'static str {
+fn format_name(type_: u32) -> Cow<'static, str> {
     match type_ {
         1 => "A",
         2 => "AHEX",
@@ -678,8 +669,8 @@ fn format_name(type_: u32) -> &'static str {
         39 => "SDATE",
         40 => "MTIME",
         41 => "YMDHMS",
-        _ => "(unknown)",
-    }
+        _ => return format!("<unknown format {type_}>").into()
+    }.into()
 }
 
 #[derive(Clone)]
@@ -764,10 +755,10 @@ pub struct Variable {
     pub name: [u8; 8],
 
     /// Print format.
-    pub print_format: u32,
+    pub print_format: Format,
 
     /// Write format.
-    pub write_format: u32,
+    pub write_format: Format,
 
     /// Missing values.
     pub missing_values: MissingValues,
@@ -790,14 +781,10 @@ impl Debug for Variable {
                 "long string continuation record"
             }
         )?;
-        writeln!(f, "Print format: {:?}", Format(self.print_format))?;
-        writeln!(f, "Write format: {:?}", Format(self.write_format))?;
+        writeln!(f, "Print format: {:?}", self.print_format)?;
+        writeln!(f, "Write format: {:?}", self.write_format)?;
         writeln!(f, "Name: {:?}", FallbackEncoding(&self.name))?;
-        writeln!(
-            f,
-            "Variable label: {:?}",
-            self.label
-        )?;
+        writeln!(f, "Variable label: {:?}", self.label)?;
         writeln!(f, "Missing values: {:?}", self.missing_values)
     }
 }
@@ -808,8 +795,8 @@ impl Variable {
         let width: i32 = endian.parse(read_bytes(r)?);
         let has_variable_label: u32 = endian.parse(read_bytes(r)?);
         let missing_value_code: i32 = endian.parse(read_bytes(r)?);
-        let print_format: u32 = endian.parse(read_bytes(r)?);
-        let write_format: u32 = endian.parse(read_bytes(r)?);
+        let print_format = Format(endian.parse(read_bytes(r)?));
+        let write_format = Format(endian.parse(read_bytes(r)?));
         let name: [u8; 8] = read_bytes(r)?;
 
         let label = match has_variable_label {
@@ -855,11 +842,18 @@ impl Debug for UntypedValue {
         let little = format!("{:?}", little);
         let big: f64 = Endian::Big.parse(self.0);
         let big = format!("{:?}", big);
-        let number = if little.len() <= big.len() { little } else { big };
+        let number = if little.len() <= big.len() {
+            little
+        } else {
+            big
+        };
         write!(f, "{number}")?;
 
         let string = fallback_encode(&self.0);
-        let string = string.split(|c: char| c == '\0' || c.is_control()).next().unwrap();
+        let string = string
+            .split(|c: char| c == '\0' || c.is_control())
+            .next()
+            .unwrap();
         write!(f, "/\"{string}\"")?;
         Ok(())
     }
@@ -1097,11 +1091,6 @@ impl ExtensionRecord for FloatInfo {
     }
 }
 
-#[derive(Clone, Debug)]
-pub enum CategoryLabels {
-    VarLabels,
-    CountedValues,
-}
 #[derive(Clone, Debug)]
 pub enum MultipleResponseType {
     MultipleDichotomy {
@@ -1235,17 +1224,17 @@ fn parse_counted_string(input: &[u8]) -> Result<(UnencodedString, &[u8]), Error>
     Ok((string.into(), rest))
 }
 
-pub struct ExtraProductInfo(String);
+pub struct ProductInfo(String);
 
-impl TextRecord for ExtraProductInfo {
+impl TextRecord for ProductInfo {
     const NAME: &'static str = "extra product info";
     fn parse(input: &str, _warn: impl Fn(Error)) -> Result<Self, Error> {
-        Ok(ExtraProductInfo(input.into()))
+        Ok(ProductInfo(input.into()))
     }
 }
 
 #[derive(Clone, Debug)]
-pub struct VarDisplayRecord(Vec<u32>);
+pub struct VarDisplayRecord(pub Vec<u32>);
 
 impl ExtensionRecord for VarDisplayRecord {
     const SUBTYPE: u32 = 11;
@@ -1450,10 +1439,13 @@ impl ExtensionRecord for LongStringMissingValueSet {
                 };
                 values.push(Value::String(value));
             }
-            let missing_values = MissingValues { values, range: None };
+            let missing_values = MissingValues {
+                values,
+                range: None,
+            };
             missing_value_set.push(LongStringMissingValues {
                 var_name,
-                missing_values
+                missing_values,
             });
         }
         Ok(LongStringMissingValueSet(missing_value_set))
@@ -1472,9 +1464,10 @@ impl ExtensionRecord for EncodingRecord {
     fn parse(ext: &Extension, _endian: Endian, _warn: impl Fn(Error)) -> Result<Self, Error> {
         ext.check_size::<Self>()?;
 
-        Ok(EncodingRecord(String::from_utf8(ext.data.clone()).map_err(
-            |_| Error::BadEncodingName { offset: ext.offset },
-        )?))
+        Ok(EncodingRecord(
+            String::from_utf8(ext.data.clone())
+                .map_err(|_| Error::BadEncodingName { offset: ext.offset })?,
+        ))
     }
 }
 
@@ -1735,20 +1728,56 @@ impl Extension {
             data,
         };
         match subtype {
-            IntegerInfo::SUBTYPE => Ok(Record::IntegerInfo(IntegerInfo::parse(&extension, endian, |_| ())?)),
-            FloatInfo::SUBTYPE => Ok(Record::FloatInfo(FloatInfo::parse(&extension, endian, |_| ())?)),
-            VarDisplayRecord::SUBTYPE => Ok(Record::VarDisplay(VarDisplayRecord::parse(&extension, endian, |_| ())?)),
-            MultipleResponseRecord::SUBTYPE | 19 => Ok(Record::MultipleResponse(MultipleResponseRecord::parse(&extension, endian, |_| ())?)),
-            LongStringValueLabelRecord::SUBTYPE => Ok(Record::LongStringValueLabels(LongStringValueLabelRecord::parse(&extension, endian, |_| ())?)),
-            EncodingRecord::SUBTYPE => Ok(Record::Encoding(EncodingRecord::parse(&extension, endian, |_| ())?)),
-            NumberOfCasesRecord::SUBTYPE => Ok(Record::NumberOfCases(NumberOfCasesRecord::parse(&extension, endian, |_| ())?)),
-            x if x == TextExtensionSubtype::VariableSets as u32 => Ok(Record::VariableSets(UnencodedString(extension.data))),
-            x if x == TextExtensionSubtype::ProductInfo as u32 => Ok(Record::ProductInfo(UnencodedString(extension.data))),
-            x if x == TextExtensionSubtype::LongNames as u32 => Ok(Record::LongNames(UnencodedString(extension.data))),
-            x if x == TextExtensionSubtype::LongStrings as u32 => Ok(Record::LongStrings(UnencodedString(extension.data))),
-            x if x == TextExtensionSubtype::FileAttributes as u32 => Ok(Record::FileAttributes(UnencodedString(extension.data))),
-            x if x == TextExtensionSubtype::VariableAttributes as u32 => Ok(Record::VariableAttributes(UnencodedString(extension.data))),
-            _ => Ok(Record::Extension(extension))
+            IntegerInfo::SUBTYPE => Ok(Record::IntegerInfo(IntegerInfo::parse(
+                &extension,
+                endian,
+                |_| (),
+            )?)),
+            FloatInfo::SUBTYPE => Ok(Record::FloatInfo(FloatInfo::parse(
+                &extension,
+                endian,
+                |_| (),
+            )?)),
+            VarDisplayRecord::SUBTYPE => Ok(Record::VarDisplay(VarDisplayRecord::parse(
+                &extension,
+                endian,
+                |_| (),
+            )?)),
+            MultipleResponseRecord::SUBTYPE | 19 => Ok(Record::MultipleResponse(
+                MultipleResponseRecord::parse(&extension, endian, |_| ())?,
+            )),
+            LongStringValueLabelRecord::SUBTYPE => Ok(Record::LongStringValueLabels(
+                LongStringValueLabelRecord::parse(&extension, endian, |_| ())?,
+            )),
+            EncodingRecord::SUBTYPE => Ok(Record::Encoding(EncodingRecord::parse(
+                &extension,
+                endian,
+                |_| (),
+            )?)),
+            NumberOfCasesRecord::SUBTYPE => Ok(Record::NumberOfCases(NumberOfCasesRecord::parse(
+                &extension,
+                endian,
+                |_| (),
+            )?)),
+            x if x == TextExtensionSubtype::VariableSets as u32 => {
+                Ok(Record::VariableSets(UnencodedString(extension.data)))
+            }
+            x if x == TextExtensionSubtype::ProductInfo as u32 => {
+                Ok(Record::ProductInfo(UnencodedString(extension.data)))
+            }
+            x if x == TextExtensionSubtype::LongNames as u32 => {
+                Ok(Record::LongNames(UnencodedString(extension.data)))
+            }
+            x if x == TextExtensionSubtype::LongStrings as u32 => {
+                Ok(Record::LongStrings(UnencodedString(extension.data)))
+            }
+            x if x == TextExtensionSubtype::FileAttributes as u32 => {
+                Ok(Record::FileAttributes(UnencodedString(extension.data)))
+            }
+            x if x == TextExtensionSubtype::VariableAttributes as u32 => {
+                Ok(Record::VariableAttributes(UnencodedString(extension.data)))
+            }
+            _ => Ok(Record::OtherExtension(extension)),
         }
     }
 }