use unicase::UniCase;
use crate::{
- format::Format,
+ format::{DisplayPlain, Format},
identifier::{ByIdentifier, HasIdentifier, Identifier},
output::pivot::{Axis3, Dimension, Footnote, Footnotes, Group, PivotTable, Value},
settings::Show,
- sys::raw::{Alignment, CategoryLabels, Measure, MissingValues, RawString, VarType},
+ sys::raw::{CategoryLabels, RawString, VarType},
};
/// An index within [Dictionary::variables].
}
}
+#[derive(Clone, Default)]
+pub struct MissingValues {
+ /// Individual missing values, up to 3 of them.
+ values: Vec<Datum>,
+
+ /// Optional range of missing values.
+ range: Option<MissingValueRange>,
+}
+
+impl Debug for MissingValues {
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ DisplayMissingValues {
+ mv: self,
+ encoding: None,
+ }
+ .fmt(f)
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum MissingValuesError {
+ TooMany,
+ TooWide,
+ MixedTypes,
+}
+
+impl MissingValues {
+ pub fn new(
+ mut values: Vec<Datum>,
+ range: Option<MissingValueRange>,
+ ) -> Result<Self, MissingValuesError> {
+ if values.len() > 3 {
+ return Err(MissingValuesError::TooMany);
+ }
+
+ let mut var_type = None;
+ for value in values.iter_mut() {
+ value.trim_end();
+ match value.width() {
+ VarWidth::String(w) if w > 8 => return Err(MissingValuesError::TooWide),
+ _ => (),
+ }
+ if var_type.is_some_and(|t| t != value.var_type()) {
+ return Err(MissingValuesError::MixedTypes);
+ }
+ var_type = Some(value.var_type());
+ }
+
+ if var_type == Some(VarType::String) && range.is_some() {
+ return Err(MissingValuesError::MixedTypes);
+ }
+
+ Ok(Self { values, range })
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.values.is_empty() && self.range.is_none()
+ }
+
+ pub fn var_type(&self) -> Option<VarType> {
+ if let Some(datum) = self.values.first() {
+ Some(datum.var_type())
+ } else if self.range.is_some() {
+ Some(VarType::Numeric)
+ } else {
+ None
+ }
+ }
+
+ pub fn contains(&self, value: &Datum) -> bool {
+ if self
+ .values
+ .iter()
+ .any(|datum| datum.eq_ignore_trailing_spaces(value))
+ {
+ return true;
+ }
+
+ match value {
+ Datum::Number(Some(number)) => self.range.is_some_and(|range| range.contains(*number)),
+ _ => false,
+ }
+ }
+
+ pub fn is_resizable(&self, width: VarWidth) -> bool {
+ self.values.iter().all(|datum| datum.is_resizable(width))
+ && self.range.iter().all(|range| range.is_resizable(width))
+ }
+
+ pub fn resize(&mut self, width: VarWidth) {
+ for datum in &mut self.values {
+ datum.resize(width);
+ }
+ if let Some(range) = &mut self.range {
+ range.resize(width);
+ }
+ }
+
+ pub fn display(&self, encoding: &'static Encoding) -> DisplayMissingValues<'_> {
+ DisplayMissingValues {
+ mv: self,
+ encoding: Some(encoding),
+ }
+ }
+}
+
+pub struct DisplayMissingValues<'a> {
+ mv: &'a MissingValues,
+ encoding: Option<&'static Encoding>,
+}
+
+impl<'a> Display for DisplayMissingValues<'a> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ if let Some(range) = &self.mv.range {
+ write!(f, "{range}")?;
+ if !self.mv.values.is_empty() {
+ write!(f, "; ")?;
+ }
+ }
+
+ for (i, value) in self.mv.values.iter().enumerate() {
+ if i > 0 {
+ write!(f, "; ")?;
+ }
+ match self.encoding {
+ Some(encoding) => value.display_plain(encoding).fmt(f)?,
+ None => value.fmt(f)?,
+ }
+ }
+
+ if self.mv.is_empty() {
+ write!(f, "none")?;
+ }
+ Ok(())
+ }
+}
+
+#[derive(Copy, Clone)]
+pub enum MissingValueRange {
+ In { low: f64, high: f64 },
+ From { low: f64 },
+ To { high: f64 },
+}
+
+impl MissingValueRange {
+ pub fn new(low: f64, high: f64) -> Self {
+ const LOWEST: f64 = f64::MIN.next_up();
+ match (low, high) {
+ (f64::MIN | LOWEST, _) => Self::To { high },
+ (_, f64::MAX) => Self::From { low },
+ (_, _) => Self::In { low, high },
+ }
+ }
+
+ pub fn low(&self) -> Option<f64> {
+ match self {
+ MissingValueRange::In { low, .. } | MissingValueRange::From { low } => Some(*low),
+ MissingValueRange::To { .. } => None,
+ }
+ }
+
+ pub fn high(&self) -> Option<f64> {
+ match self {
+ MissingValueRange::In { high, .. } | MissingValueRange::To { high } => Some(*high),
+ MissingValueRange::From { .. } => None,
+ }
+ }
+
+ pub fn contains(&self, number: f64) -> bool {
+ match self {
+ MissingValueRange::In { low, high } => (*low..*high).contains(&number),
+ MissingValueRange::From { low } => number >= *low,
+ MissingValueRange::To { high } => number <= *high,
+ }
+ }
+
+ pub fn is_resizable(&self, width: VarWidth) -> bool {
+ width.is_numeric()
+ }
+
+ pub fn resize(&self, width: VarWidth) {
+ assert_eq!(width, VarWidth::Numeric);
+ }
+}
+
+impl Display for MissingValueRange {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ match self.low() {
+ Some(low) => low.display_plain().fmt(f)?,
+ None => write!(f, "LOW")?,
+ }
+
+ write!(f, " THRU ")?;
+
+ match self.high() {
+ Some(high) => high.display_plain().fmt(f)?,
+ None => write!(f, "HIGH")?,
+ }
+ Ok(())
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum Alignment {
+ Left,
+ Right,
+ Center,
+}
+
+impl Alignment {
+ pub fn default_for_type(var_type: VarType) -> Self {
+ match var_type {
+ VarType::Numeric => Self::Right,
+ VarType::String => Self::Left,
+ }
+ }
+
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ Alignment::Left => "Left",
+ Alignment::Right => "Right",
+ Alignment::Center => "Center",
+ }
+ }
+}
+
+/// [Level of measurement](https://en.wikipedia.org/wiki/Level_of_measurement).
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum Measure {
+ /// Nominal values can only be compared for equality.
+ Nominal,
+
+ /// Ordinal values can be meaningfully ordered.
+ Ordinal,
+
+ /// Scale values can be meaningfully compared for the degree of difference.
+ Scale,
+}
+
+impl Measure {
+ pub fn default_for_type(var_type: VarType) -> Option<Measure> {
+ match var_type {
+ VarType::Numeric => None,
+ VarType::String => Some(Self::Nominal),
+ }
+ }
+
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ Measure::Nominal => "Nominal",
+ Measure::Ordinal => "Ordinal",
+ Measure::Scale => "Scale",
+ }
+ }
+}
+
#[cfg(test)]
mod test {
use std::collections::HashSet;
//! raw details. Most readers will want to use higher-level interfaces.
use crate::{
- dictionary::{Attributes, Datum, VarWidth},
+ dictionary::{
+ Alignment, Attributes, Datum, Measure, MissingValueRange, MissingValues, VarWidth,
+ },
endian::{Endian, Parse, ToBytes},
- format::{DisplayPlain, DisplayPlainF64},
+ format::DisplayPlainF64,
identifier::{Error as IdError, Identifier},
sys::encoding::{default_encoding, get_encoding, Error as EncodingError},
};
#[error("Invalid ZSAV compression code {0}")]
InvalidZsavCompression(u32),
- #[error("Document record at offset {offset:#x} has document line count ({n}) greater than the maximum number {max}.")]
+ #[error(
+ "Document record at offset {offset:#x} has document line count ({n}) greater than the maximum number {max}."
+ )]
BadDocumentLength { offset: u64, n: usize, max: usize },
#[error("At offset {offset:#x}, unrecognized record type {rec_type}.")]
BadRecordType { offset: u64, rec_type: u32 },
- #[error("In variable record starting at offset {start_offset:#x}, variable width is not in the valid range -1 to 255.")]
+ #[error(
+ "In variable record starting at offset {start_offset:#x}, variable width is not in the valid range -1 to 255."
+ )]
BadVariableWidth { start_offset: u64, width: i32 },
- #[error("In variable record starting at offset {start_offset:#x}, variable label code {code} at offset {code_offset:#x} is not 0 or 1.")]
+ #[error(
+ "In variable record starting at offset {start_offset:#x}, variable label code {code} at offset {code_offset:#x} is not 0 or 1."
+ )]
BadVariableLabelCode {
start_offset: u64,
code_offset: u64,
#[error("At offset {offset:#x}, string missing value code ({code}) is not 0, 1, 2, or 3.")]
BadStringMissingValueCode { offset: u64, code: i32 },
- #[error("At offset {offset:#x}, number of value labels ({n}) is greater than the maximum number {max}.")]
+ #[error(
+ "At offset {offset:#x}, number of value labels ({n}) is greater than the maximum number {max}."
+ )]
BadNumberOfValueLabels { offset: u64, n: u32, max: u32 },
- #[error("At offset {offset:#x}, following value label record, found record type {rec_type} instead of expected type 4 for variable index record")]
+ #[error(
+ "At offset {offset:#x}, following value label record, found record type {rec_type} instead of expected type 4 for variable index record"
+ )]
ExpectedVarIndexRecord { offset: u64, rec_type: u32 },
- #[error("At offset {offset:#x}, number of variables indexes for value labels ({n}) is greater than the maximum number ({max}).")]
+ #[error(
+ "At offset {offset:#x}, number of variables indexes for value labels ({n}) is greater than the maximum number ({max})."
+ )]
TooManyVarIndexes { offset: u64, n: u32, max: u32 },
- #[error("At offset {offset:#x}, record type 7 subtype {subtype} is too large with element size {size} and {count} elements.")]
+ #[error(
+ "At offset {offset:#x}, record type 7 subtype {subtype} is too large with element size {size} and {count} elements."
+ )]
ExtensionRecordTooLarge {
offset: u64,
subtype: u32,
count: u32,
},
- #[error("Unexpected end of file at offset {offset:#x}, {case_ofs} bytes into a {case_len}-byte case.")]
+ #[error(
+ "Unexpected end of file at offset {offset:#x}, {case_ofs} bytes into a {case_len}-byte case."
+ )]
EofInCase {
offset: u64,
case_ofs: u64,
#[error("Data ends at offset {offset:#x}, {case_ofs} bytes into a compressed case.")]
PartialCompressedCase { offset: u64, case_ofs: u64 },
- #[error("At {case_ofs} bytes into compressed case starting at offset {offset:#x}, a string was found where a number was expected.")]
+ #[error(
+ "At {case_ofs} bytes into compressed case starting at offset {offset:#x}, a string was found where a number was expected."
+ )]
CompressedNumberExpected { offset: u64, case_ofs: u64 },
- #[error("At {case_ofs} bytes into compressed case starting at offset {offset:#x}, a number was found where a string was expected.")]
+ #[error(
+ "At {case_ofs} bytes into compressed case starting at offset {offset:#x}, a number was found where a string was expected."
+ )]
CompressedStringExpected { offset: u64, case_ofs: u64 },
#[error("Impossible ztrailer_offset {0:#x}.")]
#[error("ZLIB trailer specifies unexpected {0}-byte block size.")]
WrongZlibTrailerBlockSize(u32),
- #[error("Block count {n_blocks} in ZLIB trailer at offset {offset:#x} differs from expected block count {expected_n_blocks} calculated from trailer length {ztrailer_len}.")]
+ #[error(
+ "Block count {n_blocks} in ZLIB trailer at offset {offset:#x} differs from expected block count {expected_n_blocks} calculated from trailer length {ztrailer_len}."
+ )]
BadZlibTrailerNBlocks {
offset: u64,
n_blocks: u32,
ztrailer_len: u64,
},
- #[error("ZLIB block descriptor {index} reported uncompressed data offset {actual:#x}, when {expected:#x} was expected.")]
+ #[error(
+ "ZLIB block descriptor {index} reported uncompressed data offset {actual:#x}, when {expected:#x} was expected."
+ )]
ZlibTrailerBlockWrongUncmpOfs {
index: usize,
actual: u64,
expected: u64,
},
- #[error("ZLIB block descriptor {index} reported compressed data offset {actual:#x}, when {expected:#x} was expected.")]
+ #[error(
+ "ZLIB block descriptor {index} reported compressed data offset {actual:#x}, when {expected:#x} was expected."
+ )]
ZlibTrailerBlockWrongCmpOfs {
index: usize,
actual: u64,
expected: u64,
},
- #[error("ZLIB block descriptor {index} reports compressed size {compressed_size} and uncompressed size {uncompressed_size}.")]
+ #[error(
+ "ZLIB block descriptor {index} reports compressed size {compressed_size} and uncompressed size {uncompressed_size}."
+ )]
ZlibExpansion {
index: usize,
compressed_size: u32,
uncompressed_size: u32,
},
- #[error("ZLIB trailer is at offset {zheader:#x} but {descriptors:#x} would be expected from block descriptors.")]
+ #[error(
+ "ZLIB trailer is at offset {zheader:#x} but {descriptors:#x} would be expected from block descriptors."
+ )]
ZlibTrailerOffsetInconsistency { descriptors: u64, zheader: u64 },
#[error("File metadata says it contains {expected} cases, but {actual} cases were read.")]
#[error("Unexpected end of data inside extension record.")]
UnexpectedEndOfData,
- #[error("At offset {offset:#x}, at least one valid variable index for value labels is required but none were specified.")]
+ #[error(
+ "At offset {offset:#x}, at least one valid variable index for value labels is required but none were specified."
+ )]
NoVarIndexes { offset: u64 },
#[error("At offset {offset:#x}, the first variable index is for a {var_type} variable but the following variable indexes are for {} variables: {wrong_types:?}", !var_type)]
wrong_types: Vec<u32>,
},
- #[error("At offset {offset:#x}, one or more variable indexes for value labels were not in the valid range [1,{max}] or referred to string continuations: {invalid:?}")]
+ #[error(
+ "At offset {offset:#x}, one or more variable indexes for value labels were not in the valid range [1,{max}] or referred to string continuations: {invalid:?}"
+ )]
InvalidVarIndexes {
offset: u64,
max: usize,
invalid: Vec<u32>,
},
- #[error("At offset {offset:#x}, {record} has bad size {size} bytes instead of the expected {expected_size}.")]
+ #[error(
+ "At offset {offset:#x}, {record} has bad size {size} bytes instead of the expected {expected_size}."
+ )]
BadRecordSize {
offset: u64,
record: String,
expected_size: u32,
},
- #[error("At offset {offset:#x}, {record} has bad count {count} instead of the expected {expected_count}.")]
+ #[error(
+ "At offset {offset:#x}, {record} has bad count {count} instead of the expected {expected_count}."
+ )]
BadRecordCount {
offset: u64,
record: String,
expected_count: u32,
},
- #[error("In long string missing values record starting at offset {record_offset:#x}, value length at offset {offset:#x} is {value_len} instead of the expected 8.")]
+ #[error(
+ "In long string missing values record starting at offset {record_offset:#x}, value length at offset {offset:#x} is {value_len} instead of the expected 8."
+ )]
BadLongMissingValueLength {
record_offset: u64,
offset: u64,
value_len: u32,
},
- #[error("The encoding record at offset {offset:#x} contains an encoding name that is not valid UTF-8.")]
+ #[error(
+ "The encoding record at offset {offset:#x} contains an encoding name that is not valid UTF-8."
+ )]
BadEncodingName { offset: u64 },
// XXX This is risky because `text` might be arbitarily long.
#[error("Syntax error parsing counted string (length {0:?} goes past end of input)")]
CountedStringTooLong(usize),
- #[error("Variable display record contains {count} items but should contain either {first} or {second}.")]
+ #[error(
+ "Variable display record contains {count} items but should contain either {first} or {second}."
+ )]
InvalidVariableDisplayCount {
count: usize,
first: usize,
#[error("Compression bias is {0} instead of the usual values of 0 or 100.")]
UnexpectedBias(f64),
- #[error("ZLIB block descriptor {index} reported block size {actual:#x}, when {expected:#x} was expected.")]
+ #[error(
+ "ZLIB block descriptor {index} reported block size {actual:#x}, when {expected:#x} was expected."
+ )]
ZlibTrailerBlockWrongSize {
index: usize,
actual: u32,
expected: u32,
},
- #[error("ZLIB block descriptor {index} reported block size {actual:#x}, when at most {max_expected:#x} was expected.")]
+ #[error(
+ "ZLIB block descriptor {index} reported block size {actual:#x}, when at most {max_expected:#x} was expected."
+ )]
ZlibTrailerBlockTooBig {
index: usize,
actual: u32,
.into()
}
-#[derive(Clone, Default)]
-pub struct MissingValues {
- /// Individual missing values, up to 3 of them.
- values: Vec<Datum>,
-
- /// Optional range of missing values.
- range: Option<MissingValueRange>,
-}
-
-impl Debug for MissingValues {
- fn fmt(&self, f: &mut Formatter) -> FmtResult {
- DisplayMissingValues {
- mv: self,
- encoding: None,
- }
- .fmt(f)
- }
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum MissingValuesError {
- TooMany,
- TooWide,
- MixedTypes,
-}
-
impl MissingValues {
- pub fn new(
- mut values: Vec<Datum>,
- range: Option<MissingValueRange>,
- ) -> Result<Self, MissingValuesError> {
- if values.len() > 3 {
- return Err(MissingValuesError::TooMany);
- }
-
- let mut var_type = None;
- for value in values.iter_mut() {
- value.trim_end();
- match value.width() {
- VarWidth::String(w) if w > 8 => return Err(MissingValuesError::TooWide),
- _ => (),
- }
- if var_type.is_some_and(|t| t != value.var_type()) {
- return Err(MissingValuesError::MixedTypes);
- }
- var_type = Some(value.var_type());
- }
-
- if var_type == Some(VarType::String) && range.is_some() {
- return Err(MissingValuesError::MixedTypes);
- }
-
- Ok(Self { values, range })
- }
-
- pub fn is_empty(&self) -> bool {
- self.values.is_empty() && self.range.is_none()
- }
-
- pub fn var_type(&self) -> Option<VarType> {
- if let Some(datum) = self.values.first() {
- Some(datum.var_type())
- } else if self.range.is_some() {
- Some(VarType::Numeric)
- } else {
- None
- }
- }
-
- pub fn contains(&self, value: &Datum) -> bool {
- if self
- .values
- .iter()
- .any(|datum| datum.eq_ignore_trailing_spaces(value))
- {
- return true;
- }
-
- match value {
- Datum::Number(Some(number)) => self.range.is_some_and(|range| range.contains(*number)),
- _ => false,
- }
- }
-
- pub fn is_resizable(&self, width: VarWidth) -> bool {
- self.values.iter().all(|datum| datum.is_resizable(width))
- && self.range.iter().all(|range| range.is_resizable(width))
- }
-
- pub fn resize(&mut self, width: VarWidth) {
- for datum in &mut self.values {
- datum.resize(width);
- }
- if let Some(range) = &mut self.range {
- range.resize(width);
- }
- }
-
fn read<R: Read + Seek>(
r: &mut R,
offset: u64,
}
Ok(Self::default())
}
-
- pub fn display(&self, encoding: &'static Encoding) -> DisplayMissingValues<'_> {
- DisplayMissingValues {
- mv: self,
- encoding: Some(encoding),
- }
- }
-}
-
-pub struct DisplayMissingValues<'a> {
- mv: &'a MissingValues,
- encoding: Option<&'static Encoding>,
-}
-
-impl<'a> Display for DisplayMissingValues<'a> {
- fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
- if let Some(range) = &self.mv.range {
- write!(f, "{range}")?;
- if !self.mv.values.is_empty() {
- write!(f, "; ")?;
- }
- }
-
- for (i, value) in self.mv.values.iter().enumerate() {
- if i > 0 {
- write!(f, "; ")?;
- }
- match self.encoding {
- Some(encoding) => value.display_plain(encoding).fmt(f)?,
- None => value.fmt(f)?,
- }
- }
-
- if self.mv.is_empty() {
- write!(f, "none")?;
- }
- Ok(())
- }
-}
-
-#[derive(Copy, Clone)]
-pub enum MissingValueRange {
- In { low: f64, high: f64 },
- From { low: f64 },
- To { high: f64 },
-}
-
-impl MissingValueRange {
- pub fn new(low: f64, high: f64) -> Self {
- const LOWEST: f64 = f64::MIN.next_up();
- match (low, high) {
- (f64::MIN | LOWEST, _) => Self::To { high },
- (_, f64::MAX) => Self::From { low },
- (_, _) => Self::In { low, high },
- }
- }
-
- pub fn low(&self) -> Option<f64> {
- match self {
- MissingValueRange::In { low, .. } | MissingValueRange::From { low } => Some(*low),
- MissingValueRange::To { .. } => None,
- }
- }
-
- pub fn high(&self) -> Option<f64> {
- match self {
- MissingValueRange::In { high, .. } | MissingValueRange::To { high } => Some(*high),
- MissingValueRange::From { .. } => None,
- }
- }
-
- pub fn contains(&self, number: f64) -> bool {
- match self {
- MissingValueRange::In { low, high } => (*low..*high).contains(&number),
- MissingValueRange::From { low } => number >= *low,
- MissingValueRange::To { high } => number <= *high,
- }
- }
-
- pub fn is_resizable(&self, width: VarWidth) -> bool {
- width.is_numeric()
- }
-
- pub fn resize(&self, width: VarWidth) {
- assert_eq!(width, VarWidth::Numeric);
- }
-}
-
-impl Display for MissingValueRange {
- fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
- match self.low() {
- Some(low) => low.display_plain().fmt(f)?,
- None => write!(f, "LOW")?,
- }
-
- write!(f, " THRU ")?;
-
- match self.high() {
- Some(high) => high.display_plain().fmt(f)?,
- None => write!(f, "HIGH")?,
- }
- Ok(())
- }
}
#[derive(Clone)]
pub label: Option<S>,
}
+/// Width of a variable record.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum RawWidth {
+ /// String continuation.
+ ///
+ /// One variable record of this type is present for each 8 bytes after
+ /// the first 8 bytes of a string variable, as a kind of placeholder.
Continuation,
+
+ /// Numeric.
Numeric,
+
+ /// String.
String(NonZeroU8),
}
impl RawWidth {
+ /// Returns the number of value positions corresponding to a variable with
+ /// this type.
pub fn n_values(&self) -> Option<usize> {
match self {
RawWidth::Numeric => Some(1),
RawWidth::String(width) => Some((width.get() as usize).div_ceil(8)),
- _ => None,
+ RawWidth::Continuation => None,
}
}
}
start_offset,
code_offset,
code: has_variable_label,
- })
+ });
}
};
_ => {
return Err(Warning::MultipleResponseSyntaxError(
"missing space preceding variable name",
- ))
+ ));
}
}
}
Ok((string.into(), rest))
}
-/// [Level of measurement](https://en.wikipedia.org/wiki/Level_of_measurement).
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum Measure {
- /// Nominal values can only be compared for equality.
- Nominal,
-
- /// Ordinal values can be meaningfully ordered.
- Ordinal,
-
- /// Scale values can be meaningfully compared for the degree of difference.
- Scale,
-}
-
impl Measure {
- pub fn default_for_type(var_type: VarType) -> Option<Measure> {
- match var_type {
- VarType::Numeric => None,
- VarType::String => Some(Self::Nominal),
- }
- }
-
fn try_decode(source: u32) -> Result<Option<Measure>, Warning> {
match source {
0 => Ok(None),
_ => Err(Warning::InvalidMeasurement(source)),
}
}
-
- pub fn as_str(&self) -> &'static str {
- match self {
- Measure::Nominal => "Nominal",
- Measure::Ordinal => "Ordinal",
- Measure::Scale => "Scale",
- }
- }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum Alignment {
- Left,
- Right,
- Center,
}
impl Alignment {
_ => Err(Warning::InvalidAlignment(source)),
}
}
-
- pub fn default_for_type(var_type: VarType) -> Self {
- match var_type {
- VarType::Numeric => Self::Right,
- VarType::String => Self::Left,
- }
- }
-
- pub fn as_str(&self) -> &'static str {
- match self {
- Alignment::Left => "Left",
- Alignment::Right => "Right",
- Alignment::Center => "Center",
- }
- }
}
#[derive(Clone, Debug)]