command.zig 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. const std = @import("std");
  2. const storage = @import("storage.zig");
  3. const index = @import("index.zig");
  4. const FAILURE_RESPONSE = "error";
  5. const SUCCESS_RESPONSE = "success";
  6. const Command = enum {
  7. read,
  8. write,
  9. delete,
  10. status,
  11. keys,
  12. reads,
  13. };
  14. fn parseKeyValue(buf: []const u8) ?[2][]const u8 {
  15. var kvIterator = std.mem.splitAny(u8, buf, "|");
  16. const key = kvIterator.first();
  17. return [2][]const u8{ key, kvIterator.rest() };
  18. }
  19. pub fn parse(msg: []const u8, allocator: std.mem.Allocator) ?[]const u8 {
  20. const trimSet = [_]u8{ '\n', ' ', '\r' };
  21. const cleanMsg = std.mem.trim(u8, msg, &trimSet);
  22. var messageIterator = std.mem.splitAny(u8, cleanMsg, " ");
  23. const cmdString = messageIterator.first();
  24. const cmd = std.meta.stringToEnum(Command, cmdString) orelse {
  25. return null;
  26. };
  27. switch (cmd) {
  28. .read => {
  29. const key = messageIterator.rest();
  30. const value = storage.readAlloc(key, allocator) orelse {
  31. return FAILURE_RESPONSE;
  32. };
  33. return value;
  34. },
  35. .write => {
  36. const kvPair = messageIterator.rest();
  37. const kv = parseKeyValue(kvPair) orelse {
  38. return FAILURE_RESPONSE;
  39. };
  40. if (storage.write(kv[0], kv[1])) {
  41. return SUCCESS_RESPONSE;
  42. }
  43. return FAILURE_RESPONSE;
  44. },
  45. .delete => {
  46. const key = messageIterator.rest();
  47. if (!storage.delete(key)) {
  48. return FAILURE_RESPONSE;
  49. }
  50. return SUCCESS_RESPONSE;
  51. },
  52. .keys => {
  53. return index.getAllKeys(allocator);
  54. },
  55. .reads => {
  56. const prefix = messageIterator.rest();
  57. return index.getValuesByPrefix(prefix, allocator);
  58. },
  59. .status => {
  60. return "well going our operation";
  61. },
  62. }
  63. return null;
  64. }
  65. // -- Tests --
  66. const test_allocator = std.heap.page_allocator;
  67. test "parse write command" {
  68. storage.init();
  69. const result = parse("write mykey|myvalue\r\n", test_allocator) orelse return error.TestUnexpectedResult;
  70. try std.testing.expectEqualStrings("success", result);
  71. }
  72. test "parse read command" {
  73. storage.init();
  74. // Write first, then read
  75. _ = parse("write cmd_rk|cmd_rv", test_allocator);
  76. const result = parse("read cmd_rk", test_allocator) orelse return error.TestUnexpectedResult;
  77. try std.testing.expectEqualStrings("cmd_rv", result);
  78. }
  79. test "parse read nonexistent" {
  80. storage.init();
  81. const result = parse("read cmd_nonexistent_key", test_allocator);
  82. try std.testing.expectEqualStrings("error", result.?);
  83. }
  84. test "parse delete command" {
  85. storage.init();
  86. _ = parse("write cmd_dk|cmd_dv", test_allocator);
  87. const result = parse("delete cmd_dk", test_allocator) orelse return error.TestUnexpectedResult;
  88. try std.testing.expectEqualStrings("success", result);
  89. // Verify deleted
  90. const after = parse("read cmd_dk", test_allocator);
  91. try std.testing.expectEqualStrings("error", after.?);
  92. }
  93. test "parse delete nonexistent" {
  94. storage.init();
  95. const result = parse("delete cmd_nonexistent_del", test_allocator);
  96. try std.testing.expectEqualStrings("error", result.?);
  97. }
  98. test "parse status command" {
  99. const result = parse("status", test_allocator) orelse return error.TestUnexpectedResult;
  100. try std.testing.expectEqualStrings("well going our operation", result);
  101. }
  102. test "parse unknown command returns null" {
  103. try std.testing.expectEqual(@as(?[]const u8, null), parse("foobar", test_allocator));
  104. try std.testing.expectEqual(@as(?[]const u8, null), parse("", test_allocator));
  105. }
  106. test "parse trims whitespace" {
  107. const result = parse(" status \r\n", test_allocator) orelse return error.TestUnexpectedResult;
  108. try std.testing.expectEqualStrings("well going our operation", result);
  109. }
  110. test "parseKeyValue splits on pipe" {
  111. const kv = parseKeyValue("hello|world") orelse return error.TestUnexpectedResult;
  112. try std.testing.expectEqualStrings("hello", kv[0]);
  113. try std.testing.expectEqualStrings("world", kv[1]);
  114. }
  115. test "parseKeyValue with multiple pipes" {
  116. const kv = parseKeyValue("key|val|ue|extra") orelse return error.TestUnexpectedResult;
  117. try std.testing.expectEqualStrings("key", kv[0]);
  118. try std.testing.expectEqualStrings("val|ue|extra", kv[1]);
  119. }