"miniz_oxide",
]
-[[package]]
-name = "float_next_after"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8"
-
[[package]]
name = "form_urlencoded"
version = "1.2.1"
"finl_unicode",
"flagset",
"flate2",
- "float_next_after",
"hexplay",
"indexmap",
"itertools",
clap = { version = "4.1.7", features = ["derive", "wrap_help"] }
encoding_rs = "0.8.32"
flate2 = "1.0.26"
-float_next_after = "1.0.0"
hexplay = "0.2.1"
lazy_static = "1.4.0"
num = "0.4.0"
use num::Float;
-use std::{num::FpCategory, fmt::{Display, Formatter, Result}};
+use std::{
+ fmt::{Display, Formatter, Result},
+ num::FpCategory,
+};
pub struct HexFloat<T: Float>(pub T);
#[cfg(test)]
mod hex_float_tests {
- use crate::HexFloat;
+ use crate::hexfloat::HexFloat;
use num::Float;
#[test]
assert_eq!(format!("{}", HexFloat(f64::neg_zero())), "-0.0");
}
}
-
pub mod endian;
pub mod engine;
pub mod format;
+pub mod hexfloat;
pub mod identifier;
pub mod integer;
pub mod lex;
// Written by Bruno Haible <bruno@clisp.org>. Translated to Rust by Ben Pfaff
// <blp@cs.stanford.edu>.
-use lazy_static::lazy_static;
+use std::sync::LazyLock;
fn map_aliases(s: &str) -> &'static str {
#[cfg(target_os = "freebsd")]
/// Returns the character set used by the locale configured in the operating
/// system.
pub fn locale_charset() -> &'static str {
- lazy_static! {
- static ref LOCALE_CHARSET: &'static str =
- map_aliases(&inner::locale_charset().unwrap_or(String::from("UTF-8")));
- }
+ static LOCALE_CHARSET: LazyLock<&'static str> =
+ LazyLock::new(|| map_aliases(&inner::locale_charset().unwrap_or(String::from("UTF-8"))));
&LOCALE_CHARSET
}
},
endian::Endian,
format::{Error as FormatError, Format, UncheckedFormat},
+ hexfloat::HexFloat,
identifier::{ByIdentifier, Error as IdError, Identifier},
output::pivot::{Group, Value},
sys::{
pub use crate::sys::raw::{CategoryLabels, Compression};
-#[derive(ThisError, Clone, Debug, PartialEq, Eq)]
+#[derive(ThisError, Clone, Debug)]
pub enum Error {
#[error("Missing header record")]
MissingHeaderRecord,
)]
UnexpectedEndianess { actual: i32, expected: i32 },
+ #[error(
+ "System file specifies value {actual:?} ({}) as {name} but {expected:?} ({}) was expected.",
+ HexFloat(*actual),
+ HexFloat(*expected)
+ )]
+ UnexpectedFloatValue {
+ actual: f64,
+ expected: f64,
+ name: &'static str,
+ },
+
#[error("Details TBD (cooked)")]
TBD,
}
}
};
+ if let Some(float_info) = &headers.float_info {
+ for (expected, expected2, actual, name) in [
+ (f64::MIN, None, float_info.sysmis, "SYSMIS"),
+ (f64::MAX, None, float_info.highest, "HIGHEST"),
+ (
+ f64::MIN,
+ Some(f64::MIN.next_up()),
+ float_info.lowest,
+ "LOWEST",
+ ),
+ ] {
+ if actual != expected && expected2.is_none_or(|expected2| expected2 != actual) {
+ warn(Error::UnexpectedFloatValue {
+ expected,
+ actual,
+ name,
+ });
+ }
+ }
+ }
+
if let Some(nominal_case_size) = headers.header.nominal_case_size {
let n_vars = headers.variable.len();
if n_vars != nominal_case_size as usize
-use float_next_after::NextAfter;
use num::{Bounded, Zero};
use ordered_float::OrderedFloat;
use std::{
"i64" => Token::I64,
"SYSMIS" => Token::Float(OrderedFloat(-f64::MAX)),
"PCSYSMIS" => Token::PcSysmis,
- "LOWEST" => Token::Float((-f64::MAX).next_after(0.0).into()),
+ "LOWEST" => Token::Float((-f64::MAX).next_up().into()),
"HIGHEST" => Token::Float(f64::MAX.into()),
"ENDIAN" => Token::Integer(if self.endian == Endian::Big { 1 } else { 2 }),
"COUNT" => Token::Count,
test_sysfile("bad_machine_float_info_size");
}
+#[test]
+fn wrong_special_floats() {
+ test_sysfile("wrong_special_floats");
+}
+
/// Duplicate variable name handling negative test.
///
/// SPSS-generated system file can contain duplicate variable names (see bug
--- /dev/null
+System file specifies value 0.0 (0.0) as SYSMIS but -1.7976931348623157e308 (-0x1.fffffffffffffp1023) was expected.
+
+System file specifies value 1.0 (0x1.0p0) as HIGHEST but 1.7976931348623157e308 (0x1.fffffffffffffp1023) was expected.
+
+System file specifies value 2.0 (0x1.0p1) as LOWEST but -1.7976931348623157e308 (-0x1.fffffffffffffp1023) was expected.
+
+╭──────────────────────┬────────────────────────╮
+│ Created │ 01-JAN-2011 20:53:52│
+├──────────────────────┼────────────────────────┤
+│Writer Product │PSPP synthetic test file│
+├──────────────────────┼────────────────────────┤
+│ Compression │SAV │
+│ Number of Cases│Unknown │
+╰──────────────────────┴────────────────────────╯
+
+╭─────────┬─╮
+│Variables│1│
+╰─────────┴─╯
+
+╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
+│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
+├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
+│num1│ 1│ │ │Input│ 8│Right │F8.0 │F8.0 │ │
+╰────┴────────┴─────┴─────────────────┴─────┴─────┴─────────┴────────────┴────────────┴──────────────╯
--- /dev/null
+# File header.
+"$FL2"; s60 "$(#) SPSS DATA FILE PSPP synthetic test file";
+2; 1; 1; 0; -1; 100.0; "01 Jan 11"; "20:53:52"; s64 ""; i8 0 *3;
+
+# Numeric variable, no label or missing values.
+2; 0; 0; 0; 0x050800 *2; s8 "NUM1";
+
+# Machine floating-point info record.
+7; 4; 8; 3; >>0.0<<; >>1.0<<; >>2.0<<;
+
+# Character encoding record.
+7; 20; 1; 12; "windows-1252";
+
+# End of dictionary.
+999; 0;