1use super::Entry;
2
3pub fn export_entry_bytes(entry: &Entry) -> Vec<u8> {
4 let mut out = Vec::new();
5 write_export_field(&mut out, "__CURSOR", entry.cursor.as_bytes());
6 write_export_field(
7 &mut out,
8 "__REALTIME_TIMESTAMP",
9 entry.realtime.to_string().as_bytes(),
10 );
11 write_export_field(
12 &mut out,
13 "__MONOTONIC_TIMESTAMP",
14 entry.monotonic.to_string().as_bytes(),
15 );
16 write_export_field(&mut out, "_BOOT_ID", hex::encode(entry.boot_id).as_bytes());
17
18 let mut keys: Vec<_> = entry.field_values.keys().collect();
19 keys.sort();
20 for key in keys {
21 if key == "_BOOT_ID" {
22 continue;
23 }
24 if let Some(values) = entry.field_values.get(key) {
25 for value in values {
26 write_export_field(&mut out, key, value);
27 }
28 }
29 }
30 let mut byte_name_fields: Vec<_> = entry
31 .raw_fields()
32 .filter(|field| std::str::from_utf8(field.name).is_err() && field.name != b"_BOOT_ID")
33 .collect();
34 byte_name_fields.sort_by(|left, right| {
35 left.name
36 .cmp(right.name)
37 .then_with(|| left.value.cmp(right.value))
38 });
39 for field in byte_name_fields {
40 write_export_field_bytes(&mut out, field.name, field.value);
41 }
42 out.push(b'\n');
43 out
44}
45
46pub fn export_entry(entry: &Entry) -> String {
47 String::from_utf8_lossy(&export_entry_bytes(entry)).into_owned()
48}
49
50fn write_export_field(out: &mut Vec<u8>, name: &str, value: &[u8]) {
51 write_export_field_bytes(out, name.as_bytes(), value);
52}
53
54fn write_export_field_bytes(out: &mut Vec<u8>, name: &[u8], value: &[u8]) {
55 if value
56 .iter()
57 .all(|byte| *byte == b'\t' || (0x20..0x7f).contains(byte))
58 {
59 out.extend_from_slice(name);
60 out.push(b'=');
61 out.extend_from_slice(value);
62 out.push(b'\n');
63 } else {
64 out.extend_from_slice(name);
65 out.push(b'\n');
66 out.extend_from_slice(&(value.len() as u64).to_le_bytes());
67 out.extend_from_slice(value);
68 out.push(b'\n');
69 }
70}
71
72pub fn json_entry(entry: &Entry) -> serde_json::Value {
73 let mut map = serde_json::Map::new();
74 map.insert(
75 "__CURSOR".to_string(),
76 serde_json::Value::String(entry.cursor.clone()),
77 );
78 map.insert(
79 "__REALTIME_TIMESTAMP".to_string(),
80 serde_json::Value::String(entry.realtime.to_string()),
81 );
82 map.insert(
83 "__MONOTONIC_TIMESTAMP".to_string(),
84 serde_json::Value::String(entry.monotonic.to_string()),
85 );
86 map.insert(
87 "_BOOT_ID".to_string(),
88 serde_json::Value::String(hex::encode(entry.boot_id)),
89 );
90
91 let mut keys: Vec<_> = entry.field_values.keys().collect();
92 keys.sort();
93 for key in keys {
94 if key == "_BOOT_ID" {
95 continue;
96 }
97 let values = &entry.field_values[key];
98 let json_values: Vec<_> = values
99 .iter()
100 .map(|value| json_value_for_bytes(value))
101 .collect();
102 let value = if json_values.len() == 1 {
103 json_values.into_iter().next().unwrap()
104 } else {
105 serde_json::Value::Array(json_values)
106 };
107 map.insert(key.clone(), value);
108 }
109
110 serde_json::Value::Object(map)
111}
112
113fn json_value_for_bytes(value: &[u8]) -> serde_json::Value {
114 if json_bytes_printable(value) {
115 serde_json::Value::String(String::from_utf8_lossy(value).into_owned())
116 } else {
117 serde_json::Value::Array(
118 value
119 .iter()
120 .map(|byte| serde_json::Value::Number((*byte).into()))
121 .collect(),
122 )
123 }
124}
125
126fn json_bytes_printable(value: &[u8]) -> bool {
127 let Ok(text) = std::str::from_utf8(value) else {
128 return false;
129 };
130 for ch in text.chars() {
131 let cp = ch as u32;
132 if cp < 0x20 && ch != '\t' && ch != '\n' {
133 return false;
134 }
135 if (0x7f..=0x9f).contains(&cp) {
136 return false;
137 }
138 }
139 true
140}
141
142pub fn format_entry_text(entry: &Entry) -> Vec<u8> {
143 let mut out = Vec::new();
144 if let Some(message) = entry.get("MESSAGE") {
145 out.extend_from_slice(message);
146 }
147 out.push(b'\n');
148 out
149}