From cfad49501fc881324d9bdc20a89ff90f30bbc48d Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 18 Aug 2024 18:32:16 -0700 Subject: [PATCH] work --- rust/src/cooked.rs | 14 +- rust/src/dictionary.rs | 10 +- rust/src/endian.rs | 5 + rust/src/format.rs | 191 ++++++++++++++---------- rust/src/lib.rs | 1 + rust/src/message.rs | 8 + rust/src/output/pivot/mod.rs | 275 +++++++++++++++++++++++++++++------ rust/src/settings.rs | 89 ++++++++++++ 8 files changed, 460 insertions(+), 133 deletions(-) create mode 100644 rust/src/settings.rs diff --git a/rust/src/cooked.rs b/rust/src/cooked.rs index 96468531c2..d2617df528 100644 --- a/rust/src/cooked.rs +++ b/rust/src/cooked.rs @@ -4,7 +4,7 @@ use crate::{ dictionary::{Dictionary, VarWidth, Variable}, encoding::Error as EncodingError, endian::Endian, - format::{Error as FormatError, Spec, UncheckedSpec}, + format::{Error as FormatError, Format, UncheckedFormat}, identifier::{Error as IdError, Identifier}, raw::{ self, Cases, DecodedRecord, DocumentRecord, EncodingRecord, Extension, FileAttributeRecord, @@ -59,7 +59,7 @@ pub enum Error { "Substituting {new_spec} for invalid print format on variable {variable}. {format_error}" )] InvalidPrintFormat { - new_spec: Spec, + new_spec: Format, variable: Identifier, format_error: FormatError, }, @@ -68,7 +68,7 @@ pub enum Error { "Substituting {new_spec} for invalid write format on variable {variable}. {format_error}" )] InvalidWriteFormat { - new_spec: Spec, + new_spec: Format, variable: Identifier, format_error: FormatError, }, @@ -584,12 +584,12 @@ fn fix_line_ends(s: &str) -> String { out } -fn decode_format(raw: raw::Spec, width: VarWidth, warn: impl Fn(Spec, FormatError)) -> Spec { - UncheckedSpec::try_from(raw) - .and_then(Spec::try_from) +fn decode_format(raw: raw::Spec, width: VarWidth, warn: impl Fn(Format, FormatError)) -> Format { + UncheckedFormat::try_from(raw) + .and_then(Format::try_from) .and_then(|x| x.check_width_compatibility(width)) .unwrap_or_else(|error| { - let new_format = Spec::default_for_width(width); + let new_format = Format::default_for_width(width); warn(new_format, error); new_format }) diff --git a/rust/src/dictionary.rs b/rust/src/dictionary.rs index 2c1707b257..c26009921b 100644 --- a/rust/src/dictionary.rs +++ b/rust/src/dictionary.rs @@ -12,7 +12,7 @@ use ordered_float::OrderedFloat; use unicase::UniCase; use crate::{ - format::Spec, + format::Format, identifier::{ByIdentifier, HasIdentifier, Identifier}, raw::{self, Alignment, CategoryLabels, Decoder, Measure, MissingValues, RawStr, VarType}, }; @@ -343,8 +343,8 @@ pub struct Variable { pub name: Identifier, pub width: VarWidth, pub missing_values: MissingValues, - pub print_format: Spec, - pub write_format: Spec, + pub print_format: Format, + pub write_format: Format, pub value_labels: HashMap, pub label: Option, pub measure: Option, @@ -364,8 +364,8 @@ impl Variable { name, width, missing_values: MissingValues::default(), - print_format: Spec::default_for_width(width), - write_format: Spec::default_for_width(width), + print_format: Format::default_for_width(width), + write_format: Format::default_for_width(width), value_labels: HashMap::new(), label: None, measure: Measure::default_for_type(var_type), diff --git a/rust/src/endian.rs b/rust/src/endian.rs index 3692180dba..defd7f4bfa 100644 --- a/rust/src/endian.rs +++ b/rust/src/endian.rs @@ -13,6 +13,11 @@ pub enum Endian { } impl Endian { + #[cfg(target_endian = "big")] + const NATIVE: Endian = Endian::Big; + #[cfg(target_endian = "little")] + const NATIVE: Endian = Endian::Little; + pub fn identify_u32(expected_value: u32, bytes: [u8; 4]) -> Option { let as_big: u32 = Endian::Big.parse(bytes); let as_little: u32 = Endian::Little.parse(bytes); diff --git a/rust/src/format.rs b/rust/src/format.rs index 0c5fca05bd..d0eba7d9b9 100644 --- a/rust/src/format.rs +++ b/rust/src/format.rs @@ -16,43 +16,43 @@ pub enum Error { #[error("Unknown format type {value}.")] UnknownFormat { value: u16 }, - #[error("Output format {0} specifies width {}, but {} requires an even width.", .0.w, .0.format)] - OddWidthNotAllowed(UncheckedSpec), + #[error("Output format {0} specifies width {}, but {} requires an even width.", .0.w, .0.type_)] + OddWidthNotAllowed(UncheckedFormat), - #[error("Output format {0} specifies width {}, but {} requires a width between {} and {}.", .0.w, .0.format, .0.format.min_width(), .0.format.max_width())] - BadWidth(UncheckedSpec), + #[error("Output format {0} specifies width {}, but {} requires a width between {} and {}.", .0.w, .0.type_, .0.type_.min_width(), .0.type_.max_width())] + BadWidth(UncheckedFormat), - #[error("Output format {0} specifies decimal places, but {} format does not allow any decimals.", .0.format)] - DecimalsNotAllowedForFormat(UncheckedSpec), + #[error("Output format {0} specifies decimal places, but {} format does not allow any decimals.", .0.type_)] + DecimalsNotAllowedForFormat(UncheckedFormat), - #[error("Output format {0} specifies {} decimal places, but with a width of {}, {} does not allow any decimal places.", .0.d, .0.w, .0.format)] - DecimalsNotAllowedForWidth(UncheckedSpec), + #[error("Output format {0} specifies {} decimal places, but with a width of {}, {} does not allow any decimal places.", .0.d, .0.w, .0.type_)] + DecimalsNotAllowedForWidth(UncheckedFormat), - #[error("Output format {spec} specifies {} decimal places but, with a width of {}, {} allows at most {max_d} decimal places.", .spec.d, .spec.w, .spec.format)] + #[error("Output format {spec} specifies {} decimal places but, with a width of {}, {} allows at most {max_d} decimal places.", .spec.d, .spec.w, .spec.type_)] TooManyDecimalsForWidth { - spec: UncheckedSpec, + spec: UncheckedFormat, max_d: Decimals, }, #[error("String variable is not compatible with numeric format {0}.")] - UnnamedVariableNotCompatibleWithNumericFormat(Format), + UnnamedVariableNotCompatibleWithNumericFormat(Type), #[error("Numeric variable is not compatible with string format {0}.")] - UnnamedVariableNotCompatibleWithStringFormat(Format), + UnnamedVariableNotCompatibleWithStringFormat(Type), #[error("String variable {variable} with width {width} is not compatible with format {bad_spec}. Use format {good_spec} instead.")] NamedStringVariableBadSpecWidth { variable: String, width: Width, - bad_spec: Spec, - good_spec: Spec, + bad_spec: Format, + good_spec: Format, }, #[error("String variable with width {width} is not compatible with format {bad_spec}. Use format {good_spec} instead.")] UnnamedStringVariableBadSpecWidth { width: Width, - bad_spec: Spec, - good_spec: Spec, + bad_spec: Format, + good_spec: Format, }, } @@ -72,29 +72,27 @@ pub enum Category { String, } -impl From for Category { - fn from(source: Format) -> Self { +impl From for Category { + fn from(source: Type) -> Self { match source { - Format::F | Format::Comma | Format::Dot | Format::Dollar | Format::Pct | Format::E => { - Self::Basic - } - Format::CC(_) => Self::Custom, - Format::N | Format::Z => Self::Legacy, - Format::P | Format::PK | Format::IB | Format::PIB | Format::RB => Self::Binary, - Format::PIBHex | Format::RBHex => Self::Hex, - Format::Date - | Format::ADate - | Format::EDate - | Format::JDate - | Format::SDate - | Format::QYr - | Format::MoYr - | Format::WkYr - | Format::DateTime - | Format::YMDHMS => Self::Date, - Format::MTime | Format::Time | Format::DTime => Self::Time, - Format::WkDay | Format::Month => Self::DateComponent, - Format::A | Format::AHex => Self::String, + Type::F | Type::Comma | Type::Dot | Type::Dollar | Type::Pct | Type::E => Self::Basic, + Type::CC(_) => Self::Custom, + Type::N | Type::Z => Self::Legacy, + Type::P | Type::PK | Type::IB | Type::PIB | Type::RB => Self::Binary, + Type::PIBHex | Type::RBHex => Self::Hex, + Type::Date + | Type::ADate + | Type::EDate + | Type::JDate + | Type::SDate + | Type::QYr + | Type::MoYr + | Type::WkYr + | Type::DateTime + | Type::YMDHMS => Self::Date, + Type::MTime | Type::Time | Type::DTime => Self::Time, + Type::WkDay | Type::Month => Self::DateComponent, + Type::A | Type::AHex => Self::String, } } } @@ -122,7 +120,7 @@ impl Display for CC { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum Format { +pub enum Type { // Basic numeric formats. F, Comma, @@ -176,7 +174,7 @@ pub type SignedWidth = i16; pub type Decimals = u8; -impl Format { +impl Type { pub fn max_width(self) -> Width { match self { Self::P | Self::PK | Self::PIBHex | Self::RBHex => 16, @@ -321,7 +319,7 @@ impl Format { } } -impl Display for Format { +impl Display for Type { fn fmt(&self, f: &mut Formatter) -> FmtResult { let s = match self { Self::F => "F", @@ -367,15 +365,21 @@ fn max_digits_for_bytes(bytes: usize) -> usize { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct Spec { - format: Format, +pub struct Format { + type_: Type, w: Width, d: Decimals, } -impl Spec { - pub fn format(self) -> Format { - self.format +impl Format { + pub const F40: Format = Format { + type_: Type::F, + w: 40, + d: 0, + }; + + pub fn format(self) -> Type { + self.type_ } pub fn w(self) -> Width { self.w @@ -386,21 +390,25 @@ impl Spec { pub fn default_for_width(var_width: VarWidth) -> Self { match var_width { - VarWidth::Numeric => Spec { - format: Format::F, + VarWidth::Numeric => Format { + type_: Type::F, w: 8, d: 2, }, - VarWidth::String(w) => Spec { - format: Format::A, + VarWidth::String(w) => Format { + type_: Type::A, w, d: 0, }, } } - pub fn fixed_from(source: &UncheckedSpec) -> Self { - let UncheckedSpec { format, w, d } = *source; + pub fn fixed_from(source: &UncheckedFormat) -> Self { + let UncheckedFormat { + type_: format, + w, + d, + } = *source; let (min, max) = format.width_range().into_inner(); let mut w = w.clamp(min, max); if d <= format.max_decimals(Width::MAX) { @@ -410,34 +418,38 @@ impl Spec { } } let d = d.clamp(0, format.max_decimals(w)); - Self { format, w, d } + Self { + type_: format, + w, + d, + } } pub fn var_width(self) -> VarWidth { - match self.format { - Format::A => VarWidth::String(self.w), - Format::AHex => VarWidth::String(self.w / 2), + match self.type_ { + Type::A => VarWidth::String(self.w), + Type::AHex => VarWidth::String(self.w / 2), _ => VarWidth::Numeric, } } pub fn var_type(self) -> VarType { - self.format.var_type() + self.type_.var_type() } /// Checks whether this format specification is valid for a variable with /// width `var_width`. pub fn check_width_compatibility(self, var_width: VarWidth) -> Result { // Verify that the format is right for the variable's type. - self.format.check_type_compatibility(var_width.into())?; + self.type_.check_type_compatibility(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 } + let good_spec = if self.type_ == Type::A { + Format { w, ..self } } else { - Spec { w: w * 2, ..self } + Format { w: w * 2, ..self } }; return Err(Error::UnnamedStringVariableBadSpecWidth { width: w, @@ -451,21 +463,25 @@ impl Spec { } } -impl Display for Spec { +impl Display for Format { fn fmt(&self, f: &mut Formatter) -> FmtResult { - write!(f, "{}{}", self.format, self.w)?; - if self.format.takes_decimals() || self.d > 0 { + write!(f, "{}{}", self.type_, self.w)?; + if self.type_.takes_decimals() || self.d > 0 { write!(f, ".{}", self.d)?; } Ok(()) } } -impl TryFrom for Spec { +impl TryFrom for Format { type Error = Error; - fn try_from(source: UncheckedSpec) -> Result { - let UncheckedSpec { format, w, d } = source; + fn try_from(source: UncheckedFormat) -> Result { + let UncheckedFormat { + type_: format, + w, + d, + } = source; let max_d = format.max_decimals(w); if w % format.width_step() != 0 { Err(Error::OddWidthNotAllowed(source)) @@ -483,12 +499,16 @@ impl TryFrom for Spec { Err(Error::DecimalsNotAllowedForWidth(source)) } } else { - Ok(Spec { format, w, d }) + Ok(Format { + type_: format, + w, + d, + }) } } } -impl TryFrom for Format { +impl TryFrom for Type { type Error = Error; fn try_from(source: u16) -> Result { @@ -536,15 +556,15 @@ impl TryFrom for Format { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct UncheckedSpec { - pub format: Format, +pub struct UncheckedFormat { + pub type_: Type, pub w: Width, pub d: Decimals, } -impl TryFrom for UncheckedSpec { +impl TryFrom for UncheckedFormat { type Error = Error; fn try_from(raw: raw::Spec) -> Result { @@ -553,14 +573,18 @@ impl TryFrom for UncheckedSpec { let format = raw_format.try_into()?; let w = ((raw >> 8) & 0xff) as Width; let d = (raw & 0xff) as Decimals; - Ok(Self { format, w, d }) + Ok(Self { + type_: format, + w, + d, + }) } } -impl Display for UncheckedSpec { +impl Display for UncheckedFormat { fn fmt(&self, f: &mut Formatter) -> FmtResult { - write!(f, "{}{}", self.format, self.w)?; - if self.format.takes_decimals() || self.d > 0 { + write!(f, "{}{}", self.type_, self.w)?; + if self.type_.takes_decimals() || self.d > 0 { write!(f, ".{}", self.d)?; } Ok(()) @@ -570,7 +594,7 @@ impl Display for UncheckedSpec { pub struct Settings { epoch: Option, - /// Either `b'.'` or `b','`. + /// Either `'.'` or `','`. decimal: char, /// Format `F`, `E`, `COMMA`, and `DOT` with leading zero (e.g. `0.5` @@ -578,7 +602,18 @@ pub struct Settings { include_leading_zero: bool, /// Custom currency styles. - ccs: EnumMap, + ccs: EnumMap>, +} + +impl Default for Settings { + fn default() -> Self { + Self { + epoch: None, + decimal: '.', + include_leading_zero: false, + ccs: Default::default(), + } + } } /// A numeric output style. This can express numeric formats in diff --git a/rust/src/lib.rs b/rust/src/lib.rs index b75411955f..4624edfe69 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -14,3 +14,4 @@ pub mod lex; pub mod prompt; pub mod message; pub mod macros; +pub mod settings; diff --git a/rust/src/message.rs b/rust/src/message.rs index f8682050c0..236592cad2 100644 --- a/rust/src/message.rs +++ b/rust/src/message.rs @@ -5,6 +5,7 @@ use std::{ sync::Arc, }; +use enum_map::Enum; use unicode_width::UnicodeWidthStr; /// A line number and optional column number within a source file. @@ -127,3 +128,10 @@ impl Location { self.file_name.is_none() && self.span.is_none() } } + +#[derive(Enum)] +pub enum Severity { + Error, + Warning, + Note, +} diff --git a/rust/src/output/pivot/mod.rs b/rust/src/output/pivot/mod.rs index fde78fbd81..d8f5c9f17f 100644 --- a/rust/src/output/pivot/mod.rs +++ b/rust/src/output/pivot/mod.rs @@ -55,12 +55,16 @@ //! //! 5. Output the table for user consumption. Use pivot_table_submit(). -use std::{collections::HashMap, ops::Range, sync::Arc}; +use std::{ + collections::HashMap, + ops::Range, + sync::{Arc, OnceLock}, +}; use chrono::NaiveDateTime; -use enum_map::{Enum, EnumMap}; +use enum_map::{enum_map, Enum, EnumMap}; -use crate::format::{Settings as FormatSettings, Spec}; +use crate::format::{Format, Settings as FormatSettings}; /// Areas of a pivot table for styling purposes. #[derive(Copy, Clone, Debug, Enum, PartialEq, Eq)] @@ -104,7 +108,7 @@ pub enum BoxBorder { } /// Borders between rows and columns. -#[derive(Debug, Enum)] +#[derive(Debug, Enum, PartialEq, Eq)] pub enum RowColBorder { RowHorz, RowVert, @@ -116,6 +120,7 @@ pub enum RowColBorder { /// /// The comments below talk about columns and their widths but they apply /// equally to rows and their heights. +#[derive(Default)] pub struct Sizing { /// Specific column widths, in 1/96" units. widths: Vec, @@ -136,6 +141,7 @@ pub enum Axis3 { } /// An axis within a pivot table. +#[derive(Default)] pub struct TableAxis { /// `dimensions[0]` is the innermost dimension. dimensions: Vec, @@ -172,7 +178,7 @@ pub struct Dimension { /// /// The root must always be a group, although it is allowed to have no /// subcategories. - root: Arc, + root: Group, /// All of the leaves reachable via the root. /// @@ -185,8 +191,8 @@ pub struct Dimension { /// `data_leaves[i]->data_index == i`. This might differ from what an /// in-order traversal of `root` would yield, if the user reordered /// categories. - data_leaves: Vec>, - presentation_leaves: Vec>, + data_leaves: Vec>, + presentation_leaves: Vec>, /// Display. hide_all_labels: bool, @@ -195,38 +201,100 @@ pub struct Dimension { label_depth: usize, } -/// A pivot_category is a leaf (a category) or a group. -pub struct Category { +pub struct Group { name: Value, label_depth: usize, extra_depth: usize, - type_: CategoryType, + + /// The child categories. + /// + /// A group usually has multiple children, but it is allowed to have + /// only one or even (pathologically) none. + children: Vec, + + /// Display a label for the group itself? + show_label: bool, + + show_label_in_corner: bool, } -pub enum CategoryType { - Group { - /// The child categories. - /// - /// A group usually has multiple children, but it is allowed to have - /// only one or even (pathologically) none. - children: Vec>, +pub struct Leaf { + name: Value, + label_depth: usize, + extra_depth: usize, + + group_index: usize, + data_index: usize, + presentation_index: usize, - /// Display a label for the group itself? - show_label: bool, + /// Default format for values in this category. + format: Format, - show_label_in_corner: bool, - }, - Leaf { - group_index: usize, - data_index: usize, - presentation_index: usize, + /// Honor [Table]'s `small` setting? + honor_small: bool, +} - /// Default format for values in this category. - format: Spec, +/// A pivot_category is a leaf (a category) or a group. +pub enum Category { + Group(Arc), + Leaf(Arc), +} - /// Honor [Table]'s `small` setting? - honor_small: bool, - }, +trait CategoryTrait { + fn name(&self) -> &Value; + fn label_depth(&self) -> usize; + fn extra_depth(&self) -> usize; +} + +impl CategoryTrait for Group { + fn name(&self) -> &Value { + &self.name + } + + fn label_depth(&self) -> usize { + self.label_depth + } + + fn extra_depth(&self) -> usize { + self.extra_depth + } +} + +impl CategoryTrait for Leaf { + fn name(&self) -> &Value { + &self.name + } + + fn label_depth(&self) -> usize { + self.label_depth + } + + fn extra_depth(&self) -> usize { + self.extra_depth + } +} + +impl CategoryTrait for Category { + fn name(&self) -> &Value { + match self { + Category::Group(group) => group.name(), + Category::Leaf(leaf) => leaf.name(), + } + } + + fn label_depth(&self) -> usize { + match self { + Category::Group(group) => group.label_depth(), + Category::Leaf(leaf) => leaf.label_depth(), + } + } + + fn extra_depth(&self) -> usize { + match self { + Category::Group(group) => group.extra_depth(), + Category::Leaf(leaf) => leaf.extra_depth(), + } + } } /// Styling for a pivot table. @@ -275,6 +343,79 @@ struct Look { n_orphan_lines: usize, } +impl Default for Look { + fn default() -> Self { + Self { + name: None, + omit_empty: true, + row_labels_in_corner: true, + row_heading_widths: 36..72, + col_heading_widths: 36..120, + footnote_marker_type: FootnoteMarkerType::Alphabetic, + footnote_marker_position: FootnoteMarkerPosition::Subscript, + areas: EnumMap::from_fn(|area| { + use HorzAlign::*; + use VertAlign::*; + let (halign, valign, hmargins, vmargins) = match area { + Area::Title => (Center, Middle, [8, 11], [1, 8]), + Area::Caption => (Left, Top, [8, 11], [1, 1]), + Area::Footer => (Left, Top, [11, 8], [2, 3]), + Area::Corner => (Left, Bottom, [8, 11], [1, 1]), + Area::ColumnLabels => (Left, Top, [8, 11], [1, 3]), + Area::RowLabels => (Left, Top, [8, 11], [1, 3]), + Area::Data => (Mixed, Top, [8, 11], [1, 1]), + Area::Layers => (Left, Bottom, [8, 11], [1, 3]), + }; + AreaStyle { + cell_style: CellStyle { + horz_align: halign, + vert_align: valign, + margins: enum_map! { Axis2::X => hmargins, Axis2::Y => vmargins }, + }, + font_style: FontStyle { + bold: area == Area::Title, + italic: false, + underline: false, + markup: false, + font: String::from("Sans Serif"), + fg: [Color::BLACK; 2], + bg: [Color::WHITE; 2], + size: 9, + }, + } + }), + borders: EnumMap::from_fn(|border| { + let stroke = match border { + Border::InnerFrame(_) | Border::DataLeft | Border::DataTop => Stroke::Thick, + Border::Dimensions(side) if side != RowColBorder::RowVert => Stroke::Solid, + Border::Categories(RowColBorder::ColHorz | RowColBorder::ColVert) => { + Stroke::Solid + } + _ => Stroke::None, + }; + BorderStyle { + stroke, + color: Color::BLACK, + } + }), + print_all_layers: false, + paginate_layers: false, + shrink_to_fit: EnumMap::from_fn(|_| false), + top_continuation: false, + bottom_continuation: false, + continuation: None, + n_orphan_lines: 0, + } + } +} + +impl Look { + fn shared_default() -> Arc { + static LOOK: OnceLock> = OnceLock::new(); + LOOK.get_or_init(|| Arc::new(Look::default())).clone() + } +} + pub struct AreaStyle { cell_style: CellStyle, font_style: FontStyle, @@ -321,7 +462,7 @@ pub enum VertAlign { Top, /// Centered, - Center, + Middle, /// Bottom alignment. Bottom, @@ -347,6 +488,20 @@ pub struct Color { b: u8, } +impl Color { + const BLACK: Color = Color::new(0, 0, 0); + const WHITE: Color = Color::new(255, 255, 255); + + const fn new(r: u8, g: u8, b: u8) -> Self { + Self { + alpha: 255, + r, + g, + b, + } + } +} + pub struct BorderStyle { stroke: Stroke, color: Color, @@ -401,7 +556,7 @@ pub struct Table { show_variables: Option, - weight_format: Spec, + weight_format: Format, /// Current layer indexes, with axes[PIVOT_AXIS_LAYER].n_dimensions /// elements. current_layer[i] is an offset into @@ -410,17 +565,14 @@ pub struct Table { /// and there's no corresponding leaf. current_layer: Vec, - /// Column sizing and page breaks. - column_sizing: Sizing, - - /// Row sizing and page breaks. - row_sizing: Sizing, + /// Column and row sizing and page breaks. + sizing: EnumMap, /// Format settings. settings: FormatSettings, /// Numeric grouping character (usually `.` or `,`). - grouping: char, + grouping: Option, small: f64, @@ -432,16 +584,53 @@ pub struct Table { datafile: Option, date: Option, footnotes: Vec, - title: Value, - subtype: Value, - corner_text: Value, - caption: Value, + title: Option, + subtype: Option, + corner_text: Option, + caption: Option, notes: Option, dimensions: Vec, axes: EnumMap, cells: HashMap, } +impl Table { + fn new() -> Self { + Self { + look: Look::shared_default(), + rotate_inner_column_labels: false, + rotate_outer_row_labels: false, + show_grid_lines: false, + show_title: true, + show_caption: true, + show_value: None, + show_variables: None, + weight_format: Format::F40, + current_layer: Vec::new(), + sizing: EnumMap::default(), + settings: FormatSettings::default(), // XXX from settings + grouping: None, + small: 0.0001, // XXX from settings. + command_local: None, + command_c: None, // XXX from current command name. + language: None, + locale: None, + dataset: None, + datafile: None, + date: None, + footnotes: Vec::new(), + subtype: None, + title: None, + corner_text: None, + caption: None, + notes: None, + dimensions: Vec::new(), + axes: EnumMap::default(), + cells: HashMap::new(), + } + } +} + /// Whether to show variable or value labels or the underlying value or variable name. pub enum ValueShow { /// Value or variable name only. @@ -507,7 +696,7 @@ pub struct Value { pub enum ValueInner { Number { show: ValueShow, - format: Spec, + format: Format, honor_small: bool, value: f64, var_name: Option, diff --git a/rust/src/settings.rs b/rust/src/settings.rs new file mode 100644 index 0000000000..65b0826a02 --- /dev/null +++ b/rust/src/settings.rs @@ -0,0 +1,89 @@ +use enum_map::EnumMap; + +use crate::{endian::Endian, format::Format, message::Severity, format::Settings as FormatSettings}; + +pub struct Settings { + input_integer_format: Endian, + input_float_format: Endian, + output_integer_format: Endian, + output_float_format: Endian, + + /// `MDISPLAY`: how to display matrices in `MATRIX`...`END MATRIX`. + matrix_display: MatrixDisplay, + + view_length: usize, + + view_width: usize, + + safer: bool, + + include: bool, + + route_errors_to_terminal: bool, + + route_errors_to_listing: bool, + + scompress: bool, + + undefined: bool, + + blanks: f64, + + max_messages: EnumMap, + printback: bool, + macros: MacroSettings, + max_loops: usize, + workspace: usize, + default_format: Format, + testing: bool, + fuzz_bits: usize, + scale_min: usize, + commands: Compatibility, + global: Compatibility, + syntax: Compatibility, + formats: FormatSettings, + small: f64, + +} + +pub enum Compatibility { + Compatible, + Enhanced, +} + +pub struct MacroSettings { + /// Expand macros? + expand: bool, + + /// Print macro expansions? + print_expansions: bool, + + /// Maximum iterations of `!FOR`. + max_iterations: usize, + + /// Maximum nested macro expansion levels. + max_nest: usize, +} + +/// How to display matrices in `MATRIX`...`END MATRIX`. +pub enum MatrixDisplay { + /// Output matrices as text. + Text, + + /// Output matrices as pivot tables. + Tables, +} + +pub enum OutputType { + /// Errors and warnings. + Error, + + /// Notes. + Notes, + + /// Syntax printback. + Syntax, + + /// Everything else. + Other, +} -- 2.30.2