use encoding_rs::{Encoding, UTF_8};
use serde::Serialize;
-use crate::{
- data::{BorrowedRawString, Datum, OwnedRawString, Quoted, RawString},
- dictionary::{VarType, VarWidth},
- format::DisplayPlain,
-};
-
-pub type OwnedEncodedDatum = EncodedDatum<OwnedEncodedString>;
-pub type BorrowedEncodedDatum<'a> = EncodedDatum<BorrowedEncodedString<'a>>;
-
-/// The value of a [Variable](crate::dictionary::Variable), with a string
-/// encoding.
-#[derive(Clone)]
-pub enum EncodedDatum<D> {
- /// A numeric value.
- Number(
- /// A number, or `None` for the system-missing value.
- Option<f64>,
- ),
- /// A string value.
- String(
- /// The value, in the variable's encoding.
- D,
- ),
-}
-
-impl<R> EncodedDatum<EncodedString<R>>
-where
- R: Borrow<BorrowedRawString>,
-{
- pub fn into_raw(self) -> Datum<R> {
- match self {
- EncodedDatum::Number(number) => Datum::Number(number),
- EncodedDatum::String(encoded_string) => Datum::String(encoded_string.into_raw()),
- }
- }
-
- /// Returns the [VarWidth] corresponding to this datum.
- pub fn width(&self) -> VarWidth {
- match self {
- Self::Number(_) => VarWidth::Numeric,
- Self::String(s) => VarWidth::String(s.len().try_into().unwrap()),
- }
- }
-
- pub fn borrowed<'a>(&'a self) -> EncodedDatum<BorrowedEncodedString<'a>> {
- match self {
- EncodedDatum::Number(number) => EncodedDatum::Number(*number),
- EncodedDatum::String(encoded_string) => EncodedDatum::String(encoded_string.borrowed()),
- }
- }
-
- /// Compares this datum and `other` for equality, ignoring trailing ASCII
- /// spaces in either, if they are both strings, for the purpose of
- /// comparison.
- pub fn eq_ignore_trailing_spaces<R2>(&self, other: &EncodedDatum<EncodedString<R2>>) -> bool
- where
- R2: Borrow<BorrowedRawString>,
- {
- match (self.borrowed(), other.borrowed()) {
- (EncodedDatum::Number(lhs), EncodedDatum::Number(rhs)) => lhs == rhs,
- (EncodedDatum::String(lhs), EncodedDatum::String(rhs)) => {
- lhs.eq_ignore_trailing_spaces(&rhs)
- }
- _ => false,
- }
- }
-
- pub fn quoted(&self) -> QuotedEncodedDatum<'_> {
- QuotedEncodedDatum(self.borrowed())
- }
-}
-
-impl<D> EncodedDatum<D> {
- /// Constructs a new numerical [EncodedDatum] for the system-missing value.
- pub const fn sysmis() -> Self {
- Self::Number(None)
- }
-
- /// Returns the number inside this datum, or `None` if this is a string
- /// datum.
- pub fn as_number(&self) -> Option<Option<f64>> {
- match self {
- Self::Number(number) => Some(*number),
- Self::String(_) => None,
- }
- }
-
- /// Returns the string inside this datum, or `None` if this is a numeric
- /// datum.
- pub fn as_string(&self) -> Option<&D> {
- match self {
- Self::Number(_) => None,
- Self::String(s) => Some(s),
- }
- }
-
- /// Returns the string inside this datum as a mutable borrow, or `None` if
- /// this is a numeric datum.
- pub fn as_string_mut(&mut self) -> Option<&mut D> {
- match self {
- Self::Number(_) => None,
- Self::String(s) => Some(s),
- }
- }
-
- /// Returns the [VarType] corresponding to this datum.
- pub fn var_type(&self) -> VarType {
- match self {
- Self::Number(_) => VarType::Numeric,
- Self::String(_) => VarType::String,
- }
- }
-}
-
-impl OwnedEncodedDatum {
- /// Resizes this datum to the given `width`. Returns `Ok(())` if
- /// successful, if and only if this datum and `width` are both string or
- /// both numeric and, for string widths, resizing would not drop any
- /// non-space characters.
- pub fn resize(&mut self, width: VarWidth) -> Result<(), ()> {
- match (self, width) {
- (Self::Number(_), VarWidth::Numeric) => Ok(()),
- (Self::String(s), VarWidth::String(new_width)) => s.resize(new_width as usize),
- _ => Err(()),
- }
- }
-
- /// Removes trailing ASCII spaces from this datum, if it is a string.
- pub fn trim_end(&mut self) {
- match self {
- Self::Number(_) => (),
- Self::String(s) => s.trim_end(),
- }
- }
-}
-
-impl<D> Display for EncodedDatum<D>
-where
- D: Display,
-{
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Self::Number(None) => write!(f, "SYSMIS"),
- Self::Number(Some(number)) => number.display_plain().fmt(f),
- Self::String(string) => write!(f, "{string}"),
- }
- }
-}
-
-impl<D> Serialize for EncodedDatum<D>
-where
- D: Serialize,
-{
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- {
- match self {
- EncodedDatum::Number(number) => number.serialize(serializer),
- EncodedDatum::String(encoded_string) => encoded_string.serialize(serializer),
- }
- }
-}
-
-impl<D> From<f64> for EncodedDatum<D> {
- fn from(number: f64) -> Self {
- Some(number).into()
- }
-}
-
-impl<D> From<Option<f64>> for EncodedDatum<D> {
- fn from(value: Option<f64>) -> Self {
- Self::Number(value)
- }
-}
-
-impl From<&str> for OwnedEncodedDatum {
- fn from(value: &str) -> Self {
- Self::String(OwnedEncodedString::from(value))
- }
-}
-
-/// Helper struct for displaying a value in double quotes.
-pub struct QuotedEncodedDatum<'a>(BorrowedEncodedDatum<'a>);
-
-impl Display for QuotedEncodedDatum<'_> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match &self.0 {
- EncodedDatum::Number(None) => write!(f, "SYSMIS"),
- EncodedDatum::Number(Some(number)) => number.display_plain().fmt(f),
- EncodedDatum::String(string) => write!(f, "\"{}\"", string.as_str()),
- }
- }
-}
+use crate::data::{BorrowedRawString, OwnedRawString, Quoted, RawString};
pub type OwnedEncodedString = EncodedString<OwnedRawString>;
pub type BorrowedEncodedString<'a> = EncodedString<&'a BorrowedRawString>;