work
authorBen Pfaff <blp@cs.stanford.edu>
Tue, 20 May 2025 23:56:04 +0000 (16:56 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Tue, 20 May 2025 23:56:04 +0000 (16:56 -0700)
rust/pspp/src/dictionary.rs
rust/pspp/src/output/pivot/mod.rs
rust/pspp/src/sys/raw.rs

index cbbde90349e636923ec88ad7edb7d28cf3d9a858..6994d1289c67d61c2655570e3a7d672c8aca8d8b 100644 (file)
@@ -21,7 +21,10 @@ use unicase::UniCase;
 use crate::{
     format::Format,
     identifier::{ByIdentifier, HasIdentifier, Identifier},
-    output::pivot::{Axis3, Dimension, Group, PivotTable, Value as PivotValue},
+    output::pivot::{
+        Axis3, Dimension, Footnote, Footnotes, Group, PivotTable, Value as PivotValue,
+    },
+    settings::Show,
     sys::raw::{Alignment, CategoryLabels, Measure, MissingValues, RawString, VarType},
 };
 
@@ -371,7 +374,7 @@ impl Dictionary {
     ///
     /// The operation fails if the dictionary already contains a variable with
     /// the same name (or a variant with different case), or if `variable`'s
-    /// encoding differs from the dictionary's
+    /// encoding differs from the dictionary's.
     pub fn add_var(&mut self, variable: Variable) -> Result<DictIndex, AddVarError> {
         if variable.encoding != self.encoding {
             Err(AddVarError::WrongEncoding {
@@ -650,12 +653,15 @@ impl<'a> OutputValueLabels<'a> {
         }
 
         let mut values = Group::new("Variable Value").with_label_shown();
+        let mut footnotes = Footnotes::new();
+        let missing_footnote = footnotes.push(Footnote::new("User-missing value"));
         for variable in &self.dictionary.variables {
             let mut group = Group::new(&**variable);
             let mut values = variable.value_labels.iter().collect::<Vec<_>>();
             values.sort();
             for (value, label) in values {
-                let value = PivotValue::new_variable(variable);
+                let value = PivotValue::new_variable_value(variable, value)
+                    .with_show_value_label(Some(Show::Value));
                 //group.push();
                 todo!()
             }
index 5d5a3d0ac148bf35fb52839c1a5723b43ae75e0f..e4225c73fac665ff179491ddb9784996c053771f 100644 (file)
@@ -1741,7 +1741,34 @@ impl Value {
         }
     }
     pub fn new_variable_value(variable: &Variable, value: &DataValue) -> Self {
-        todo!()
+        let var_name = Some(variable.name.as_str().into());
+        let value_label = variable.value_labels.get(value).cloned();
+        match value {
+            DataValue::Number(number) => Self::new(ValueInner::Number(NumberValue {
+                show: None,
+                format: match variable.print_format.var_type() {
+                    VarType::Numeric => variable.print_format,
+                    VarType::String => {
+                        #[cfg(debug_assertions)]
+                        panic!("cannot create numeric pivot value with string format");
+
+                        #[cfg(not(debug_assertions))]
+                        Format::F8_2
+                    }
+                },
+                honor_small: false,
+                value: *number,
+                var_name,
+                value_label,
+            })),
+            DataValue::String(string) => Self::new(ValueInner::String(StringValue {
+                show: None,
+                hex: variable.print_format.type_() == Type::AHex,
+                s: string.decode(variable.encoding).into_owned(),
+                var_name,
+                value_label,
+            })),
+        }
     }
     pub fn new_number(x: Option<f64>) -> Self {
         Self::new_number_with_format(x, Format::F8_2)
@@ -1762,9 +1789,30 @@ impl Value {
         }))
     }
     pub fn with_footnote(mut self, footnote: &Arc<Footnote>) -> Self {
+        self.add_footnote(footnote);
+        self
+    }
+    pub fn add_footnote(&mut self, footnote: &Arc<Footnote>) {
         let footnotes = &mut self.styling.get_or_insert_default().footnotes;
         footnotes.push(footnote.clone());
         footnotes.sort_by_key(|f| f.index);
+    }
+    pub fn with_show_value_label(mut self, show: Option<Show>) -> Self {
+        match &mut self.inner {
+            ValueInner::Number(number_value) => {
+                number_value.show = show;
+            }
+            ValueInner::String(string_value) => {
+                string_value.show = show;
+            }
+            _ => (),
+        }
+        self
+    }
+    pub fn with_show_variable_label(mut self, show: Option<Show>) -> Self {
+        if let ValueInner::Variable(variable_value) = &mut self.inner {
+            variable_value.show = show;
+        }
         self
     }
     pub const fn empty() -> Self {
index fde85784e36f28f3a2b39365ba5ea491540786fc..ad879dfa1b029b1117f6e67cafcca87878c4340d 100644 (file)
@@ -1145,6 +1145,17 @@ impl MissingValues {
         self.values.is_empty() && self.range.is_none()
     }
 
+    pub fn contains(&self, value: &Value) -> bool {
+        if self.values.contains(value) {
+            return true;
+        }
+
+        match value {
+            Value::Number(Some(number)) => self.range.is_some_and(|range| range.contains(*number)),
+            _ => false,
+        }
+    }
+
     fn read<R: Read + Seek>(
         r: &mut R,
         offset: u64,
@@ -1271,6 +1282,14 @@ impl MissingValueRange {
             MissingValueRange::From { .. } => None,
         }
     }
+
+    pub fn contains(&self, number: f64) -> bool {
+        match self {
+            MissingValueRange::In { low, high } => (*low..*high).contains(&number),
+            MissingValueRange::From { low } => number >= *low,
+            MissingValueRange::To { high } => number <= *high,
+        }
+    }
 }
 
 impl Display for MissingValueRange {