Skip to content

Commit

Permalink
feat: luau add qsv_writejson helper - WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
jqnatividad committed Dec 24, 2024
1 parent 36069bf commit bd0797a
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 0 deletions.
54 changes: 54 additions & 0 deletions src/cmd/luau.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1827,6 +1827,60 @@ fn setup_helpers(
})?;
luau.globals().set("qsv_loadjson", qsv_loadjson)?;

// this is a helper function to save a Luau table to a JSON file.
//
// qsv_writejson(table_or_value: any, filepath: string, pretty: boolean)
// table_or_value: the Luau table or value to save as JSON
// filepath: the path of the JSON file to save
// pretty: whether to format the JSON with indentation (optional, defaults to false)
// returns: true if successful.
// A Luau runtime error if the filepath is invalid or JSON conversion fails.
//
let qsv_writejson = luau.create_function(
move |_, (table_or_value, filepath, pretty): (mlua::Value, String, Option<bool>)| {
use sanitize_filename::sanitize;

if filepath.is_empty() {
return helper_err!("qsv_writejson", "filepath cannot be empty.");
}

let sanitized_filename = sanitize(filepath);

// // Convert Lua value to serde_json::Value using proper serialization
// let json_value = serde_json::to_string(&table_or_value).map_err(|e| {
// mlua::Error::RuntimeError(format!("Failed to convert Luau value to JSON: {e}"))
// })?;

// Create file
let file = std::fs::File::create(&sanitized_filename).map_err(|e| {
mlua::Error::RuntimeError(format!(
"Failed to create JSON file \"{}\": {e}",
sanitized_filename
))
})?;

// Write JSON
if pretty.unwrap_or(false) {
serde_json::to_writer_pretty(file, &table_or_value).map_err(|e| {
mlua::Error::RuntimeError(format!("Failed to write pretty JSON to file: {e}"))
})?;
} else {
serde_json::to_writer(file, &table_or_value).map_err(|e| {
mlua::Error::RuntimeError(format!("Failed to write JSON to file: {e}"))
})?;
}

// info!(
// "qsv_writejson() - saved {} bytes to file: {}",
// json_value.to_string().len(),
// sanitized_filename
// );

Ok(true)
},
)?;
luau.globals().set("qsv_writejson", qsv_writejson)?;

// this is a helper function that can be called from the BEGIN, MAIN & END scripts to write to
// a file. The file will be created if it does not exist. The file will be appended to if it
// already exists. The filename will be sanitized and will be written to the current working
Expand Down
150 changes: 150 additions & 0 deletions tests/test_luau.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use newline_converter::dos2unix;
use serde_json::json;

use crate::workdir::Workdir;

Expand Down Expand Up @@ -2931,3 +2932,152 @@ fn luau_cumulative_stats() {
];
assert_eq!(got, expected);
}

#[test]
fn luau_writejson_simple() {
let wrk = Workdir::new("luau_writejson");
wrk.create(
"data.csv",
vec![
svec!["letter", "number"],
svec!["a", "13"],
svec!["b", "24"],
svec!["c", "72"],
svec!["d", "7"],
],
);

// Test writing a simple table to JSON
let mut cmd = wrk.command("luau");
cmd.arg("map")
.arg("result")
.arg(r#"
BEGIN {
-- Create a table to store
local data = {
numbers = {13, 24, 72, 7},
letters = {"a", "b", "c", "d"},
nested = {
key = "value",
array = {1, 2, 3}
}
}
-- Write both pretty and compact JSON
qsv_writejson(data, "output.json", false)
qsv_writejson(data, "output_pretty.json", true)
}!
return "ok"
"#)
.arg("data.csv");

wrk.assert_success(&mut cmd);

// Verify the compact JSON output
let json_content = std::fs::read_to_string(wrk.path("output.json")).unwrap();
let json: serde_json::Value = serde_json::from_str(&json_content).unwrap();

assert_eq!(json!("{\"numbers\":[13,24,72,7],\"nested\":{\"key\":\"value\",\"array\":[1,2,3]},\"letters\":[\"a\",\"b\",\"c\",\"d\"]}"), json);

// Verify the pretty JSON output has newlines and indentation
let pretty_content = std::fs::read_to_string(wrk.path("output_pretty.json")).unwrap();
let expected = r#""{\"numbers\":[13,24,72,7],\"nested\":{\"key\":\"value\",\"array\":[1,2,3]},\"letters\":[\"a\",\"b\",\"c\",\"d\"]}""#;

assert_eq!(pretty_content, expected);

// The actual content should be the same when parsed
let pretty_json: serde_json::Value = serde_json::from_str(&pretty_content).unwrap();
assert_eq!(json, pretty_json);
}

#[test]
fn luau_writejson_error_cases() {
let wrk = Workdir::new("luau_writejson_errors");
wrk.create(
"data.csv",
vec![
svec!["letter", "number"],
svec!["a", "13"],
],
);

// Test invalid filename
let mut cmd = wrk.command("luau");
cmd.arg("map")
.arg("result")
.arg(r#"
BEGIN {
qsv_writejson({test = "value"}, "", false)
}!
return "should not get here"
"#)
.arg("data.csv");

wrk.assert_err(&mut cmd);

// Test writing to a directory that doesn't exist
let mut cmd = wrk.command("luau");
cmd.arg("map")
.arg("result")
.arg(r#"
BEGIN {
qsv_writejson({test = "value"}, "nonexistent/dir/file.json", false)
}!
return "should not get here"
"#)
.arg("data.csv");

wrk.assert_err(&mut cmd);
}

#[test]
fn luau_writejson_special_values() {
let wrk = Workdir::new("luau_writejson_special");
wrk.create(
"data.csv",
vec![
svec!["letter", "number"],
svec!["a", "13"],
],
);

// Test writing various Lua types
let mut cmd = wrk.command("luau");
cmd.arg("map")
.arg("result")
.arg(r#"
BEGIN {
local data = {
null_value = nil,
boolean_true = true,
boolean_false = false,
number_integer = 42,
number_float = 3.14,
empty_string = "",
empty_table = {},
nested_empty = {empty = {}}
}
qsv_writejson(data, "special.json", true)
}!
return "ok"
"#)
.arg("data.csv");

wrk.assert_success(&mut cmd);

// Verify the JSON output
let json_content = std::fs::read_to_string(wrk.path("special.json")).unwrap();
let json: serde_json::Value = serde_json::from_str(&json_content).unwrap();

assert!(json["null_value"].is_null());
assert_eq!(json["boolean_true"], json!(true));
assert_eq!(json["boolean_false"], json!(false));
assert_eq!(json["number_integer"], json!(42));
assert_eq!(json["number_float"], json!(3.14));
assert_eq!(json["empty_string"], json!(""));
assert_eq!(json["empty_table"], json!({}));
assert_eq!(json["nested_empty"]["empty"], json!({}));
}

0 comments on commit bd0797a

Please sign in to comment.