work
[pspp] / rust / src / cooked.rs
1 use std::borrow::Cow;
2
3 use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
4 use encoding_rs::Encoding;
5
6 use crate::{
7     Error,
8     {endian::Endian, CategoryLabels, Compression},
9     format::Spec,
10 };
11
12 pub struct Decoder {
13     pub compression: Option<Compression>,
14     pub endian: Endian,
15     pub encoding: &'static Encoding,
16 }
17
18 impl Decoder {
19     fn decode_string<'a>(&self, input: &'a [u8], warn: &impl Fn(Error)) -> Cow<'a, str> {
20         let (output, malformed) = self.encoding.decode_without_bom_handling(input);
21         if malformed {
22             warn(Error::TBD);
23         }
24         output
25     }
26 }
27
28 pub trait Decode: Sized {
29     type Input;
30     fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Self;
31 }
32
33 #[derive(Clone)]
34 pub struct Header {
35     pub eye_catcher: String,
36     pub weight_index: Option<usize>,
37     pub n_cases: Option<u64>,
38     pub creation: NaiveDateTime,
39     pub file_label: String,
40 }
41
42 impl Decode for Header {
43     type Input = crate::raw::Header;
44
45     fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Self {
46         let eye_catcher = decoder.decode_string(&input.eye_catcher, &warn);
47         let file_label = decoder.decode_string(&input.file_label, &warn);
48         let creation_date = decoder.decode_string(&input.creation_date, &warn);
49         let creation_date = NaiveDate::parse_from_str(&creation_date, "%v").unwrap_or_else(|_| {
50             warn(Error::InvalidCreationDate {
51                 creation_date: creation_date.into(),
52             });
53             Default::default()
54         });
55         let creation_time = decoder.decode_string(&input.creation_time, &warn);
56         let creation_time =
57             NaiveTime::parse_from_str(&creation_time, "%H:%M:%S").unwrap_or_else(|_| {
58                 warn(Error::InvalidCreationTime {
59                     creation_time: creation_time.into(),
60                 });
61                 Default::default()
62             });
63         Header {
64             eye_catcher: eye_catcher.into(),
65             weight_index: input.weight_index.map(|n| n as usize),
66             n_cases: input.n_cases.map(|n| n as u64),
67             creation: NaiveDateTime::new(creation_date, creation_time),
68             file_label: file_label.into(),
69         }
70     }
71 }
72
73 pub struct Variable {
74     pub width: i32,
75     pub name: String,
76     pub print_format: Spec,
77     pub write_format: Spec,
78 }
79
80 #[derive(Clone)]
81 pub struct Document(Vec<String>);
82
83 impl Decode for Document {
84     type Input = crate::raw::Document;
85
86     fn decode(decoder: &Decoder, input: &Self::Input, warn: impl Fn(Error)) -> Self {
87         Document(
88             input
89                 .lines
90                 .iter()
91                 .map(|s| decoder.decode_string(s, &warn).into())
92                 .collect(),
93         )
94     }
95 }
96
97 pub use crate::raw::FloatInfo;
98 pub use crate::raw::IntegerInfo;
99
100 #[derive(Clone, Debug)]
101 pub enum MultipleResponseType {
102     MultipleDichotomy {
103         value: String,
104         labels: CategoryLabels,
105     },
106     MultipleCategory,
107 }
108 #[derive(Clone, Debug)]
109 pub struct MultipleResponseSet {
110     pub name: String,
111     pub label: String,
112     pub mr_type: MultipleResponseType,
113     pub vars: Vec<String>,
114 }
115
116 #[derive(Clone, Debug)]
117 pub struct MultipleResponseRecord(Vec<MultipleResponseSet>);
118
119 #[derive(Clone, Debug)]
120 pub struct ProductInfo(String);
121
122 pub enum Measure {
123     Nominal,
124     Ordinal,
125     Scale,
126 }
127
128 pub enum Alignment {
129     Left,
130     Right,
131     Center,
132 }
133
134 pub struct VarDisplay {
135     pub measure: Option<Measure>,
136     pub width: u32,
137     pub align: Option<Alignment>,
138 }
139
140 pub struct VarDisplayRecord(pub Vec<VarDisplay>);