Skip to content

Commit 4cdbd72

Browse files
committed
chore: development v0.3.34 - comprehensive testing complete [auto-commit]
1 parent ecff0a9 commit 4cdbd72

30 files changed

+2069
-2014
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ exclude = [
3636
# Workspace Package Metadata (inherited by all crates)
3737
# ─────────────────────────────────────────────────────────────────────────────
3838
[workspace.package]
39-
version = "0.3.27"
39+
version = "0.3.34"
4040
edition = "2024"
4141
rust-version = "1.85"
4242
license = "MPL-2.0 OR LicenseRef-UFFS-Commercial"
@@ -87,7 +87,7 @@ zerocopy = { version = "0.8", features = ["derive"] }
8787
serde_json = "1.0.149"
8888

8989
# ───── CLI ─────
90-
clap = { version = "4.5.60", features = ["derive", "env", "unicode", "wrap_help"] }
90+
clap = { version = "4.6.0", features = ["derive", "env", "unicode", "wrap_help"] }
9191
indicatif = "0.18.4"
9292

9393
# ───── TUI ─────
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//! JSON formatting helpers (test-only on non-Windows platforms).
2+
//!
3+
//! On Windows, these live in `streaming.rs` and are used for production code.
4+
//! On non-Windows, they're only needed for test parity validation.
5+
6+
/// Escape a string for JSON output.
7+
pub fn format_json_string(value: &str) -> String {
8+
let mut escaped = String::with_capacity(value.len() + 2);
9+
escaped.push('"');
10+
for ch in value.chars() {
11+
match ch {
12+
'"' => escaped.push_str("\\\""),
13+
'\\' => escaped.push_str("\\\\"),
14+
'\u{08}' => escaped.push_str("\\b"),
15+
'\u{0C}' => escaped.push_str("\\f"),
16+
'\n' => escaped.push_str("\\n"),
17+
'\r' => escaped.push_str("\\r"),
18+
'\t' => escaped.push_str("\\t"),
19+
control if control <= '\u{1F}' => push_json_unicode_escape(&mut escaped, control),
20+
other => escaped.push(other),
21+
}
22+
}
23+
escaped.push('"');
24+
escaped
25+
}
26+
27+
fn push_json_unicode_escape(buf: &mut String, ch: char) {
28+
const HEX: &[char; 16] = &[
29+
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
30+
];
31+
let code = ch as u32;
32+
buf.push_str("\\u");
33+
for shift in [12_u32, 8_u32, 4_u32, 0_u32] {
34+
let nibble = usize::try_from((code >> shift) & 0xF).unwrap_or_default();
35+
buf.push(*HEX.get(nibble).unwrap_or(&'0'));
36+
}
37+
}
38+
39+
/// Format a cell value for JSON output.
40+
pub fn format_json_value(col: &uffs_polars::Column, row_idx: usize) -> String {
41+
use uffs_polars::{AnyValue, TimeUnit};
42+
43+
match col.get(row_idx) {
44+
Ok(AnyValue::Null) | Err(_) => "null".to_owned(),
45+
Ok(AnyValue::String(value)) => format_json_string(value),
46+
Ok(AnyValue::Boolean(boolean)) => if boolean { "true" } else { "false" }.to_owned(),
47+
Ok(AnyValue::Datetime(ts, TimeUnit::Microseconds, _)) => {
48+
let secs = ts.div_euclid(1_000_000);
49+
let micros = u32::try_from(ts.rem_euclid(1_000_000)).unwrap_or_default();
50+
chrono::DateTime::from_timestamp(secs, micros * 1000).map_or_else(
51+
|| "null".to_owned(),
52+
|datetime| format_json_string(&datetime.format("%Y-%m-%d %H:%M:%S").to_string()),
53+
)
54+
}
55+
Ok(AnyValue::UInt8(n)) => n.to_string(),
56+
Ok(AnyValue::UInt16(n)) => n.to_string(),
57+
Ok(AnyValue::UInt32(n)) => n.to_string(),
58+
Ok(AnyValue::UInt64(n)) => n.to_string(),
59+
Ok(AnyValue::Int8(n)) => n.to_string(),
60+
Ok(AnyValue::Int16(n)) => n.to_string(),
61+
Ok(AnyValue::Int32(n)) => n.to_string(),
62+
Ok(AnyValue::Int64(n)) => n.to_string(),
63+
Ok(AnyValue::Float32(n)) => n.to_string(),
64+
Ok(AnyValue::Float64(n)) => n.to_string(),
65+
Ok(value) => format_json_string(&value.to_string()),
66+
}
67+
}
68+

0 commit comments

Comments
 (0)