Repository: Zapix/mtpylon (language: python)

Issue:
Load/Dump service schema Message 
Add cusom loaders/dumpers for Message schema

https://github.com/Zapix/mtpylon/blob/ed4b4c1b972fa0b7781254188c5b952fa5b724ed/mtpylon/service_schema/constructors/message.py#L9

- [ ] set the length in the bytes of body to message for dumpers.
- [ ] cut the length of loaded message


Discussion / hints:

Fix patch:
diff --git a/mtpylon/service_schema/__init__.py b/mtpylon/service_schema/__init__.py
index f569619..2d0bce3 100644
--- a/mtpylon/service_schema/__init__.py
+++ b/mtpylon/service_schema/__init__.py
@@ -1,85 +1,6 @@
 # -*- coding: utf-8 -*-
-from ..schema import Schema
-from .constructors import (
-    ResPQ,
-    P_Q_inner_data,
-    Server_DH_Params,
-    Server_DH_inner_data,
-    Client_DH_Inner_Data,
-    Set_client_DH_params_answer,
-    RpcResult,
-    RpcError,
-    RpcDropAnswer,
-    FutureSalt,
-    FutureSalts,
-    Pong,
-    NewSession,
-    Message,
-    MessageContainer,
-    MessageCopy,
-    MsgsAck,
-    BadMsgNotification,
-    MsgResendReq,
-    MsgsStateReq,
-    MsgsStateInfo,
-    MsgsAllInfo,
-    MsgDetailedInfo,
-    BindAuthKeyInner,
-    DestroyAuthKeyRes,
-    DestroySessionRes
-)
-from .functions import (
-    req_pq,
-    req_pq_multi,
-    req_DH_params,
-    set_client_DH_params,
-    rpc_drop_answer,
-    get_future_salts,
-    ping,
-    ping_delay_disconnect,
-    destroy_auth_key,
-    destroy_session
-)
+from .service_schema import service_schema
 
-service_schema = Schema(
-    constructors=[
-        ResPQ,
-        P_Q_inner_data,
-        Server_DH_Params,
-        Server_DH_inner_data,
-        Client_DH_Inner_Data,
-        Set_client_DH_params_answer,
-        RpcResult,
-        RpcError,
-        RpcDropAnswer,
-        FutureSalt,
-        FutureSalts,
-        Pong,
-        NewSession,
-        Message,
-        MessageContainer,
-        MessageCopy,
-        MsgsAck,
-        BadMsgNotification,
-        MsgResendReq,
-        MsgsStateReq,
-        MsgsStateInfo,
-        MsgsAllInfo,
-        MsgDetailedInfo,
-        BindAuthKeyInner,
-        DestroyAuthKeyRes,
-        DestroySessionRes,
-    ],
-    functions=[
-        req_pq,
-        req_pq_multi,
-        req_DH_params,
-        set_client_DH_params,
-        rpc_drop_answer,
-        get_future_salts,
-        ping,
-        ping_delay_disconnect,
-        destroy_auth_key,
-        destroy_session
-    ]
-)
+__all__ = [
+    'service_schema'
+]
diff --git a/mtpylon/service_schema/serialization/__init__.py b/mtpylon/service_schema/serialization/__init__.py
new file mode 100644
index 0000000..40a96af
--- /dev/null
+++ b/mtpylon/service_schema/serialization/__init__.py
@@ -0,0 +1,1 @@
+# -*- coding: utf-8 -*-
diff --git a/mtpylon/service_schema/serialization/message.py b/mtpylon/service_schema/serialization/message.py
new file mode 100644
index 0000000..9450743
--- /dev/null
+++ b/mtpylon/service_schema/serialization/message.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+from mtpylon.serialization.loaded import LoadedValue
+from mtpylon.serialization.int import dump as dump_int, load as load_int
+from mtpylon.serialization.long import dump as dump_long, load as load_long
+from mtpylon.serialization.object import (
+    dump as dump_object,
+    load as load_object
+)
+
+from ..service_schema import service_schema
+from ..constructors.message import Message
+
+
+def dump(value: Message) -> bytes:
+    data = service_schema[Message]
+
+    return (
+        dump_int(data.id) +
+        dump_long(value.msg_id) +
+        dump_int(value.seqno) +
+        dump_int(len(value.body)) +
+        dump_object(value.body)
+    )
+
+
+def load(input: bytes) -> LoadedValue[Message]:
+    msg_id = load_long(input[4:]).value
+    seqno = load_int(input[12:]).value
+    bytes_count = load_int(input[16:]).value
+    body = load_object(input[20:20 + bytes_count]).value
+
+    return LoadedValue(
+        value=Message(
+            msg_id=msg_id,
+            seqno=seqno,
+            bytes=bytes_count,
+            body=body
+        ),
+        offset=20 + bytes_count
+    )
diff --git a/mtpylon/service_schema/service_schema.py b/mtpylon/service_schema/service_schema.py
new file mode 100644
index 0000000..178f9a0
--- /dev/null
+++ b/mtpylon/service_schema/service_schema.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+from mtpylon import Schema
+from .constructors import (
+    ResPQ,
+    P_Q_inner_data,
+    Server_DH_Params,
+    Server_DH_inner_data,
+    Client_DH_Inner_Data,
+    Set_client_DH_params_answer,
+    RpcResult,
+    RpcError,
+    RpcDropAnswer,
+    FutureSalt,
+    FutureSalts,
+    Pong,
+    NewSession,
+    Message,
+    MessageContainer,
+    MessageCopy,
+    MsgsAck,
+    BadMsgNotification,
+    MsgResendReq,
+    MsgsStateReq,
+    MsgsStateInfo,
+    MsgsAllInfo,
+    MsgDetailedInfo,
+    BindAuthKeyInner,
+    DestroyAuthKeyRes,
+    DestroySessionRes
+)
+from .functions import (
+    req_pq,
+    req_pq_multi,
+    req_DH_params,
+    set_client_DH_params,
+    rpc_drop_answer,
+    get_future_salts,
+    ping,
+    ping_delay_disconnect,
+    destroy_auth_key,
+    destroy_session
+)
+
+service_schema = Schema(
+    constructors=[
+        ResPQ,
+        P_Q_inner_data,
+        Server_DH_Params,
+        Server_DH_inner_data,
+        Client_DH_Inner_Data,
+        Set_client_DH_params_answer,
+        RpcResult,
+        RpcError,
+        RpcDropAnswer,
+        FutureSalt,
+        FutureSalts,
+        Pong,
+        NewSession,
+        Message,
+        MessageContainer,
+        MessageCopy,
+        MsgsAck,
+        BadMsgNotification,
+        MsgResendReq,
+        MsgsStateReq,
+        MsgsStateInfo,
+        MsgsAllInfo,
+        MsgDetailedInfo,
+        BindAuthKeyInner,
+        DestroyAuthKeyRes,
+        DestroySessionRes,
+    ],
+    functions=[
+        req_pq,
+        req_pq_multi,
+        req_DH_params,
+        set_client_DH_params,
+        rpc_drop_answer,
+        get_future_salts,
+        ping,
+        ping_delay_disconnect,
+        destroy_auth_key,
+        destroy_session
+    ]
+)


Test patch:
diff --git a/tests/test_service_schema/__init__.py b/tests/test_service_schema/__init__.py
new file mode 100644
index 0000000..40a96af
--- /dev/null
+++ b/tests/test_service_schema/__init__.py
@@ -0,0 +1,1 @@
+# -*- coding: utf-8 -*-
diff --git a/tests/test_service_schema/test_serialization/__init__.py b/tests/test_service_schema/test_serialization/__init__.py
new file mode 100644
index 0000000..40a96af
--- /dev/null
+++ b/tests/test_service_schema/test_serialization/__init__.py
@@ -0,0 +1,1 @@
+# -*- coding: utf-8 -*-
diff --git a/tests/test_service_schema/test_serialization/test_message.py b/tests/test_service_schema/test_serialization/test_message.py
new file mode 100644
index 0000000..ea65f0d
--- /dev/null
+++ b/tests/test_service_schema/test_serialization/test_message.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+from mtpylon.utils import long
+from mtpylon.service_schema.constructors import Message
+from mtpylon.service_schema.serialization.message import load, dump
+
+message = Message(
+    msg_id=long(32),
+    seqno=0,
+    bytes=14,
+    body=b'dumped message'
+)
+
+dumped_message = (
+    b'\x11\xe5\xb8[' +
+    b'\x20\x00\x00\x00\x00\x00\x00\x00' +
+    b'\x00\x00\x00\x00' +
+    b'\x0e\x00\x00\x00' +
+    b'dumped message'
+)
+
+
+def test_dump_message():
+    assert dump(message) == dumped_message
+
+
+def test_load_message():
+    loaded = load(dumped_message + b'prefix that should be skipped')
+
+    assert loaded.offset == len(dumped_message)
+    assert loaded.value == message
diff --git a/tests/test_service_schema.py b/tests/test_service_schema/test_service_schema.py
similarity index 99%
rename from tests/test_service_schema.py
rename to tests/test_service_schema/test_service_schema.py
index c35a4ca..2b90ff4 100644
--- a/tests/test_service_schema.py
+++ b/tests/test_service_schema/test_service_schema.py
@@ -3,7 +3,7 @@
 
 import pytest
 
-from mtpylon.service_schema import service_schema
+from mtpylon.service_schema.service_schema import service_schema
 from mtpylon.schema import CombinatorData, FunctionData