From d90fb5c7fe591bec9c6e72dcd35b94e77629e846 Mon Sep 17 00:00:00 2001 From: fluo10 Date: Tue, 29 Apr 2025 20:18:25 +0900 Subject: [PATCH] Implement csv record and deserializer/serializer --- Cargo.lock | 22 ++++++++++ dpts-core/Cargo.toml | 1 + dpts-core/src/csv.rs | 97 ++++++++++++++++++++++++++++++++++++++++++++ dpts-core/src/lib.rs | 1 + 4 files changed, 121 insertions(+) create mode 100644 dpts-core/src/csv.rs diff --git a/Cargo.lock b/Cargo.lock index 427a792..8272fb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -668,6 +668,27 @@ dependencies = [ "typenum", ] +[[package]] +name = "csv" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" +dependencies = [ + "memchr", +] + [[package]] name = "darling" version = "0.20.11" @@ -767,6 +788,7 @@ dependencies = [ "axum", "chrono", "clap", + "csv", "dotenv", "dpts-migration", "log", diff --git a/dpts-core/Cargo.toml b/dpts-core/Cargo.toml index f2df2bc..b5db980 100644 --- a/dpts-core/Cargo.toml +++ b/dpts-core/Cargo.toml @@ -17,6 +17,7 @@ log = "0.4.27" serde = { version = "1.0", features = ["derive"] } thiserror = "2.0" tokio = "1.44.2" +csv = "1.3.1" [dependencies.sea-orm] version = "1.1" diff --git a/dpts-core/src/csv.rs b/dpts-core/src/csv.rs new file mode 100644 index 0000000..651c112 --- /dev/null +++ b/dpts-core/src/csv.rs @@ -0,0 +1,97 @@ +use chrono::{DateTime, NaiveDateTime}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +pub struct CsvRecord{ + pub timestamp: NaiveDateTime, + #[serde(with = "string_to_escape")] + pub comment: String, + pub tag: String, + pub count: i32, +} + +pub mod string_to_escape { + use serde::{Deserialize, Deserializer, Serializer}; + + pub fn deserialize<'de, D>(d: D) -> Result + where D: Deserializer<'de> + { + Ok(String::deserialize(d)? + .replace("\\n", "\n") + .replace("\\t", "\t") + .replace("\\\"", "\"") + .replace("\\\\", "\\") + ) + } + pub fn serialize(s: &str, serializer: S) -> Result + where S: Serializer + { + serializer.serialize_str(&s + + .replace("\n", "\\n") + .replace("\t", "\\t") + .replace("\"", "\\\"") + .replace("\\", "\\\\") + ) + } +} + + + +#[cfg(test)] +pub mod tests { + use super::*; + use chrono::{NaiveDate, NaiveTime}; + + const RECORD_CSV:&str = r#"timestamp,comment,tag,count +2025-05-01T12:34:56,test\ntest,test,1"#; + + fn get_record_struct() -> Vec{ + vec![CsvRecord { + timestamp: NaiveDate::from_ymd_opt(2025, 5, 1) + .unwrap().and_hms_micro_opt(12, 34, 56, 0).unwrap(), + comment: "test +test".to_owned(), + tag: "test".to_owned(), + count: 1, + }] + } + + #[test] + fn serialize_string() { + + } + + #[test] + fn deserialize_string() { + + } + + #[test] + fn deserialize_record() { + println!("{:?}", RECORD_CSV); + let mut rdr = csv::ReaderBuilder::new() + .trim(csv::Trim::All) + .escape(Some(b'\\')) + .from_reader(RECORD_CSV.as_bytes()); + let mut raw_record = csv::ByteRecord::new(); + let headers = rdr.byte_headers().unwrap().clone(); + println!("{:?}", &headers); + let mut records: Vec = vec![]; + while rdr.read_byte_record(&mut raw_record).unwrap() { + println!("{:?}", &raw_record); + let record: CsvRecord = raw_record.deserialize(Some(&headers)).unwrap(); + records.push(record); + } + assert_eq!(records, get_record_struct()); + } + + #[test] + fn serialize_record() { + todo!() + } + + + +} \ No newline at end of file diff --git a/dpts-core/src/lib.rs b/dpts-core/src/lib.rs index 7d5e2ce..b1fb568 100644 --- a/dpts-core/src/lib.rs +++ b/dpts-core/src/lib.rs @@ -1,3 +1,4 @@ +pub mod csv; pub mod data; pub mod entity; pub mod error;