more experimenting
authorBen Pfaff <blp@cs.stanford.edu>
Thu, 31 Jul 2025 21:24:44 +0000 (14:24 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Thu, 31 Jul 2025 21:24:44 +0000 (14:24 -0700)
rust/pspp/src/data.rs

index 294023fdebf2efdd0e2cd906f5b02c5f268fbe4d..7c97aa4e58f637f964690a7ae9f0e19c76070c51 100644 (file)
@@ -104,30 +104,107 @@ impl RawStringTrait for String {
     }
 }
 
-impl RawStringTrait for Vec<u8> {
+#[derive(PartialEq, Eq, PartialOrd, Ord)]
+struct ByteStr<'a>(&'a [u8]);
+
+impl Serialize for ByteStr<'_> {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        if let Ok(s) = str::from_utf8(self.0) {
+            let (variant_index, variant) = if self.0.iter().all(|b| b.is_ascii()) {
+                (0, "Ascii")
+            } else {
+                (1, "Utf8")
+            };
+            let mut tuple =
+                serializer.serialize_tuple_variant("RawString", variant_index, variant, 1)?;
+            tuple.serialize_field(s)?;
+            tuple.end()
+        } else {
+            let mut tuple = serializer.serialize_tuple_variant("RawString", 2, "Windows1252", 1)?;
+            tuple.serialize_field(&decode_latin1(self.0))?;
+            tuple.end()
+        }
+    }
+}
+
+impl Debug for ByteStr<'_> {
+    // If `s` is valid UTF-8, displays it as UTF-8, otherwise as Latin-1
+    // (actually bytes interpreted as Unicode code points).
+    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
+        let s = from_utf8(&self.0).map_or_else(|_| decode_latin1(self.0), Cow::from);
+        write!(f, "{s:?}")
+    }
+}
+
+impl RawStringTrait for ByteString {
+    fn raw_string_bytes(&self) -> &[u8] {
+        self.0.as_slice()
+    }
+}
+
+#[derive(PartialEq, Eq, PartialOrd, Ord)]
+struct ByteString(Vec<u8>);
+
+impl Serialize for ByteString {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        if let Ok(s) = str::from_utf8(&self.0) {
+            let (variant_index, variant) = if self.0.iter().all(|b| b.is_ascii()) {
+                (0, "Ascii")
+            } else {
+                (1, "Utf8")
+            };
+            let mut tuple =
+                serializer.serialize_tuple_variant("RawString", variant_index, variant, 1)?;
+            tuple.serialize_field(s)?;
+            tuple.end()
+        } else {
+            let mut tuple = serializer.serialize_tuple_variant("RawString", 2, "Windows1252", 1)?;
+            tuple.serialize_field(&decode_latin1(&self.0))?;
+            tuple.end()
+        }
+    }
+}
+
+impl Debug for ByteString {
+    // If `s` is valid UTF-8, displays it as UTF-8, otherwise as Latin-1
+    // (actually bytes interpreted as Unicode code points).
+    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
+        let s =
+            from_utf8(&self.0.borrow()).map_or_else(|_| decode_latin1(self.0.borrow()), Cow::from);
+        write!(f, "{s:?}")
+    }
+}
+
+impl RawStringTrait for ByteStr<'_> {
     fn raw_string_bytes(&self) -> &[u8] {
-        self.as_slice()
+        self.0
     }
 }
 
-impl MutRawString for Vec<u8> {
+impl MutRawString for ByteString {
     fn resize(&mut self, new_len: usize) -> Result<(), ()> {
-        match new_len.cmp(&self.len()) {
+        match new_len.cmp(&self.0.len()) {
             Ordering::Less => {
-                if !self[new_len..].iter().all(|b| *b == b' ') {
+                if !self.0[new_len..].iter().all(|b| *b == b' ') {
                     return Err(());
                 }
-                self.truncate(new_len);
+                self.0.truncate(new_len);
             }
             Ordering::Equal => (),
-            Ordering::Greater => self.extend((self.len()..new_len).map(|_| b' ')),
+            Ordering::Greater => self.0.extend((self.0.len()..new_len).map(|_| b' ')),
         }
         Ok(())
     }
 
     /// Removes any trailing ASCII spaces.
     fn trim_end(&mut self) {
-        while self.pop_if(|c| *c == b' ').is_some() {}
+        while self.0.pop_if(|c| *c == b' ').is_some() {}
     }
 }