qualia_core_db/sparql_library/serialisers/
json_serializer.rs1use std::io::Write;
6
7use crate::NQuin;
8
9#[derive(Debug, Clone)]
11pub struct JsonSerializationProfile {
12 pub field_names: Vec<String>,
13 pub predicate_hashes: Vec<u64>,
14 pub datatypes: Vec<JsonDatatype>,
15}
16
17#[derive(Debug, Clone, Copy)]
19pub enum JsonDatatype {
20 Integer,
21 Float,
22 DateTime,
23 StringRef,
24}
25
26pub fn serialize_quins_to_json<W: Write>(
28 writer: &mut W,
29 quins: &[NQuin],
30 profile: &JsonSerializationProfile,
31) -> Result<(), String> {
32 writeln!(writer, "[").map_err(|e| format!("Failed to write JSON array start: {}", e))?;
33
34 let mut subjects: std::collections::HashMap<u64, Vec<&NQuin>> =
36 std::collections::HashMap::new();
37 for quin in quins {
38 subjects
39 .entry(quin.subject)
40 .or_insert_with(Vec::new)
41 .push(quin);
42 }
43
44 let mut first = true;
45 for (_subject, row_quins) in subjects {
46 if !first {
47 writeln!(writer, ",").map_err(|e| format!("Failed to write JSON comma: {}", e))?;
48 }
49 first = false;
50
51 write!(writer, " {{").map_err(|e| format!("Failed to write JSON object start: {}", e))?;
52
53 let mut field_first = true;
54 for (i, &pred_hash) in profile.predicate_hashes.iter().enumerate() {
55 let field_name = &profile.field_names[i];
56 let datatype = profile
57 .datatypes
58 .get(i)
59 .copied()
60 .unwrap_or(JsonDatatype::StringRef);
61
62 if let Some(quin) = row_quins.iter().find(|q| q.predicate == pred_hash) {
64 if !field_first {
65 write!(writer, ",")
66 .map_err(|e| format!("Failed to write JSON field comma: {}", e))?;
67 }
68 field_first = false;
69
70 write!(
71 writer,
72 "\"{}\": {}",
73 field_name,
74 format_quin_value(quin, datatype)
75 )
76 .map_err(|e| format!("Failed to write JSON field: {}", e))?;
77 }
78 }
79
80 write!(writer, "}}").map_err(|e| format!("Failed to write JSON object end: {}", e))?;
81 }
82
83 writeln!(writer, "\n]").map_err(|e| format!("Failed to write JSON array end: {}", e))?;
84
85 Ok(())
86}
87
88fn format_quin_value(quin: &NQuin, datatype: JsonDatatype) -> String {
90 match datatype {
91 JsonDatatype::Integer => {
92 let val = quin.object & 0x0FFF_FFFF_FFFF_FFFF; val.to_string()
94 }
95 JsonDatatype::Float => {
96 let tag = quin.object & 0xF000_0000_0000_0000;
97 if tag == (0b010 << 60) {
98 let bits = (quin.object & 0xFFFF_FFFF) as u32;
99 let float_val = f32::from_bits(bits);
100 float_val.to_string()
101 } else {
102 "null".to_string()
103 }
104 }
105 JsonDatatype::DateTime => {
106 let tag = quin.object & 0xF000_0000_0000_0000;
107 if tag == (0b011 << 60) {
108 let millis = quin.object & 0x0FFF_FFFF_FFFF_FFFF;
109 let dt = chrono::DateTime::from_timestamp(millis as i64 / 1000, 0);
110 let dt_str = dt.map(|d| d.to_rfc3339()).unwrap_or_default();
111 format!("\"{}\"", dt_str)
112 } else {
113 "null".to_string()
114 }
115 }
116 JsonDatatype::StringRef => {
117 format!("\"{:x}\"", quin.object)
119 }
120 }
121}