work
[pspp] / rust / src / cooked.rs
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c84ffd65de177cc97b091db6e059aeac7c33777a 100644 (file)
@@ -0,0 +1,140 @@
+use std::borrow::Cow;
+
+use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
+use encoding_rs::Encoding;
+
+use crate::{
+    Error,
+    {endian::Endian, CategoryLabels, Compression},
+    format::UncheckedFormat,
+};
+
+pub struct Decoder {
+    pub compression: Option<Compression>,
+    pub endian: Endian,
+    pub encoding: &'static Encoding,
+}
+
+impl Decoder {
+    fn decode_string<'a>(&self, input: &'a [u8], warn: &impl Fn(Error)) -> Cow<'a, str> {
+        let (output, malformed) = self.encoding.decode_without_bom_handling(input);
+        if malformed {
+            warn(Error::TBD);
+        }
+        output
+    }
+}
+
+pub trait Decode: Sized {
+    type Input;
+    fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Self;
+}
+
+#[derive(Clone)]
+pub struct Header {
+    pub eye_catcher: String,
+    pub weight_index: Option<usize>,
+    pub n_cases: Option<u64>,
+    pub creation: NaiveDateTime,
+    pub file_label: String,
+}
+
+impl Decode for Header {
+    type Input = crate::raw::Header;
+
+    fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Self {
+        let eye_catcher = decoder.decode_string(&input.eye_catcher, &warn);
+        let file_label = decoder.decode_string(&input.file_label, &warn);
+        let creation_date = decoder.decode_string(&input.creation_date, &warn);
+        let creation_date = NaiveDate::parse_from_str(&creation_date, "%v").unwrap_or_else(|_| {
+            warn(Error::InvalidCreationDate {
+                creation_date: creation_date.into(),
+            });
+            Default::default()
+        });
+        let creation_time = decoder.decode_string(&input.creation_time, &warn);
+        let creation_time =
+            NaiveTime::parse_from_str(&creation_time, "%H:%M:%S").unwrap_or_else(|_| {
+                warn(Error::InvalidCreationTime {
+                    creation_time: creation_time.into(),
+                });
+                Default::default()
+            });
+        Header {
+            eye_catcher: eye_catcher.into(),
+            weight_index: input.weight_index.map(|n| n as usize),
+            n_cases: input.n_cases.map(|n| n as u64),
+            creation: NaiveDateTime::new(creation_date, creation_time),
+            file_label: file_label.into(),
+        }
+    }
+}
+
+pub struct Variable {
+    pub width: i32,
+    pub name: String,
+    pub print_format: UncheckedFormat,
+    pub write_format: UncheckedFormat,
+}
+
+#[derive(Clone)]
+pub struct Document(Vec<String>);
+
+impl Decode for Document {
+    type Input = crate::raw::Document;
+
+    fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Self {
+        Document(
+            input
+                .lines
+                .iter()
+                .map(|s| decoder.decode_string(s, &warn).into())
+                .collect(),
+        )
+    }
+}
+
+pub use crate::raw::FloatInfo;
+pub use crate::raw::IntegerInfo;
+
+#[derive(Clone, Debug)]
+pub enum MultipleResponseType {
+    MultipleDichotomy {
+        value: String,
+        labels: CategoryLabels,
+    },
+    MultipleCategory,
+}
+#[derive(Clone, Debug)]
+pub struct MultipleResponseSet {
+    pub name: String,
+    pub label: String,
+    pub mr_type: MultipleResponseType,
+    pub vars: Vec<String>,
+}
+
+#[derive(Clone, Debug)]
+pub struct MultipleResponseRecord(Vec<MultipleResponseSet>);
+
+#[derive(Clone, Debug)]
+pub struct ProductInfo(String);
+
+pub enum Measure {
+    Nominal,
+    Ordinal,
+    Scale,
+}
+
+pub enum Alignment {
+    Left,
+    Right,
+    Center,
+}
+
+pub struct VarDisplay {
+    pub measure: Option<Measure>,
+    pub width: u32,
+    pub align: Option<Alignment>,
+}
+
+pub struct VarDisplayRecord(pub Vec<VarDisplay>);