work
[pspp] / rust / src / raw.rs
index 4ab11c3043dcf4205601df0a0c10f767ada7b07e..ba7124a3856c9e9f18c384aed5f5fed787242e8a 100644 (file)
@@ -1,4 +1,7 @@
-use crate::endian::{Endian, Parse, ToBytes};
+use crate::{
+    endian::{Endian, Parse, ToBytes},
+    identifier::{Error as IdError, Identifier},
+};
 
 use encoding_rs::{mem::decode_latin1, DecoderResult, Encoding};
 use flate2::read::ZlibDecoder;
@@ -158,6 +161,24 @@ pub enum Error {
     #[error("Invalid variable display alignment value {0}")]
     InvalidAlignment(u32),
 
+    #[error("Invalid attribute name.  {0}")]
+    InvalidAttributeName(IdError),
+
+    #[error("Invalid variable name in attribute record.  {0}")]
+    InvalidAttributeVariableName(IdError),
+
+    #[error("Invalid short name in long variable name record.  {0}")]
+    InvalidShortName(IdError),
+
+    #[error("Invalid name in long variable name record.  {0}")]
+    InvalidLongName(IdError),
+
+    #[error("Invalid variable name in very long string record.  {0}")]
+    InvalidLongStringName(IdError),
+
+    #[error("Invalid variable name in variable set record.  {0}")]
+    InvalidVariableSetName(IdError),
+
     #[error("Details TBD")]
     TBD,
 }
@@ -451,6 +472,14 @@ impl Decoder {
             output.into()
         }
     }
+
+    pub fn decode_identifier(&self, input: &RawString) -> Result<Identifier, IdError> {
+        self.new_identifier(&self.decode(input))
+    }
+
+    pub fn new_identifier(&self, name: &str) -> Result<Identifier, IdError> {
+        Identifier::new(name, self.encoding)
+    }
 }
 
 impl<S> Header for HeaderRecord<S>
@@ -1998,17 +2027,16 @@ impl TextRecord {
             TextRecordType::FileAttributes => {
                 Ok(FileAttributeRecord::decode(self, decoder).map(|fa| Record::FileAttributes(fa)))
             }
-            TextRecordType::VariableAttributes => {
-                Ok(Some(Record::VariableAttributes(
-VariableAttributeRecord::decode(self, decoder))))
-            }
+            TextRecordType::VariableAttributes => Ok(Some(Record::VariableAttributes(
+                VariableAttributeRecord::decode(self, decoder),
+            ))),
         }
     }
 }
 
 #[derive(Clone, Debug)]
 pub struct VeryLongString {
-    pub short_name: String,
+    pub short_name: Identifier,
     pub length: u16,
 }
 
@@ -2017,17 +2045,37 @@ impl VeryLongString {
         let Some((short_name, length)) = input.split_once('=') else {
             return Err(Error::TBD);
         };
+        let short_name = decoder
+            .new_identifier(short_name)
+            .map_err(Error::InvalidLongStringName)?;
         let length = length.parse().map_err(|_| Error::TBD)?;
-        Ok(VeryLongString {
-            short_name: short_name.into(),
-            length,
-        })
+        Ok(VeryLongString { short_name, length })
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct VeryLongStringsRecord(Vec<VeryLongString>);
+
+impl VeryLongStringsRecord {
+    fn decode(source: &TextRecord, decoder: &Decoder) -> Self {
+        let input = decoder.decode(&source.text);
+        let mut very_long_strings = Vec::new();
+        for tuple in input
+            .split('\0')
+            .map(|s| s.trim_end_matches('\t'))
+            .filter(|s| !s.is_empty())
+        {
+            if let Some(vls) = VeryLongString::parse(decoder, tuple).warn_on_error(&decoder.warn) {
+                very_long_strings.push(vls)
+            }
+        }
+        VeryLongStringsRecord(very_long_strings)
     }
 }
 
 #[derive(Clone, Debug)]
 pub struct Attribute {
-    pub name: String,
+    pub name: Identifier,
     pub values: Vec<String>,
 }
 
@@ -2036,6 +2084,9 @@ impl Attribute {
         let Some((name, mut input)) = input.split_once('(') else {
             return Err(Error::TBD);
         };
+        let name = decoder
+            .new_identifier(name)
+            .map_err(Error::InvalidAttributeName)?;
         let mut values = Vec::new();
         loop {
             let Some((value, rest)) = input.split_once('\n') else {
@@ -2051,10 +2102,7 @@ impl Attribute {
                 values.push(value.into());
             }
             if let Some(rest) = rest.strip_prefix(')') {
-                let attribute = Attribute {
-                    name: name.into(),
-                    values,
-                };
+                let attribute = Attribute { name, values };
                 return Ok((attribute, rest));
             };
             input = rest;
@@ -2107,7 +2155,7 @@ impl FileAttributeRecord {
 
 #[derive(Clone, Debug)]
 pub struct VarAttributeSet {
-    pub long_var_name: String,
+    pub long_var_name: Identifier,
     pub attributes: AttributeSet,
 }
 
@@ -2116,9 +2164,12 @@ impl VarAttributeSet {
         let Some((long_var_name, rest)) = input.split_once(':') else {
             return Err(Error::TBD);
         };
+        let long_var_name = decoder
+            .new_identifier(long_var_name)
+            .map_err(Error::InvalidAttributeVariableName)?;
         let (attributes, rest) = AttributeSet::parse(decoder, rest, Some('/'))?;
         let var_attribute = VarAttributeSet {
-            long_var_name: long_var_name.into(),
+            long_var_name,
             attributes,
         };
         Ok((var_attribute, rest))
@@ -2147,29 +2198,27 @@ impl VariableAttributeRecord {
 }
 
 #[derive(Clone, Debug)]
-pub struct VeryLongStringsRecord(Vec<VeryLongString>);
-
-impl VeryLongStringsRecord {
-    fn decode(source: &TextRecord, decoder: &Decoder) -> Self {
-        let input = decoder.decode(&source.text);
-        let mut very_long_strings = Vec::new();
-        for tuple in input
-            .split('\0')
-            .map(|s| s.trim_end_matches('\t'))
-            .filter(|s| !s.is_empty())
-        {
-            if let Some(vls) = VeryLongString::parse(decoder, tuple).warn_on_error(&decoder.warn) {
-                very_long_strings.push(vls)
-            }
-        }
-        VeryLongStringsRecord(very_long_strings)
-    }
+pub struct LongName {
+    pub short_name: Identifier,
+    pub long_name: Identifier,
 }
 
-#[derive(Clone, Debug)]
-pub struct LongName {
-    pub short_name: String,
-    pub long_name: String,
+impl LongName {
+    fn parse(input: &str, decoder: &Decoder) -> Result<Self, Error> {
+        let Some((short_name, long_name)) = input.split_once('=') else {
+            return Err(Error::TBD);
+        };
+        let short_name = decoder
+            .new_identifier(short_name)
+            .map_err(Error::InvalidShortName)?;
+        let long_name = decoder
+            .new_identifier(long_name)
+            .map_err(Error::InvalidLongName)?;
+        Ok(LongName {
+            short_name,
+            long_name,
+        })
+    }
 }
 
 #[derive(Clone, Debug)]
@@ -2180,13 +2229,8 @@ impl LongNamesRecord {
         let input = decoder.decode(&source.text);
         let mut names = Vec::new();
         for pair in input.split('\t').filter(|s| !s.is_empty()) {
-            if let Some((short_name, long_name)) = pair.split_once('=') {
-                names.push(LongName {
-                    short_name: short_name.into(),
-                    long_name: long_name.into(),
-                });
-            } else {
-                decoder.warn(Error::TBD)
+            if let Some(long_name) = LongName::parse(pair, decoder).warn_on_error(&decoder.warn) {
+                names.push(long_name);
             }
         }
         LongNamesRecord(names)
@@ -2205,13 +2249,22 @@ impl ProductInfoRecord {
 #[derive(Clone, Debug)]
 pub struct VariableSet {
     pub name: String,
-    pub vars: Vec<String>,
+    pub vars: Vec<Identifier>,
 }
 
 impl VariableSet {
-    fn parse(input: &str) -> Result<Self, Error> {
+    fn parse(input: &str, decoder: &Decoder) -> Result<Self, Error> {
         let (name, input) = input.split_once('=').ok_or(Error::TBD)?;
-        let vars = input.split_ascii_whitespace().map(String::from).collect();
+        let mut vars = Vec::new();
+        for var in input.split_ascii_whitespace() {
+            if let Some(identifier) = decoder
+                .new_identifier(var)
+                .map_err(Error::InvalidVariableSetName)
+                .warn_on_error(&decoder.warn)
+            {
+                vars.push(identifier);
+            }
+        }
         Ok(VariableSet {
             name: name.into(),
             vars,
@@ -2230,7 +2283,7 @@ impl VariableSetRecord {
         let mut sets = Vec::new();
         let input = decoder.decode(&source.text);
         for line in input.lines() {
-            if let Some(set) = VariableSet::parse(line).warn_on_error(&decoder.warn) {
+            if let Some(set) = VariableSet::parse(line, decoder).warn_on_error(&decoder.warn) {
                 sets.push(set)
             }
         }