const std = @import("std"); pub fn parseToolArgsObject(alloc: std.mem.Allocator, args_json: []const u8) !std.json.ObjectMap { const parsed = try std.json.parseFromSlice(std.json.Value, alloc, args_json, .{}); // The returned object map is backed by the parse allocation; callers should // use an arena or allocator lifetime that outlives all borrowed values. if (parsed.value != .object) return error.InvalidToolArguments; return parsed.value.object;} pub fn requiredStringArg(args: std.json.ObjectMap, key: []const u8) ![]const u8 { const value = args.get(key) orelse return error.InvalidToolArguments; if (value != .string) return error.InvalidToolArguments; return value.string;} pub fn optionalStringArg(args: std.json.ObjectMap, key: []const u8) ?[]const u8 { const value = args.get(key) orelse return null; if (value != .string) return null; return value.string;} pub fn optionalBoolArg(args: std.json.ObjectMap, key: []const u8) ?bool { const value = args.get(key) orelse return null; if (value != .bool) return null; return value.bool;} pub fn optionalIntArg(args: std.json.ObjectMap, key: []const u8) ?i64 { const value = args.get(key) orelse return null; if (value != .integer) return null; return value.integer;} test "parseToolArgsObject parses object roots" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const arena = arena_state.allocator(); const args = try parseToolArgsObject(arena, "{\"path\":\"src/main.zig\"}"); try std.testing.expectEqualStrings("src/main.zig", args.get("path").?.string);} test "parseToolArgsObject rejects non-object roots" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const arena = arena_state.allocator(); try std.testing.expectError(error.InvalidToolArguments, parseToolArgsObject(arena, "[]"));} test "requiredStringArg rejects missing and wrong-type values" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const arena = arena_state.allocator(); const args = try parseToolArgsObject(arena, "{\"path\":\"src/main.zig\",\"count\":3}"); try std.testing.expectEqualStrings("src/main.zig", try requiredStringArg(args, "path")); try std.testing.expectError(error.InvalidToolArguments, requiredStringArg(args, "missing")); try std.testing.expectError(error.InvalidToolArguments, requiredStringArg(args, "count"));} test "optional typed args return payloads only for matching tags" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const arena = arena_state.allocator(); const args = try parseToolArgsObject(arena, "{\"name\":\"fx\",\"enabled\":true,\"count\":3,\"other\":1.25}"); try std.testing.expectEqualStrings("fx", optionalStringArg(args, "name").?); try std.testing.expect(optionalStringArg(args, "missing") == null); try std.testing.expect(optionalStringArg(args, "enabled") == null); try std.testing.expectEqual(true, optionalBoolArg(args, "enabled").?); try std.testing.expect(optionalBoolArg(args, "missing") == null); try std.testing.expect(optionalBoolArg(args, "name") == null); try std.testing.expectEqual(@as(i64, 3), optionalIntArg(args, "count").?); try std.testing.expect(optionalIntArg(args, "missing") == null); try std.testing.expect(optionalIntArg(args, "other") == null);}
const std = @import("std");
pub fn parseToolArgsObject(alloc: std.mem.Allocator, args_json: []const u8) !std.json.ObjectMap {
const parsed = try std.json.parseFromSlice(std.json.Value, alloc, args_json, .{});
// The returned object map is backed by the parse allocation; callers should
// use an arena or allocator lifetime that outlives all borrowed values.
if (parsed.value != .object) return error.InvalidToolArguments;
return parsed.value.object;
}
pub fn requiredStringArg(args: std.json.ObjectMap, key: []const u8) ![]const u8 {
const value = args.get(key) orelse return error.InvalidToolArguments;
if (value != .string) return error.InvalidToolArguments;
return value.string;
}
pub fn optionalStringArg(args: std.json.ObjectMap, key: []const u8) ?[]const u8 {
const value = args.get(key) orelse return null;
if (value != .string) return null;
return value.string;
}
pub fn optionalBoolArg(args: std.json.ObjectMap, key: []const u8) ?bool {
const value = args.get(key) orelse return null;
if (value != .bool) return null;
return value.bool;
}
pub fn optionalIntArg(args: std.json.ObjectMap, key: []const u8) ?i64 {
const value = args.get(key) orelse return null;
if (value != .integer) return null;
return value.integer;
}
test "parseToolArgsObject parses object roots" {
var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena_state.deinit();
const arena = arena_state.allocator();
const args = try parseToolArgsObject(arena, "{\"path\":\"src/main.zig\"}");
try std.testing.expectEqualStrings("src/main.zig", args.get("path").?.string);
}
test "parseToolArgsObject rejects non-object roots" {
var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena_state.deinit();
const arena = arena_state.allocator();
try std.testing.expectError(error.InvalidToolArguments, parseToolArgsObject(arena, "[]"));
}
test "requiredStringArg rejects missing and wrong-type values" {
var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena_state.deinit();
const arena = arena_state.allocator();
const args = try parseToolArgsObject(arena, "{\"path\":\"src/main.zig\",\"count\":3}");
try std.testing.expectEqualStrings("src/main.zig", try requiredStringArg(args, "path"));
try std.testing.expectError(error.InvalidToolArguments, requiredStringArg(args, "missing"));
try std.testing.expectError(error.InvalidToolArguments, requiredStringArg(args, "count"));
}
test "optional typed args return payloads only for matching tags" {
var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena_state.deinit();
const arena = arena_state.allocator();
const args = try parseToolArgsObject(arena, "{\"name\":\"fx\",\"enabled\":true,\"count\":3,\"other\":1.25}");
try std.testing.expectEqualStrings("fx", optionalStringArg(args, "name").?);
try std.testing.expect(optionalStringArg(args, "missing") == null);
try std.testing.expect(optionalStringArg(args, "enabled") == null);
try std.testing.expectEqual(true, optionalBoolArg(args, "enabled").?);
try std.testing.expect(optionalBoolArg(args, "missing") == null);
try std.testing.expect(optionalBoolArg(args, "name") == null);
try std.testing.expectEqual(@as(i64, 3), optionalIntArg(args, "count").?);
try std.testing.expect(optionalIntArg(args, "missing") == null);
try std.testing.expect(optionalIntArg(args, "other") == null);
}