work
[pspp] / rust / src / format.rs
index 857c05e67f16490ea4dc7b7ac55ee3e21816469a..0fc82a3987670b0cf08b79e1d386df78057d0a5d 100644 (file)
@@ -5,7 +5,10 @@ use std::{
 
 use thiserror::Error as ThisError;
 
-use crate::raw::{VarType, self};
+use crate::{
+    cooked::VarWidth,
+    raw::{self, VarType},
+};
 
 #[derive(ThisError, Debug)]
 pub enum Error {
@@ -173,8 +176,6 @@ pub enum Format {
     AHex,
 }
 
-pub const MAX_STRING: Width = 32767;
-
 pub type Width = u16;
 pub type SignedWidth = i16;
 
@@ -185,8 +186,8 @@ impl Format {
         match self {
             Self::P | Self::PK | Self::PIBHex | Self::RBHex => 16,
             Self::IB | Self::PIB | Self::RB => 8,
-            Self::A => MAX_STRING,
-            Self::AHex => MAX_STRING * 2,
+            Self::A => 32767,
+            Self::AHex => 32767 * 2,
             _ => 40,
         }
     }
@@ -305,10 +306,12 @@ impl Format {
     pub fn var_type(self) -> VarType {
         match self {
             Self::A | Self::AHex => VarType::String,
-            _ => VarType::Number,
+            _ => VarType::Numeric,
         }
     }
 
+    /// Checks whether this format is valid for a variable with the given
+    /// `var_type`.
     pub fn check_type_compatibility(
         self,
         variable: Option<&str>,
@@ -316,7 +319,7 @@ impl Format {
     ) -> Result<(), Error> {
         let my_type = self.var_type();
         match (my_type, var_type) {
-            (VarType::Number, VarType::String) => {
+            (VarType::Numeric, VarType::String) => {
                 if let Some(variable) = variable {
                     Err(Error::NamedVariableNotCompatibleWithNumericFormat {
                         variable: variable.into(),
@@ -326,7 +329,7 @@ impl Format {
                     Err(Error::UnnamedVariableNotCompatibleWithNumericFormat(self))
                 }
             }
-            (VarType::String, VarType::Number) => {
+            (VarType::String, VarType::Numeric) => {
                 if let Some(variable) = variable {
                     Err(Error::NamedVariableNotCompatibleWithStringFormat {
                         variable: variable.into(),
@@ -404,10 +407,18 @@ impl Spec {
         self.d
     }
 
-    pub fn default_for_width(w: Width) -> Self {
-        match w {
-            0 => Spec { format: Format::F, w: 8, d: 2 },
-            _ => Spec { format: Format::A, w: w, d: 0 },
+    pub fn default_for_width(var_width: VarWidth) -> Self {
+        match var_width {
+            VarWidth::Numeric => Spec {
+                format: Format::F,
+                w: 8,
+                d: 2,
+            },
+            VarWidth::String(w) => Spec {
+                format: Format::A,
+                w,
+                d: 0,
+            },
         }
     }
 
@@ -425,11 +436,11 @@ impl Spec {
         Self { format, w, d }
     }
 
-    pub fn var_width(self) -> Width {
+    pub fn var_width(self) -> VarWidth {
         match self.format {
-            Format::A => self.w,
-            Format::AHex => self.w / 2,
-            _ => 0,
+            Format::A => VarWidth::String(self.w),
+            Format::AHex => VarWidth::String(self.w / 2),
+            _ => VarWidth::Numeric,
         }
     }
 
@@ -437,33 +448,43 @@ impl Spec {
         self.format.var_type()
     }
 
-    pub fn check_width_compatibility(self, variable: Option<&str>, w: Width) -> Result<Self, Error> {
-        self.format.check_type_compatibility(variable, self.var_type())?;
-        let expected_width = self.var_width();
-        if w != expected_width {
-            let bad_spec = self;
-            let good_spec = if self.format == Format::A {
-                Spec { w, ..self }
-            } else {
-                Spec { w: w * 2, ..self }
-            };
-            if let Some(variable) = variable {
-                Err(Error::NamedStringVariableBadSpecWidth {
-                    variable: variable.into(),
-                    width: w,
-                    bad_spec,
-                    good_spec,
-                })
-            } else {
-                Err(Error::UnnamedStringVariableBadSpecWidth {
-                    width: w,
-                    bad_spec,
-                    good_spec,
-                })
+    /// Checks whether this format specification is valid for a variable with
+    /// width `var_width`.
+    pub fn check_width_compatibility(
+        self,
+        variable: Option<&str>,
+        var_width: VarWidth,
+    ) -> Result<Self, Error> {
+        // Verify that the format is right for the variable's type.
+        self.format
+            .check_type_compatibility(variable, var_width.into())?;
+
+        if let VarWidth::String(w) = var_width {
+            if var_width != self.var_width() {
+                let bad_spec = self;
+                let good_spec = if self.format == Format::A {
+                    Spec { w, ..self }
+                } else {
+                    Spec { w: w * 2, ..self }
+                };
+                if let Some(variable) = variable {
+                    return Err(Error::NamedStringVariableBadSpecWidth {
+                        variable: variable.into(),
+                        width: w,
+                        bad_spec,
+                        good_spec,
+                    });
+                } else {
+                    return Err(Error::UnnamedStringVariableBadSpecWidth {
+                        width: w,
+                        bad_spec,
+                        good_spec,
+                    });
+                }
             }
-        } else {
-            Ok(self)
         }
+
+        Ok(self)
     }
 }