work
authorBen Pfaff <blp@cs.stanford.edu>
Mon, 11 Aug 2025 21:09:18 +0000 (14:09 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Mon, 11 Aug 2025 21:09:18 +0000 (14:09 -0700)
rust/pspp/src/output/csv.rs

index 5690f1b711cd557e44620d84ea537cd6f3abb9ce..b7768566800a56302bf641420a707872f67d4837 100644 (file)
@@ -23,7 +23,10 @@ use std::{
     sync::Arc,
 };
 
-use serde::{Deserialize, Serialize};
+use serde::{
+    de::{Unexpected, Visitor},
+    Deserialize, Deserializer, Serialize,
+};
 
 use crate::output::pivot::Coord2;
 
@@ -45,11 +48,37 @@ pub struct CsvDriver {
 }
 
 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
+#[serde(default)]
 struct CsvOptions {
+    #[serde(deserialize_with = "deserialize_ascii_char")]
     quote: u8,
     delimiter: u8,
 }
 
+fn deserialize_ascii_char<'de, D>(deserializer: D) -> Result<u8, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    struct AsciiCharVisitor;
+    impl<'de> Visitor<'de> for AsciiCharVisitor {
+        type Value = u8;
+        fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+            write!(f, "a single ASCII character")
+        }
+        fn visit_str<E>(self, s: &str) -> Result<u8, E>
+        where
+            E: serde::de::Error,
+        {
+            if s.len() == 1 {
+                Ok(s.chars().next().unwrap() as u8)
+            } else {
+                Err(serde::de::Error::invalid_value(Unexpected::Str(s), &self))
+            }
+        }
+    }
+    deserializer.deserialize_char(AsciiCharVisitor)
+}
+
 impl Default for CsvOptions {
     fn default() -> Self {
         Self {