From 6d5f1db5433b61dcca2ef78dbc53ff5d989a1e2a Mon Sep 17 00:00:00 2001
From: DerekBum <alextruewestern@gmail.com>
Date: Fri, 31 May 2024 19:43:58 +0300
Subject: [PATCH] box: feature `tuple:format` to get a format of a tuple

This patch adds `tuple:format()` method to get a format
of a tuple.

Closes #10005

@TarantoolBot document
Title: New `format` method for `box.tuple`
Product: Tarantool
Since: 3.2

The `tuple:format` method returns a format of a tuple.
---
 .../unreleased/gh-10005-tuple-get-format.md   |  3 ++
 src/box/lua/tuple.c                           | 18 +++++++
 src/box/lua/tuple.lua                         |  1 +
 src/box/lua/tuple_format.c                    | 20 +++++---
 src/box/lua/tuple_format.h                    |  7 +++
 src/box/lua/tuple_format.lua                  |  8 +++
 .../gh_10005_tuple_get_format_test.lua        | 50 +++++++++++++++++++
 7 files changed, 101 insertions(+), 6 deletions(-)
 create mode 100644 changelogs/unreleased/gh-10005-tuple-get-format.md
 create mode 100644 test/app-luatest/gh_10005_tuple_get_format_test.lua

diff --git a/changelogs/unreleased/gh-10005-tuple-get-format.md b/changelogs/unreleased/gh-10005-tuple-get-format.md
new file mode 100644
index 0000000000..674d1494a1
--- /dev/null
+++ b/changelogs/unreleased/gh-10005-tuple-get-format.md
@@ -0,0 +1,3 @@
+## feature/box
+
+* Added the `tuple:format` method to get a format of a tuple (gh-10005).
diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
index 5eedde8721..30d6b64e68 100644
--- a/src/box/lua/tuple.c
+++ b/src/box/lua/tuple.c
@@ -757,6 +757,23 @@ lbox_tuple_info(lua_State *L)
 	return 1;
 }
 
+/**
+ * Push to Lua stack an array with the information about a format of a tuple.
+ * Elements of the array are maps
+ * {'name' = 'field_name', 'type' = 'field_type'}.
+ */
+static int
+lbox_tuple_get_format(lua_State *L)
+{
+	int argc = lua_gettop(L);
+	if (argc != 1)
+		luaL_error(L, "Usage: tuple:format()");
+
+	struct tuple *tuple = luaT_checktuple(L, 1);
+	struct tuple_format *format = tuple_format(tuple);
+	return box_tuple_format_serialize_impl(L, format);
+}
+
 static const struct luaL_Reg lbox_tuple_meta[] = {
 	{"__gc", lbox_tuple_gc},
 	{"tostring", lbox_tuple_to_string},
@@ -766,6 +783,7 @@ static const struct luaL_Reg lbox_tuple_meta[] = {
 	{"tuple_field_by_path", lbox_tuple_field_by_path},
 	{"new", lbox_tuple_new},
 	{"info", lbox_tuple_info},
+	{"tuple_get_format", lbox_tuple_get_format},
 	{NULL, NULL}
 };
 
diff --git a/src/box/lua/tuple.lua b/src/box/lua/tuple.lua
index ffe3294148..416c66fe9e 100644
--- a/src/box/lua/tuple.lua
+++ b/src/box/lua/tuple.lua
@@ -332,6 +332,7 @@ local methods = {
     ["update"]      = tuple_update;
     ["upsert"]      = tuple_upsert;
     ["bsize"]       = tuple_bsize;
+    ["format"]      = box.tuple.format;
     ["tomap"]       = internal.tuple.tuple_to_map;
     ["info"]        = internal.tuple.info;
 }
diff --git a/src/box/lua/tuple_format.c b/src/box/lua/tuple_format.c
index 3b5d8e7b46..eedc92be9a 100644
--- a/src/box/lua/tuple_format.c
+++ b/src/box/lua/tuple_format.c
@@ -106,17 +106,15 @@ lbox_tuple_format_tostring(struct lua_State *L)
 	return 1;
 }
 
-/*
- * Returns the format clause with which this tuple format was created.
- */
-static int
-lbox_tuple_format_serialize(struct lua_State *L)
+int
+box_tuple_format_serialize_impl(struct lua_State *L,
+				struct tuple_format *format)
 {
-	struct tuple_format *format = luaT_check_tuple_format(L, 1);
 	if (format->data == NULL) {
 		lua_createtable(L, 0, 0);
 		return 1;
 	}
+
 	const char *data = format->data;
 	luamp_decode(L, luaL_msgpack_default, &data);
 	luaL_findtable(L, LUA_GLOBALSINDEX, "box.internal.space", 1);
@@ -127,6 +125,16 @@ lbox_tuple_format_serialize(struct lua_State *L)
 	return 1;
 }
 
+/*
+ * Returns the format clause with which this tuple format was created.
+ */
+static int
+lbox_tuple_format_serialize(struct lua_State *L)
+{
+	struct tuple_format *format = luaT_check_tuple_format(L, 1);
+	return box_tuple_format_serialize_impl(L, format);
+}
+
 /*
  * Simply returns `ipairs(format:totable())`.
  */
diff --git a/src/box/lua/tuple_format.h b/src/box/lua/tuple_format.h
index 66a35d5b0b..9c0b9ca993 100644
--- a/src/box/lua/tuple_format.h
+++ b/src/box/lua/tuple_format.h
@@ -28,6 +28,13 @@ luaT_check_tuple_format(struct lua_State *L, int narg);
 void
 box_lua_tuple_format_init(struct lua_State *L);
 
+/**
+ * Returns the clause for the given format.
+ */
+int
+box_tuple_format_serialize_impl(struct lua_State *L,
+				struct tuple_format *format);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/src/box/lua/tuple_format.lua b/src/box/lua/tuple_format.lua
index 31c6fd108c..35ea98797b 100644
--- a/src/box/lua/tuple_format.lua
+++ b/src/box/lua/tuple_format.lua
@@ -1,5 +1,7 @@
 local utils = require('internal.utils')
 
+local internal = box.internal
+
 -- new() needs a wrapper in Lua, because format normalization needs to be done
 -- in Lua.
 box.tuple.format.new = function(format)
@@ -7,3 +9,9 @@ box.tuple.format.new = function(format)
     format = box.internal.space.normalize_format(nil, nil, format, 2)
     return box.internal.tuple_format.new(format)
 end
+
+setmetatable(box.tuple.format, {
+    __call = function(_, t)
+        return internal.tuple.tuple_get_format(t)
+    end,
+})
diff --git a/test/app-luatest/gh_10005_tuple_get_format_test.lua b/test/app-luatest/gh_10005_tuple_get_format_test.lua
new file mode 100644
index 0000000000..b2d42d4a44
--- /dev/null
+++ b/test/app-luatest/gh_10005_tuple_get_format_test.lua
@@ -0,0 +1,50 @@
+local server = require('luatest.server')
+local t = require('luatest')
+
+local g = t.group()
+
+g.before_all(function(cg)
+    cg.server = server:new()
+    cg.server:start()
+end)
+
+g.after_all(function(cg)
+    cg.server:drop()
+end)
+
+-- Checks that `tuple:format` works as expected.
+g.test_tuple_format = function()
+    local f = box.tuple.format.new({{'id', 'number'}, {'name', 'string'}})
+    local tuple = box.tuple.new({1, 'Flint'}, {format = f})
+    t.assert_equals(type(tuple:format()), 'table')
+    t.assert_equals(tuple:format(), f:totable())
+
+    tuple = box.tuple.new({1, 'Flint', true}, {format = f})
+    t.assert_equals(type(tuple:format()), 'table')
+    t.assert_equals(tuple:format(), f:totable())
+end
+
+-- Checks that `box.tuple.format` works as expected.
+g.test_box_tuple_format = function()
+    local f = box.tuple.format.new({{'id', 'number'}, {'name', 'string'}})
+    local tuple = box.tuple.new({1, 'Flint'}, {format = f})
+    t.assert_equals(type(box.tuple.format(tuple)), 'table')
+    t.assert_equals(box.tuple.format(tuple), f:totable())
+    t.assert_equals(box.tuple.format(tuple), tuple:format())
+
+    tuple = box.tuple.new({1, 'Flint', true}, {format = f})
+    t.assert_equals(type(box.tuple.format(tuple)), 'table')
+    t.assert_equals(box.tuple.format(tuple), f:totable())
+    t.assert_equals(box.tuple.format(tuple), tuple:format())
+end
+
+-- Checks that `tuple:format` and `box.tuple.format` work as expected
+-- without set format.
+g.test_tuple_format_with_no_format = function()
+    local tuple = box.tuple.new({1, 'Flint'})
+    t.assert_equals(type(tuple:format()), 'table')
+    t.assert_equals(tuple:format(), {})
+
+    t.assert_equals(type(box.tuple.format(tuple)), 'table')
+    t.assert_equals(box.tuple.format(tuple), {})
+end
-- 
GitLab