continue implementation
[pspp] / rust / src / raw.rs
index 544481906f20153aabd415e50f507b198375c73f..986bb92a52bb389726c8875582130305aa3772d2 100644 (file)
@@ -1,4 +1,5 @@
 use crate::{
+    cooked::VarWidth,
     endian::{Endian, Parse, ToBytes},
     identifier::{Error as IdError, Identifier},
 };
@@ -185,6 +186,9 @@ pub enum Error {
     #[error("Invalid multiple response set variable name.  {0}")]
     InvalidMrSetVariableName(IdError),
 
+    #[error("Invalid variable name in long string missing values record.  {0}")]
+    InvalidLongStringMissingValueVariableName(IdError),
+
     #[error("Details TBD")]
     TBD,
 }
@@ -398,7 +402,7 @@ impl HeaderRecord<RawString> {
         })
     }
 
-    fn decode<'a>(&'a self, decoder: &Decoder) -> HeaderRecord<Cow<'a, str>> {
+    pub fn decode<'a>(&'a self, decoder: &Decoder) -> HeaderRecord<Cow<'a, str>> {
         let eye_catcher = decoder.decode(&self.eye_catcher);
         let file_label = decoder.decode(&self.file_label);
         let creation_date = decoder.decode(&self.creation_date);
@@ -421,9 +425,9 @@ impl HeaderRecord<RawString> {
     }
 }
 
-struct Decoder {
-    encoding: &'static Encoding,
-    warn: Box<dyn Fn(Error)>,
+pub struct Decoder {
+    pub encoding: &'static Encoding,
+    pub warn: Box<dyn Fn(Error)>,
 }
 
 impl Decoder {
@@ -552,14 +556,14 @@ pub enum VarType {
 }
 
 impl VarType {
-    fn from_width(width: i32) -> VarType {
+    pub fn from_width(width: VarWidth) -> VarType {
         match width {
-            0 => VarType::Numeric,
-            _ => VarType::String,
+            VarWidth::Numeric => Self::Numeric,
+            VarWidth::String(_) => Self::String,
         }
     }
 
-    fn opposite(self) -> VarType {
+    pub fn opposite(self) -> VarType {
         match self {
             Self::Numeric => Self::String,
             Self::String => Self::Numeric,
@@ -848,7 +852,11 @@ where
                 };
                 match record {
                     Record::Variable(VariableRecord { width, .. }) => {
-                        self.var_types.push(VarType::from_width(width));
+                        self.var_types.push(if width == 0 {
+                            VarType::Numeric
+                        } else {
+                            VarType::String
+                        });
                     }
                     Record::EndOfHeaders(_) => {
                         self.state = if let Some(Compression::ZLib) = self.header.compression {
@@ -1016,7 +1024,7 @@ fn format_name(type_: u32) -> Cow<'static, str> {
 }
 
 #[derive(Clone)]
-pub struct MissingValues<S>
+pub struct MissingValues<S = String>
 where
     S: Debug,
 {
@@ -1063,6 +1071,18 @@ where
     }
 }
 
+impl<S> Default for MissingValues<S>
+where
+    S: Debug,
+{
+    fn default() -> Self {
+        Self {
+            values: Vec::new(),
+            range: None,
+        }
+    }
+}
+
 impl MissingValues<RawStr<8>> {
     fn read<R: Read + Seek>(
         r: &mut R,
@@ -1079,7 +1099,11 @@ impl MissingValues<RawStr<8>> {
             (_, _) => return Err(Error::BadStringMissingValueCode { offset, code }),
         };
 
-        let var_type = VarType::from_width(width);
+        let var_type = if width == 0 {
+            VarType::Numeric
+        } else {
+            VarType::String
+        };
 
         let mut values = Vec::new();
         for _ in 0..n_values {
@@ -1209,7 +1233,7 @@ impl VariableRecord<RawString, RawStr<8>> {
         }))
     }
 
-    fn decode<'a>(&'a self, decoder: &Decoder) -> VariableRecord<Cow<'a, str>, String> {
+    pub fn decode<'a>(&'a self, decoder: &Decoder) -> VariableRecord<Cow<'a, str>, String> {
         VariableRecord {
             offsets: self.offsets.clone(),
             width: self.width,
@@ -1506,7 +1530,7 @@ impl DocumentRecord<RawDocumentLine> {
         }
     }
 
-    fn decode<'a>(&'a self, decoder: &Decoder) -> DocumentRecord<Cow<'a, str>> {
+    pub fn decode<'a>(&'a self, decoder: &Decoder) -> DocumentRecord<Cow<'a, str>> {
         DocumentRecord {
             offsets: self.offsets.clone(),
             lines: self
@@ -1792,6 +1816,13 @@ pub enum Measure {
 }
 
 impl Measure {
+    pub fn default_for_type(var_type: VarType) -> Option<Measure> {
+        match var_type {
+            VarType::Numeric => None,
+            VarType::String => Some(Self::Nominal),
+        }
+    }
+
     fn try_decode(source: u32) -> Result<Option<Measure>, Error> {
         match source {
             0 => Ok(None),
@@ -1820,6 +1851,13 @@ impl Alignment {
             _ => Err(Error::InvalidAlignment(source)),
         }
     }
+
+    pub fn default_for_type(var_type: VarType) -> Self {
+        match var_type {
+            VarType::Numeric => Self::Right,
+            VarType::String => Self::Left,
+        }
+    }
 }
 
 #[derive(Clone, Debug)]
@@ -1892,11 +1930,14 @@ where
 }
 
 impl LongStringMissingValues<RawString, RawStr<8>> {
-    fn decode<'a>(&self, decoder: &Decoder) -> LongStringMissingValues<String, String> {
-        LongStringMissingValues {
-            var_name: decoder.decode(&self.var_name).to_string(),
+    fn decode<'a>(
+        &self,
+        decoder: &Decoder,
+    ) -> Result<LongStringMissingValues<Identifier, String>, IdError> {
+        Ok(LongStringMissingValues {
+            var_name: decoder.decode_identifier(&self.var_name)?,
             missing_values: self.missing_values.decode(decoder),
-        }
+        })
     }
 }
 
@@ -1959,8 +2000,21 @@ impl ExtensionRecord for LongStringMissingValueRecord<RawString, RawStr<8>> {
 }
 
 impl LongStringMissingValueRecord<RawString, RawStr<8>> {
-    fn decode<'a>(&self, decoder: &Decoder) -> LongStringMissingValueRecord<String, String> {
-        LongStringMissingValueRecord(self.0.iter().map(|mv| mv.decode(decoder)).collect())
+    pub fn decode<'a>(
+        &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
+                .decode(decoder)
+                .map_err(|err| Error::InvalidLongStringMissingValueVariableName(err))
+                .warn_on_error(&decoder.warn)
+            {
+                mvs.push(mv);
+            }
+        }
+        LongStringMissingValueRecord(mvs)
     }
 }