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,
"Substituting {new_spec} for invalid print format on variable {variable}. {format_error}"
)]
InvalidPrintFormat {
- new_spec: Spec,
+ new_spec: Format,
variable: Identifier,
format_error: FormatError,
},
"Substituting {new_spec} for invalid write format on variable {variable}. {format_error}"
)]
InvalidWriteFormat {
- new_spec: Spec,
+ new_spec: Format,
variable: Identifier,
format_error: FormatError,
},
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
})
use unicase::UniCase;
use crate::{
- format::Spec,
+ format::Format,
identifier::{ByIdentifier, HasIdentifier, Identifier},
raw::{self, Alignment, CategoryLabels, Decoder, Measure, MissingValues, RawStr, VarType},
};
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<Value, String>,
pub label: Option<String>,
pub measure: Option<Measure>,
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),
}
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<Self> {
let as_big: u32 = Endian::Big.parse(bytes);
let as_little: u32 = Endian::Little.parse(bytes);
#[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,
},
}
String,
}
-impl From<Format> for Category {
- fn from(source: Format) -> Self {
+impl From<Type> 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,
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-pub enum Format {
+pub enum Type {
// Basic numeric formats.
F,
Comma,
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,
}
}
-impl Display for Format {
+impl Display for Type {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
let s = match self {
Self::F => "F",
}
#[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
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) {
}
}
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<Self, Error> {
// 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,
}
}
-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<UncheckedSpec> for Spec {
+impl TryFrom<UncheckedFormat> for Format {
type Error = Error;
- fn try_from(source: UncheckedSpec) -> Result<Self, Self::Error> {
- let UncheckedSpec { format, w, d } = source;
+ fn try_from(source: UncheckedFormat) -> Result<Self, Self::Error> {
+ let UncheckedFormat {
+ type_: format,
+ w,
+ d,
+ } = source;
let max_d = format.max_decimals(w);
if w % format.width_step() != 0 {
Err(Error::OddWidthNotAllowed(source))
Err(Error::DecimalsNotAllowedForWidth(source))
}
} else {
- Ok(Spec { format, w, d })
+ Ok(Format {
+ type_: format,
+ w,
+ d,
+ })
}
}
}
-impl TryFrom<u16> for Format {
+impl TryFrom<u16> for Type {
type Error = Error;
fn try_from(source: u16) -> Result<Self, Self::Error> {
}
#[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<raw::Spec> for UncheckedSpec {
+impl TryFrom<raw::Spec> for UncheckedFormat {
type Error = Error;
fn try_from(raw: raw::Spec) -> Result<Self, Self::Error> {
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(())
pub struct Settings {
epoch: Option<i32>,
- /// Either `b'.'` or `b','`.
+ /// Either `'.'` or `','`.
decimal: char,
/// Format `F`, `E`, `COMMA`, and `DOT` with leading zero (e.g. `0.5`
include_leading_zero: bool,
/// Custom currency styles.
- ccs: EnumMap<CC, NumberStyle>,
+ ccs: EnumMap<CC, Option<NumberStyle>>,
+}
+
+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
pub mod prompt;
pub mod message;
pub mod macros;
+pub mod settings;
sync::Arc,
};
+use enum_map::Enum;
use unicode_width::UnicodeWidthStr;
/// A line number and optional column number within a source file.
self.file_name.is_none() && self.span.is_none()
}
}
+
+#[derive(Enum)]
+pub enum Severity {
+ Error,
+ Warning,
+ Note,
+}
//!
//! 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)]
}
/// Borders between rows and columns.
-#[derive(Debug, Enum)]
+#[derive(Debug, Enum, PartialEq, Eq)]
pub enum RowColBorder {
RowHorz,
RowVert,
///
/// 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<i32>,
}
/// An axis within a pivot table.
+#[derive(Default)]
pub struct TableAxis {
/// `dimensions[0]` is the innermost dimension.
dimensions: Vec<Dimension>,
///
/// The root must always be a group, although it is allowed to have no
/// subcategories.
- root: Arc<Category>,
+ root: Group,
/// All of the leaves reachable via the root.
///
/// `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<Arc<Category>>,
- presentation_leaves: Vec<Arc<Category>>,
+ data_leaves: Vec<Arc<Leaf>>,
+ presentation_leaves: Vec<Arc<Leaf>>,
/// Display.
hide_all_labels: bool,
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<Category>,
+
+ /// 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<Box<Category>>,
+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<Group>),
+ Leaf(Arc<Leaf>),
+}
- /// 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.
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<Look> {
+ static LOOK: OnceLock<Arc<Look>> = OnceLock::new();
+ LOOK.get_or_init(|| Arc::new(Look::default())).clone()
+ }
+}
+
pub struct AreaStyle {
cell_style: CellStyle,
font_style: FontStyle,
Top,
/// Centered,
- Center,
+ Middle,
/// Bottom alignment.
Bottom,
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,
show_variables: Option<ValueShow>,
- weight_format: Spec,
+ weight_format: Format,
/// Current layer indexes, with axes[PIVOT_AXIS_LAYER].n_dimensions
/// elements. current_layer[i] is an offset into
/// and there's no corresponding leaf.
current_layer: Vec<usize>,
- /// 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<Axis2, Sizing>,
/// Format settings.
settings: FormatSettings,
/// Numeric grouping character (usually `.` or `,`).
- grouping: char,
+ grouping: Option<char>,
small: f64,
datafile: Option<String>,
date: Option<NaiveDateTime>,
footnotes: Vec<Footnote>,
- title: Value,
- subtype: Value,
- corner_text: Value,
- caption: Value,
+ title: Option<Value>,
+ subtype: Option<Value>,
+ corner_text: Option<Value>,
+ caption: Option<Value>,
notes: Option<String>,
dimensions: Vec<Dimension>,
axes: EnumMap<Axis3, TableAxis>,
cells: HashMap<u64, Value>,
}
+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.
pub enum ValueInner {
Number {
show: ValueShow,
- format: Spec,
+ format: Format,
honor_small: bool,
value: f64,
var_name: Option<String>,
--- /dev/null
+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<Severity, usize>,
+ 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,
+}