qualia_core_db/sparql_library/serialisers/
csv_serializer.rs1use std::io::Write;
6
7use crate::NQuin;
8
9#[derive(Debug, Clone)]
11pub struct CsvSerializationProfile {
12 pub headers: Vec<String>,
13 pub predicate_hashes: Vec<u64>,
14 pub datatypes: Vec<CsvDatatype>,
15}
16
17#[derive(Debug, Clone, Copy)]
19pub enum CsvDatatype {
20 Integer,
21 Float,
22 DateTime,
23 StringRef,
24}
25
26pub fn serialize_quins_to_csv<W: Write>(
28 writer: &mut W,
29 quins: &[NQuin],
30 profile: &CsvSerializationProfile,
31) -> Result<(), String> {
32 writeln!(writer, "{}", profile.headers.join(","))
34 .map_err(|e| format!("Failed to write CSV headers: {}", e))?;
35
36 let mut subjects: std::collections::HashMap<u64, Vec<&NQuin>> =
38 std::collections::HashMap::new();
39 for quin in quins {
40 subjects
41 .entry(quin.subject)
42 .or_insert_with(Vec::new)
43 .push(quin);
44 }
45
46 for (_subject, row_quins) in subjects {
48 let mut row_values = Vec::with_capacity(profile.headers.len());
49
50 for (i, &pred_hash) in profile.predicate_hashes.iter().enumerate() {
51 let datatype = profile
52 .datatypes
53 .get(i)
54 .copied()
55 .unwrap_or(CsvDatatype::StringRef);
56
57 let value = row_quins
59 .iter()
60 .find(|q| q.predicate == pred_hash)
61 .map(|quin| format_quin_value(quin, datatype))
62 .unwrap_or(String::new());
63
64 row_values.push(value);
65 }
66
67 writeln!(writer, "{}", row_values.join(","))
68 .map_err(|e| format!("Failed to write CSV row: {}", e))?;
69 }
70
71 Ok(())
72}
73
74fn format_quin_value(quin: &NQuin, datatype: CsvDatatype) -> String {
76 match datatype {
77 CsvDatatype::Integer => {
78 let val = quin.object & 0x0FFF_FFFF_FFFF_FFFF; val.to_string()
80 }
81 CsvDatatype::Float => {
82 let tag = quin.object & 0xF000_0000_0000_0000;
83 if tag == (0b010 << 60) {
84 let bits = (quin.object & 0xFFFF_FFFF) as u32;
85 let float_val = f32::from_bits(bits);
86 float_val.to_string()
87 } else {
88 String::new()
89 }
90 }
91 CsvDatatype::DateTime => {
92 let tag = quin.object & 0xF000_0000_0000_0000;
93 if tag == (0b011 << 60) {
94 let millis = quin.object & 0x0FFF_FFFF_FFFF_FFFF;
95 let dt = chrono::DateTime::from_timestamp(millis as i64 / 1000, 0);
96 dt.map(|d| d.to_rfc3339()).unwrap_or_default()
97 } else {
98 String::new()
99 }
100 }
101 CsvDatatype::StringRef => {
102 format!("{:x}", quin.object)
104 }
105 }
106}