1use std::{
20 collections::BTreeMap,
21 fmt::{Debug, Display},
22 hash::{DefaultHasher, Hash, Hasher},
23 ops::{Deref, Not},
24 str::FromStr,
25};
26
27use displaydoc::Display;
28use encoding_rs::{Encoding, UTF_8};
29use hashbrown::HashMap;
30use indexmap::Equivalent;
31use num::integer::div_ceil;
32use serde::{Serialize, ser::SerializeSeq};
33use thiserror::Error as ThisError;
34use unicase::UniCase;
35
36use crate::{
37 data::{
38 ByteStr, ByteString, Datum, Encoded, EncodedString, RawString, ResizeError, WithEncoding,
39 },
40 format::{DisplayPlain, Format},
41 identifier::{HasIdentifier, Identifier},
42};
43
44#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
46pub enum VarType {
47 Numeric,
49
50 String,
55}
56
57impl VarType {
58 pub fn is_numeric(&self) -> bool {
59 *self == Self::Numeric
60 }
61
62 pub fn is_string(&self) -> bool {
63 *self == Self::String
64 }
65}
66
67impl Not for VarType {
68 type Output = Self;
69
70 fn not(self) -> Self::Output {
71 match self {
72 Self::Numeric => Self::String,
73 Self::String => Self::Numeric,
74 }
75 }
76}
77
78impl Not for &VarType {
79 type Output = VarType;
80
81 fn not(self) -> Self::Output {
82 !*self
83 }
84}
85
86impl Display for VarType {
87 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
88 match self {
89 VarType::Numeric => write!(f, "numeric"),
90 VarType::String => write!(f, "string"),
91 }
92 }
93}
94
95#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)]
99pub enum VarWidth {
100 Numeric,
102
103 String(
105 u16,
110 ), }
112
113impl VarWidth {
114 pub const MAX_STRING: u16 = 32767;
115
116 pub fn n_dict_indexes(self) -> usize {
117 match self {
118 VarWidth::Numeric => 1,
119 VarWidth::String(w) => div_ceil(w as usize, 8),
120 }
121 }
122
123 fn width_predicate(a: VarWidth, b: VarWidth, f: impl Fn(u16, u16) -> u16) -> Option<VarWidth> {
124 match (a, b) {
125 (VarWidth::Numeric, VarWidth::Numeric) => Some(VarWidth::Numeric),
126 (VarWidth::String(a), VarWidth::String(b)) => Some(VarWidth::String(f(a, b))),
127 _ => None,
128 }
129 }
130
131 pub fn wider(a: VarWidth, b: VarWidth) -> Option<VarWidth> {
136 Self::width_predicate(a, b, |a, b| a.max(b))
137 }
138
139 pub fn narrower(a: VarWidth, b: VarWidth) -> Option<VarWidth> {
141 Self::width_predicate(a, b, |a, b| a.min(b))
142 }
143
144 pub fn default_display_width(&self) -> u32 {
145 match self {
146 VarWidth::Numeric => 8,
147 VarWidth::String(width) => *width.min(&32) as u32,
148 }
149 }
150
151 pub fn is_long_string(&self) -> bool {
152 if let Self::String(width) = self {
153 *width > 8
154 } else {
155 false
156 }
157 }
158
159 pub fn as_string_width(&self) -> Option<usize> {
160 match self {
161 VarWidth::Numeric => None,
162 VarWidth::String(width) => Some(*width as usize),
163 }
164 }
165
166 pub fn is_numeric(&self) -> bool {
167 *self == Self::Numeric
168 }
169
170 pub fn is_string(&self) -> bool {
171 !self.is_numeric()
172 }
173
174 pub fn is_very_long_string(&self) -> bool {
177 match *self {
178 VarWidth::Numeric => false,
179 VarWidth::String(width) => width > 255,
180 }
181 }
182
183 pub const SEGMENT_SIZE: usize = 252;
186
187 pub fn segments(&self) -> Segments {
194 Segments::new(*self)
195 }
196
197 pub fn n_chunks(&self) -> Option<usize> {
203 match *self {
204 VarWidth::Numeric => Some(1),
205 VarWidth::String(w) if w <= 255 => Some(w.div_ceil(8) as usize),
206 VarWidth::String(_) => None,
207 }
208 }
209
210 pub fn segment_alloc_width(&self, segment_idx: usize) -> usize {
215 debug_assert!(segment_idx < self.segments().len());
216 debug_assert!(self.is_very_long_string());
217
218 if segment_idx < self.segments().len() - 1 {
219 255
220 } else {
221 self.as_string_width().unwrap() - segment_idx * Self::SEGMENT_SIZE
222 }
223 }
224
225 pub fn display_adjective(&self) -> VarWidthAdjective {
226 VarWidthAdjective(*self)
227 }
228
229 pub fn codepage_to_unicode(&mut self) {
230 match self {
231 VarWidth::Numeric => (),
232 VarWidth::String(width) => *width = width.saturating_mul(3).min(Self::MAX_STRING),
233 }
234 }
235}
236
237pub struct Segments {
238 width: VarWidth,
239 i: usize,
240 n: usize,
241}
242impl Segments {
243 pub fn new(width: VarWidth) -> Self {
244 Self {
245 width,
246 i: 0,
247 n: if width.is_very_long_string() {
248 width
249 .as_string_width()
250 .unwrap()
251 .div_ceil(VarWidth::SEGMENT_SIZE)
252 } else {
253 1
254 },
255 }
256 }
257}
258
259impl Iterator for Segments {
260 type Item = VarWidth;
261
262 fn next(&mut self) -> Option<Self::Item> {
263 let i = self.i;
264 if i >= self.n {
265 None
266 } else {
267 self.i += 1;
268 match self.width {
269 VarWidth::Numeric => Some(VarWidth::Numeric),
270 VarWidth::String(_) if i < self.n - 1 => Some(VarWidth::String(255)),
271 VarWidth::String(width) => Some(VarWidth::String(
272 width - (self.n as u16 - 1) * VarWidth::SEGMENT_SIZE as u16,
273 )),
274 }
275 }
276 }
277
278 fn size_hint(&self) -> (usize, Option<usize>) {
279 let n = self.n - self.i;
280 (n, Some(n))
281 }
282}
283
284impl ExactSizeIterator for Segments {}
285
286impl From<VarWidth> for VarType {
287 fn from(source: VarWidth) -> Self {
288 match source {
289 VarWidth::Numeric => VarType::Numeric,
290 VarWidth::String(_) => VarType::String,
291 }
292 }
293}
294
295pub struct VarWidthAdjective(VarWidth);
296
297impl Display for VarWidthAdjective {
298 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
299 match self.0 {
300 VarWidth::Numeric => write!(f, "numeric"),
301 VarWidth::String(width) => write!(f, "{width}-byte string"),
302 }
303 }
304}
305
306#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
307pub enum Role {
308 #[default]
309 Input,
310 Target,
311 Both,
312 None,
313 Partition,
314 Split,
315}
316
317impl Role {
318 pub fn as_str(&self) -> &'static str {
319 match self {
320 Role::Input => "Input",
321 Role::Target => "Target",
322 Role::Both => "Both",
323 Role::None => "None",
324 Role::Partition => "Partition",
325 Role::Split => "Split",
326 }
327 }
328}
329
330impl FromStr for Role {
331 type Err = InvalidRole;
332
333 fn from_str(s: &str) -> Result<Self, Self::Err> {
334 for (string, value) in [
335 ("input", Role::Input),
336 ("target", Role::Target),
337 ("both", Role::Both),
338 ("none", Role::None),
339 ("partition", Role::Partition),
340 ("split", Role::Split),
341 ] {
342 if string.eq_ignore_ascii_case(s) {
343 return Ok(value);
344 }
345 }
346 Err(InvalidRole::UnknownRole(s.into()))
347 }
348}
349
350impl TryFrom<i32> for Role {
351 type Error = InvalidRole;
352
353 fn try_from(value: i32) -> Result<Self, Self::Error> {
354 match value {
355 0 => Ok(Role::Input),
356 1 => Ok(Role::Target),
357 2 => Ok(Role::Both),
358 3 => Ok(Role::None),
359 4 => Ok(Role::Partition),
360 5 => Ok(Role::Split),
361 _ => Err(InvalidRole::UnknownRole(value.to_string())),
362 }
363 }
364}
365
366impl From<Role> for i32 {
367 fn from(value: Role) -> Self {
368 match value {
369 Role::Input => 0,
370 Role::Target => 1,
371 Role::Both => 2,
372 Role::None => 3,
373 Role::Partition => 4,
374 Role::Split => 5,
375 }
376 }
377}
378
379#[derive(Clone, Default, PartialEq, Eq, Serialize)]
380pub struct Attributes(pub BTreeMap<Identifier, Vec<String>>);
381
382impl Attributes {
383 pub fn new() -> Self {
384 Self(BTreeMap::new())
385 }
386
387 pub fn contains_name(&self, name: &Identifier) -> bool {
388 self.0.contains_key(name)
389 }
390
391 pub fn insert(&mut self, name: Identifier, values: Vec<String>) {
392 self.0.insert(name, values);
393 }
394
395 pub fn with(mut self, name: Identifier, values: Vec<String>) -> Self {
396 self.insert(name, values);
397 self
398 }
399
400 pub fn append(&mut self, other: &mut Self) {
401 self.0.append(&mut other.0)
402 }
403
404 pub fn role(&self) -> Result<Option<Role>, InvalidRole> {
405 self.try_into()
406 }
407
408 pub fn iter(&self, include_at: bool) -> impl Iterator<Item = (&Identifier, &[String])> {
409 self.0.iter().filter_map(move |(name, values)| {
410 if include_at || !name.0.starts_with('@') {
411 Some((name, values.as_slice()))
412 } else {
413 None
414 }
415 })
416 }
417
418 pub fn has_any(&self, include_at: bool) -> bool {
419 self.iter(include_at).next().is_some()
420 }
421
422 pub fn codepage_to_unicode(&mut self) {
423 let mut new = BTreeMap::new();
424 while let Some((mut name, value)) = self.0.pop_first() {
425 name.codepage_to_unicode();
426 new.insert(name, value);
427 }
428 self.0 = new;
429 }
430}
431
432impl Debug for Attributes {
433 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
434 self.0.fmt(f)
435 }
436}
437
438#[derive(Clone, Debug, ThisError, PartialEq, Eq)]
439pub enum InvalidRole {
440 #[error("Unknown role {0:?}.")]
441 UnknownRole(String),
442
443 #[error("Role attribute $@Role must have exactly one value (not {0}).")]
444 InvalidValues(usize),
445}
446
447impl TryFrom<&Attributes> for Option<Role> {
448 type Error = InvalidRole;
449
450 fn try_from(value: &Attributes) -> Result<Self, Self::Error> {
451 let role = Identifier::new("$@Role").unwrap();
452 value.0.get(&role).map_or(Ok(None), |attribute| {
453 if let Ok([string]) = <&[String; 1]>::try_from(attribute.as_slice()) {
454 match string.parse::<i32>() {
455 Ok(integer) => Ok(Some(Role::try_from(integer)?)),
456 Err(_) => Err(InvalidRole::UnknownRole(string.clone())),
457 }
458 } else {
459 Err(InvalidRole::InvalidValues(attribute.len()))
460 }
461 })
462 }
463}
464
465#[derive(Clone, Debug, Serialize)]
469pub struct Variable {
470 pub name: Identifier,
474
475 pub width: VarWidth,
477
478 missing_values: MissingValues,
485
486 pub print_format: Format,
488
489 pub write_format: Format,
491
492 pub value_labels: ValueLabels,
494
495 pub label: Option<String>,
498
499 pub measure: Option<Measure>,
501
502 pub role: Role,
504
505 pub display_width: u32,
507
508 pub alignment: Alignment,
510
511 pub leave: bool,
513
514 pub short_names: Vec<Identifier>,
517
518 pub attributes: Attributes,
520
521 encoding: &'static Encoding,
526}
527
528impl Variable {
529 pub fn new(name: Identifier, width: VarWidth, encoding: &'static Encoding) -> Self {
530 let var_type = VarType::from(width);
531 let leave = name.class().must_leave();
532 Self {
533 name,
534 width,
535 missing_values: MissingValues::default(),
536 print_format: Format::default_for_width(width),
537 write_format: Format::default_for_width(width),
538 value_labels: ValueLabels::new(),
539 label: None,
540 measure: Measure::default_for_type(var_type),
541 role: Role::default(),
542 display_width: width.default_display_width(),
543 alignment: Alignment::default_for_type(var_type),
544 leave,
545 short_names: Vec::new(),
546 attributes: Attributes::new(),
547 encoding,
548 }
549 }
550
551 pub fn encoding(&self) -> &'static Encoding {
552 self.encoding
553 }
554
555 pub fn is_numeric(&self) -> bool {
556 self.width.is_numeric()
557 }
558
559 pub fn is_string(&self) -> bool {
560 self.width.is_string()
561 }
562
563 pub fn label(&self) -> Option<&String> {
564 self.label.as_ref()
565 }
566
567 pub fn resize(&mut self, width: VarWidth) {
568 let _ = self.missing_values.resize(width);
569
570 self.value_labels.resize(width);
571
572 self.print_format.resize(width);
573 self.write_format.resize(width);
574
575 self.width = width;
576 }
577
578 pub fn missing_values(&self) -> &MissingValues {
579 &self.missing_values
580 }
581
582 pub fn missing_values_mut(&mut self) -> MissingValuesMut<'_> {
583 MissingValuesMut {
584 inner: &mut self.missing_values,
585 width: self.width,
586 }
587 }
588
589 pub fn codepage_to_unicode(&mut self) {
590 self.name.codepage_to_unicode();
591 self.width.codepage_to_unicode();
592 self.missing_values.codepage_to_unicode();
593 self.print_format.codepage_to_unicode();
594 self.write_format.codepage_to_unicode();
595 self.attributes.codepage_to_unicode();
596 self.encoding = UTF_8;
597
598 self.short_names.clear();
601 }
602}
603
604impl HasIdentifier for Variable {
605 fn identifier(&self) -> &UniCase<String> {
606 &self.name.0
607 }
608}
609
610#[derive(Clone, Default, PartialEq, Eq)]
615pub struct ValueLabels(pub HashMap<Datum<ByteString>, String>);
616
617impl Equivalent<Datum<ByteString>> for Datum<&ByteStr> {
618 fn equivalent(&self, key: &Datum<ByteString>) -> bool {
619 self == key
620 }
621}
622
623impl ValueLabels {
624 pub fn new() -> Self {
625 Self::default()
626 }
627
628 pub fn is_empty(&self) -> bool {
629 self.len() == 0
630 }
631
632 pub fn len(&self) -> usize {
633 self.0.len()
634 }
635
636 pub fn get<T>(&self, value: &Datum<T>) -> Option<&str>
637 where
638 T: RawString,
639 {
640 self.0.get(&value.as_raw()).map(|s| s.as_str())
641 }
642
643 pub fn insert(&mut self, value: Datum<ByteString>, label: impl Into<String>) -> Option<String> {
644 self.0.insert(value, label.into())
645 }
646
647 pub fn is_resizable(&self, width: VarWidth) -> bool {
648 self.0.keys().all(|datum| datum.is_resizable(width))
649 }
650
651 pub fn resize(&mut self, width: VarWidth) {
652 self.0 = self
653 .0
654 .drain()
655 .filter_map(|(mut datum, string)| {
656 datum.resize(width).is_ok().then_some((datum, string))
657 })
658 .collect();
659 }
660
661 pub fn codepage_to_unicode(&mut self, encoding: &'static Encoding) {
662 self.0 = self
663 .0
664 .drain()
665 .map(|(key, value)| {
666 let mut key = key.with_encoding(encoding);
667 key.codepage_to_unicode();
668 (key.without_encoding(), value)
669 })
670 .collect();
671 }
672}
673
674impl Serialize for ValueLabels {
675 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
676 where
677 S: serde::Serializer,
678 {
679 let mut map = serializer.serialize_seq(Some(self.0.len()))?;
680 for tuple in &self.0 {
681 map.serialize_element(&tuple)?;
682 }
683 map.end()
684 }
685}
686
687impl Debug for ValueLabels {
688 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
689 self.0.fmt(f)
690 }
691}
692
693impl Hash for ValueLabels {
694 fn hash<H: Hasher>(&self, state: &mut H) {
695 let mut hash = 0;
696 for (k, v) in &self.0 {
697 let mut hasher = DefaultHasher::new();
698 k.hash(&mut hasher);
699 v.hash(&mut hasher);
700 hash ^= hasher.finish();
701 }
702 state.write_u64(hash);
703 }
704}
705
706impl<'a> IntoIterator for &'a ValueLabels {
707 type Item = (&'a Datum<ByteString>, &'a String);
708
709 type IntoIter = hashbrown::hash_map::Iter<'a, Datum<ByteString>, String>;
710
711 fn into_iter(self) -> Self::IntoIter {
712 self.0.iter()
713 }
714}
715
716pub struct MissingValuesMut<'a> {
717 inner: &'a mut MissingValues,
718 width: VarWidth,
719}
720
721impl<'a> Deref for MissingValuesMut<'a> {
722 type Target = MissingValues;
723
724 fn deref(&self) -> &Self::Target {
725 self.inner
726 }
727}
728
729impl<'a> MissingValuesMut<'a> {
730 pub fn replace(&mut self, mut new: MissingValues) -> Result<(), MissingValuesError> {
731 new.resize(self.width)?;
732 *self.inner = new;
733 Ok(())
734 }
735
736 pub fn add_value(
737 &mut self,
738 mut value: Datum<WithEncoding<ByteString>>,
739 ) -> Result<(), MissingValuesError> {
740 if self.inner.values.len() > 2
741 || (self.inner.range().is_some() && self.inner.values.len() > 1)
742 {
743 Err(MissingValuesError::TooMany)
744 } else if value.var_type() != VarType::from(self.width) {
745 Err(MissingValuesError::MixedTypes)
746 } else if value.is_sysmis() {
747 Err(MissingValuesError::SystemMissing)
748 } else if value.resize(self.width.min(VarWidth::String(8))).is_err() {
749 Err(MissingValuesError::TooWide)
750 } else {
751 value.trim_end();
752 self.inner.values.push(value);
753 Ok(())
754 }
755 }
756
757 pub fn add_values(
758 &mut self,
759 values: impl IntoIterator<Item = Datum<WithEncoding<ByteString>>>,
760 ) -> Result<(), MissingValuesError> {
761 let n = self.inner.values.len();
762 for value in values {
763 self.add_value(value)
764 .inspect_err(|_| self.inner.values.truncate(n))?;
765 }
766 Ok(())
767 }
768
769 pub fn add_range(&mut self, range: MissingValueRange) -> Result<(), MissingValuesError> {
770 if self.inner.range.is_some() || self.inner.values().len() > 1 {
771 Err(MissingValuesError::TooMany)
772 } else if self.width != VarWidth::Numeric {
773 Err(MissingValuesError::MixedTypes)
774 } else {
775 self.inner.range = Some(range);
776 Ok(())
777 }
778 }
779}
780
781#[derive(Clone, Default, Serialize, PartialEq)]
783pub struct MissingValues {
784 values: Vec<Datum<WithEncoding<ByteString>>>,
786
787 range: Option<MissingValueRange>,
789}
790
791impl Debug for MissingValues {
792 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
793 write!(f, "{self}")
794 }
795}
796
797impl Display for MissingValues {
798 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
799 if let Some(range) = &self.range {
800 write!(f, "{range}")?;
801 if !self.values.is_empty() {
802 write!(f, "; ")?;
803 }
804 }
805
806 for (i, value) in self.values.iter().enumerate() {
807 if i > 0 {
808 write!(f, "; ")?;
809 }
810 write!(f, "{}", value.quoted())?;
811 }
812
813 if self.is_empty() {
814 write!(f, "none")?;
815 }
816 Ok(())
817 }
818}
819
820#[derive(Display, Copy, Clone, Debug, ThisError)]
822pub enum MissingValuesError {
823 TooMany,
825
826 TooWide,
828
829 MixedTypes,
831
832 SystemMissing,
834}
835
836impl From<ResizeError> for MissingValuesError {
837 fn from(value: ResizeError) -> Self {
838 match value {
839 ResizeError::MixedTypes => MissingValuesError::MixedTypes,
840 ResizeError::TooWide => MissingValuesError::TooWide,
841 }
842 }
843}
844
845impl MissingValues {
846 pub fn clear(&mut self) {
847 *self = Self::default();
848 }
849 pub fn values(&self) -> &[Datum<WithEncoding<ByteString>>] {
850 &self.values
851 }
852
853 pub fn range(&self) -> Option<&MissingValueRange> {
854 self.range.as_ref()
855 }
856
857 pub fn new(
858 mut values: Vec<Datum<WithEncoding<ByteString>>>,
859 range: Option<MissingValueRange>,
860 ) -> Result<Self, MissingValuesError> {
861 if values.len() > 3 {
862 return Err(MissingValuesError::TooMany);
863 }
864
865 let mut var_type = None;
866 for value in values.iter_mut() {
867 value.trim_end();
868 if value.width().is_long_string() {
869 return Err(MissingValuesError::TooWide);
870 }
871 if var_type.is_some_and(|t| t != value.var_type()) {
872 return Err(MissingValuesError::MixedTypes);
873 }
874 var_type = Some(value.var_type());
875 }
876
877 if var_type == Some(VarType::String) && range.is_some() {
878 return Err(MissingValuesError::MixedTypes);
879 }
880
881 Ok(Self { values, range })
882 }
883
884 pub fn is_empty(&self) -> bool {
885 self.values.is_empty() && self.range.is_none()
886 }
887
888 pub fn var_type(&self) -> Option<VarType> {
889 if let Some(datum) = self.values.first() {
890 Some(datum.var_type())
891 } else if self.range.is_some() {
892 Some(VarType::Numeric)
893 } else {
894 None
895 }
896 }
897
898 pub fn contains<S>(&self, value: &Datum<S>) -> bool
899 where
900 S: EncodedString,
901 {
902 if self
903 .values
904 .iter()
905 .any(|datum| datum.eq_ignore_trailing_spaces(value))
906 {
907 return true;
908 }
909
910 if let Some(Some(number)) = value.as_number()
911 && let Some(range) = self.range
912 {
913 range.contains(number)
914 } else {
915 false
916 }
917 }
918
919 pub fn resize(&mut self, width: VarWidth) -> Result<(), MissingValuesError> {
920 fn inner(this: &mut MissingValues, width: VarWidth) -> Result<(), MissingValuesError> {
921 for datum in &mut this.values {
922 datum.resize(width)?;
923 datum.trim_end();
924 }
925 if let Some(range) = &mut this.range {
926 range.resize(width)?;
927 }
928 Ok(())
929 }
930 inner(self, width).inspect_err(|_| self.clear())
931 }
932
933 pub fn codepage_to_unicode(&mut self) {
934 self.values = self
935 .values
936 .drain(..)
937 .map(|value| match value {
938 Datum::Number(number) => Datum::Number(number),
939 Datum::String(s) => Datum::String(if s.encoding() != UTF_8 {
940 let mut new_s = ByteString::from(s.as_str());
941 new_s.0.truncate(8);
942 WithEncoding::new(new_s, UTF_8)
943 } else {
944 s
945 }),
946 })
947 .collect();
948 }
949}
950
951#[derive(Copy, Clone, Debug, Serialize, PartialEq)]
952pub enum MissingValueRange {
953 In { low: f64, high: f64 },
954 From { low: f64 },
955 To { high: f64 },
956}
957
958impl MissingValueRange {
959 pub fn new(low: f64, high: f64) -> Self {
960 const LOWEST: f64 = f64::MIN.next_up();
961 match (low, high) {
962 (f64::MIN | LOWEST, _) => Self::To { high },
963 (_, f64::MAX) => Self::From { low },
964 (_, _) => Self::In { low, high },
965 }
966 }
967
968 pub fn low(&self) -> Option<f64> {
969 match self {
970 MissingValueRange::In { low, .. } | MissingValueRange::From { low } => Some(*low),
971 MissingValueRange::To { .. } => None,
972 }
973 }
974
975 pub fn high(&self) -> Option<f64> {
976 match self {
977 MissingValueRange::In { high, .. } | MissingValueRange::To { high } => Some(*high),
978 MissingValueRange::From { .. } => None,
979 }
980 }
981
982 pub fn contains(&self, number: f64) -> bool {
983 match self {
984 MissingValueRange::In { low, high } => (*low..*high).contains(&number),
985 MissingValueRange::From { low } => number >= *low,
986 MissingValueRange::To { high } => number <= *high,
987 }
988 }
989
990 pub fn resize(&self, width: VarWidth) -> Result<(), MissingValuesError> {
991 if width.is_numeric() {
992 Ok(())
993 } else {
994 Err(MissingValuesError::MixedTypes)
995 }
996 }
997}
998
999impl Display for MissingValueRange {
1000 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1001 match self.low() {
1002 Some(low) => low.display_plain().fmt(f)?,
1003 None => write!(f, "LOW")?,
1004 }
1005
1006 write!(f, " THRU ")?;
1007
1008 match self.high() {
1009 Some(high) => high.display_plain().fmt(f)?,
1010 None => write!(f, "HIGH")?,
1011 }
1012 Ok(())
1013 }
1014}
1015
1016#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
1017pub enum Alignment {
1018 Left,
1019 Right,
1020 Center,
1021}
1022
1023impl Alignment {
1024 pub fn default_for_type(var_type: VarType) -> Self {
1025 match var_type {
1026 VarType::Numeric => Self::Right,
1027 VarType::String => Self::Left,
1028 }
1029 }
1030
1031 pub fn as_str(&self) -> &'static str {
1032 match self {
1033 Alignment::Left => "Left",
1034 Alignment::Right => "Right",
1035 Alignment::Center => "Center",
1036 }
1037 }
1038}
1039
1040#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
1042pub enum Measure {
1043 Nominal,
1045
1046 Ordinal,
1048
1049 Scale,
1051}
1052
1053impl Measure {
1054 pub fn default_for_type(var_type: VarType) -> Option<Measure> {
1055 match var_type {
1056 VarType::Numeric => None,
1057 VarType::String => Some(Self::Nominal),
1058 }
1059 }
1060
1061 pub fn as_str(&self) -> &'static str {
1062 match self {
1063 Measure::Nominal => "Nominal",
1064 Measure::Ordinal => "Ordinal",
1065 Measure::Scale => "Scale",
1066 }
1067 }
1068}
1069
1070#[cfg(test)]
1071mod tests {
1072 use encoding_rs::{UTF_8, WINDOWS_1252};
1073
1074 use crate::{
1075 data::{ByteString, Datum, RawString, WithEncoding},
1076 variable::{MissingValues, ValueLabels, VarWidth},
1077 };
1078
1079 #[test]
1080 fn var_width_codepage_to_unicode() {
1081 fn check_unicode(input: VarWidth, expected: VarWidth) {
1082 let mut actual = input;
1083 actual.codepage_to_unicode();
1084 assert_eq!(actual, expected);
1085 }
1086
1087 check_unicode(VarWidth::Numeric, VarWidth::Numeric);
1088 check_unicode(VarWidth::String(1), VarWidth::String(3));
1089 check_unicode(VarWidth::String(2), VarWidth::String(6));
1090 check_unicode(VarWidth::String(3), VarWidth::String(9));
1091 check_unicode(VarWidth::String(1000), VarWidth::String(3000));
1092 check_unicode(VarWidth::String(20000), VarWidth::String(32767));
1093 check_unicode(VarWidth::String(30000), VarWidth::String(32767));
1094 }
1095
1096 #[test]
1097 fn missing_values_codepage_to_unicode() {
1098 fn windows_1252(s: &str) -> WithEncoding<ByteString> {
1099 ByteString::from(WINDOWS_1252.encode(s).0).with_encoding(WINDOWS_1252)
1100 }
1101
1102 let mut actual = MissingValues::new(
1103 vec![
1104 Datum::String(windows_1252("abcdefgh")),
1105 Datum::String(windows_1252("éèäî ")),
1106 Datum::String(windows_1252("aaéèäîdf")),
1107 ],
1108 None,
1109 )
1110 .unwrap();
1111 actual.codepage_to_unicode();
1112
1113 fn utf_8(s: &str) -> WithEncoding<ByteString> {
1114 ByteString::from(s).with_encoding(UTF_8)
1115 }
1116
1117 let expected = MissingValues::new(
1118 vec![
1119 Datum::String(utf_8("abcdefgh")),
1120 Datum::String(utf_8("éèäî")),
1121 Datum::String(utf_8("aaéèä")),
1122 ],
1123 None,
1124 )
1125 .unwrap();
1126
1127 assert_eq!(&actual, &expected);
1128 }
1129
1130 #[test]
1131 fn value_labels_codepage_to_unicode() {
1132 fn windows_1252(s: &str) -> Datum<ByteString> {
1133 Datum::String(ByteString::from(WINDOWS_1252.encode(s).0))
1134 }
1135
1136 let mut actual = ValueLabels::new();
1137 actual.insert(windows_1252("abcd"), "Label 1");
1138 actual.insert(windows_1252("éèäî"), "Label 2");
1139 actual.codepage_to_unicode(WINDOWS_1252);
1140
1141 let mut expected = ValueLabels::new();
1142 expected.insert(Datum::String(ByteString::from("abcd ")), "Label 1");
1143 expected.insert(Datum::String(ByteString::from("éèäî ")), "Label 2");
1144
1145 assert_eq!(&actual, &expected);
1146 }
1147}