From c983ed0c897572acd82025e7143a6a88fbfc2e4c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 21 Jan 2026 05:09:49 +0000 Subject: [PATCH 01/10] Initial plan From e96941f7cf18b684177e936ca1d882133e538cc2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 21 Jan 2026 05:14:17 +0000 Subject: [PATCH 02/10] Replace json-output boolean with format string option Co-authored-by: cryi <36897290+cryi@users.noreply.github.com> --- src/ami/internals/interface/app.lua | 15 ++++++---- tests/test/modify_n_show.lua | 44 +++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/ami/internals/interface/app.lua b/src/ami/internals/interface/app.lua index 3b0d192..467be2c 100644 --- a/src/ami/internals/interface/app.lua +++ b/src/ami/internals/interface/app.lua @@ -182,23 +182,28 @@ local function new(options) description = "Removes value from list or dictionary", type = "boolean" }, - ["json-output"] = { - description = "Outputs value as JSON (not HJSON)", - type = "boolean" + format = { + description = "Output format for the configuration file (hjson or json)", + type = "string" } }, action = function (options, _, args) ami_assert(#args > 1 or (#args == 1 and options.unset), "invalid arguments to modify command - needs path and value or --unset and path", EXIT_MODIFY_ERROR) options.set = options.set or (not options.unset and not options.add and not options.remove) -- default to set - local options_without_file = table.filter(options, function(key, v) return key ~= "file" and v == true end) + local options_without_file = table.filter(options, function(key, v) return key ~= "file" and key ~= "format" and v == true end) ami_assert(#table.keys(options_without_file) <= 1, "only one modification mode can be specified", EXIT_MODIFY_ERROR) local mode = "auto" if #table.keys(options_without_file) == 1 then mode = table.keys(options_without_file)[1] end - am.modify_file(mode, options.file, args[1].value, #args > 1 and args[2].value or nil) + local format = options.format + if type(format) == "string" then + ami_assert(format == "hjson" or format == "json", "format must be either 'hjson' or 'json'", EXIT_MODIFY_ERROR) + end + + am.modify_file(mode, options.file, args[1].value, #args > 1 and args[2].value or nil, format) end }, show = { diff --git a/tests/test/modify_n_show.lua b/tests/test/modify_n_show.lua index 09fb0d9..8f1079d 100644 --- a/tests/test/modify_n_show.lua +++ b/tests/test/modify_n_show.lua @@ -383,3 +383,47 @@ add_fail_test(test, "syntax and logic errors", { expected_error = "unknown option", }, }) + +-- Test format option +test["format option: json and hjson output"] = function() + local test_dir = "tests/tmp/ami_test_format" + fs.mkdirp(test_dir) + fs.remove(test_dir, { recurse = true, content_only = true }) + + -- Test with JSON format + local json_file = path.combine(test_dir, "test.json") + fs.write_file(json_file, "{}") + + local args_json = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test.json", "--format=json", "test.key", "value123" } + local ok, err = pcall(ami, table.unpack(args_json)) + test.assert(ok, "Failed to modify with format=json: " .. tostring(err)) + + -- Check that the file is in JSON format (no comments, strict JSON) + local json_content = fs.read_file(json_file) + test.assert(json_content ~= nil, "Failed to read json file") + -- JSON format should not have comments and should use quotes for keys + test.assert(json_content:find('"test"') ~= nil, "JSON output should have quoted keys") + + -- Test with HJSON format (default) + local hjson_file = path.combine(test_dir, "test.hjson") + fs.write_file(hjson_file, "{}") + + local args_hjson = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test.hjson", "--format=hjson", "test.key", "value456" } + local ok, err = pcall(ami, table.unpack(args_hjson)) + test.assert(ok, "Failed to modify with format=hjson: " .. tostring(err)) + + -- Check that the file is in HJSON format + local hjson_content = fs.read_file(hjson_file) + test.assert(hjson_content ~= nil, "Failed to read hjson file") + + -- Test with invalid format + local invalid_file = path.combine(test_dir, "test_invalid.hjson") + fs.write_file(invalid_file, "{}") + + local args_invalid = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test_invalid.hjson", "--format=xml", "test.key", "value789" } + local ok, err = pcall(ami, table.unpack(args_invalid)) + test.assert(not ok, "Should fail with invalid format") + test.assert(tostring(err):find("format must be either 'hjson' or 'json'") ~= nil, "Error message should mention valid formats") + + os.chdir(default_cwd) +end From 22053e250b233e79bf26fed907d24e562af1e1af Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 21 Jan 2026 05:30:20 +0000 Subject: [PATCH 03/10] Move format validation to util, add aliases, update description Co-authored-by: cryi <36897290+cryi@users.noreply.github.com> --- src/ami/internals/interface/app.lua | 11 ++++------- src/ami/internals/util.lua | 5 +++++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/ami/internals/interface/app.lua b/src/ami/internals/interface/app.lua index 467be2c..6e95bf9 100644 --- a/src/ami/internals/interface/app.lua +++ b/src/ami/internals/interface/app.lua @@ -164,6 +164,7 @@ local function new(options) options = { file = { index = 1, + aliases = {"f"}, description = "Path to configuration file to modify (Defaults to app.h/json)" }, set = { @@ -183,7 +184,8 @@ local function new(options) type = "boolean" }, format = { - description = "Output format for the configuration file (hjson or json)", + aliases = {"t"}, + description = "Content type of file we are modifying (hjson or json)", type = "string" } }, @@ -198,12 +200,7 @@ local function new(options) mode = table.keys(options_without_file)[1] end - local format = options.format - if type(format) == "string" then - ami_assert(format == "hjson" or format == "json", "format must be either 'hjson' or 'json'", EXIT_MODIFY_ERROR) - end - - am.modify_file(mode, options.file, args[1].value, #args > 1 and args[2].value or nil, format) + am.modify_file(mode, options.file, args[1].value, #args > 1 and args[2].value or nil, options.format) end }, show = { diff --git a/src/ami/internals/util.lua b/src/ami/internals/util.lua index b4d7d76..ff63baf 100644 --- a/src/ami/internals/util.lua +++ b/src/ami/internals/util.lua @@ -223,6 +223,11 @@ function util.modify_file(mode, file, path, value, output_format) end if type(output_format) ~= "string" then output_format = "hjson" + else + -- Validate format + if output_format ~= "hjson" and output_format ~= "json" then + return nil, "format must be either 'hjson' or 'json'" + end end if type(file) ~= "string" then file, _ = find_default_modify_file() From 038aae8ede1a4712ba49b330032b416717c51302 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 21 Jan 2026 05:37:41 +0000 Subject: [PATCH 04/10] Rename format option to type, rename output_format parameter to type Co-authored-by: cryi <36897290+cryi@users.noreply.github.com> --- src/am.lua | 6 +++--- src/ami/internals/interface/app.lua | 6 +++--- src/ami/internals/util.lua | 21 +++++++++++---------- tests/test/modify_n_show.lua | 24 ++++++++++++------------ 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/am.lua b/src/am.lua index 501e4f5..7c16767 100644 --- a/src/am.lua +++ b/src/am.lua @@ -283,8 +283,8 @@ end ---@param file string? ---@param path string ---@param value any ----@param output_format "json"|"hjson"? -function am.modify_file(mode, file, path, value, output_format) +---@param type "json"|"hjson"? +function am.modify_file(mode, file, path, value, type) -- split path by dot local path_parts = {} for part in string.gmatch(path, "[^%.]+") do @@ -298,7 +298,7 @@ function am.modify_file(mode, file, path, value, output_format) value = parsed_value end - local ok, err = ami_util.modify_file(mode, file, path_parts, value, output_format) + local ok, err = ami_util.modify_file(mode, file, path_parts, value, type) ami_assert(ok, "failed to modify configuration: " .. tostring(err), EXIT_MODIFY_ERROR) log_success"Requested modification applied." end diff --git a/src/ami/internals/interface/app.lua b/src/ami/internals/interface/app.lua index 6e95bf9..4ca5ca7 100644 --- a/src/ami/internals/interface/app.lua +++ b/src/ami/internals/interface/app.lua @@ -183,7 +183,7 @@ local function new(options) description = "Removes value from list or dictionary", type = "boolean" }, - format = { + type = { aliases = {"t"}, description = "Content type of file we are modifying (hjson or json)", type = "string" @@ -193,14 +193,14 @@ local function new(options) ami_assert(#args > 1 or (#args == 1 and options.unset), "invalid arguments to modify command - needs path and value or --unset and path", EXIT_MODIFY_ERROR) options.set = options.set or (not options.unset and not options.add and not options.remove) -- default to set - local options_without_file = table.filter(options, function(key, v) return key ~= "file" and key ~= "format" and v == true end) + local options_without_file = table.filter(options, function(key, v) return key ~= "file" and key ~= "type" and v == true end) ami_assert(#table.keys(options_without_file) <= 1, "only one modification mode can be specified", EXIT_MODIFY_ERROR) local mode = "auto" if #table.keys(options_without_file) == 1 then mode = table.keys(options_without_file)[1] end - am.modify_file(mode, options.file, args[1].value, #args > 1 and args[2].value or nil, options.format) + am.modify_file(mode, options.file, args[1].value, #args > 1 and args[2].value or nil, options.type) end }, show = { diff --git a/src/ami/internals/util.lua b/src/ami/internals/util.lua index ff63baf..d1283f6 100644 --- a/src/ami/internals/util.lua +++ b/src/ami/internals/util.lua @@ -215,23 +215,24 @@ end ---@param file string? ---@param path string[] ---@param value any ----@param output_format "json"|"hjson"? +---@param type "json"|"hjson"? ---@return boolean?, string? -function util.modify_file(mode, file, path, value, output_format) - if type(mode) ~= "string" then +function util.modify_file(mode, file, path, value, type) + local _type = _G.type -- Save the built-in type function + if _type(mode) ~= "string" then mode = "auto" end - if type(output_format) ~= "string" then - output_format = "hjson" + if _type(type) ~= "string" then + type = "hjson" else -- Validate format - if output_format ~= "hjson" and output_format ~= "json" then - return nil, "format must be either 'hjson' or 'json'" + if type ~= "hjson" and type ~= "json" then + return nil, "type must be either 'hjson' or 'json'" end end - if type(file) ~= "string" then + if _type(file) ~= "string" then file, _ = find_default_modify_file() - if type(file) ~= "string" then return nil, "no valid configuration file found to modify" end + if _type(file) ~= "string" then return nil, "no valid configuration file found to modify" end end local raw_content, err = fs.read_file(file --[[@as string ]]) @@ -264,7 +265,7 @@ function util.modify_file(mode, file, path, value, output_format) end if not result then return nil, "failed to set new value in configuration" end - local marshal_fn = output_format == "json" and hjson.stringify_to_json or hjson.stringify + local marshal_fn = type == "json" and hjson.stringify_to_json or hjson.stringify local new_raw_content, err = marshal_fn(result, { indent = "\t", sort_keys = true }) if not new_raw_content then return nil, "failed to serialize modified configuration: " .. tostring(err) end local ok, err = fs.write_file(file .. ".new" --[[@as string ]], new_raw_content --[[@as string ]]) diff --git a/tests/test/modify_n_show.lua b/tests/test/modify_n_show.lua index 8f1079d..87a9000 100644 --- a/tests/test/modify_n_show.lua +++ b/tests/test/modify_n_show.lua @@ -384,19 +384,19 @@ add_fail_test(test, "syntax and logic errors", { }, }) --- Test format option -test["format option: json and hjson output"] = function() +-- Test type option +test["type option: json and hjson output"] = function() local test_dir = "tests/tmp/ami_test_format" fs.mkdirp(test_dir) fs.remove(test_dir, { recurse = true, content_only = true }) - -- Test with JSON format + -- Test with JSON type local json_file = path.combine(test_dir, "test.json") fs.write_file(json_file, "{}") - local args_json = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test.json", "--format=json", "test.key", "value123" } + local args_json = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test.json", "--type=json", "test.key", "value123" } local ok, err = pcall(ami, table.unpack(args_json)) - test.assert(ok, "Failed to modify with format=json: " .. tostring(err)) + test.assert(ok, "Failed to modify with type=json: " .. tostring(err)) -- Check that the file is in JSON format (no comments, strict JSON) local json_content = fs.read_file(json_file) @@ -404,26 +404,26 @@ test["format option: json and hjson output"] = function() -- JSON format should not have comments and should use quotes for keys test.assert(json_content:find('"test"') ~= nil, "JSON output should have quoted keys") - -- Test with HJSON format (default) + -- Test with HJSON type (default) local hjson_file = path.combine(test_dir, "test.hjson") fs.write_file(hjson_file, "{}") - local args_hjson = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test.hjson", "--format=hjson", "test.key", "value456" } + local args_hjson = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test.hjson", "--type=hjson", "test.key", "value456" } local ok, err = pcall(ami, table.unpack(args_hjson)) - test.assert(ok, "Failed to modify with format=hjson: " .. tostring(err)) + test.assert(ok, "Failed to modify with type=hjson: " .. tostring(err)) -- Check that the file is in HJSON format local hjson_content = fs.read_file(hjson_file) test.assert(hjson_content ~= nil, "Failed to read hjson file") - -- Test with invalid format + -- Test with invalid type local invalid_file = path.combine(test_dir, "test_invalid.hjson") fs.write_file(invalid_file, "{}") - local args_invalid = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test_invalid.hjson", "--format=xml", "test.key", "value789" } + local args_invalid = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test_invalid.hjson", "--type=xml", "test.key", "value789" } local ok, err = pcall(ami, table.unpack(args_invalid)) - test.assert(not ok, "Should fail with invalid format") - test.assert(tostring(err):find("format must be either 'hjson' or 'json'") ~= nil, "Error message should mention valid formats") + test.assert(not ok, "Should fail with invalid type") + test.assert(tostring(err):find("type must be either 'hjson' or 'json'") ~= nil, "Error message should mention valid types") os.chdir(default_cwd) end From 3b9940dd40d7bc33e0ac49d2a1121fa96bb658d3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 21 Jan 2026 05:38:38 +0000 Subject: [PATCH 05/10] Fix type() shadowing issue in am.modify_file Co-authored-by: cryi <36897290+cryi@users.noreply.github.com> --- src/am.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/am.lua b/src/am.lua index 7c16767..f4b986e 100644 --- a/src/am.lua +++ b/src/am.lua @@ -285,6 +285,7 @@ end ---@param value any ---@param type "json"|"hjson"? function am.modify_file(mode, file, path, value, type) + local _type = _G.type -- Save the built-in type function -- split path by dot local path_parts = {} for part in string.gmatch(path, "[^%.]+") do @@ -292,7 +293,7 @@ function am.modify_file(mode, file, path, value, type) end -- try parse value as json/hjson - if type(value) == "string" then + if _type(value) == "string" then local parsed_value, err = hjson.parse(value) ami_assert(err == nil, "failed to parse value: " .. tostring(err), EXIT_MODIFY_ERROR) value = parsed_value From b7278e7dbeedb9db488d1a9622a43af01bdc47f7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 21 Jan 2026 05:53:45 +0000 Subject: [PATCH 06/10] Use do-end closure instead of _G to handle type shadowing Co-authored-by: cryi <36897290+cryi@users.noreply.github.com> --- src/am.lua | 34 +++++++------ src/ami/internals/util.lua | 100 +++++++++++++++++++------------------ 2 files changed, 69 insertions(+), 65 deletions(-) diff --git a/src/am.lua b/src/am.lua index f4b986e..db4ff7d 100644 --- a/src/am.lua +++ b/src/am.lua @@ -284,24 +284,26 @@ end ---@param path string ---@param value any ---@param type "json"|"hjson"? -function am.modify_file(mode, file, path, value, type) - local _type = _G.type -- Save the built-in type function - -- split path by dot - local path_parts = {} - for part in string.gmatch(path, "[^%.]+") do - table.insert(path_parts, part) - end +do + local typeof = type + function am.modify_file(mode, file, path, value, type) + -- split path by dot + local path_parts = {} + for part in string.gmatch(path, "[^%.]+") do + table.insert(path_parts, part) + end - -- try parse value as json/hjson - if _type(value) == "string" then - local parsed_value, err = hjson.parse(value) - ami_assert(err == nil, "failed to parse value: " .. tostring(err), EXIT_MODIFY_ERROR) - value = parsed_value - end + -- try parse value as json/hjson + if typeof(value) == "string" then + local parsed_value, err = hjson.parse(value) + ami_assert(err == nil, "failed to parse value: " .. tostring(err), EXIT_MODIFY_ERROR) + value = parsed_value + end - local ok, err = ami_util.modify_file(mode, file, path_parts, value, type) - ami_assert(ok, "failed to modify configuration: " .. tostring(err), EXIT_MODIFY_ERROR) - log_success"Requested modification applied." + local ok, err = ami_util.modify_file(mode, file, path_parts, value, type) + ami_assert(ok, "failed to modify configuration: " .. tostring(err), EXIT_MODIFY_ERROR) + log_success"Requested modification applied." + end end ---#DES am.get_value_from_file() diff --git a/src/ami/internals/util.lua b/src/ami/internals/util.lua index d1283f6..706861a 100644 --- a/src/ami/internals/util.lua +++ b/src/ami/internals/util.lua @@ -217,63 +217,65 @@ end ---@param value any ---@param type "json"|"hjson"? ---@return boolean?, string? -function util.modify_file(mode, file, path, value, type) - local _type = _G.type -- Save the built-in type function - if _type(mode) ~= "string" then - mode = "auto" - end - if _type(type) ~= "string" then - type = "hjson" - else - -- Validate format - if type ~= "hjson" and type ~= "json" then - return nil, "type must be either 'hjson' or 'json'" +do + local typeof = type + function util.modify_file(mode, file, path, value, type) + if typeof(mode) ~= "string" then + mode = "auto" end - end - if _type(file) ~= "string" then - file, _ = find_default_modify_file() - if _type(file) ~= "string" then return nil, "no valid configuration file found to modify" end - end - - local raw_content, err = fs.read_file(file --[[@as string ]]) - if not raw_content then - if table.includes({ "auto", "set" }, mode) then - raw_content = "{}" - elseif table.includes({ "add" }, mode) then - raw_content = "[]" + if typeof(type) ~= "string" then + type = "hjson" else - return nil, err or "failed to read configuration file" + -- Validate format + if type ~= "hjson" and type ~= "json" then + return nil, "type must be either 'hjson' or 'json'" + end + end + if typeof(file) ~= "string" then + file, _ = find_default_modify_file() + if typeof(file) ~= "string" then return nil, "no valid configuration file found to modify" end end - end - local content, err = hjson.parse(raw_content --[[@as string ]]) - if not content then return nil, "failed to parse configuration file '" .. tostring(file) .. "': " .. tostring(err) end - if not modify_handlers[mode] then return nil, "invalid modify mode: " .. tostring(mode) end + local raw_content, err = fs.read_file(file --[[@as string ]]) + if not raw_content then + if table.includes({ "auto", "set" }, mode) then + raw_content = "{}" + elseif table.includes({ "add" }, mode) then + raw_content = "[]" + else + return nil, err or "failed to read configuration file" + end + end + local content, err = hjson.parse(raw_content --[[@as string ]]) + if not content then return nil, "failed to parse configuration file '" .. tostring(file) .. "': " .. tostring(err) end - local default = value - if table.includes({"add", "remove"}, mode) then - default = {} - end - local current_value = table.get(content, path, default) + if not modify_handlers[mode] then return nil, "invalid modify mode: " .. tostring(mode) end - local new_value, err = modify_handlers[mode](current_value, value) - if not new_value and err then return nil, "modification failed: " .. tostring(err) end + local default = value + if table.includes({"add", "remove"}, mode) then + default = {} + end + local current_value = table.get(content, path, default) + + local new_value, err = modify_handlers[mode](current_value, value) + if not new_value and err then return nil, "modification failed: " .. tostring(err) end - local result, err = table.set(content, path, new_value) - if err == "cannot set nested value on a non-table object" then - return nil, "cannot set nested value on a non-table value at path: " .. table.concat(path, ".") + local result, err = table.set(content, path, new_value) + if err == "cannot set nested value on a non-table object" then + return nil, "cannot set nested value on a non-table value at path: " .. table.concat(path, ".") + end + if not result then return nil, "failed to set new value in configuration" end + + local marshal_fn = type == "json" and hjson.stringify_to_json or hjson.stringify + local new_raw_content, err = marshal_fn(result, { indent = "\t", sort_keys = true }) + if not new_raw_content then return nil, "failed to serialize modified configuration: " .. tostring(err) end + local ok, err = fs.write_file(file .. ".new" --[[@as string ]], new_raw_content --[[@as string ]]) + if not ok then return nil, "failed to write modified configuration to file '" .. tostring(file) .. ".new': " .. tostring(err) end + -- replace original file + local ok, err = os.rename(file .. ".new" --[[@as string ]], file --[[@as string ]]) + if not ok then return nil, "failed to replace original configuration file '" .. tostring(file) .. "': " .. tostring(err) end + return true end - if not result then return nil, "failed to set new value in configuration" end - - local marshal_fn = type == "json" and hjson.stringify_to_json or hjson.stringify - local new_raw_content, err = marshal_fn(result, { indent = "\t", sort_keys = true }) - if not new_raw_content then return nil, "failed to serialize modified configuration: " .. tostring(err) end - local ok, err = fs.write_file(file .. ".new" --[[@as string ]], new_raw_content --[[@as string ]]) - if not ok then return nil, "failed to write modified configuration to file '" .. tostring(file) .. ".new': " .. tostring(err) end - -- replace original file - local ok, err = os.rename(file .. ".new" --[[@as string ]], file --[[@as string ]]) - if not ok then return nil, "failed to replace original configuration file '" .. tostring(file) .. "': " .. tostring(err) end - return true end ---checks configurations From 60074b529767ba689a33cc115fd36bac309040bc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 21 Jan 2026 05:55:32 +0000 Subject: [PATCH 07/10] Fix indentation inconsistency Co-authored-by: cryi <36897290+cryi@users.noreply.github.com> --- src/ami/internals/util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ami/internals/util.lua b/src/ami/internals/util.lua index 706861a..f13e3bd 100644 --- a/src/ami/internals/util.lua +++ b/src/ami/internals/util.lua @@ -245,7 +245,7 @@ do else return nil, err or "failed to read configuration file" end - end + end local content, err = hjson.parse(raw_content --[[@as string ]]) if not content then return nil, "failed to parse configuration file '" .. tostring(file) .. "': " .. tostring(err) end From e8e3132e43d07fe72dabfb85ddc7960b1ebb2541 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 21 Jan 2026 06:09:09 +0000 Subject: [PATCH 08/10] Rename type parameter to content_type, remove do-end closures, fix test Co-authored-by: cryi <36897290+cryi@users.noreply.github.com> --- src/am.lua | 35 ++++++------ src/ami/internals/util.lua | 101 +++++++++++++++++------------------ tests/test/modify_n_show.lua | 4 +- 3 files changed, 67 insertions(+), 73 deletions(-) diff --git a/src/am.lua b/src/am.lua index db4ff7d..9512e82 100644 --- a/src/am.lua +++ b/src/am.lua @@ -283,27 +283,24 @@ end ---@param file string? ---@param path string ---@param value any ----@param type "json"|"hjson"? -do - local typeof = type - function am.modify_file(mode, file, path, value, type) - -- split path by dot - local path_parts = {} - for part in string.gmatch(path, "[^%.]+") do - table.insert(path_parts, part) - end - - -- try parse value as json/hjson - if typeof(value) == "string" then - local parsed_value, err = hjson.parse(value) - ami_assert(err == nil, "failed to parse value: " .. tostring(err), EXIT_MODIFY_ERROR) - value = parsed_value - end +---@param content_type "json"|"hjson"? +function am.modify_file(mode, file, path, value, content_type) + -- split path by dot + local path_parts = {} + for part in string.gmatch(path, "[^%.]+") do + table.insert(path_parts, part) + end - local ok, err = ami_util.modify_file(mode, file, path_parts, value, type) - ami_assert(ok, "failed to modify configuration: " .. tostring(err), EXIT_MODIFY_ERROR) - log_success"Requested modification applied." + -- try parse value as json/hjson + if type(value) == "string" then + local parsed_value, err = hjson.parse(value) + ami_assert(err == nil, "failed to parse value: " .. tostring(err), EXIT_MODIFY_ERROR) + value = parsed_value end + + local ok, err = ami_util.modify_file(mode, file, path_parts, value, content_type) + ami_assert(ok, "failed to modify configuration: " .. tostring(err), EXIT_MODIFY_ERROR) + log_success"Requested modification applied." end ---#DES am.get_value_from_file() diff --git a/src/ami/internals/util.lua b/src/ami/internals/util.lua index f13e3bd..36453c3 100644 --- a/src/ami/internals/util.lua +++ b/src/ami/internals/util.lua @@ -215,67 +215,64 @@ end ---@param file string? ---@param path string[] ---@param value any ----@param type "json"|"hjson"? +---@param content_type "json"|"hjson"? ---@return boolean?, string? -do - local typeof = type - function util.modify_file(mode, file, path, value, type) - if typeof(mode) ~= "string" then - mode = "auto" - end - if typeof(type) ~= "string" then - type = "hjson" - else - -- Validate format - if type ~= "hjson" and type ~= "json" then - return nil, "type must be either 'hjson' or 'json'" - end - end - if typeof(file) ~= "string" then - file, _ = find_default_modify_file() - if typeof(file) ~= "string" then return nil, "no valid configuration file found to modify" end +function util.modify_file(mode, file, path, value, content_type) + if type(mode) ~= "string" then + mode = "auto" + end + if type(content_type) ~= "string" then + content_type = "hjson" + else + -- Validate format + if content_type ~= "hjson" and content_type ~= "json" then + return nil, "type must be either 'hjson' or 'json'" end + end + if type(file) ~= "string" then + file, _ = find_default_modify_file() + if type(file) ~= "string" then return nil, "no valid configuration file found to modify" end + end - local raw_content, err = fs.read_file(file --[[@as string ]]) - if not raw_content then - if table.includes({ "auto", "set" }, mode) then - raw_content = "{}" - elseif table.includes({ "add" }, mode) then - raw_content = "[]" - else - return nil, err or "failed to read configuration file" - end + local raw_content, err = fs.read_file(file --[[@as string ]]) + if not raw_content then + if table.includes({ "auto", "set" }, mode) then + raw_content = "{}" + elseif table.includes({ "add" }, mode) then + raw_content = "[]" + else + return nil, err or "failed to read configuration file" end - local content, err = hjson.parse(raw_content --[[@as string ]]) - if not content then return nil, "failed to parse configuration file '" .. tostring(file) .. "': " .. tostring(err) end + end + local content, err = hjson.parse(raw_content --[[@as string ]]) + if not content then return nil, "failed to parse configuration file '" .. tostring(file) .. "': " .. tostring(err) end - if not modify_handlers[mode] then return nil, "invalid modify mode: " .. tostring(mode) end + if not modify_handlers[mode] then return nil, "invalid modify mode: " .. tostring(mode) end - local default = value - if table.includes({"add", "remove"}, mode) then - default = {} - end - local current_value = table.get(content, path, default) + local default = value + if table.includes({"add", "remove"}, mode) then + default = {} + end + local current_value = table.get(content, path, default) - local new_value, err = modify_handlers[mode](current_value, value) - if not new_value and err then return nil, "modification failed: " .. tostring(err) end + local new_value, err = modify_handlers[mode](current_value, value) + if not new_value and err then return nil, "modification failed: " .. tostring(err) end - local result, err = table.set(content, path, new_value) - if err == "cannot set nested value on a non-table object" then - return nil, "cannot set nested value on a non-table value at path: " .. table.concat(path, ".") - end - if not result then return nil, "failed to set new value in configuration" end - - local marshal_fn = type == "json" and hjson.stringify_to_json or hjson.stringify - local new_raw_content, err = marshal_fn(result, { indent = "\t", sort_keys = true }) - if not new_raw_content then return nil, "failed to serialize modified configuration: " .. tostring(err) end - local ok, err = fs.write_file(file .. ".new" --[[@as string ]], new_raw_content --[[@as string ]]) - if not ok then return nil, "failed to write modified configuration to file '" .. tostring(file) .. ".new': " .. tostring(err) end - -- replace original file - local ok, err = os.rename(file .. ".new" --[[@as string ]], file --[[@as string ]]) - if not ok then return nil, "failed to replace original configuration file '" .. tostring(file) .. "': " .. tostring(err) end - return true + local result, err = table.set(content, path, new_value) + if err == "cannot set nested value on a non-table object" then + return nil, "cannot set nested value on a non-table value at path: " .. table.concat(path, ".") end + if not result then return nil, "failed to set new value in configuration" end + + local marshal_fn = content_type == "json" and hjson.stringify_to_json or hjson.stringify + local new_raw_content, err = marshal_fn(result, { indent = "\t", sort_keys = true }) + if not new_raw_content then return nil, "failed to serialize modified configuration: " .. tostring(err) end + local ok, err = fs.write_file(file .. ".new" --[[@as string ]], new_raw_content --[[@as string ]]) + if not ok then return nil, "failed to write modified configuration to file '" .. tostring(file) .. ".new': " .. tostring(err) end + -- replace original file + local ok, err = os.rename(file .. ".new" --[[@as string ]], file --[[@as string ]]) + if not ok then return nil, "failed to replace original configuration file '" .. tostring(file) .. "': " .. tostring(err) end + return true end ---checks configurations diff --git a/tests/test/modify_n_show.lua b/tests/test/modify_n_show.lua index 87a9000..5bf919d 100644 --- a/tests/test/modify_n_show.lua +++ b/tests/test/modify_n_show.lua @@ -399,7 +399,7 @@ test["type option: json and hjson output"] = function() test.assert(ok, "Failed to modify with type=json: " .. tostring(err)) -- Check that the file is in JSON format (no comments, strict JSON) - local json_content = fs.read_file(json_file) + local json_content = fs.read_file(path.combine(test_dir, "test.json")) test.assert(json_content ~= nil, "Failed to read json file") -- JSON format should not have comments and should use quotes for keys test.assert(json_content:find('"test"') ~= nil, "JSON output should have quoted keys") @@ -413,7 +413,7 @@ test["type option: json and hjson output"] = function() test.assert(ok, "Failed to modify with type=hjson: " .. tostring(err)) -- Check that the file is in HJSON format - local hjson_content = fs.read_file(hjson_file) + local hjson_content = fs.read_file(path.combine(test_dir, "test.hjson")) test.assert(hjson_content ~= nil, "Failed to read hjson file") -- Test with invalid type From ac6d87013e775ff64afc26bfd8daf353cf75941a Mon Sep 17 00:00:00 2001 From: v Date: Wed, 21 Jan 2026 14:10:51 +0100 Subject: [PATCH 09/10] fixed --- src/am.lua | 6 +- src/ami/internals/interface/app.lua | 159 ++++++++++++++++------------ src/ami/internals/util.lua | 12 ++- tests/test/modify_n_show.lua | 39 ++++--- 4 files changed, 127 insertions(+), 89 deletions(-) diff --git a/src/am.lua b/src/am.lua index 9512e82..b1952f5 100644 --- a/src/am.lua +++ b/src/am.lua @@ -285,6 +285,10 @@ end ---@param value any ---@param content_type "json"|"hjson"? function am.modify_file(mode, file, path, value, content_type) + if type(content_type) ~= "string" then + content_type = "hjson" + end + -- split path by dot local path_parts = {} for part in string.gmatch(path, "[^%.]+") do @@ -292,7 +296,7 @@ function am.modify_file(mode, file, path, value, content_type) end -- try parse value as json/hjson - if type(value) == "string" then + if table.includes({ "json", "hjson" }, content_type) and type(value) == "string" then local parsed_value, err = hjson.parse(value) ami_assert(err == nil, "failed to parse value: " .. tostring(err), EXIT_MODIFY_ERROR) value = parsed_value diff --git a/src/ami/internals/interface/app.lua b/src/ami/internals/interface/app.lua index 4ca5ca7..41dcd1b 100644 --- a/src/ami/internals/interface/app.lua +++ b/src/ami/internals/interface/app.lua @@ -43,7 +43,7 @@ local function new(options) index = 0, description = "ami 'info' sub command", summary = implementation_status .. " Prints runtime info and status of the app", - action = violation_fallback + action = violation_fallback, }, setup = { index = 1, @@ -52,24 +52,24 @@ local function new(options) options = { environment = { index = 0, - aliases = {"env"}, - description = "Creates application environment" + aliases = { "env" }, + description = "Creates application environment", }, app = { index = 1, - description = "Generates app folder structure and files" + description = "Generates app folder structure and files", }, configure = { index = 2, - description = "Configures application and renders templates" + description = "Configures application and renders templates", }, ["no-validate"] = { index = 3, - description = "Disables platform and configuration validation" - } + description = "Disables platform and configuration validation", + }, }, -- (options, command, args, cli) - action = function(options) + action = function (options) local no_options = #table.keys(options) == 0 if no_options or options.environment then @@ -84,9 +84,9 @@ local function new(options) end if (no_options or options.configure) and not am.app.__are_templates_generated() then - am.app.render() + am.app.render() end - end + end, }, validate = { index = 2, @@ -95,42 +95,42 @@ local function new(options) options = { platform = { index = 1, - description = "Validates application platform" + description = "Validates application platform", }, configuration = { index = 2, - description = "Validates application configuration" - } + description = "Validates application configuration", + }, }, - action = violation_fallback + action = violation_fallback, }, start = { index = 3, - aliases = {"s"}, + aliases = { "s" }, description = "ami 'start' sub command ", summary = implementation_status .. " Starts the app", -- (options, command, args, cli) - action = violation_fallback + action = violation_fallback, }, stop = { index = 4, description = "ami 'stop' sub command", summary = implementation_status .. " Stops the app", -- (options, command, args, cli) - action = violation_fallback + action = violation_fallback, }, update = { index = 5, description = "ami 'update' command", summary = "Updates the app or returns setup required", -- (options, command, args, cli) - action = function() + action = function () local available, id, ver = am.app.is_update_available() if available then ami_error("Found new version " .. ver .. " of " .. id .. ", please run setup...", EXIT_SETUP_REQUIRED) end - log_info("Application is up to date.") - end + log_info"Application is up to date." + end, }, remove = { index = 6, @@ -138,24 +138,26 @@ local function new(options) summary = "Remove the app or parts based on options", options = { all = { - description = "Removes entire application keeping only app.hjson" + description = "Removes entire application keeping only app.hjson", + }, + force = { + description = "Forces removal of application", + hidden = true, }, - force = { - description = "Forces removal of application", - hidden = true, - } }, -- (options, command, args, cli) - action = function(options) - ami_assert(am.__has_app_specific_interface or options.force, "you are trying to remove app, but app specific removal routine is not available. Use '--force' to force removal", EXIT_APP_REMOVE_ERROR) - if options.all then + action = function (options) + ami_assert(am.__has_app_specific_interface or options.force, + "you are trying to remove app, but app specific removal routine is not available. Use '--force' to force removal", + EXIT_APP_REMOVE_ERROR) + if options.all then am.app.remove() - log_success("Application removed.") + log_success"Application removed." else am.app.remove_data() - log_success("Application data removed.") + log_success"Application data removed." end - end + end, }, modify = { description = "ami 'modify' sub command", @@ -164,45 +166,62 @@ local function new(options) options = { file = { index = 1, - aliases = {"f"}, - description = "Path to configuration file to modify (Defaults to app.h/json)" + aliases = { "f" }, + description = "Path to configuration file to modify (Defaults to app.h/json)", }, set = { description = "Sets value at path", - type = "boolean" + type = "boolean", }, unset = { description = "Unsets value at path", - type = "boolean" + type = "boolean", }, add = { description = "Adds value to list or dictionary", - type = "boolean" + type = "boolean", }, remove = { description = "Removes value from list or dictionary", - type = "boolean" + type = "boolean", + }, + json = { + description = "Forces file to be treated as JSON", + type = "boolean", + }, + ["type"] = { + aliases = { "t" }, + description = "Content type of the file (json or hjson, defaults to hjson)", }, - type = { - aliases = {"t"}, - description = "Content type of file we are modifying (hjson or json)", - type = "string" - } }, action = function (options, _, args) - ami_assert(#args > 1 or (#args == 1 and options.unset), "invalid arguments to modify command - needs path and value or --unset and path", EXIT_MODIFY_ERROR) - options.set = options.set or (not options.unset and not options.add and not options.remove) -- default to set + ami_assert(#args > 1 or (#args == 1 and options.unset), + "invalid arguments to modify command - needs path and value or --unset and path", EXIT_MODIFY_ERROR) + options.set = options.set or + (not options.unset and not options.add and not options.remove) -- default to set - local options_without_file = table.filter(options, function(key, v) return key ~= "file" and key ~= "type" and v == true end) - ami_assert(#table.keys(options_without_file) <= 1, "only one modification mode can be specified", EXIT_MODIFY_ERROR) + local modify_options = table.filter(options, function (key, v) + return v and not table.includes({ "file", "json", "type" }, key) + end) + ami_assert(#table.keys(modify_options) <= 1, "only one modification mode can be specified", + EXIT_MODIFY_ERROR) local mode = "auto" - if #table.keys(options_without_file) == 1 then - mode = table.keys(options_without_file)[1] + if #table.keys(modify_options) == 1 then + mode = table.keys(modify_options)[1] end - am.modify_file(mode, options.file, args[1].value, #args > 1 and args[2].value or nil, options.type) - end - }, + local content_type = "hjson" + if options.json then + content_type = "json" + elseif options.type then + ami_assert(table.includes({ "json", "hjson" }, options.type), + "type must be either 'hjson' or 'json'", EXIT_MODIFY_ERROR) + content_type = options.type + end + + am.modify_file(mode, options.file, args[1].value, #args > 1 and args[2].value or nil, content_type) + end, + }, show = { description = "ami 'show' sub command", summary = "Shows value from app configuration file", @@ -210,19 +229,19 @@ local function new(options) options = { file = { index = 1, - description = "Path to configuration file to show from (Defaults to app.h/json)" - } + description = "Path to configuration file to show from (Defaults to app.h/json)", + }, }, action = function (options, _, args) am.show_file(options.file, type(args) == "table" and #args > 0 and args[1].value or nil) - end + end, }, about = { index = 7, description = "ami 'about' sub command", summary = implementation_status .. " Prints informations about app", -- (options, command, args, cli) - action = violation_fallback + action = violation_fallback, }, pack = { description = "ami 'pack' sub command", @@ -230,20 +249,20 @@ local function new(options) options = { output = { index = 1, - aliases = {"o"}, - description = "Output path for the archive" + aliases = { "o" }, + description = "Output path for the archive", }, light = { index = 2, - description = "If used the archive will not include application data" - } + description = "If used the archive will not include application data", + }, }, action = function (options) - am.app.pack({ + am.app.pack{ destination = options.output, - mode = options.light and "light" or "full" - }) - end + mode = options.light and "light" or "full", + } + end, }, unpack = { description = "ami 'unpack' sub command", @@ -252,19 +271,19 @@ local function new(options) options = { source = { index = 1, - description = "Path to the archive" - } + description = "Path to the archive", + }, }, action = function (options) options.__rerun = table.get(options, "__rerun", true) am.app.unpack(options) - log_success("application unpacked") - end - } + log_success"application unpacked" + end, + }, } - return base + return base end return { - new = new + new = new, } diff --git a/src/ami/internals/util.lua b/src/ami/internals/util.lua index 36453c3..d087623 100644 --- a/src/ami/internals/util.lua +++ b/src/ami/internals/util.lua @@ -224,9 +224,8 @@ function util.modify_file(mode, file, path, value, content_type) if type(content_type) ~= "string" then content_type = "hjson" else - -- Validate format - if content_type ~= "hjson" and content_type ~= "json" then - return nil, "type must be either 'hjson' or 'json'" + if not table.includes({ "hjson", "json" }, content_type) then + return nil, "content type must be either 'hjson' or 'json'" end end if type(file) ~= "string" then @@ -264,7 +263,12 @@ function util.modify_file(mode, file, path, value, content_type) end if not result then return nil, "failed to set new value in configuration" end - local marshal_fn = content_type == "json" and hjson.stringify_to_json or hjson.stringify + local marshallers = { + hjson = hjson.stringify, + json = hjson.stringify_to_json + } + + local marshal_fn = marshallers[content_type] local new_raw_content, err = marshal_fn(result, { indent = "\t", sort_keys = true }) if not new_raw_content then return nil, "failed to serialize modified configuration: " .. tostring(err) end local ok, err = fs.write_file(file .. ".new" --[[@as string ]], new_raw_content --[[@as string ]]) diff --git a/tests/test/modify_n_show.lua b/tests/test/modify_n_show.lua index 5bf919d..3b9bc12 100644 --- a/tests/test/modify_n_show.lua +++ b/tests/test/modify_n_show.lua @@ -385,7 +385,7 @@ add_fail_test(test, "syntax and logic errors", { }) -- Test type option -test["type option: json and hjson output"] = function() +test["type option: json and hjson output"] = function () local test_dir = "tests/tmp/ami_test_format" fs.mkdirp(test_dir) fs.remove(test_dir, { recurse = true, content_only = true }) @@ -393,37 +393,48 @@ test["type option: json and hjson output"] = function() -- Test with JSON type local json_file = path.combine(test_dir, "test.json") fs.write_file(json_file, "{}") - - local args_json = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test.json", "--type=json", "test.key", "value123" } + + local args_json = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test.json", "--type=json", + "test.key", "value123" } local ok, err = pcall(ami, table.unpack(args_json)) + os.chdir(default_cwd) test.assert(ok, "Failed to modify with type=json: " .. tostring(err)) - + -- Check that the file is in JSON format (no comments, strict JSON) local json_content = fs.read_file(path.combine(test_dir, "test.json")) test.assert(json_content ~= nil, "Failed to read json file") -- JSON format should not have comments and should use quotes for keys - test.assert(json_content:find('"test"') ~= nil, "JSON output should have quoted keys") - + test.assert(json_content:find'"test"' ~= nil, "JSON output should have quoted keys") + -- Test with HJSON type (default) local hjson_file = path.combine(test_dir, "test.hjson") fs.write_file(hjson_file, "{}") - - local args_hjson = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test.hjson", "--type=hjson", "test.key", "value456" } + + local args_hjson = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test.hjson", "--type=hjson", + "test.key", "value456" } local ok, err = pcall(ami, table.unpack(args_hjson)) + os.chdir(default_cwd) test.assert(ok, "Failed to modify with type=hjson: " .. tostring(err)) - + -- Check that the file is in HJSON format local hjson_content = fs.read_file(path.combine(test_dir, "test.hjson")) test.assert(hjson_content ~= nil, "Failed to read hjson file") - + -- Test with invalid type local invalid_file = path.combine(test_dir, "test_invalid.hjson") fs.write_file(invalid_file, "{}") - - local args_invalid = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test_invalid.hjson", "--type=xml", "test.key", "value789" } + + local args_invalid = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test_invalid.hjson", + "--type=xml", "test.key", "value789" } local ok, err = pcall(ami, table.unpack(args_invalid)) + os.chdir(default_cwd) test.assert(not ok, "Should fail with invalid type") - test.assert(tostring(err):find("type must be either 'hjson' or 'json'") ~= nil, "Error message should mention valid types") - + test.assert(tostring(err):find"type must be either 'hjson' or 'json'" ~= nil, + "Error message should mention valid types") + os.chdir(default_cwd) end + +if not TEST then + test.summary() +end From 7a80af61c4c5e3feff4167c4e2835ce1905cac1a Mon Sep 17 00:00:00 2001 From: v Date: Wed, 21 Jan 2026 14:15:08 +0100 Subject: [PATCH 10/10] fixed --- src/ami/internals/interface/app.lua | 14 +++++------- tests/test/modify_n_show.lua | 34 ++++++++++++++--------------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/ami/internals/interface/app.lua b/src/ami/internals/interface/app.lua index 41dcd1b..e4977c6 100644 --- a/src/ami/internals/interface/app.lua +++ b/src/ami/internals/interface/app.lua @@ -189,9 +189,9 @@ local function new(options) description = "Forces file to be treated as JSON", type = "boolean", }, - ["type"] = { - aliases = { "t" }, - description = "Content type of the file (json or hjson, defaults to hjson)", + hjson = { + description = "Forces file to be treated as HJSON (default)", + type = "boolean", }, }, action = function (options, _, args) @@ -201,7 +201,7 @@ local function new(options) (not options.unset and not options.add and not options.remove) -- default to set local modify_options = table.filter(options, function (key, v) - return v and not table.includes({ "file", "json", "type" }, key) + return v and not table.includes({ "file", "json", "hjson" }, key) end) ami_assert(#table.keys(modify_options) <= 1, "only one modification mode can be specified", EXIT_MODIFY_ERROR) @@ -210,13 +210,11 @@ local function new(options) mode = table.keys(modify_options)[1] end + ami_assert(not (options.json and options.hjson), + "only one format flag (--json or --hjson) can be specified", EXIT_MODIFY_ERROR) local content_type = "hjson" if options.json then content_type = "json" - elseif options.type then - ami_assert(table.includes({ "json", "hjson" }, options.type), - "type must be either 'hjson' or 'json'", EXIT_MODIFY_ERROR) - content_type = options.type end am.modify_file(mode, options.file, args[1].value, #args > 1 and args[2].value or nil, content_type) diff --git a/tests/test/modify_n_show.lua b/tests/test/modify_n_show.lua index 3b9bc12..f8e02f1 100644 --- a/tests/test/modify_n_show.lua +++ b/tests/test/modify_n_show.lua @@ -384,21 +384,21 @@ add_fail_test(test, "syntax and logic errors", { }, }) --- Test type option -test["type option: json and hjson output"] = function () +-- Test format flags +test["format flags: json and hjson output"] = function () local test_dir = "tests/tmp/ami_test_format" fs.mkdirp(test_dir) fs.remove(test_dir, { recurse = true, content_only = true }) - -- Test with JSON type + -- Test with --json flag local json_file = path.combine(test_dir, "test.json") fs.write_file(json_file, "{}") - local args_json = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test.json", "--type=json", + local args_json = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test.json", "--json", "test.key", "value123" } local ok, err = pcall(ami, table.unpack(args_json)) os.chdir(default_cwd) - test.assert(ok, "Failed to modify with type=json: " .. tostring(err)) + test.assert(ok, "Failed to modify with --json: " .. tostring(err)) -- Check that the file is in JSON format (no comments, strict JSON) local json_content = fs.read_file(path.combine(test_dir, "test.json")) @@ -406,31 +406,31 @@ test["type option: json and hjson output"] = function () -- JSON format should not have comments and should use quotes for keys test.assert(json_content:find'"test"' ~= nil, "JSON output should have quoted keys") - -- Test with HJSON type (default) + -- Test with --hjson flag (explicit, same as default) local hjson_file = path.combine(test_dir, "test.hjson") fs.write_file(hjson_file, "{}") - local args_hjson = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test.hjson", "--type=hjson", + local args_hjson = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test.hjson", "--hjson", "test.key", "value456" } local ok, err = pcall(ami, table.unpack(args_hjson)) os.chdir(default_cwd) - test.assert(ok, "Failed to modify with type=hjson: " .. tostring(err)) + test.assert(ok, "Failed to modify with --hjson: " .. tostring(err)) -- Check that the file is in HJSON format local hjson_content = fs.read_file(path.combine(test_dir, "test.hjson")) test.assert(hjson_content ~= nil, "Failed to read hjson file") - -- Test with invalid type - local invalid_file = path.combine(test_dir, "test_invalid.hjson") - fs.write_file(invalid_file, "{}") + -- Test that using both --json and --hjson fails + local both_file = path.combine(test_dir, "test_both.hjson") + fs.write_file(both_file, "{}") - local args_invalid = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test_invalid.hjson", - "--type=xml", "test.key", "value789" } - local ok, err = pcall(ami, table.unpack(args_invalid)) + local args_both = { "--log-level=error", "--path=" .. test_dir, "modify", "--file=test_both.hjson", + "--json", "--hjson", "test.key", "value789" } + local ok, err = pcall(ami, table.unpack(args_both)) os.chdir(default_cwd) - test.assert(not ok, "Should fail with invalid type") - test.assert(tostring(err):find"type must be either 'hjson' or 'json'" ~= nil, - "Error message should mention valid types") + test.assert(not ok, "Should fail when both --json and --hjson are specified") + test.assert(tostring(err):find"only one format flag" ~= nil, + "Error message should mention only one format flag can be specified") os.chdir(default_cwd) end