From e9fd47c49649201cd2d98e2dc7558d04041cec3e Mon Sep 17 00:00:00 2001 From: boseji Date: Thu, 5 Sep 2024 09:11:50 +0530 Subject: [PATCH] =?UTF-8?q?=E0=A4=B5=E0=A5=8D=E0=A4=AF=E0=A4=B5=E0=A4=B8?= =?UTF-8?q?=E0=A5=8D=E0=A4=A5=E0=A4=BE=E0=A4=AA=E0=A4=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE.txt | 202 ++++++++++++++++++++++++++++++ README.md | 132 ++++++++++++++++++++ archived-old-code.tar | Bin 0 -> 136192 bytes doc.go | 146 ++++++++++++++++++++++ example_Discover_test.go | 105 ++++++++++++++++ example_Protocol_test.go | 132 ++++++++++++++++++++ example_Specify_test.go | 109 ++++++++++++++++ go.mod | 3 + org.go | 129 +++++++++++++++++++ org_test.go | 215 ++++++++++++++++++++++++++++++++ tppi.go | 114 +++++++++++++++++ tppi_test.go | 261 +++++++++++++++++++++++++++++++++++++++ 12 files changed, 1548 insertions(+) create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 archived-old-code.tar create mode 100644 doc.go create mode 100644 example_Discover_test.go create mode 100644 example_Protocol_test.go create mode 100644 example_Specify_test.go create mode 100644 go.mod create mode 100644 org.go create mode 100644 org_test.go create mode 100644 tppi.go create mode 100644 tppi_test.go diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..70d8a9b --- /dev/null +++ b/README.md @@ -0,0 +1,132 @@ +> +> ॐ भूर्भुवः स्वः +> +> तत्स॑वि॒तुर्वरे॑ण्यं॒ +> +> भर्गो॑ दे॒वस्य॑ धीमहि। +> +> धियो॒ यो नः॑ प्रचो॒दया॑त्॥ +> + +# बोसजी के द्वारा रचित टिप्पी अधिलेखन प्रकृया। + +> एक सरल संचार सहायक और संलग्न तंत्र। +> + +***एक रचनात्मक भारतीय उत्पाद।*** + +## `tppi` stands for Tilde Pipe Plus Interface + +> A simple communication helper and enclosing mechanism called **TPPI protocol**. + +There are two parts to this protocol: + +- *Organization* - Build the compatible data representation called **TPPI content**. +- *Encapsulation* - Prepare a compatible packet with necessary safeguards called **TPPI packet**. + +This is **string oriented protocol** with *special safeguards* for **TPPI protocol.** + +The following symbols are considered special to **TPPI protocol**: + +- `~` Tilde Symbol +- `|` Pipe Symbol +- `+` Plus Symbol + +These *symbol are replaced* by the following as *safeguards* for **TPPI packet**: + +- `|` converts to `\\x7C` +- `+` converts to `\\x2B` + +These *symbols are replaced* for *safeguards* in data specified for **TPPI contents** : + + `~` converts to `%7E` + `|` converts to `%7C` + `+` converts to `%2B` + +The **TPPI protocol** contains special annotations: + +- `~|` is used to indicate the **start** of the **TPPI Packet** +- `|~` is used to indicate the **end** of the **TPPI Packet** +- `|` is used to separate the **TPPI contents** in a **TPPI Packet** +- `~` are used to organize the **TPPI contents** with Type, tags, & data. +- `~||~` indicates a **TPPI packet** *without any contents* +- `+` is used to join TPPI packets together. + +Collection rule for TPPI packets: + +- If **TPPI packet** are sent one at a time then no special addition is needed. +- In case *collection* of **TPPI packets** need to be sent then `+` symbol is used. + +Rules for **TPPI content**: + +- The *content mist be filtered* with necessary *safeguards*. +- **Type Signature**: Each **TPPI content** must have a **Type Signature** that tells what type of data it contains and helps in *Discovery process* later. In case no **Type Signature** is provided `UN` would be used to indicate `unknown` Type`. + **Type Signature** can't be left blank and recommended to be added. +- **Tag**: Each **TPPI content** can have a string name or tag that can help better identify the data enclosed. This would later be used for *Discovery process*. This is an *Optional field* and can be omitted in the **TPPI content**. +- **Data**: The **TPPI content** encloses the data provided in *string form*, that can later be retrieved using the *Discovery process*. +- The fields **Type Signature**, *(optional)* **Tag** and **Data** are separated by `~` Symbol in a **TPPI content**. + +**TPPI Content Processes**: + +- **Specify**: In this process the **Type Signature**, *(optional)* **Tag** and **Data** are joined together in **TPPI Content** form. This follows the rules for **TPPI content**. This typically happens before preparing the **TPPI packet**. +- **Discover**: In this process the **Type Signature**, *(optional)* **Tag** and **Data** are recovered from the supplied **TPPI Content**. This follows the same rules for **TPPI content** in order to find the enclosed data. This is typically done after receiving the TPPI packet and getting the **TPPI contents**. + +**TPPI Packet Processes**: + +- **Assemble**: In this process the **TPPI contents** are *filtered* and *joined together* into a **TPPI packet** following respective rules. This however *should not be used* for Joining *multiple TPPI packets*. +- **Disassemble**: In this process the incoming **TPPI packet** is broken into into multiple **TPPI contents** with *filtering/safeguards* removed. This however *should not be used* directly on the *Incoming TPPI packet* as *it would not be able to split apart TPPI packets*. +- **Join**: This is the process of *Joining multiple packets* before sending over the **combined TPPI packets**. +- **Split**: This is process perform as soon as the **TPPI packets** are received. *Only after this the process* of *Disassembly can begin*. + +*Background behind the name:* + +> The name `tppi` has been taken from a story of an imaginative kid +> discovering the mysteries of the Universe and the world all around +> from a remote village in the heart of Karnataka, Bharat(India). +> + +## कार्यविधि - Usage + +Include into a project: + +```sh +go get github.com/boseji/go-tppi@latest +``` + +Usage in Program: + +```go +package main + +import ( + "fmt" + "github.com/boseji/go-tppi" +) + +func main() { + fmt.Println(tppi.Assemble("Hari Aum")) + + // Output: + // ~|Hari Aum|~ +} +``` + +## License + +`SPDX: Apache-2.0` + +`tppi` stands for Tilde Pipe Plus Interface + +Copyright (C) 2024 Abhijit Bose (aka. Boseji). All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/archived-old-code.tar b/archived-old-code.tar new file mode 100644 index 0000000000000000000000000000000000000000..d6b7e998a40d73a873900e6b4a20268eb4201b02 GIT binary patch literal 136192 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 WEvanU6x(26gMkeOHW;`!82Eo5RI$kb literal 0 HcmV?d00001 diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..ab8bd88 --- /dev/null +++ b/doc.go @@ -0,0 +1,146 @@ +// +// ॐ भूर्भुवः स्वः +// तत्स॑वि॒तुर्वरे॑ण्यं॒ +// भर्गो॑ दे॒वस्य॑ धीमहि। +// धियो॒ यो नः॑ प्रचो॒दया॑त्॥ +// +// +// बोसजी के द्वारा रचित टिप्पी अधिलेखन प्रकृया। +// ================================ +// +// एक सरल संचार सहायक और संलग्न तंत्र। +// +// ~~~~~~~~~~~~~~~~~~~~~~~ +// एक रचनात्मक भारतीय उत्पाद। +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// +// Sources +// -------- +// https://github.com/boseji/go-tppi +// +// +// License +// -------- +// +// SPDX: Apache-2.0 +// +// Copyright (C) 2024 Abhijit Bose (aka. Boseji). All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX short identifier: Apache-2.0 + +// ॐ भूर्भुवः स्वः +// तत्स॑वि॒तुर्वरे॑ण्यं॒ +// भर्गो॑ दे॒वस्य॑ धीमहि। +// धियो॒ यो नः॑ प्रचो॒दया॑त्॥ +// +// बोसजी के द्वारा रचित टिप्पी अधिलेखन प्रकृया। +// ================================= +// +// एक सरल संचार सहायक और संलग्न तंत्र। +// +// ~~~~~~~~~~~~~~~~~~~~~~~ +// एक रचनात्मक भारतीय उत्पाद। +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// tppi stands for Tilde Pipe Plus Interface, +// A simple communication helper and enclosing mechanism called TPPI protocol. +// +// There are two parts to this protocol: +// +// Organization - Build the compatible data representation called **TPPI content**. +// Encapsulation - Prepare a compatible packet with necessary safeguards called **TPPI packet**. +// +// This is string oriented protocol with special safeguards for TPPI protocol. +// +// The following symbols are considered special to TPPI protocol: +// +// `~` Tilde Symbol +// `|` Pipe Symbol +// `+` Plus Symbol +// +// These symbol are replaced by the following as safeguards for TPPI packet: +// +// `|` converts to `\\x7C` +// `+` converts to `\\x2B` +// +// These symbols are replaced for safeguards in data specified for +// TPPI contents : +// +// `~` converts to `%7E` +// `|` converts to `%7C` +// `+` converts to `%2B` +// +// The TPPI protocol contains special annotations: +// +// `~|` is used to indicate the **start** of the TPPI Packet +// `|~` is used to indicate the **end** of the TPPI Packet +// `|` is used to separate the TPPI contents in a TPPI Packet +// `~` are used to organize the TPPI contents with Type, tags, & data. +// `~||~` indicates a TPPI packet without any contents +// `+` is used to join TPPI packets together. +// +// Collection rule for TPPI packets: +// +// If TPPI packet are sent one at a time then no special addition is needed. +// In case collection of TPPI packets need to be sent then `+` symbol is used. +// +// Rules for TPPI content: +// +// The content mist be filtered with necessary safeguards. +// Type Signature : Each TPPI content must have a Type Signature that tells +// what type of data it contains and helps in Discovery process later. +// In case no Type Signature is provided `UN` would be used to indicate +// `unknown` Type`. +// Type Signature can't be left blank and recommended to be added. +// Tag : Each TPPI content can have a string name or tag that can help better +// identify the data enclosed. This would later be used for Discovery +// process. This is an Optional field and can be omitted in the +// TPPI content. +// Data: The TPPI content encloses the data provided in string form, that +// can later be retrieved using the Discovery process. +// The fields Type Signature, (optional) Tag and Data are separated +// by `~` Symbol in a TPPI content. +// +// TPPI Content Processes: +// +// Specify : In this process the Type Signature, (optional) Tag and Data are +// joined together in TPPI Content form. This follows the rules +// for TPPI content. This typically happens before preparing the +// TPPI packet. +// Discover : In this process the Type Signature, (optional) Tag and Data are +// recovered from the supplied TPPI Content. This follows the same rules +// for TPPI content in order to find the enclosed data. This is typically +// done after receiving the TPPI packet and getting the TPPI contents. +// +// TPPI Packet Processes: +// +// Assemble : In this process the TPPI contents are filtered and joined +// together into a TPPI packet following respective rules. +// This however should not be used for Joining multiple TPPI packets. +// Disassemble : In this process the incoming TPPI packet is broken into +// into multiple TPPI contents with filtering removed. +// This however should not be used directly on the Incoming TPPI packet +// as it would not be able to split apart TPPI packets. +// Join : This is the process of Joining multiple packets before sending over +// the combined TPPI packets. +// Split : This is process perform as soon as the TPPI packets are received. +// Only after this the process of Disassembly can begin. +// +// Background behind the name: +// +// The name `tppi` has been taken from a story of an imaginative kid +// discovering the mysteries of the Universe and the world all around +// from a remote village in the heart of Karnataka, Bharat(India). +package tppi diff --git a/example_Discover_test.go b/example_Discover_test.go new file mode 100644 index 0000000..7d5b152 --- /dev/null +++ b/example_Discover_test.go @@ -0,0 +1,105 @@ +// +// ॐ भूर्भुवः स्वः +// तत्स॑वि॒तुर्वरे॑ण्यं॒ +// भर्गो॑ दे॒वस्य॑ धीमहि। +// धियो॒ यो नः॑ प्रचो॒दया॑त्॥ +// +// +// बोसजी के द्वारा रचित टिप्पी अधिलेखन प्रकृया। +// ================================ +// +// एक सरल संचार सहायक और संलग्न तंत्र। +// +// ~~~~~~~~~~~~~~~~~~~~~~~ +// एक रचनात्मक भारतीय उत्पाद। +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// +// Sources +// -------- +// https://github.com/boseji/go-tppi +// +// +// License +// -------- +// +// SPDX: Apache-2.0 +// +// Copyright (C) 2024 Abhijit Bose (aka. Boseji). All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX short identifier: Apache-2.0 + +package tppi_test + +import ( + "fmt" + "strings" + + "guthub.com/boseji/go-tppi" +) + +func (b *myBool) Recover(Type, Tag, Data string) error { + if Type != myBoolTypeSignature { + return fmt.Errorf("invalid bool type") + } + b.Tag = Tag + if Data == myBoolDataTrue { + b.bool = true + } else if Data == myBoolDataFalse { + b.bool = false + } else { + return fmt.Errorf("invalid bool data") + } + return nil +} + +func (m *myStruct) Recover(Type, Tag, Data string) (err error) { + if Type != myStructTypeSig { + return fmt.Errorf("wrong type supplied") + } + _ = Tag + sa := strings.Split(Data, " ") + if len(sa) != 3 { + return fmt.Errorf("malformed data") + } + + _, err = fmt.Sscanf(Data, myStructFormat, &m.TimeStamp, &m.Value, &m.Topic) + return +} + +func ExampleDiscover() { + b := myBool{} + srcBool := "B~bit%7E5~1" + err := tppi.Discover(srcBool, b.Recover) + if err != nil { + fmt.Printf("failed to Discover - %v\n", err) + return + } + fmt.Printf("%q Transforms back %#v\n", srcBool, b) + + m := myStruct{} + srcStruct := "MSTR~myStruct~134000000 12.345000 \"sensor/12\"" + err = tppi.Discover(srcStruct, m.Recover) + if err != nil { + fmt.Printf("failed to Discover - %v\n", err) + return + } + fmt.Printf("%q Transforms back\n", srcStruct) + fmt.Printf("%#v", m) + + // Output: + // "B~bit%7E5~1" Transforms back tppi_test.myBool{bool:true, Tag:"bit~5"} + // "MSTR~myStruct~134000000 12.345000 \"sensor/12\"" Transforms back + // tppi_test.myStruct{TimeStamp:134000000, Value:12.345, Topic:"sensor/12"} +} diff --git a/example_Protocol_test.go b/example_Protocol_test.go new file mode 100644 index 0000000..6727349 --- /dev/null +++ b/example_Protocol_test.go @@ -0,0 +1,132 @@ +// +// ॐ भूर्भुवः स्वः +// तत्स॑वि॒तुर्वरे॑ण्यं॒ +// भर्गो॑ दे॒वस्य॑ धीमहि। +// धियो॒ यो नः॑ प्रचो॒दया॑त्॥ +// +// +// बोसजी के द्वारा रचित टिप्पी अधिलेखन प्रकृया। +// ================================ +// +// एक सरल संचार सहायक और संलग्न तंत्र। +// +// ~~~~~~~~~~~~~~~~~~~~~~~ +// एक रचनात्मक भारतीय उत्पाद। +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// +// Sources +// -------- +// https://github.com/boseji/go-tppi +// +// +// License +// -------- +// +// SPDX: Apache-2.0 +// +// Copyright (C) 2024 Abhijit Bose (aka. Boseji). All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX short identifier: Apache-2.0 + +package tppi_test + +import ( + "fmt" + + "guthub.com/boseji/go-tppi" +) + +func ExampleAssemble() { + ba := []myBool{ + {true, "Bit~0"}, + {false, "Bit-1"}, + {false, "Bit-2"}, + {true, "Bit-3"}, + } + sa := make([]string, 0, len(ba)) + for _, b := range ba { + sa = append(sa, tppi.Specify(b.Type(), b.Tag, b.String)) + } + s := tppi.Assemble(sa...) + s = tppi.PacketJoin(s) + fmt.Println(s) + + // Output: + // ~|B~Bit%7E0~1|B~Bit-1~0|B~Bit-2~0|B~Bit-3~1|~ +} + +func ExampleDisassemble() { + ba := make([]bool, 4) + ta := make([]string, 4) + srcBool := "~|B~Bit%7E0~1|B~Bit-1~0|B~Bit-2~0|B~Bit-3~1|~" + s := tppi.SplitPacket(srcBool) + if len(s) != 1 { + fmt.Println("expected it to 1 packet") + return + } + sa := tppi.Disassemble(s[0]) + if len(sa) != len(ba) { + fmt.Println("Packet does not contain enough data") + return + } + for i, s := range sa { + err := tppi.Discover(s, func(s1, s2, s3 string) error { + if s1 != "B" { + return fmt.Errorf("invalid type Signature") + } + ta[i] = s2 + switch s3 { + case "1": + ba[i] = true + case "0": + ba[i] = false + default: + return fmt.Errorf("invalid value") + } + return nil + }) + if err != nil { + fmt.Println("unable to discover -", err) + return + } + } + fmt.Println(ba) + fmt.Println(ta) + + // Output: + // [true false false true] + // [Bit~0 Bit-1 Bit-2 Bit-3] +} + +func ExamplePacketJoin() { + sa := []string{ + "~|B~Bit%7E0~1|B~Bit-1~0|B~Bit-2~0|B~Bit-3~1|~", + "~|S~Bit Wise Data|~", + } + s := tppi.PacketJoin(sa...) + fmt.Println(s) + + // Output: + // ~|B~Bit%7E0~1|B~Bit-1~0|B~Bit-2~0|B~Bit-3~1|~+~|S~Bit Wise Data|~ +} + +func ExampleSplitPacket() { + s := "~|B~Bit%7E0~1|B~Bit-1~0|B~Bit-2~0|B~Bit-3~1|~+~|S~Bit Wise Data|~" + sa := tppi.SplitPacket(s) + fmt.Println(sa) + + // Output: + // [~|B~Bit%7E0~1|B~Bit-1~0|B~Bit-2~0|B~Bit-3~1|~ ~|S~Bit Wise Data|~] +} diff --git a/example_Specify_test.go b/example_Specify_test.go new file mode 100644 index 0000000..f30f0f3 --- /dev/null +++ b/example_Specify_test.go @@ -0,0 +1,109 @@ +// +// ॐ भूर्भुवः स्वः +// तत्स॑वि॒तुर्वरे॑ण्यं॒ +// भर्गो॑ दे॒वस्य॑ धीमहि। +// धियो॒ यो नः॑ प्रचो॒दया॑त्॥ +// +// +// बोसजी के द्वारा रचित टिप्पी अधिलेखन प्रकृया। +// ================================ +// +// एक सरल संचार सहायक और संलग्न तंत्र। +// +// ~~~~~~~~~~~~~~~~~~~~~~~ +// एक रचनात्मक भारतीय उत्पाद। +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// +// Sources +// -------- +// https://github.com/boseji/go-tppi +// +// +// License +// -------- +// +// SPDX: Apache-2.0 +// +// Copyright (C) 2024 Abhijit Bose (aka. Boseji). All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX short identifier: Apache-2.0 + +package tppi_test + +import ( + "fmt" + "time" + + "guthub.com/boseji/go-tppi" +) + +type myBool struct { + bool + Tag string +} + +const ( + myBoolTypeSignature = "B" + myBoolDataTrue = "1" + myBoolDataFalse = "0" +) + +func (b myBool) Type() string { + return myBoolTypeSignature +} + +func (b myBool) String() string { + if b.bool { + return myBoolDataTrue + } + return myBoolDataFalse +} + +type myStruct struct { + TimeStamp time.Duration + Value float32 + Topic string +} + +const ( + myStructTypeSig = "MSTR" + myStructFormat = "%d %f %q" +) + +func (m myStruct) Type() string { + return myStructTypeSig +} + +func (m myStruct) String() string { + return fmt.Sprintf(myStructFormat, m.TimeStamp, m.Value, m.Topic) +} + +func ExampleSpecify() { + b := myBool{true, "bit~5"} + s := tppi.Specify(b.Type(), b.Tag, b.String) + fmt.Printf("%#v transforms into %q\n", b, s) + + m := myStruct{134 * time.Millisecond, 12.345, "sensor/12"} + sa := tppi.Specify(m.Type(), "myStruct", m.String) + fmt.Printf("%#v\n", m) + fmt.Println("transforms into") + fmt.Println(sa) + + // Output: + // tppi_test.myBool{bool:true, Tag:"bit~5"} transforms into "B~bit%7E5~1" + // tppi_test.myStruct{TimeStamp:134000000, Value:12.345, Topic:"sensor/12"} + // transforms into + // MSTR~myStruct~134000000 12.345000 "sensor/12" +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b6f422d --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module guthub.com/boseji/go-tppi + +go 1.22.6 diff --git a/org.go b/org.go new file mode 100644 index 0000000..5adbdaa --- /dev/null +++ b/org.go @@ -0,0 +1,129 @@ +// +// ॐ भूर्भुवः स्वः +// तत्स॑वि॒तुर्वरे॑ण्यं॒ +// भर्गो॑ दे॒वस्य॑ धीमहि। +// धियो॒ यो नः॑ प्रचो॒दया॑त्॥ +// +// +// बोसजी के द्वारा रचित टिप्पी अधिलेखन प्रकृया। +// ================================ +// +// एक सरल संचार सहायक और संलग्न तंत्र। +// +// ~~~~~~~~~~~~~~~~~~~~~~~ +// एक रचनात्मक भारतीय उत्पाद। +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// +// Sources +// -------- +// https://github.com/boseji/go-tppi +// +// +// License +// -------- +// +// SPDX: Apache-2.0 +// +// Copyright (C) 2024 Abhijit Bose (aka. Boseji). All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX short identifier: Apache-2.0 + +package tppi + +import ( + "fmt" + "strings" +) + +// Specify helps to organize the data to create TPPI content. +// Here we need the Type Signature, an optional Tag and a function, that +// returns the string form of the Data to be enclosed in the TPPI content. +// The Type, Tag and Data is filtered with TPPI content safeguards before being +// processed into the TPPI content form. The data function has the signature +// `func() string`, this only gives one value considered as the string +// string representation of the data. +func Specify(typeSignature, tag string, fn func() string) string { + ta := make([]string, 0, 3) + if len(typeSignature) == 0 { + typeSignature = "UN" + } + ta = append(ta, typeSignature) + if len(tag) > 0 { + ta = append(ta, tag) + } + if fn != nil { + ta = append(ta, fn()) + } else { + return "" // We can't do much without this function + } + // Filter out the Data with TPPI content safeguards + sa := make([]string, 0, len(ta)) + for _, i := range ta { + i = strings.ReplaceAll(i, "|", "%7C") + i = strings.ReplaceAll(i, "+", "%2B") + i = strings.ReplaceAll(i, "~", "%7E") + sa = append(sa, i) + } + s := strings.Join(sa, "~") + return s +} + +// Discover helps to recover the data from the TPPI content. +// In order to begin the operation we need the TPPI content in string form. +// We also need a function that would be called with the discovered +// Type Signature, Tag and Data in string form. This function helps +// to recreate the data from the String form enclosed earlier in the +// TPPI content form. Here is the signature of the function +// `func(TypeSignature, Tag, Data string) error`. The function can also +// return an error indicating that the data or any of the parameters +// are invalid or damaged. The supplied Type Signature, Tag and Data are +// restored from the fileted TPPI content safeguards before calling the +// function. +func Discover(s string, fn func(string, string, string) error) (err error) { + if len(s) == 0 || fn == nil { + err = fmt.Errorf("wrong inputs for discover operation") + return + } + if strings.ContainsAny(s, "|+") { + err = fmt.Errorf("wrong data or damage can't discover") + return + } + // Split with Separator + sa := strings.Split(s, "~") + // Filter + if len(sa) > 1 { + for i, s := range sa { + s = strings.ReplaceAll(s, "%7C", "|") + s = strings.ReplaceAll(s, "%2B", "+") + s = strings.ReplaceAll(s, "%7E", "~") + sa[i] = s + } + } + // Check + switch len(sa) { + case 2: + err = fn(sa[0], "", sa[1]) + case 3: + err = fn(sa[0], sa[1], sa[2]) + default: + err = fmt.Errorf("invalid data discovered") + } + if err != nil { + err = fmt.Errorf("failed to discover due to process error - %v", + err) + return + } + return +} diff --git a/org_test.go b/org_test.go new file mode 100644 index 0000000..ddb2114 --- /dev/null +++ b/org_test.go @@ -0,0 +1,215 @@ +// +// ॐ भूर्भुवः स्वः +// तत्स॑वि॒तुर्वरे॑ण्यं॒ +// भर्गो॑ दे॒वस्य॑ धीमहि। +// धियो॒ यो नः॑ प्रचो॒दया॑त्॥ +// +// +// बोसजी के द्वारा रचित टिप्पी अधिलेखन प्रकृया। +// ================================ +// +// एक सरल संचार सहायक और संलग्न तंत्र। +// +// ~~~~~~~~~~~~~~~~~~~~~~~ +// एक रचनात्मक भारतीय उत्पाद। +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// +// Sources +// -------- +// https://github.com/boseji/go-tppi +// +// +// License +// -------- +// +// SPDX: Apache-2.0 +// +// Copyright (C) 2024 Abhijit Bose (aka. Boseji). All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX short identifier: Apache-2.0 + +package tppi + +import ( + "fmt" + "reflect" + "strings" + "testing" +) + +func Test_Specify(t *testing.T) { + type args struct { + typeSig string + tag string + sfn func() string + } + tests := []struct { + name string + args args + wantS string + }{ + { + name: "String Type", + args: args{ + typeSig: "S", + tag: "", + sfn: func() string { + return "Test String" + }, + }, + wantS: "S~Test String", + }, + { + name: "String with tag", + args: args{ + typeSig: "S", + tag: "value", + sfn: func() string { + return "Test String" + }, + }, + wantS: "S~value~Test String", + }, + { + name: "String with no function", + args: args{ + typeSig: "S", + tag: "", + sfn: nil, + }, + wantS: "", + }, + { + name: "String without type and Tag", + args: args{ + typeSig: "", + tag: "Testing", + sfn: func() string { + return "Test String" + }, + }, + wantS: "UN~Testing~Test String", + }, + { + name: "String with special Characters", + args: args{ + typeSig: "S", + tag: "", + sfn: func() string { + return "Test String+ with| several~Special Characters" + }, + }, + wantS: "S~Test String%2B with%7C several%7ESpecial Characters", + }, + { + name: "Int Type", + args: args{ + typeSig: "I", + tag: "", + sfn: func() string { + return fmt.Sprintf("%x", 0x3508) + }, + }, + wantS: "I~3508", + }, + { + name: "Error Type", + args: args{ + typeSig: "E", + tag: "", + sfn: func() string { + return fmt.Errorf("custom Error").Error() + }, + }, + wantS: "E~custom Error", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Specify(tt.args.typeSig, tt.args.tag, tt.args.sfn) + if strings.Compare(got, tt.wantS) != 0 { + t.Errorf("error in values got %v want %v", got, tt.wantS) + } + }) + } +} + +func Test_Discover(t *testing.T) { + tests := []struct { + name string + arg string + wantSa []string + wantErr bool + wantInnerError bool + }{ + { + name: "String Type", + arg: "S~Test String", + wantSa: []string{"S", "", "Test String"}, + }, + { + name: "String with tag", + arg: "S~value~Test String", + wantSa: []string{"S", "value", "Test String"}, + }, + { + name: "String with tag with special chars", + arg: "S~value%7C4~Test String", + wantSa: []string{"S", "value|4", "Test String"}, + }, + { + name: "Negative empty string", + arg: "", + wantErr: true, + }, + { + name: "Negative Inner Error string", + arg: "S~value~Test String", + wantErr: true, + wantInnerError: true, + }, + { + name: "Negative corrupt string", + arg: "S~val|ue~Test Str+ing", + wantErr: true, + }, + { + name: "Negative corrupt string2", + arg: "S value Test String", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sa := make([]string, 0, 3) + absorb := func(s1, s2, s3 string) error { + sa = append(sa, s1, s2, s3) + if tt.wantInnerError { + return fmt.Errorf("inner error") + } + return nil + } + err := Discover(tt.arg, absorb) + if (err != nil) != tt.wantErr { + t.Errorf("error in Discover() - %v want error %v", + err, tt.wantErr) + return + } + if !tt.wantErr && !reflect.DeepEqual(tt.wantSa, sa) { + t.Errorf("error in values got %v want %v", sa, tt.wantSa) + } + }) + } +} diff --git a/tppi.go b/tppi.go new file mode 100644 index 0000000..ce858a4 --- /dev/null +++ b/tppi.go @@ -0,0 +1,114 @@ +// +// ॐ भूर्भुवः स्वः +// तत्स॑वि॒तुर्वरे॑ण्यं॒ +// भर्गो॑ दे॒वस्य॑ धीमहि। +// धियो॒ यो नः॑ प्रचो॒दया॑त्॥ +// +// +// बोसजी के द्वारा रचित टिप्पी अधिलेखन प्रकृया। +// ================================ +// +// एक सरल संचार सहायक और संलग्न तंत्र। +// +// ~~~~~~~~~~~~~~~~~~~~~~~ +// एक रचनात्मक भारतीय उत्पाद। +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// +// Sources +// -------- +// https://github.com/boseji/go-tppi +// +// +// License +// -------- +// +// SPDX: Apache-2.0 +// +// Copyright (C) 2024 Abhijit Bose (aka. Boseji). All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX short identifier: Apache-2.0 + +package tppi + +import ( + "fmt" + "strings" +) + +// Assemble encloses the TPPI contents into the TPPI packet with safeguards. +func Assemble(sa ...string) (s string) { + for _, i := range sa { + // Filter out data with TPPI packet safeguard + i = strings.ReplaceAll(i, "|", "\\x7C") + i = strings.ReplaceAll(i, "+", "\\x2B") + //i = strings.ReplaceAll(i, "~", "\\x7E") + s += i + "|" + } + // Remove the last Pipe Symbol if content is provided + s, _ = strings.CutSuffix(s, "|") + s = "~|" + s + "|~" + return +} + +// Disassemble restores collection of TPPI Contents from the enclosed +// TPPI packet removing the necessary safeguards. +func Disassemble(s string) (sa []string) { + t, _ := strings.CutPrefix(s, "~|") + t, _ = strings.CutSuffix(t, "|~") + ta := strings.Split(t, "|") + sa = make([]string, 0, len(ta)) + for _, i := range ta { + i = strings.ReplaceAll(i, "\\x7C", "|") + i = strings.ReplaceAll(i, "\\x2B", "+") + //i = strings.ReplaceAll(i, "\\x7E", "~") + sa = append(sa, i) + } + return +} + +// ValidPacket verifies if the packet indeed conforms to TPPI protocol or not. +// It also validates the TPPI contents to follow the required guidelines. +func ValidPacket(s string) (err error) { + var sp []string + // Multi Protocol Strings + if strings.Contains(s, "+") { + sp = strings.Split(s, "+") + } else { + sp = append(sp, s) + } + for _, p := range sp { + if !strings.HasPrefix(p, "~|") { + err = fmt.Errorf("not valid packet as missing start indicator") + return + } + if !strings.HasSuffix(p, "|~") { + err = fmt.Errorf("not valid packet as missing end indicator") + } + } + return +} + +// PacketJoin helps to join multiple TPPI packets together. +// This can optionally be run after the Assemble function to bring together +// Multiple TPPI packets together. +func PacketJoin(sa ...string) string { + return strings.Join(sa, "+") +} + +// SplitPacket allows to get back multiple TPPI packets. +// This needs to be run before running the Disassemble function. +func SplitPacket(s string) []string { + return strings.Split(s, "+") +} diff --git a/tppi_test.go b/tppi_test.go new file mode 100644 index 0000000..496f172 --- /dev/null +++ b/tppi_test.go @@ -0,0 +1,261 @@ +// +// ॐ भूर्भुवः स्वः +// तत्स॑वि॒तुर्वरे॑ण्यं॒ +// भर्गो॑ दे॒वस्य॑ धीमहि। +// धियो॒ यो नः॑ प्रचो॒दया॑त्॥ +// +// +// बोसजी के द्वारा रचित टिप्पी अधिलेखन प्रकृया। +// ================================ +// +// एक सरल संचार सहायक और संलग्न तंत्र। +// +// ~~~~~~~~~~~~~~~~~~~~~~~ +// एक रचनात्मक भारतीय उत्पाद। +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// +// Sources +// -------- +// https://github.com/boseji/go-tppi +// +// +// License +// -------- +// +// SPDX: Apache-2.0 +// +// Copyright (C) 2024 Abhijit Bose (aka. Boseji). All rights reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX short identifier: Apache-2.0 + +package tppi + +import ( + "reflect" + "strings" + "testing" +) + +func Test_Assemble(t *testing.T) { + tests := []struct { + name string + args []string + wantS string + }{ + { + name: "Basic test of Assembly", + args: []string{ + "P1", + "P2", + }, + wantS: "~|P1|P2|~", + }, + { + name: "Assemble string containing special chars", + args: []string{ + "P1|", + "P~2", + }, + wantS: "~|P1\\x7C|P~2|~", + }, + { + name: "Assemble string containing multiple special chars", + args: []string{ + "Pa~rt1|", + "P|+art~2", + }, + wantS: "~|Pa~rt1\\x7C|P\\x7C\\x2Bart~2|~", + }, + { + name: "Blank contents", + args: []string{}, + wantS: "~||~", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Assemble(tt.args...) + if strings.Compare(got, tt.wantS) != 0 { + t.Errorf("error in values got %v want %v", got, tt.wantS) + } + }) + } +} + +func Test_Disassemble(t *testing.T) { + tests := []struct { + name string + args string + wantSa []string + }{ + { + name: "Basic test of Assembly", + args: "~|P1|P2|~", + wantSa: []string{ + "P1", + "P2", + }, + }, + { + name: "Assemble string containing special chars", + args: "~|P1\\x7C|P~2|~", + wantSa: []string{ + "P1|", + "P~2", + }, + }, + { + name: "Assemble string containing multiple special chars", + args: "~|Pa~rt1\\x7C|P\\x7C\\x2Bart~2|~", + wantSa: []string{ + "Pa~rt1|", + "P|+art~2", + }, + }, + { + name: "Blank contents", + args: "", + wantSa: []string{""}, + }, + { + name: "Blank packet", + args: "~||~", + wantSa: []string{""}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Disassemble(tt.args) + if !reflect.DeepEqual(got, tt.wantSa) { + t.Errorf("error in values got %v want %v", got, tt.wantSa) + } + }) + } +} + +func Test_ValidPacket(t *testing.T) { + tests := []struct { + name string + arg string + wantErr bool + }{ + { + name: "Basic Single Packet", + arg: "~|Pa~rt1\\x7C|P\\x7C\\x2Bart~2|~", + }, + { + name: "Dual Packets", + arg: "~|P1\\x7C|P~2|~+~|Pa~rt1\\x7C|P\\x7C\\x2Bart~2|~", + }, + { + name: "Error Single Packet1", + arg: "~Pa~rt1\\x7C|P\\x7C\\x2Bart~2|~", + wantErr: true, + }, + { + name: "Error Single Packet2", + arg: "~|Pa~rt1\\x7C|P\\x7C\\x2Bart~2|", + wantErr: true, + }, + { + name: "Error Single Packet3", + arg: "~|Pa~rt1\\x7C|~P+\\x2Bart~2|~", + wantErr: true, + }, + { + name: "Error Single Packet4", + arg: "~|Pa~rt1\\x7C+P\\x7C\\x2Bart~2|~", + wantErr: true, + }, + { + name: "Error Single Packet5", + arg: "~|Pa~rt1\\x7C|~~|+~|P\\x7C\\x2Bart~2|~", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ValidPacket(tt.arg) + if (err != nil) != tt.wantErr { + t.Errorf("error ValidPacket() got %v want error %v", + err, tt.wantErr) + } + }) + } +} + +func Test_PacketJoin(t *testing.T) { + tests := []struct { + name string + args []string + wantS string + }{ + { + name: "Join a single packet", + args: []string{ + "~||~", + }, + wantS: "~||~", + }, + { + name: "Join two packet", + args: []string{ + "~||~", + "~|Pa~rt1\\x7C|P\\x7C\\x2Bart~2|~", + }, + wantS: "~||~+~|Pa~rt1\\x7C|P\\x7C\\x2Bart~2|~", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := PacketJoin(tt.args...) + if strings.Compare(got, tt.wantS) != 0 { + t.Errorf("error in values got %v want %v", got, tt.wantS) + } + }) + } +} + +func Test_PacketSplit(t *testing.T) { + tests := []struct { + name string + arg string + wantSa []string + }{ + { + name: "Join a single packet", + arg: "~||~", + wantSa: []string{ + "~||~", + }, + }, + { + name: "Join two packet", + arg: "~||~+~|Pa~rt1\\x7C|P\\x7C\\x2Bart~2|~", + wantSa: []string{ + "~||~", + "~|Pa~rt1\\x7C|P\\x7C\\x2Bart~2|~", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := SplitPacket(tt.arg) + if !reflect.DeepEqual(got, tt.wantSa) { + t.Errorf("error in values got %v want %v", got, tt.wantSa) + } + }) + } +}