Skip to main content

pspp/output/pivot/
value.rs

1// PSPP - a program for statistical analysis.
2// Copyright (C) 2025 Free Software Foundation, Inc.
3//
4// This program is free software: you can redistribute it and/or modify it under
5// the terms of the GNU General Public License as published by the Free Software
6// Foundation, either version 3 of the License, or (at your option) any later
7// version.
8//
9// This program is distributed in the hope that it will be useful, but WITHOUT
10// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
12// details.
13//
14// You should have received a copy of the GNU General Public License along with
15// this program.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Data cell contents.
18//!
19//! This module contains [Value], which is the contents of a single pivot table
20//! cell, plus what it in turn contains.
21
22// Warn about missing docs, but not for items declared with `#[cfg(test)]`.
23#![cfg_attr(not(test), warn(missing_docs))]
24#![warn(dead_code)]
25
26use crate::{
27    calendar::{date_time_to_pspp, time_to_pspp},
28    data::{ByteString, Datum, EncodedString, WithEncoding},
29    format::{self, DATETIME40_0, Decimal, F8_2, F40, Format, TIME40_0, Type, UncheckedFormat},
30    output::pivot::{
31        Footnote, FootnoteMarkerType,
32        look::{CellStyle, FontStyle},
33    },
34    settings::{Settings, Show},
35    spv::html::Markup,
36    variable::{VarType, Variable},
37};
38use chrono::{NaiveDateTime, NaiveTime};
39use itertools::Itertools;
40use serde::{
41    Serialize, Serializer,
42    ser::{SerializeMap, SerializeStruct},
43};
44use std::{
45    borrow::Borrow,
46    fmt::{Debug, Display, Write},
47    iter::{once, repeat},
48    sync::Arc,
49};
50
51/// The content of a single pivot table cell.
52///
53/// A [Value] is also a pivot table's title, caption, footnote marker and
54/// contents, and so on.
55#[derive(Clone, Default, PartialEq)]
56pub struct Value {
57    /// Content.
58    pub inner: ValueInner,
59
60    /// Optional styling.
61    pub styling: Option<Box<ValueStyle>>,
62}
63
64impl Serialize for Value {
65    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
66    where
67        S: serde::Serializer,
68    {
69        self.inner.serialize(serializer)
70    }
71}
72/// Wrapper for [Value] that serializes in a plain way:
73///
74/// - Numbers: The number.
75/// - Strings: The string.
76/// - Variables: The variable name.
77/// - Text: The localized text string.
78/// - Markup: A string containing HTML for the markup.
79/// - Template: The formatted template string.
80/// - Empty: `()`.
81#[derive(Copy, Clone, Debug, Default, PartialEq)]
82pub struct BareValue<T>(pub T);
83impl<T> Serialize for BareValue<T>
84where
85    T: Borrow<Value>,
86{
87    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
88    where
89        S: Serializer,
90    {
91        let value = self.0.borrow();
92        match &value.inner {
93            ValueInner::Datum(datum_value) => datum_value.serialize_bare(serializer),
94            ValueInner::Variable(variable_value) => variable_value.var_name.serialize(serializer),
95            ValueInner::Text(text_value) => text_value.localized.serialize(serializer),
96            ValueInner::Markup(markup) => markup.serialize(serializer),
97            ValueInner::Template(_) => value.display(()).to_string().serialize(serializer),
98            ValueInner::Empty => ().serialize(serializer),
99        }
100    }
101}
102impl Value {
103    /// Constructs a new `Value`, initially with no styling.
104    ///
105    /// Usually one of the other constructors is more convenient.
106    pub fn new(inner: ValueInner) -> Self {
107        Self {
108            inner,
109            styling: None,
110        }
111    }
112
113    /// Constructs a new `Value` from `number` with a default [F8_2] format.
114    /// Some related useful methods are:
115    ///
116    /// - [with_source_variable], to add information about the variable that the
117    ///   datum came from (or use [new_datum_from_variable] as a shortcut to
118    ///   combine both).
119    ///
120    /// - [with_format] to override the default format.
121    ///
122    /// [with_source_variable]: Self::with_source_variable
123    /// [new_datum_from_variable]: Self::new_datum_from_variable
124    /// [with_format]: Self::with_format
125    pub fn new_number(number: Option<f64>) -> Self {
126        Self::new(ValueInner::Datum(DatumValue::new_number(number)))
127    }
128
129    /// Construct a new `Value` from `number` with format [F40].
130    pub fn new_integer(x: Option<f64>) -> Self {
131        Self::new_number(x).with_format(F40)
132    }
133
134    /// Constructs a new `Value` as a number whose value is `date_time`, which
135    /// is converted to the [PSPP date representation](crate::calendar), with
136    /// format [DATETIME40_0].
137    pub fn new_date(date_time: NaiveDateTime) -> Self {
138        Self::new_number(Some(date_time_to_pspp(date_time))).with_format(DATETIME40_0)
139    }
140
141    /// Constructs a new `Value` as a number whose value is `time`, which is
142    /// converted to the [PSPP time representation](crate::calendar), with
143    /// format [TIME40_0].
144    pub fn new_time(time: NaiveTime) -> Self {
145        Self::new_number(Some(time_to_pspp(time))).with_format(TIME40_0)
146    }
147
148    /// Constructs a new `Value` from localizable text string `s`.
149    ///
150    /// PSPP doesn't support internationalization yet, so this does the same
151    /// thing as [new_user_text] for now.
152    ///
153    /// [new_user_text]: Self::new_user_text
154    pub fn new_text(s: impl Into<String>) -> Self {
155        Self::new_user_text(s)
156    }
157
158    /// Constructs a new `Value` from localizable text string `localized`,
159    /// English string `c`, and identifier `id`.  If the string came from the
160    /// user, `user_provided` should be true.
161    pub fn new_general_text(localized: String, c: String, id: String, user_provided: bool) -> Self {
162        Self::new(ValueInner::Text(TextValue {
163            user_provided,
164            c: (c != localized).then_some(c),
165            id: (id != localized).then_some(id),
166            localized,
167        }))
168    }
169
170    /// Constructs a new `Value` from `markup`.
171    pub fn new_markup(markup: Markup) -> Self {
172        Self::new(ValueInner::Markup(markup))
173    }
174
175    /// Constructs a new text `Value` from `s`, which should have been provided
176    /// by the user.
177    pub fn new_user_text(s: impl Into<String>) -> Self {
178        Self::new(ValueInner::new_user_text(s))
179    }
180
181    /// Constructs a new `Value` from `variable`.
182    pub fn new_variable(variable: &Variable) -> Self {
183        Self::new(ValueInner::Variable(VariableValue {
184            show: None,
185            var_name: String::from(variable.name.as_str()),
186            variable_label: variable.label.clone(),
187        }))
188    }
189
190    /// Constructs a new `Value` from `datum` with a default format.  Some
191    /// related useful methods are:
192    ///
193    /// - [with_source_variable], to add information about the variable that the
194    ///   datum came from (or use [new_datum_from_variable] as a shortcut to
195    ///   combine both).
196    ///
197    /// - [with_format] to override the default format.
198    ///
199    /// [with_source_variable]: Self::with_source_variable
200    /// [new_datum_from_variable]: Self::new_datum_from_variable
201    /// [with_format]: Self::with_format
202    pub fn new_datum<B>(datum: &Datum<B>) -> Self
203    where
204        B: EncodedString,
205    {
206        Self::new(ValueInner::Datum(DatumValue::new(datum)))
207    }
208
209    /// Construct a new, empty `Value`.
210    pub const fn new_empty() -> Self {
211        // Can't use `Self::default()` because that is non-const.
212        Value {
213            inner: ValueInner::Empty,
214            styling: None,
215        }
216    }
217
218    /// Returns a reference to a statically allocated empty `Value`.
219    pub const fn static_empty() -> &'static Self {
220        static EMPTY: Value = Value::new_empty();
221        &EMPTY
222    }
223
224    /// Returns true if this `Value` is empty and unstyled.
225    pub const fn is_empty(&self) -> bool {
226        self.inner.is_empty() && self.styling.is_none()
227    }
228
229    /// Returns this value with its value label, format, and variable name from
230    /// `variable`.
231    pub fn with_source_variable(self, variable: &Variable) -> Self {
232        let value_label = self
233            .datum()
234            .and_then(|datum| variable.value_labels.get(&datum).map(String::from));
235        self.with_value_label(value_label)
236            .with_format(variable.print_format)
237            .with_variable_name(Some(variable.name.as_str().into()))
238    }
239
240    /// Returns this value with its display format set to `format`, if it is a
241    /// [DatumValue].
242    pub fn with_format(self, format: impl Into<ValueFormat>) -> Self {
243        Self {
244            inner: self.inner.with_format(format),
245            ..self
246        }
247    }
248
249    /// Construct a new `Value` from `datum`, which is a value of `variable`.
250    pub fn new_datum_from_variable(datum: &Datum<ByteString>, variable: &Variable) -> Self {
251        Self::new_datum(&datum.as_encoded(variable.encoding())).with_source_variable(variable)
252    }
253
254    /// Returns the inner [Datum], if this value is a [DatumValue].
255    pub fn datum(&self) -> Option<&Datum<WithEncoding<ByteString>>> {
256        self.inner.datum()
257    }
258
259    /// Returns this `Value` with the added `footnote`.
260    pub fn with_footnote(mut self, footnote: &Arc<Footnote>) -> Self {
261        self.add_footnote(footnote);
262        self
263    }
264
265    /// Returns this `Value` with the added `footnotes`.
266    pub fn with_footnotes<'a>(
267        mut self,
268        footnotes: impl IntoIterator<Item = &'a Arc<Footnote>>,
269    ) -> Self {
270        for footnote in footnotes {
271            self.add_footnote(footnote);
272        }
273        self
274    }
275
276    /// Adds `footnote` to this `Value`.
277    pub fn add_footnote(&mut self, footnote: &Arc<Footnote>) {
278        let footnotes = &mut self.styling_mut().footnotes;
279        footnotes.push(footnote.clone());
280        footnotes.sort_by_key(|f| f.index);
281    }
282
283    /// Removes all of the footnotes from `value`.
284    pub fn clear_footnotes(&mut self) {
285        if let Some(styling) = &mut self.styling
286            && !styling.footnotes.is_empty()
287        {
288            styling.footnotes.clear();
289            if styling.is_empty() {
290                self.styling = None;
291            }
292        }
293    }
294
295    /// Returns this `Value` with the added `subscripts`.
296    pub fn with_subscripts<'a>(
297        mut self,
298        subscripts: impl IntoIterator<Item = impl Into<String>>,
299    ) -> Self {
300        self.styling_mut()
301            .subscripts
302            .extend(subscripts.into_iter().map(|s| s.into()));
303        self
304    }
305
306    /// Returns this `Value` with the added `subscript`.
307    pub fn with_subscript(mut self, subscript: impl Into<String>) -> Self {
308        self.add_subscript(subscript);
309        self
310    }
311
312    /// Adds `subscript` to this `Value`.
313    pub fn add_subscript(&mut self, subscript: impl Into<String>) {
314        self.styling_mut().subscripts.push(subscript.into());
315    }
316
317    /// Returns this `Value` with `show` as the [Show] setting for value labels,
318    /// if this is a [DatumValue].
319    pub fn with_show_value_label(mut self, show: Option<Show>) -> Self {
320        if let Some(datum_value) = self.inner.as_datum_value_mut() {
321            datum_value.show = show;
322        }
323        self
324    }
325
326    /// Returns this `Value` with `value_label` as the value label, if this is a
327    /// [DatumValue].
328    ///
329    /// Use [with_source_variable], instead, to automatically add a value label
330    /// and other information from a source variable.
331    ///
332    /// [with_source_variable]: Self::with_source_variable
333    pub fn with_value_label(mut self, value_label: Option<String>) -> Self {
334        if let Some(datum_value) = self.inner.as_datum_value_mut() {
335            datum_value.value_label = value_label.clone()
336        }
337        self
338    }
339
340    /// Returns this `Value` with `variable_name` as the variable's name, if
341    /// this is a [DatumValue].
342    ///
343    /// Use [with_source_variable], instead, to automatically add a variable
344    /// name and other information from a source variable.
345    ///
346    /// [with_source_variable]: Self::with_source_variable
347    pub fn with_variable_name(mut self, variable_name: Option<String>) -> Self {
348        if let Some(datum_value) = self.inner.as_datum_value_mut() {
349            datum_value.variable = variable_name.clone()
350        }
351        self
352    }
353
354    /// Returns this `Value` with `show` as the [Show] setting for variable
355    /// labels, if this is a [VariableValue].
356    pub fn with_show_variable_label(mut self, show: Option<Show>) -> Self {
357        if let ValueInner::Variable(variable_value) = &mut self.inner {
358            variable_value.show = show;
359        }
360        self
361    }
362
363    /// Returns this `Value` with the specified `font_style`.
364    pub fn with_font_style(mut self, font_style: FontStyle) -> Self {
365        self.set_font_style(font_style);
366        self
367    }
368
369    /// Sets the `Value`'s font style.
370    pub fn set_font_style(&mut self, font_style: FontStyle) {
371        self.styling_mut().font_style = Some(font_style);
372    }
373
374    /// Returns this `Value` with the specified `cell_style`.
375    pub fn with_cell_style(mut self, cell_style: CellStyle) -> Self {
376        self.set_cell_style(cell_style);
377        self
378    }
379
380    /// Sets the `Value`'s cell style.
381    pub fn set_cell_style(&mut self, cell_style: CellStyle) {
382        self.styling_mut().cell_style = Some(cell_style);
383    }
384
385    /// Returns this `Value` with the specified `styling`.
386    pub fn with_styling(self, styling: Option<Box<ValueStyle>>) -> Self {
387        Self { styling, ..self }
388    }
389
390    /// Returns the styling for this `Value` for modification.
391    ///
392    /// If this `Value` doesn't have styling yet, this creates it.
393    pub fn styling_mut(&mut self) -> &mut ValueStyle {
394        self.styling.get_or_insert_default()
395    }
396
397    /// Returns this `Value`'s font style, if it has one.
398    pub fn font_style(&self) -> Option<&FontStyle> {
399        self.styling
400            .as_ref()
401            .map(|styling| styling.font_style.as_ref())
402            .flatten()
403    }
404
405    /// Returns this `Value`'s cell style, if it has one.
406    pub fn cell_style(&self) -> Option<&CellStyle> {
407        self.styling
408            .as_ref()
409            .map(|styling| styling.cell_style.as_ref())
410            .flatten()
411    }
412
413    /// Returns this `Value`'s subscripts.
414    pub fn subscripts(&self) -> &[String] {
415        self.styling
416            .as_ref()
417            .map_or(&[], |styling| &styling.subscripts)
418    }
419
420    /// Returns this `Value`'s footnotes.
421    pub fn footnotes(&self) -> &[Arc<Footnote>] {
422        self.styling
423            .as_ref()
424            .map_or(&[], |styling| &styling.footnotes)
425    }
426
427    /// Returns an object that will format this value, including subscripts and
428    /// superscripts and footnotes.  `options` controls whether variable and
429    /// value labels are included.
430    pub fn display(&self, options: impl Into<ValueOptions>) -> DisplayValue<'_> {
431        let display = self.inner.display(options);
432        match &self.styling {
433            Some(styling) => display.with_styling(styling),
434            None => display,
435        }
436    }
437
438    /// Serializes this value in a plain way, like [BareValue].  This function
439    /// can be used on a field as `#[serde(serialize_with =
440    /// Value::serialize_bare)]`.
441    pub fn serialize_bare<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
442    where
443        S: Serializer,
444    {
445        BareValue(self).serialize(serializer)
446    }
447}
448
449impl From<&str> for Value {
450    fn from(value: &str) -> Self {
451        Self::new_text(value)
452    }
453}
454
455impl From<String> for Value {
456    fn from(value: String) -> Self {
457        Self::new_text(value)
458    }
459}
460
461impl From<&Variable> for Value {
462    fn from(variable: &Variable) -> Self {
463        Self::new_variable(variable)
464    }
465}
466
467/// Helper struct for printing a [Value] with `format!` and `{}`.
468///
469/// Create this struct with [Value::display].
470#[derive(Clone, Debug)]
471pub struct DisplayValue<'a> {
472    inner: &'a ValueInner,
473    subscripts: &'a [String],
474    footnotes: &'a [Arc<Footnote>],
475    options: ValueOptions,
476    show_value: bool,
477    show_label: Option<&'a str>,
478}
479
480impl<'a> DisplayValue<'a> {
481    /// Returns the subscripts to be displayed, as an iterator of `&str`.
482    pub fn subscripts(&self) -> impl Iterator<Item = &str> + ExactSizeIterator + Clone {
483        self.subscripts.iter().map(String::as_str)
484    }
485
486    /// Returns true if the value to be displayed includes subscripts.
487    pub fn has_subscripts(&self) -> bool {
488        !self.subscripts.is_empty()
489    }
490
491    /// Returns the footnotes to be displayed, as an iterator.
492    ///
493    /// The iterator can have fewer elements than there are footnotes, because
494    /// footnotes can be hidden.
495    pub fn footnotes(&self) -> impl Iterator<Item = impl Display> + Clone {
496        self.footnotes
497            .iter()
498            .filter(|f| f.show)
499            .map(|f| f.display_marker(self.options.clone()))
500    }
501
502    /// Returns true if there are footnotes to be displayed.
503    ///
504    /// Because footnotes can be hidden, this method can return false for values
505    /// with footnotes.
506    pub fn has_footnotes(&self) -> bool {
507        self.footnotes().next().is_some()
508    }
509
510    /// Returns this [DisplayValue] modified so that it won't show any
511    /// subscripts or footnotes.
512    pub fn without_suffixes(self) -> Self {
513        Self {
514            subscripts: &[],
515            footnotes: &[],
516            ..self
517        }
518    }
519
520    /// Returns this [DisplayValue] modified so that it will only show the
521    /// suffixes and footnotes, not the body.
522    pub fn without_body(self) -> Self {
523        Self {
524            inner: &ValueInner::Empty,
525            ..self
526        }
527    }
528
529    /// Returns the [Markup] to be formatted, if any.
530    pub fn markup(&self) -> Option<&Markup> {
531        self.inner.as_markup()
532    }
533
534    /// Returns this display split into `(body, suffixes)` where `suffixes` is
535    /// subscripts and footnotes and `body` is everything else.
536    pub fn split(self) -> (Self, Self) {
537        (self.clone().without_suffixes(), self.without_body())
538    }
539
540    /// Returns this display with subscripts and footnotes taken from `styling`.
541    ///
542    /// (This display can't use the other parts of `styling`, since we're just
543    /// formatting plain text.)
544    pub fn with_styling(mut self, styling: &'a ValueStyle) -> Self {
545        self.subscripts = styling.subscripts.as_slice();
546        self.footnotes = styling.footnotes.as_slice();
547        self
548    }
549
550    /// Returns this display with the given `subscripts.`
551    pub fn with_subscripts(self, subscripts: &'a [String]) -> Self {
552        Self { subscripts, ..self }
553    }
554
555    /// Returns this display with the given `footnotes.`
556    pub fn with_footnotes(self, footnotes: &'a [Arc<Footnote>]) -> Self {
557        Self { footnotes, ..self }
558    }
559
560    /// Returns true if this display will format to the empty string.
561    pub fn is_empty(&self) -> bool {
562        self.inner.is_empty() && self.subscripts.is_empty() && self.footnotes.is_empty()
563    }
564
565    /// Returns the character that the formatted value would use for a decimal
566    /// point if it has one, or `None` if the value isn't a datum value and
567    /// therefore doesn't have a decimal point.
568    pub fn decimal(&self) -> Option<Decimal> {
569        self.inner
570            .as_datum_value()
571            .map(|datum_value| datum_value.decimal())
572    }
573
574    fn small(&self) -> f64 {
575        self.options.small
576    }
577
578    /// Returns a variable type for the value to be displayed.
579    ///
580    /// We consider a numeric value displayed by itself to be numeric, but if
581    /// the value label is displayed then it is considered to be a string.
582    /// Anything else is also a string.
583    ///
584    /// This is useful for passing to [HorzAlign::for_mixed], although maybe
585    /// this method should just return [HorzAlign] directly.
586    ///
587    /// [HorzAlign]: crate::output::pivot::look::HorzAlign
588    /// [HorzAlign::for_mixed]: crate::output::pivot::look::HorzAlign::for_mixed
589    pub fn var_type(&self) -> VarType {
590        if let Some(datum_value) = self.inner.as_datum_value()
591            && datum_value.datum.is_number()
592            && self.show_label.is_none()
593        {
594            VarType::Numeric
595        } else {
596            VarType::String
597        }
598    }
599}
600
601impl Display for DisplayValue<'_> {
602    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
603        match self.inner {
604            ValueInner::Datum(datum_value) => datum_value.display(self, f),
605            ValueInner::Variable(variable_value) => variable_value.display(self, f),
606            ValueInner::Markup(markup) => write!(f, "{markup}"),
607            ValueInner::Text(text_value) => write!(f, "{text_value}"),
608            ValueInner::Template(template_value) => template_value.display(self, f),
609            ValueInner::Empty => Ok(()),
610        }?;
611
612        for (subscript, delimiter) in self.subscripts.iter().zip(once('_').chain(repeat(','))) {
613            write!(f, "{delimiter}{subscript}")?;
614        }
615
616        if !self.footnotes.is_empty() {
617            write!(
618                f,
619                "[{}]",
620                self.footnotes
621                    .iter()
622                    .map(|f| f.display_marker(&self.options))
623                    .format(",")
624            )?;
625        }
626
627        Ok(())
628    }
629}
630
631impl Debug for Value {
632    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
633        let name = match &self.inner {
634            ValueInner::Datum(_) => "Datum",
635            ValueInner::Variable(_) => "Variable",
636            ValueInner::Text(_) => "Text",
637            ValueInner::Markup(_) => "Markup",
638            ValueInner::Template(_) => "Template",
639            ValueInner::Empty => "Empty",
640        };
641        write!(f, "{name}:{:?}", self.display(()).to_string())?;
642        if let Some(markup) = self.inner.as_markup() {
643            write!(f, " (markup: {markup:?})")?;
644        }
645        if let Some(styling) = &self.styling {
646            write!(f, " ({styling:?})")?;
647        }
648        Ok(())
649    }
650}
651
652/// A [Format] inside a [Value].
653///
654/// Most `Value`s contain ordinary [Format]s, but occasionally one will have a
655/// special format that is like [Type::F] except that nonzero numbers with
656/// magnitude below a small threshold are instead shown in scientific notation.
657#[derive(Copy, Clone, Debug, PartialEq)]
658pub enum ValueFormat {
659    /// Any ordinary format.
660    Other(Format),
661
662    /// Displays numbers smaller than [PivotTableStyle::small] in scientific
663    /// notation, and otherwise in the enclosed format (which should be
664    /// [Type::F] format).
665    ///
666    /// [PivotTableStyle::small]: super::PivotTableStyle::small
667    SmallE(Format),
668}
669
670impl ValueFormat {
671    /// Returns the inner [Format].
672    pub fn inner(&self) -> Format {
673        match self {
674            ValueFormat::Other(format) => *format,
675            ValueFormat::SmallE(format) => *format,
676        }
677    }
678
679    /// Returns this format as applied to the given `number` with `small` as the
680    /// threshold.
681    pub fn apply(&self, number: Option<f64>, small: f64) -> Format {
682        if let ValueFormat::SmallE(format) = self
683            && let Some(number) = number
684            && number != 0.0
685            && number.abs() < small
686        {
687            UncheckedFormat::new(Type::E, 40, format.d() as u8).fix()
688        } else {
689            self.inner()
690        }
691    }
692
693    /// Returns true if this is [ValueFormat::SmallE].
694    pub fn is_small_e(&self) -> bool {
695        matches!(self, ValueFormat::SmallE(_))
696    }
697}
698
699impl From<Format> for ValueFormat {
700    fn from(format: Format) -> Self {
701        Self::Other(format)
702    }
703}
704
705impl Serialize for ValueFormat {
706    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
707    where
708        S: Serializer,
709    {
710        match self {
711            ValueFormat::Other(format) => format.serialize(serializer),
712            ValueFormat::SmallE(format) => {
713                #[derive(Serialize)]
714                struct SmallE(Format);
715                SmallE(*format).serialize(serializer)
716            }
717        }
718    }
719}
720
721/// A datum and how to display it.
722#[derive(Clone, Debug, PartialEq)]
723pub struct DatumValue {
724    /// The datum.
725    pub datum: Datum<WithEncoding<ByteString>>,
726
727    /// The display format.
728    pub format: ValueFormat,
729
730    /// Whether to show `value` or `value_label` or both.
731    ///
732    /// If this is unset, then a higher-level default is used.
733    pub show: Option<Show>,
734
735    /// The name of the variable that `value` came from, if any.
736    pub variable: Option<String>,
737
738    /// The value label associated with `value`, if any.
739    pub value_label: Option<String>,
740}
741
742impl Serialize for DatumValue {
743    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
744    where
745        S: serde::Serializer,
746    {
747        if let ValueFormat::Other(format) = self.format
748            && format.type_() == Type::F
749            && self.variable.is_none()
750            && self.value_label.is_none()
751        {
752            self.datum.serialize(serializer)
753        } else {
754            let mut s = serializer.serialize_map(None)?;
755            s.serialize_entry("datum", &self.datum)?;
756            s.serialize_entry("format", &self.format)?;
757            if let Some(show) = self.show {
758                s.serialize_entry("show", &show)?;
759            }
760            if let Some(variable) = &self.variable {
761                s.serialize_entry("variable", variable)?;
762            }
763            if let Some(value_label) = &self.value_label {
764                s.serialize_entry("value_label", value_label)?;
765            }
766            s.end()
767        }
768    }
769}
770
771impl DatumValue {
772    /// Constructs a new `DatumValue` for `datum`.
773    pub fn new<B>(datum: &Datum<B>) -> Self
774    where
775        B: EncodedString,
776    {
777        Self {
778            datum: datum.cloned(),
779            format: ValueFormat::Other(F8_2),
780            show: None,
781            variable: None,
782            value_label: None,
783        }
784    }
785
786    /// Constructs a new `DatumValue` for `number`.
787    pub fn new_number(number: Option<f64>) -> Self {
788        Self::new(&Datum::<&str>::Number(number))
789    }
790
791    /// Returns this `DatumValue` with the given `format`.
792    pub fn with_format(self, format: ValueFormat) -> Self {
793        Self { format, ..self }
794    }
795
796    /// Writes this value to `f` using the settings in `display`.
797    pub fn display<'a>(
798        &self,
799        display: &DisplayValue<'a>,
800        f: &mut std::fmt::Formatter<'_>,
801    ) -> std::fmt::Result {
802        if display.show_value {
803            match &self.datum {
804                Datum::Number(number) => {
805                    let format = self.format.apply(*number, display.small());
806                    self.datum
807                        .display(format)
808                        .with_settings(&display.options.settings)
809                        .without_leading_spaces()
810                        .fmt(f)?;
811                }
812                Datum::String(s) => {
813                    if self.format.inner().type_() == Type::AHex {
814                        write!(f, "{}", s.inner.display_hex())?;
815                    } else {
816                        f.write_str(&s.as_str())?;
817                    }
818                }
819            }
820        }
821        if let Some(label) = display.show_label {
822            if display.show_value {
823                f.write_char(' ')?;
824            }
825            f.write_str(label)?;
826        }
827        Ok(())
828    }
829
830    /// Returns the decimal point used in the formatted value, if any.
831    pub fn decimal(&self) -> Decimal {
832        self.datum.display(self.format.inner()).decimal()
833    }
834
835    /// Serializes this value to `serializer` in the "bare" manner described for
836    /// [BareValue].
837    pub fn serialize_bare<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
838    where
839        S: Serializer,
840    {
841        if let Datum::Number(Some(number)) = &self.datum
842            && let number = *number
843            && number.trunc() == number
844            && number >= -(1i64 << 53) as f64
845            && number <= (1i64 << 53) as f64
846        {
847            Some(number as u64).serialize(serializer)
848        } else {
849            self.datum.serialize(serializer)
850        }
851    }
852}
853
854/// A variable name.
855#[derive(Clone, Debug, Serialize, PartialEq)]
856pub struct VariableValue {
857    /// Variable name.
858    pub var_name: String,
859
860    /// Variable label, if any.
861    pub variable_label: Option<String>,
862
863    /// Whether to show `var_name` or `variable_label` or both.
864    ///
865    /// If this is unset, then a higher-level default is used.
866    pub show: Option<Show>,
867}
868
869impl VariableValue {
870    fn display(&self, display: &DisplayValue<'_>, f: &mut std::fmt::Formatter) -> std::fmt::Result {
871        if display.show_value {
872            f.write_str(&self.var_name)?;
873        }
874        if let Some(label) = display.show_label {
875            if display.show_value {
876                f.write_char(' ')?;
877            }
878            f.write_str(label)?;
879        }
880        Ok(())
881    }
882}
883
884/// A text string.
885///
886/// A `TextValue` is used for text within a table, such as a title, a column or
887/// row heading, or a footnote.  (String data values are better represented as
888/// [DatumValue].)
889#[derive(Clone, Debug, PartialEq)]
890pub struct TextValue {
891    /// Whether the text came from the user.
892    ///
893    /// PSPP can localize text that it writes itself, but not text provided by
894    /// the user.
895    pub user_provided: bool,
896
897    /// Localized.
898    ///
899    /// This is the main output string.
900    pub localized: String,
901
902    /// English version of the string.
903    ///
904    /// Only for strings that are not user-provided, and only if it is different
905    /// from `localized`.
906    pub c: Option<String>,
907
908    /// Identifier.
909    ///
910    /// Only for strings that are not user-provided, and only if it is different
911    /// from `localized`.
912    pub id: Option<String>,
913}
914
915impl Serialize for TextValue {
916    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
917    where
918        S: serde::Serializer,
919    {
920        if self.user_provided && self.c.is_none() && self.id.is_none() {
921            serializer.serialize_str(&self.localized)
922        } else {
923            let mut s = serializer.serialize_struct(
924                "TextValue",
925                2 + self.c.is_some() as usize + self.id.is_some() as usize,
926            )?;
927            s.serialize_field("user_provided", &self.user_provided)?;
928            s.serialize_field("localized", &self.localized)?;
929            if let Some(c) = &self.c {
930                s.serialize_field("c", &c)?;
931            }
932            if let Some(id) = &self.id {
933                s.serialize_field("id", &id)?;
934            }
935            s.end()
936        }
937    }
938}
939
940impl Display for TextValue {
941    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
942        f.write_str(&self.localized)
943    }
944}
945
946impl TextValue {
947    /// Returns the localized version of this `TextValue`.
948    pub fn localized(&self) -> &str {
949        self.localized.as_str()
950    }
951
952    /// Returns the English version of this `TextValue`.
953    pub fn c(&self) -> &str {
954        self.c.as_ref().unwrap_or(&self.localized).as_str()
955    }
956
957    /// Returns an identifier for this `TextValue`.
958    pub fn id(&self) -> &str {
959        self.id.as_ref().unwrap_or(&self.localized).as_str()
960    }
961}
962
963/// A template with substitutions.
964#[derive(Clone, Debug, Serialize, PartialEq)]
965pub struct TemplateValue {
966    /// Template string.
967    ///
968    /// The documentation for [Value] in the PSPP manual describes the template
969    /// syntax.
970    ///
971    /// [Value]: https://pspp.benpfaff.org/manual/spv/light-detail.html#value
972    pub localized: String,
973
974    /// Arguments to the template string.
975    pub args: Vec<Vec<Value>>,
976
977    /// Optional identifier for the template.
978    pub id: Option<String>,
979}
980
981impl TemplateValue {
982    fn display<'a>(
983        &self,
984        display: &DisplayValue<'a>,
985        f: &mut std::fmt::Formatter<'_>,
986    ) -> std::fmt::Result {
987        #[derive(Copy, Clone, Debug)]
988        struct InnerTemplate<'b> {
989            template: &'b str,
990            escape: char,
991        }
992
993        impl<'b> InnerTemplate<'b> {
994            fn new(template: &'b str, escape: char) -> Self {
995                Self { template, escape }
996            }
997
998            fn extract(input: &'b str, escape: char, end: char) -> (Self, &'b str) {
999                let mut prev = None;
1000                for (index, c) in input.char_indices() {
1001                    if c == end && prev != Some('\\') {
1002                        return (Self::new(&input[..index], escape), &input[index + 1..]);
1003                    }
1004                    prev = Some(c);
1005                }
1006                (Self::new(input, escape), "")
1007            }
1008
1009            fn expand(
1010                &self,
1011                options: &ValueOptions,
1012                f: &mut std::fmt::Formatter<'_>,
1013                args: &mut std::slice::Iter<Value>,
1014            ) -> Result<usize, std::fmt::Error> {
1015                let mut iter = self.template.chars();
1016
1017                // Always consume at least 1 argument to avoid infinite loop.
1018                let mut args_consumed = 1;
1019
1020                while let Some(c) = iter.next() {
1021                    match c {
1022                        '\\' => {
1023                            let c = iter.next().unwrap_or('\\') as char;
1024                            let c = if c == 'n' { '\n' } else { c };
1025                            write!(f, "{c}")?;
1026                        }
1027                        c if c == self.escape => {
1028                            let (index, rest) = consume_int(iter.as_str());
1029                            iter = rest.chars();
1030                            if let Some(index) = index.checked_sub(1)
1031                                && let Some(arg) = args.as_slice().get(index)
1032                            {
1033                                args_consumed = args_consumed.max(index + 1);
1034                                write!(f, "{}", arg.display(options))?;
1035                            }
1036                        }
1037                        c => write!(f, "{c}")?,
1038                    }
1039                }
1040                for _ in 0..args_consumed {
1041                    args.next();
1042                }
1043                Ok(args_consumed)
1044            }
1045        }
1046
1047        fn consume_int(input: &str) -> (usize, &str) {
1048            let mut n = 0;
1049            for (index, c) in input.char_indices() {
1050                match c.to_digit(10) {
1051                    Some(digit) => n = n * 10 + digit as usize,
1052                    None => return (n, &input[index..]),
1053                }
1054            }
1055            (n, "")
1056        }
1057
1058        // Arguments are formatted without leading zeros for `PCT` and `DOLLAR`.
1059        // (I don't know why.)
1060        let mut options = display.options.clone();
1061        options.settings.leading_zero_pct = false;
1062
1063        let mut iter = self.localized.chars();
1064        while let Some(c) = iter.next() {
1065            match c {
1066                '\\' => {
1067                    let c = match iter.next() {
1068                        None => '\\',
1069                        Some('n') => '\n',
1070                        Some(c) => c,
1071                    };
1072                    f.write_char(c)?;
1073                }
1074                '^' => {
1075                    let (index, rest) = consume_int(iter.as_str());
1076                    if let Some(index) = index.checked_sub(1)
1077                        && let Some(arg) = self.args.get(index)
1078                        && let Some(arg) = arg.first()
1079                    {
1080                        write!(f, "{}", arg.display(&options))?;
1081                    }
1082                    iter = rest.chars();
1083                }
1084                '[' => {
1085                    let (a, rest) = InnerTemplate::extract(iter.as_str(), '%', ':');
1086                    let (b, rest) = InnerTemplate::extract(rest, '^', ':');
1087                    let (c, rest) = InnerTemplate::extract(rest, '$', ']');
1088                    let (index, rest) = consume_int(rest);
1089                    iter = rest.chars();
1090
1091                    let (first, mid, last) = if a.template.is_empty() {
1092                        (b, b, b)
1093                    } else if c.template.is_empty() {
1094                        (a, b, b)
1095                    } else {
1096                        (a, b, c)
1097                    };
1098                    if let Some(index) = index.checked_sub(1)
1099                        && let Some(args) = self.args.get(index)
1100                    {
1101                        let mut args = args.iter();
1102                        let n = first.expand(&options, f, &mut args)?;
1103                        while args.len() > n {
1104                            mid.expand(&options, f, &mut args)?;
1105                        }
1106                        if args.len() > 0 {
1107                            last.expand(&options, f, &mut args)?;
1108                        }
1109                    }
1110                }
1111                c => f.write_char(c)?,
1112            }
1113        }
1114        Ok(())
1115    }
1116}
1117
1118/// Possible content for a [Value].
1119#[derive(Clone, Debug, Default, Serialize, PartialEq)]
1120#[serde(rename_all = "snake_case")]
1121pub enum ValueInner {
1122    /// A [Datum] value.
1123    Datum(
1124        /// The datum.
1125        DatumValue,
1126    ),
1127    /// A variable name.
1128    Variable(
1129        /// The variable.
1130        VariableValue,
1131    ),
1132    /// Plain text.
1133    Text(
1134        /// The text.
1135        TextValue,
1136    ),
1137    /// Rich text.
1138    Markup(
1139        /// The rich text.
1140        Markup,
1141    ),
1142    /// A template with substitutions.
1143    Template(
1144        /// The template.
1145        TemplateValue,
1146    ),
1147    /// An empty value.
1148    #[default]
1149    Empty,
1150}
1151
1152impl ValueInner {
1153    /// Returns true if this is a [ValueInner::Empty].
1154    pub const fn is_empty(&self) -> bool {
1155        matches!(self, Self::Empty)
1156    }
1157
1158    /// Returns this value with its display format set to `format`, if it is a
1159    /// [DatumValue].
1160    pub fn with_format(mut self, format: impl Into<ValueFormat>) -> Self {
1161        if let Some(datum_value) = self.as_datum_value_mut() {
1162            datum_value.format = format.into();
1163        }
1164        self
1165    }
1166
1167    /// Returns the [Datum] inside this value, if it is a [DatumValue].
1168    pub fn datum(&self) -> Option<&Datum<WithEncoding<ByteString>>> {
1169        self.as_datum_value().map(|d| &d.datum)
1170    }
1171
1172    /// Returns the [Show] value inside this value, if it has one, or [None]
1173    /// otherwise.
1174    fn show(&self) -> Option<Show> {
1175        match self {
1176            ValueInner::Datum(DatumValue { show, .. })
1177            | ValueInner::Variable(VariableValue { show, .. }) => *show,
1178            _ => None,
1179        }
1180    }
1181
1182    /// Returns the value label or variable label inside this value, if it has
1183    /// one.
1184    pub fn label(&self) -> Option<&str> {
1185        self.value_label().or_else(|| self.variable_label())
1186    }
1187
1188    /// Returns the value label inside this value, if it has one.
1189    fn value_label(&self) -> Option<&str> {
1190        self.as_datum_value()
1191            .and_then(|d| d.value_label.as_ref().map(String::as_str))
1192    }
1193
1194    /// Returns the variable label inside this value, if it has one.
1195    fn variable_label(&self) -> Option<&str> {
1196        self.as_variable_value()
1197            .and_then(|d| d.variable_label.as_ref().map(String::as_str))
1198    }
1199
1200    /// Returns the [DatumValue] inside this value, if it is
1201    /// [ValueInner::Datum].
1202    pub fn as_datum_value(&self) -> Option<&DatumValue> {
1203        match self {
1204            ValueInner::Datum(datum) => Some(datum),
1205            _ => None,
1206        }
1207    }
1208
1209    /// Returns the [DatumValue] inside this value, mutably, if it is
1210    /// [ValueInner::Datum].
1211    pub fn as_datum_value_mut(&mut self) -> Option<&mut DatumValue> {
1212        match self {
1213            ValueInner::Datum(datum) => Some(datum),
1214            _ => None,
1215        }
1216    }
1217
1218    /// Returns the [VariableValue] inside this value, if it is
1219    /// [ValueInner::Variable].
1220    pub fn as_variable_value(&self) -> Option<&VariableValue> {
1221        match self {
1222            ValueInner::Variable(variable) => Some(variable),
1223            _ => None,
1224        }
1225    }
1226
1227    /// Returns the [VariableValue] inside this value, mutably, if it is
1228    /// [ValueInner::Variable].
1229    pub fn as_variable_value_mut(&mut self) -> Option<&mut VariableValue> {
1230        match self {
1231            ValueInner::Variable(variable) => Some(variable),
1232            _ => None,
1233        }
1234    }
1235
1236    /// Returns the [Markup] inside this value, if it is [ValueInner::Markup].
1237    fn as_markup(&self) -> Option<&Markup> {
1238        match self {
1239            ValueInner::Markup(markup) => Some(markup),
1240            _ => None,
1241        }
1242    }
1243
1244    /// Returns an object that will format this value.  Settings on `options`
1245    /// control whether variable and value labels are included.
1246    pub fn display(&self, options: impl Into<ValueOptions>) -> DisplayValue<'_> {
1247        fn interpret_show(
1248            global_show: impl Fn() -> Show,
1249            table_show: Option<Show>,
1250            value_show: Option<Show>,
1251            label: &str,
1252        ) -> (bool, Option<&str>) {
1253            match value_show.or(table_show).unwrap_or_else(global_show) {
1254                Show::Value => (true, None),
1255                Show::Label => (false, Some(label)),
1256                Show::Both => (true, Some(label)),
1257            }
1258        }
1259
1260        let options = options.into();
1261        let (show_value, show_label) = if let Some(value_label) = self.value_label() {
1262            interpret_show(
1263                || Settings::global().show_values,
1264                options.show_values,
1265                self.show(),
1266                value_label,
1267            )
1268        } else if let Some(variable_label) = self.variable_label() {
1269            interpret_show(
1270                || Settings::global().show_variables,
1271                options.show_variables,
1272                self.show(),
1273                variable_label,
1274            )
1275        } else {
1276            (true, None)
1277        };
1278        DisplayValue {
1279            inner: self,
1280            subscripts: &[],
1281            footnotes: &[],
1282            options,
1283            show_value,
1284            show_label,
1285        }
1286    }
1287
1288    /// Constructs a new text `ValueInner` from `s`, which should have been
1289    /// provided by the user.
1290    pub fn new_user_text(s: impl Into<String>) -> Self {
1291        let s: String = s.into();
1292        if !s.is_empty() {
1293            Self::Text(TextValue {
1294                user_provided: true,
1295                localized: s,
1296                c: None,
1297                id: None,
1298            })
1299        } else {
1300            Self::Empty
1301        }
1302    }
1303}
1304
1305/// Styling inside a [Value].
1306///
1307/// Most [Value]s use a default style, so this is a separate [Box]ed structure
1308/// to save memory.
1309#[derive(Clone, Debug, Default, PartialEq)]
1310pub struct ValueStyle {
1311    /// Cell style.
1312    pub cell_style: Option<CellStyle>,
1313
1314    /// Font style.
1315    pub font_style: Option<FontStyle>,
1316
1317    /// Subscripts.
1318    pub subscripts: Vec<String>,
1319
1320    /// Footnotes.
1321    pub footnotes: Vec<Arc<Footnote>>,
1322}
1323
1324impl ValueStyle {
1325    /// Returns true if this [ValueStyle] is empty.
1326    ///
1327    /// This will return false if the font style exists but is the default font
1328    /// style, and similarly for the cell style.
1329    pub fn is_empty(&self) -> bool {
1330        self.font_style.is_none()
1331            && self.cell_style.is_none()
1332            && self.subscripts.is_empty()
1333            && self.footnotes.is_empty()
1334    }
1335}
1336
1337/// Options for displaying a [Value].
1338#[derive(Clone, Debug)]
1339pub struct ValueOptions {
1340    /// Whether to show values or value labels, or both.
1341    ///
1342    /// When this is `None`, a global default is used.
1343    pub show_values: Option<Show>,
1344
1345    /// Whether to show variable names or variable labels, or both.
1346    ///
1347    /// When this is `None`, a global default is used.
1348    pub show_variables: Option<Show>,
1349
1350    /// Numbers whose magnitudes are less than this value are displayed in
1351    /// scientific notation.  A value of 0 disables this feature.
1352    pub small: f64,
1353
1354    /// Where to put the footnote markers.
1355    pub footnote_marker_type: FootnoteMarkerType,
1356
1357    /// Settings for formatting [Datum]s.
1358    pub settings: format::Settings,
1359}
1360
1361impl Default for ValueOptions {
1362    fn default() -> Self {
1363        Self {
1364            show_values: None,
1365            show_variables: None,
1366            small: 0.0001,
1367            footnote_marker_type: FootnoteMarkerType::default(),
1368            settings: Settings::global().formats.clone(),
1369        }
1370    }
1371}
1372
1373impl From<()> for ValueOptions {
1374    fn from(_: ()) -> Self {
1375        ValueOptions::default()
1376    }
1377}
1378
1379impl From<&ValueOptions> for ValueOptions {
1380    fn from(value: &ValueOptions) -> Self {
1381        value.clone()
1382    }
1383}